Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

protocol for 2 wire bus

Status
Not open for further replies.

Dr_Doggy

Well-Known Member
1 host, multiple slaves

I want to use the PICs ICSP bus for 2 wire(max) comm line after programming, so i will program chip, remove from pickit then put header on to master

Most code found is for when im using proper spi pins, but since i want to put it on b6&7 i can not use the program registers commonly used in the examples i am finding....

so first, what protocol should i use, maybe there is something better i didn't think of, rite now im thinking of 1-wire or i2c(more familar with i2c), any other suggestions,
also is there code, or psudo code somewhere on the web that i can rip(c++)?


EDIT: just after posting i found code for a master:
https://www.robot-electronics.co.uk/i2c-tutorial

but still need code for slave!?
 
Last edited:
I'd go with I2C as you have the flexibility to add multiple masters if you want. Plus with software addressing won't hog up IO pins for per device chip select.

I2C code isn't all that hard to write. Consult the data sheet for your PIC to learn how to write your own. What compiler are you using?
 
You don't say which pics you intend to use. However, if not already decided I'd advise picking one of the new enhanced chips with peripheral steering. Even the 8 pin 16F18313 can have the hardware I2C lines moved to the ICSP pins.

Mike.
 
peripheral steering,! now that sounds handy!

but i was hoping to just use up some small mid range chips that i have laying around such as the 16f688 and the 16f526 maybe
 
If you want to use old chips then you need to bitbang I2C. It's fairly easy to do and the're lots of examples around.

Mike.
 
This looks good been playing with xc8 maybe I'll try this out myself.
Code:
typedef struct
{
  unsigned int PIN0:1;
  unsigned int PIN1:1;
  unsigned int PIN2:1;
  unsigned int PIN3:1;
  unsigned int PIN4:1;
  unsigned int PIN5:1;
  unsigned int PIN6:1;
  unsigned int PIN7:1;
} PORT;

/* TODO: Example address shown, but the proper address */
#define PORT0 *(volatile PORT *)0x1234

/* Define the port used for I2C data and clk as shown above to access them pin wise */
#define I2C_DATA PORT0.PIN0
#define I2C_CLK  PORT0.PIN1

#define HIGH 1
#define LOW  0

/* I2C Start - bit bang */
void I2C_START(void)
{
    /* I2C Start condition, data line goes low when clock is high */
    I2C_DATA = HIGH;
    I2C_CLK = HIGH;
    I2C_DATA = LOW;
    I2C_CLK = LOW;
}

/* I2C Stop - bit bang */
void I2C_STOP (void)
{
    /* I2C Stop condition, clock goes high when data is low */
    I2C_CLK = LOW;
    I2C_DATA = LOW;
    I2C_CLK = HIGH;
    I2C_DATA = HIGH;
}

/* I2C Write - bit bang */
void I2C_WRITE(unsigned char data)
{
    unsigned char outBits;
    unsigned char inBit;
  
    /* 8 bits */
    for(outBits = 0; outBits < 8; outBits++)
    {
       if(data & 0x80)
           I2C_DATA = 1;
        else
           I2C_DATA = 0;
          data  <<= 1;
        /* Generate clock for 8 data bits */
        SCLK = HIGH;
        SCLK = LOW;                  
    }
  
    /* Generate clock for ACK */
    I2C_CLK = HIGH;
        /* Wait for clock to go high, clock stretching */
        while(I2C_CLK);
        /* Clock high, valid ACK */
    inBit = I2C_DATA;
    I2C_CLK = LOW;                  
}

unsigned char I2C_READ (void)
{
    unsigned char inData, inBits;

    inData = 0x00;
    /* 8 bits */
    for(inBits = 0; inBits < 8; inBits++)
    {
        inData <<= 1;
        I2C_CLK = HIGH;
          inData |= I2C_DATA;
        I2C_CLK = LOW;                  
    }

   return inData;
}

