Menu

I2C Bus, ACK and NAKs

Help
Anobium
2013-09-28
2013-10-02
  • Anobium

    Anobium - 2013-09-28

    Has anyone decoded the current I2C protocol? Any fixes available?

    I have just spent many hours trying to figure out errors I was getting using a DS1307 and a 24LC256 - both use i2c.

    I have discovered that the protocol is not perfect and multiple reads and writes to the same device simple did not work. The ACK handshaking is not right - I believe.

    When I review the handshaking, using a protocol analyser, between the Master and the slaves, I see error in communications.

    I have a work around. But, has someone fix the I2C hand shaking for ACK/NAK?

    Thanks.

     
    • ofuzzy1

      ofuzzy1 - 2013-09-30

      Well that explains the weirdness I was getting.
      Having issues with the MCP79410 rtc as well.

      Two thoughts:
      1. Slow down the system clock try 1Mhz.
      2. Are the resistors near the last part in the i2c chain.

      Neither worked.

      It appears that Sda is not being released [tristated] at the end of a cycle.

      1

      see the images attached.

      Scl = line 0 and 5
      Sda = line 1 and 2
      
      i2c = the I2c subroutines start [hi] and finish [low].
      
      I am sharing the LCD data bus d4-d7 on c.0 - c.3 while holding EN [CS] low.
      

      sub i2cWrite (in i2cDevice, i2cAddress, i2cData) #NR
       'reg_status = RCSTA
       'rcsta = 0 ' shutdown the serial port to share with the i2c bus
       LCD_Enable  = 0 ' Make sure the LCD will not be pestered with the i2c Writes on the LCD Databus
       LCD_RS = 0
      LCD_DB5 = 1
       Rotate i2cDevice left Simple' *2 = << 1 ' is shifted left 1 because of The RW bit: $26 << 1 = $4C
       i2cstart
        I2CSend i2cDevice + I2C_Write_BIT  ' Who + how
        I2CSend i2cAddress             ' Where to put it
        I2CSend i2cData                ' What to put there
       I2CStop
       RCSTA = reg_status
       Rotate i2cDevice right Simple
      I2C_DATA= 1 'trisC.0 = 1 '  I2C_DATA  PORTc.0
      trisC.3 = 1 ' I2C_CLOCK PORTc.3
      LCD_DB5 = 0
      end sub
      
      Function i2cRead (in i2cDevice, i2cAddress)
       Rotate i2cDevice left Simple ' *2 = << 1 ' is shifted left 1 because of The RW bit: $26 << 1 = $4C
       'reg_status = RCSTA
       'rcsta = 0 ' shutdown the serial port to share with the i2c bus
       LCD_Enable  = 0 ' Make sure the LCD will not be pestered with the i2c Writes on the LCD Databus
       LCD_RS = 0
       LCD_DB5 = 1
      I2CStart
        I2CSend     i2cDevice + I2C_Write_BIT  ' Who + how,  Tell it where to read
        I2CSend     i2cAddress             ' Where to GET it,  Give it the location
      I2CStop
      I2CStart
        I2CSend     i2cDevice + I2C_Read_BIT   ' Who + how,  Tell it you want to read
         I2CReceive  i2cData                ' What to GET from there
       I2CStop
      i2cRead  =  i2cData
      RCSTA = reg_status
      Rotate i2cDevice right Simple
      I2C_DATA= 1 'trisC.0 = 1 '  I2C_DATA  PORTc.0
      trisC.3 = 1 ' I2C_CLOCK PORTc.3
      LCD_DB5 = 0
      
      end function
      
       

      Last edit: ofuzzy1 2013-09-30
      • kent_twt4

        kent_twt4 - 2013-09-30

        Dimitris seems to have a working example for I2CRecieve in his DS1307 routine here: https://sourceforge.net/p/gcbasic/discussion/579126/thread/1d3f96c9/ It appears that there is an extra I2CStop? in the I2CRead function. Restart doesn't look right, nor does the master Nack.

         
  • Anobium

    Anobium - 2013-09-30

    I am in the same place.

    I have just discovered this. Send a few values, then read and then the lines are not in the correct state. I thought I had it resolved than I messed up.

    My first issue... I need to confirm my decoding method is value. Can you decode the following please?

    I get 20> 20> (means 0x40 is being sent twice, this is how my decoder works) will 0x20 as the 7 bits of number 0x40. So, I am assuming that IC2Send OK.

    I2CStart
    I2CSend 0x40, TRUE
    I2CStop
    I2CStart
    I2CSend 0x40, TRUE
    I2CStop
    

    The try,

    I2CStart
    I2CSend 0x40, TRUE
    I2CSend  MCP23008_GPIO, TRUE
    I2CStart
    readdevice = 0x40 + 1
    I2CSend  readdevice, TRUE
    I2CReceive  readdevice, TRUE
    I2CStop
    

    My decoder shows... request to device 0x40 (0x20 being the 7 bit address), requests 0x09 (09+), device responds with 0x20 (020<).... SO, where DID the F0+ come from? badly clocked data? I think so.

    20> 09+ 20< F0+

    Are you able to confirm these results. I do not want to be chasing my tail. :-)

    And, you should reset you power to all devices prior to trying this. As when you get a protocol failure the Ic2 devices are all messed up.

     
  • Anonymous

    Anonymous - 2013-10-02

    Hi everyone,

    I, too am having some trouble getting the 24LC256 to work over the I2C bus with a 16F88. I'm using the header file provided by Kent in the "Contributor Section." My rig is simply to send some text to the eeprom via a terminal (with a pause after each character), and then later turn around and read the eeprom to the terminal.

    The code works perfectly for reading the eeprom, but I've been unable to write to it (garbage goes in). I've been working on it for a week now, tried many variations of both code and hardware but just can't get the write to work.

    Any leads from other people? By the way, I had no trouble making it work reliably with some PICAXE chips, so it sure seems like it should be possible with GC Basic.

    Thomas Henry

     
    • kent_twt4

      kent_twt4 - 2013-10-02

      Thomas, why don't you start a new thread. Post your I2C and Usart code, and we'll see if things can be sorted.

       
  • Anobium

    Anobium - 2013-10-02

    Update from me. I will publish later today when I get home.

    I have essentially re-written I2C.h from the start. There is something not quite right in the existing code. I am sure that it is our use of it but I tried and tried (four days) until I started the re-write on Sunday.

    My findings on the existing code are very similar to others, the code leaves the SDA and/or CLK in the incorrect state and ACK/NACKS are not handled correctly for every device. Again, I am sure this could be my use of the functions but I gave up on the existing code.

    So, I have taken Microchip App Note AN1488 and re-coded into GCB.

    Last night, after only four hours of coding, I have four devices working. One Master and three different I2C devices. I have no errors and I can selectively choose read / writes between the devices. It essentially works. The new code splits byte send/read, bit send/read and ACK/NACK into different functions - this provides a greater level of flexibility and control to ensure this GCB I2C handler follows the device protocol correctly.

    My test setup is:
    16F1937 - Master
    12LC256 - Eeprom
    DS1307 - RTC & NVRAM
    MCP23008 - Serial expander
    and... a Picaxe 18M2... also, an I2C MASTER. I have been using this device as my baseline to ensure the new GCB I2C code complies with the device I2C protocol. Why did I do this? The Picaxe device write/reads to these devices with no issue. It is good to have a baseline!

    So, I can release this code, so, others can test. I currently have all the functions called v2_IC2_[name] this provides uniqueness in the functions but I think I would replace the existing code base. What is the best way forward?

    I also have written a couple of library functions to support this devices using the new I2C code. I have one for the EEPROM device (this supports Low-Density and High-Density devices as these have different addressing schemes), I will adapt the DS1307 that has been also published and I will write one for the MCP type devices.

    Examples of the use of the new code base:

    eeprom_wr_byte ( ug_addr , 0x44 ): 'write to EEPROM at ug_addr at address 0x44
    

    This is supported by the function:

    '.............................................................................
    '          Writes data to the EEPROM address
    '.............................................................................
    Sub eeprom_wr_byte ( addr, data_out)
          'eeprom_wr_byte(addr,data_out);         ' write data to addr EEPROM address
    
          'generate Start COndition
            v2_i2c_start
    
          'send WRITE command
            v2_i2c_writebyte( EEPROM_WR )
    
          'comment the next line if a Low-Density (<=2kB) device is used
           addrh = uf_shift ( addr, 8, >> )
           addrl = addr & 0xff
          'address MSB
          'comment the next line if a Low-Density (<=2kB) device is used
            v2_i2c_writebyte(addrh)
          'address LSB
            v2_i2c_writebyte(addrl)
    
          'send data
            v2_i2c_writebyte( data_out )
    
          'initiate Stop Condition
            v2_I2C_Stop
    
           ' ACK polling
            v2_ack_poll( EEPROM_WR )
    
    end sub
    ~~~~~
    
    For the MCP23008 this works using the new code.  My LEDS attached the MCP device light up....
    

    ' see the Data sheet for this protocol. there is not ACK/NACKS required.
    v2_i2c_start
    v2_i2c_writebyte( 0x40 )
    v2_i2c_writebyte( MCP23008_IODIR )
    v2_i2c_writebyte( 0x0 )
    'initiate Stop Condition
    v2_I2C_Stop

    v2_i2c_start
    v2_i2c_writebyte( 0x40 )
    v2_i2c_writebyte( MCP23008_GPIO )
    v2_i2c_writebyte( 0x00 )
    'initiate Stop Condition
    v2_I2C_Stop

    wait 2 s

    v2_i2c_start
    v2_i2c_writebyte( 0x40 )
    v2_i2c_writebyte( MCP23008_GPIO )
    v2_i2c_writebyte( 0xff )
    'initiate Stop Condition
    v2_I2C_Stop

    end
    ~~~~

    In my test code I write to the MCP device twice (once to set the port direction and then to set ports off), write to the EEPROM address #1, read the EEPROM address #1, write to the EEPROM address #2, read the EEPROM address #2, read the EEPROM address #1 then write to the MCP device once (to set ports on). This now works.

    My code is currently full of serial debug... I promise to remove.

    Anobium

     
    • Anobium

      Anobium - 2013-10-02

      However..... for those who have still got the original I2C.H - try this. Does this resolve the issues?

      Save a copy of i2c,h and then edit i2c.h and change the file as follows:

      'Constants (shouldn't change)
      #define I2C_DATA_HIGH Dir I2C_DATA In
      #define I2C_DATA_LOW Dir I2C_DATA Out: Set I2C_DATA Off
      #define I2C_CLOCK_HIGH Set I2C_CLOCK On
      #define I2C_CLOCK_LOW  Set I2C_CLOCK Off
      
      #define I2CStopped I2COldState
      
      'Subs
      Sub InitI2C
          'Initialisation routine
          'Release data and clock lines
          I2C_DATA_HIGH
                Dir I2C_CLOCK Out
          I2C_CLOCK_HIGH
      
          'Set old state flag (slave mode)
          #if I2C_MODE = Slave
              I2COldState = 255
          #endif
      
      End Sub
      

      You should replace the CONSTANTS and the Sub routine.

      For DS1307 and MCP23xxx devices I think you can just use I2CSend as normal. For the 24LC256 you need to send an additional acknowledgement (see device datasheet, section 7) at the end after the IC2Stop but I do not know how the existing code can do this. So, I think the existing code is limited to devices that do not need ACKNOWLEDGE POLLING.

      Please test and post the results. This is a workaround, it is NOT the new code but something I discovered when I was trying to resolve my issues.

       
  • Anobium

    Anobium - 2013-10-02

    New code is ready. Testing in my config for MASTER device.

    I have zero transmission errors. I am testing as follows:

      ANSIERASECREEN_SW
      ANSI_SW ( 0,0)
      SerPrint 1, "V2 I2C":CRLF(1)
    
          ' set clock
          DS1307_SetTime ( 6, 30, 0 )
    
          ' setup MCP23008
          mcp23008_sendbyte(MCP23008_DEVICE_1_WRITE, MCP23008_IODIR  , 0x00)' turn all pins output
    
          '
          '' set ports status on MCP23008
          '
          mcp23008_sendbyte(MCP23008_DEVICE_1_WRITE, MCP23008_GPIO, 0x00)' turn all pins off
    
                ' set ports status on MCP23008
          mcp23008_sendbyte(MCP23008_DEVICE_1_WRITE, MCP23008_GPIO, 0x57)' turn all pins on
    
                ' read ports status on MCP23008
          mcp23008_readbyte(MCP23008_DEVICE_1_READ, MCP23008_GPIO, ug_data )' turn all pins on
          CRLF(1)
          serprint 1, "0) Result = "
          serprint 1, hex(ug_data):CRLF(1)
    
          ug_addr = 1
          eeprom_wr_byte ( EEPROM_DEVICE_1, 1 , 0x56 )
    
          ug_addr = 1
          ug_data = eeprom_rd_byte ( EEPROM_DEVICE_1, 1 )
          CRLF(1)
          serprint 1, "1) Result = "
          serprint 1, hex(ug_data):CRLF(1)
    
          ug_addr = 2
          eeprom_wr_byte ( EEPROM_DEVICE_1, ug_addr , 0x23 )
    
          ug_addr = 2
          ug_data = eeprom_rd_byte ( EEPROM_DEVICE_1, ug_addr )
    
          serprint 1, "2) Result = "
          serprint 1, hex(ug_data):CRLF(1)
    
          ug_addr = 1
          ug_data = eeprom_rd_byte ( EEPROM_DEVICE_1, ug_addr )
    
          serprint 1, "3) Result = "
          serprint 1, hex(ug_data):CRLF(1)
    
          ' set ports status on MCP23008
          mcp23008_sendbyte(MCP23008_DEVICE_1, MCP23008_GPIO, 0xff)' turn all pins on
    
          serprint 1, "End":CRLF(1)
          end
    

    I just need some volunteers to do some testing.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.