/* Examble for writing to I2C Slave */
void writeI2CSlave (unsigned char data)  
{
    /* Start */
      I2C_START();
    /* Slave address */
      I2C_WRITE(0xAA)
    /* Slave control byte */
      I2C_WRITE(0xBB);
    /* Slave data */
      I2C_WRITE(data);
    /* Stop */
      I2C_STOP();
}

/* Examble for reading from I2C Slave */
unsigned char readI2CSlave(unsigned char data)
{
      unsigned char inData;

    /* Start */
      I2C_START();
    /* Slave address */
      I2C_WRITE(0xAA);
    /* Slave control byte */
      I2C_WRITE(data);
    /* Stop */
      I2C_STOP();
  
    /* Start */
      I2C_START();
    /* Slave address + read */
      I2C_WRITE(0xAA | 1);
    /* Read */
    inData = I2C_READ();

      return inData;               
}
Why is this messing up the format I fixed it
 
Last edited:
Wouldn't a better approach be to initialize all 0's to the SDA/SCL PORT pin bits while manipulating the SDA/SCL TRIS bits instead so that the pins behave more like open drain pins rather than push/pull outputs?
 
Last edited:
ok! so for the master device i was able to use the SDA & SCL pins on the Arduino for the master device:

Code:
#include <Wire.h>   
uint8_t I2C_device_address = 0x64;

void I2c_setup()
{
   Wire.begin();
}

unsigned char I2C_Read()
{
   unsigned char buffer = 12;   unsigned char return_bytes = 1;
   Wire.beginTransmission(I2C_device_address);
   Wire.write(buffer);
   Wire.endTransmission();
   Wire.requestFrom(I2C_device_address, return_bytes);
   return (Wire.read());
}

on the PIC slave device i am using pins A 0 & 1 (the icsp pins), also on this i enabled the internal weak pull up resistors, since resistors are called for in the schematics, hope that is adequate equivalent...!?

But@! I run it to problem when hooking the two ports together, it seems that when trying to attach the two together i get brownout on the arduino.... even if i unhook the icsp from the pickit.....

is there additional circuitry i should add? (maybe i should throw in some more resistors on the bus?)
Once i seen a schematic with a block diagram that had icsp isolation circuit block on it..... maybe i should add one of these(any schematics i can/should get for this?) .... but not my main problem since brownout still happens when pickit is not attached to clk & dat???
 
ok! so for the master device i was able to use the SDA & SCL pins on the Arduino for the master device:

Code:
#include <Wire.h>  
uint8_t I2C_device_address = 0x64;

void I2c_setup()
{
   Wire.begin();
}

unsigned char I2C_Read()
{
   unsigned char buffer = 12;   unsigned char return_bytes = 1;
   Wire.beginTransmission(I2C_device_address);
   Wire.write(buffer);
   Wire.endTransmission();
   Wire.requestFrom(I2C_device_address, return_bytes);
   return (Wire.read());
}

on the PIC slave device i am using pins A 0 & 1 (the icsp pins), also on this i enabled the internal weak pull up resistors, since resistors are called for in the schematics, hope that is adequate equivalent...!?

But@! I run it to problem when hooking the two ports together, it seems that when trying to attach the two together i get brownout on the arduino.... even if i unhook the icsp from the pickit.....

is there additional circuitry i should add? (maybe i should throw in some more resistors on the bus?)
Once i seen a schematic with a block diagram that had icsp isolation circuit block on it..... maybe i should add one of these(any schematics i can/should get for this?) .... but not my main problem since brownout still happens when pickit is not attached to clk & dat???

I2C bus resistors are usually in the order of 470R-1K. The faster the bit rate the lower the value.

Internal weak pull ups are in the order of 25K. Too high for the application.

What are you using for a power supply?
 
I am using a CAT24C256 EEPROM (32Kbyte) as a buffer between hardware I2C 16F886 (asm) and an ESP8266-12e (C code). So the EEPROM (Addr#1) is the slave and I have two masters.

Would the hardware in the chips arbitrate any buss contention? My concern is how do I know there was a contention and to attempt a retransmit after a couple seconds?
 
Thanks John, both those comments confused the heck out of me, but allowed me to reread this protocol, i get now that i wasn't browning out but rather hanging on the clock stretching... also the bus now has the proper pull-ups, AND! when the i2c is idle the pickit has no problem programming with everything still on the bus!!
Also i was not aware that the microcontroller(s) never actually outputted a logic 1 ,and now see what you were talking about TRIS and open drain....Kudos!

Here is some working host/slave code that i bit banged out, the only thing that is different from proper i2c is that i slowed it down to 100us per bitshift, its also missing is the clock stretch, guys are welcome to try and/or improve!

Host was Arduino, and client was Pic:



Master:
Code:
#include <Arduino.h>
#include <Wire.h>   

 char SDAk = 20;
 char SCLk = 21;

void I2c_start(void)
{
   // start bit
   pinMode(SDAk, OUTPUT);
   digitalWrite(SDAk, LOW);
   delayMicroseconds(100);
   pinMode(SCLk, OUTPUT);
   digitalWrite(SCLk, LOW);
   delayMicroseconds(100);

}

void I2c_stop(void)
{
   pinMode(SCLk, INPUT);
   delayMicroseconds(100);
   pinMode(SDAk, INPUT);
   delayMicroseconds(100);
}


void I2c_clock_pulse(void)
{
   pinMode(SCLk, INPUT);   
   delayMicroseconds(100);
   pinMode(SCLk, OUTPUT); digitalWrite(SCLk, LOW);   
   delayMicroseconds(100);
}




unsigned char I2c_write(unsigned char dat)
{
   unsigned char ctr;
   unsigned char ak;
   // msb first
   for (ctr = 0; ctr < 8; ctr++)
   {
     if (dat & 128){   pinMode(SDAk, INPUT);}
     else{ pinMode(SDAk, OUTPUT); digitalWrite(SDAk, LOW); }delayMicroseconds(100);

     I2c_clock_pulse();
     dat = dat << 1;
   }

   // listen for ack   
   pinMode(SDAk, INPUT);
   ak = !digitalRead(SDAk);
   pinMode(SDAk, OUTPUT); digitalWrite(SDAk, LOW);
   I2c_clock_pulse();

   return ak;
}

unsigned char I2c_read(void)
{
   unsigned char r = 0;
   unsigned char ctr = 0;

   pinMode(SDAk, INPUT);
   for (ctr = 0; ctr < 8; ctr++)
   {
     r = r * 2;
     if (digitalRead(SDAk) == 1){ r += 1; }   delayMicroseconds(100);     
     I2c_clock_pulse();
   }

   // send  ack  
   pinMode(SDAk, OUTPUT); digitalWrite(SDAk, LOW);  delayMicroseconds(100);
   I2c_clock_pulse();


   return r;
}

void I2c_setup(void)
{
   unsigned char cnt;
   // port outpuut
   pinMode(SCLk, INPUT);
   pinMode(SDAk, INPUT);
   delayMicroseconds(100);


   Serial.println("Looking for 2-Wire devices...");
   for (cnt = 1; cnt < 127; cnt++)
   {
     I2c_start();
     if (I2c_write(cnt << 1)){ Serial.println(cnt); }
     I2c_stop();
     delay(100);

   }
   Serial.println("Done");


}

unsigned char I2C_WriteDeviceRegister(unsigned char I2C_device_address1, unsigned char buffer, unsigned char val)
{
   unsigned char ack = 0;
   I2c_start();
   ack += I2c_write(I2C_device_address1 << 1);
   ack += 2* I2c_write(buffer);
   ack += 4* I2c_write(val);
   I2c_stop();
   delay(100);
   return ack;
}

unsigned char I2C_ReadDevice(unsigned char I2C_device_address1, unsigned char buffer, unsigned char return_bytes)
{
   unsigned char ack = 0;
   unsigned char ret = 0;
   retry:
   return_bytes = 1;      
   I2c_start();
   ack +=  I2c_write(I2C_device_address1 << 1);
   ack += 2 * I2c_write(buffer);
   I2c_stop();
   delay(100);
   //if (ack < 4){ goto retry; }
   
   I2c_start();
   ack += 4 *  I2c_write((I2C_device_address1 << 1) + 1);
   ret = I2c_read();
   I2c_stop();
   delay(100);

   return (ret);

}


void loop() {

   uint8_t I2C_device_address = 0x0F;
   uint8_t cnt = 0x0F;
   unsigned int b = 0;
   String s = "";

   while (1){

   
   begining:

     pinMode(8, OUTPUT);
     digitalWrite(22, HIGH);
 


     //// TESTING I2C
     Serial.println("new i2c:");
     Serial.println(I2C_WriteDeviceRegister(I2C_device_address, 9, 123));
     delay(1000);

     Serial.println(I2C_ReadDevice(I2C_device_address, 9, 1));
     delay(1000);
 
     b = 0; s = "";
     b += (I2C_ReadDevice(I2C_device_address, 5, 1)) * 256;
     b += (I2C_ReadDevice(I2C_device_address, 6, 1));
     s += "Capacitance = ";
     s += b;
     s += "\r\n";
     Serial.println(s);
     delay(1000); 
     goto begining;

}


}



Slave, interrupt on port a0:
Code:
  unsigned char rgstr = 0;
  unsigned char dataregister[] = {0x0F,200,94,112,0,  7,2,100,1,3,  14,24,30,32,34,  40,185,195};


unsigned char I2c_WaitForClock1withBreak(void)
{
  unsigned char lstportval = 0;
  unsigned char timeout = 0;
  lstportval = PORTA & 1;
  for(timeout= dataregister[1];((PORTAbits.RA1 == 1) & (timeout>0));timeout--)
  {
  if (lstportval != (PORTA & 1)){return 1;}
  }   
  return 0;
}

unsigned char I2c_WaitForClock_High_Low(void)
{
  unsigned char lstportval = 0;
  unsigned char timeout = 0;
  lstportval = PORTA & 1;
  for(timeout= dataregister[1];((PORTAbits.RA1 == 1) & (timeout>0));timeout--)
  {   
  }   
  return 0;
}


void send_ack(unsigned char a)
{   
  if (a == 1){TRISAbits.TRISA0 = 0; PORTAbits.RA0 = 0;}  
  while(PORTAbits.RA1 == 0){}   
  I2c_WaitForClock_High_Low();
  TRISAbits.TRISA0 = 1;
}

unsigned char get_ack(void)
{
  unsigned char a = 0;  
  while(PORTAbits.RA1 == 0){}   
  if (PORTAbits.RA0 == 0){a = 1;} else{a = 0;}   
  I2c_WaitForClock_High_Low();
  return a;
}

void interrupt interupt(void)
 {
  unsigned char cnt = 0;
  unsigned char reg = 0;  
  unsigned char ack = 0;  
  unsigned char ack1 = 0;  
  unsigned char ack2 = 0;   
  unsigned char input = 0;
  unsigned char read_counter = 0;
  unsigned char I2C_addressa = 0;   


  if (((PORTA & 2) == 0)) {goto rtn;}  // if clock is 0 or dat is 1
  I2C_addressa = dataregister[0] << 1;   
  I2c_WaitForClock_High_Low();  
   
   
  //  listen for device address   
  input=0;   
  for (cnt=0;cnt<8;cnt++)   
  {   
  input *= 2;   
  while(PORTAbits.RA1 == 0){}   
  if ((PORTA & 1) == 1){ input += 1;}   
  I2c_WaitForClock_High_Low();   
  if ((input & 0b11111110 ) == I2C_addressa){ack=1;}else{ack=0;}
  }   
  send_ack(ack);
  if (ack == 0){goto rtn;}   
   
   
   
   
  // i2c write --- first routine   
  if (input == I2C_addressa)   
  {   
  rgstr=0;  
  TRISAbits.TRISA0 = 1;   
  for (cnt=0;cnt<8;cnt++)
  {
  rgstr = rgstr << 1;   
  while(PORTAbits.RA1 == 0){}  
  if ((PORTA & 1) == 1){rgstr +=1;}   
  I2c_WaitForClock_High_Low();  
  }   
  // check for valid register
  if (rgstr < 16){ack1 = 1; send_ack(1);}else{send_ack(0);}   
  while(PORTAbits.RA1 == 0){}   

  // check for stop bit and return   
  if(I2c_WaitForClock1withBreak() || ack1 == 0 ){goto bcast;}
  // or continue to write
  LED(1);
  reg = 0;
  for (cnt=0;cnt<8;cnt++)
  {
  reg = reg << 1;   
  if ((PORTA & 1) == 1){reg +=1;}  
  while(PORTAbits.RA1 == 0){}  
  if (cnt < 7){I2c_WaitForClock_High_Low();}
  }   
  send_ack(1);
  dataregister[rgstr] = reg; 
   
  if (dataregister[8]){
  txPickit( 0xEE, dataregister[3]);  
  txPickit( 0xEE, dataregister[3]);
  txPickit( rgstr, dataregister[3]);  
  txPickit( dataregister[rgstr], dataregister[3]);
  txPickit( 0xEE, dataregister[3]);
  txPickit( 0xEE, dataregister[3]);  
  __delay_ms(100);
  }
  }   
      
   
  // i2c read --- second routine  // write call from register   
  else if (input == I2C_addressa + 1)   
  {   
  LED(2);
  reg = dataregister[rgstr];  
  TRISAbits.TRISA0 = 0;
  for (cnt=0;cnt<8;cnt++)
  {
  if (reg & 128){TRISAbits.TRISA0 = 1;}else{PORTAbits.RA0 = 0;TRISAbits.TRISA0 = 0;}
  while(PORTAbits.RA1 == 0){}  
  I2c_WaitForClock_High_Low();
  reg = reg << 1;   
  if (cnt == 7)TRISAbits.TRISA0 = 1;   
  }   
//  while(PORTAbits.RA1 == 0){}   
  if (get_ack()){
  read_counter++;ack2 = 1;
  }
  }
   
  bcast:
   
   
  if (dataregister[8]){
  txPickit( 0x0F, dataregister[3]);  
  txPickit( I2C_addressa, dataregister[3]);  
  txPickit( input & 0xFF, dataregister[3]);  
  txPickit( rgstr, dataregister[3]);   
  txPickit( dataregister[rgstr], dataregister[3]);   
  txPickit( ack2, dataregister[3]);   
  txPickit( 0xF0, dataregister[3]);   
  }
  rtn:  
  LED(0);
//  if (read_counter >0){if (rgstr < 16){rgstr++;}}
  cnt = PORTA;  // dummy bit 2 = sCL,  dummy bit1 = sDA
  RAIF= 0;  // enable interrupt
  RAIE= 1;  // enable interrupt
  GIE = 1;  // enable global interrupts
}
 
That is correct. All the uC does is assert (pull down) and deassert (release or tristate) the bus lines. The pull up resistor outputs a 1 when the pins tristate. Without the resistors, the bus lines will just be tristated when the pins tristate and deassert the lines.

Arduino has a two wire interface that does I2C? Why bit bang?
 
One problem with developing an I2C product is if you stop during an I2C read operation. The bus can hang in some situations and the only way to get it working again is to power everything down. To overcome this, before enabling the I2C bus, I check the data line for logic low and continually clock the clock line until the data line is released (high).

Mike.
 
..... just how i dev my understanding ... got around the hang up earlier ... slowed the code down lower than 100khz (this was the main reason since my bus is fairly long) ...

actually , i didnt realize the whole de-assertion thing until most of that code was written, i kept reading it, but didn't understand the full meaning!
first i started with the address handshake, then the ack bit, then the rest!
 
You can get around bus length issues by using lower value pull up resistors.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top