• AVR Freaks

Hot!I2C Check for NACK?

Author
billreinhold
Super Member
  • Total Posts : 189
  • Reward points : 0
  • Joined: 2011/02/10 16:35:50
  • Location: Atlanta, GA
  • Status: offline
2019/06/12 12:44:01 (permalink)
0

I2C Check for NACK?

I'm trying to implement acknowledge polling for a 24LC512 I2C EEPROM, meaning sending a control byte for a Write command and checking for ACK or NACK. 
 
A write command works fine with the device correctly sending ACKs during the transfer. 
 
I then send control bytes as follows and expect a call to DRV_I2C_TransferStatusGet ( i2c_driver_handle_, buffer_handle ) to return DRV_I2C_BUFFER_EVENT_ERROR if a NACK is received, but it does not.  Once the transfer is complete and the NACK has been received, this call returns DRV_I2C_BUFFER_EVENT_COMPLETE.
 
How can I check for a NACK?
 
    DRV_I2C_BUFFER_HANDLE buffer_handle = DRV_I2C_Transmit ( 
         i2c_driver_handle_,
         device_address_,
         NULL,
         0,
         NULL);

 
I'm using the bit-bang version of the Harmony I2C driver with a PIC32MZ2048EFH144.
post edited by billreinhold - 2019/06/12 12:47:00
#1

16 Replies Related Threads

    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/13 11:00:30 (permalink)
    0
    Interesting.
    You are sending no bytes.  Is that correct?  You are initiating the I2C transaction with the address of the slave, but then sending it no bytes.
     
    Assuming I am not reading something wrong, I think the slave is acknowledging its address, and then, because you have specified 0 data, the master is sending the stop signal.  Thus, the transaction you are commanding is indeed completing successfully.
     
    I'm not sure I have ever tried that.
    You might try sending the Wrong address to the slave.  That should result in a NACK.
     
     
    Having said all that, are you sure you don't want something like this:
     
     const char COMMAND[] = "A"    ;I am not sure what "control byte" you need.
    DRV_2C_BUFFER_HANDLE buffer_handle = DRV_I2C_Transmit (
             i2c_driver_handle_,
             device_address_,
             COMMAND,
             1,
             NULL);
     
    I hope that helps.
    #2
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/17 16:13:16 (permalink)
    0
    gary.attarian
    You are sending no bytes.  Is that correct?  You are initiating the I2C transaction with the address of the slave, but then sending it no bytes.

     
    Yep, correct, no bytes.  Check out the device datasheet page 12:  
    http://ww1.microchip.com/downloads/en/devicedoc/21754m.pdf 
     
    And it works as advertised -- the EEPROM does not ACK until it's ready from the previous write, so the poll results in a series of control bytes with no ACK and then when it finishes the write cycle and is ready for another operation it returns the ACK. 
     
    But like you said, the master does complete the transaction because there's just the one byte and regardless of ACK or NACK, Harmony returns successfully with no way of determining which.
     
    If I add an address byte, that will correctly return the fail condition, but once it's ready and the ACK is returned, it seems to start the internal EEPROM write cycle so it's not ready again.
     
    It appears the only way to make this work correctly would be to write a custom driver that checks the ACKSTAT bit, but the PIC32MZ has a problem with I2C that necessitates use of the bit bang driver and I'm not going to write my own.  I guess I could look at modifying the Harmony code.
    #3
    ric
    Super Member
    • Total Posts : 22768
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: I2C Check for NACK? 2019/06/17 16:26:33 (permalink)
    0
    gary.attarian
    ..
    Assuming I am not reading something wrong, I think the slave is acknowledging its address, and then, because you have specified 0 data, the master is sending the stop signal. 
    ...

    No, if the slave is busy it will NOT acknowledge its address.
    That is the whole point of "ACK polling".
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #4
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/17 16:28:21 (permalink)
    0
    But the question is, does Harmony support ACK polling?  Am I missing something?
    #5
    ric
    Super Member
    • Total Posts : 22768
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: I2C Check for NACK? 2019/06/17 16:32:04 (permalink)
    0
    Sorry, don't know. I've never used Harmony.
    I'm just supporting your position that this SHOULD work.
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #6
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/18 10:57:00 (permalink)
    0
    If the slave device does not ACK the address (control byte in your case) then Harmony should report an error.
     
    Are you sure that the slave is NACKING something?
     
    You might be able to simulate something like this by disconnecting the slave and seeing that your transaction is indeed NACKED.  Or less destructively by changing the slave address byte in your write command to something incorrect.
     
    It has been a long time since I looked, but I am pretty sure that the driver will report an error if you write an address which does not get acknowledged by a slave.
     
    I am assuming that you have the appropriate pullups?
    #7
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/18 11:11:40 (permalink)
    0
    gary.attarianIf the slave device does not ACK the address (control byte in your case) then Harmony should report an error.

     
    I could not agree more.  That is of course exactly what I expected.

    The logic analyzer clearly shows the NACK but Harmony does not report an error unless the operation does not complete.  In this case, since I'm only sending one byte the operation does complete and Harmony does not report an error.
     
    Yes I have the pullups.  As I mentioned in my initial post, a write command works fine with the device correctly sending ACKs during the transfer.  And then the device correctly NACKs the control byte while it's busy with its internal write cycle.  And then it ACKs when the write cycle is complete.  And as far as I can tell Harmony cannot be used to determine when that happens.
    #8
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/18 12:26:46 (permalink)
    0
    Hmmmm.....
     
    Ok, I just took a quick trip down memory lane and looked into the Bit Bang Driver Code.  I think I may have found a bug.  
    If you can try an experiment for me (I don't have a setup right now to test this), please use the DRV_I2C_Transmit_Then_Receive function.  With size 0 receive buffer, obviously.  I'm not certain, but it looks like perhaps the check for the NACK of the address only occurs if there are bytes ready to be transmitted, or if the driver expects to switch to receive mode.
     
    #9
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/18 17:31:27 (permalink)
    0
    gary.attarian
    ...please use the DRV_I2C_Transmit_Then_Receive function.  

     
    I tried it... it also returns DRV_I2C_BUFFER_EVENT_COMPLETE after a NACK.
    #10
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/20 07:22:41 (permalink)
    0
    Oops.  Your right.  The bug is in that code path too.
     
    I'm looking at drv_i2c_bb.c.  Assuming I have the same version as you etc. .....
     
    It looks like the I2C_SCL_LOW_DATA state inside the if (dObj->I2CSWCounter == 0) condition, we need another condition.
     
    I'm not certain, but it looks like we need to add something which looks like 
    if (dObj->I2CACKStatus == M_NACK) before the else clause.
     
    I'm a little concerned about the case where a NACK is expected.  My suspicion is that this code works as it does because it expects to see a NACK after the last byte of a transmission.  Which, in your case, is really after the address byte.
     
    So, I'm afraid that the short answer to your question is that the driver is not really supportive of acknowledge polling.  Which I'm sure you already realize.  ;)
     
    I'm afraid the only solutions I can see is to modify the driver code.  I'm a bit nervous to suggest a modification in case I am not looking at the same file or version of the file as you have.  I believe that we could modify the code with just a couple lines.  
     
    Can you confirm your Harmony version and whether or not your project references the drv_i2c_bb.c file?
     
    #11
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/20 08:36:04 (permalink)
    0
    What I am thinking is changing this:

    //If the WRITE_READ operation has reached the end of its WRITE
    // portion, AND there are bytes to receive, AND the last byte
    // was ACKed, then the operation is set to READ and a ReStart
    // is begun.
    else if ( (!lBufferObj->transferSize)
                 && (lBufferObj->readtransferSize)
                 && (dObj->I2CACKStatus == M_ACK)
                 && (lBufferObj->operation == DRV_I2C_OP_WRITE_READ) )
    {
        lBufferObj->operation = DRV_I2C_OP_WRITE_READ;  
        dObj->i2cState = I2C_SDA_HIGH_RESTART;
        dObj->I2CSWCounter = 9;
    // Finally, if the last clock was the last clock in our operation,
    // then begin the STOP signal sequence.
    } else {
        dObj->i2cState = I2C_SCL_SDA_LOW_STOP;
    }

     
     
    To something like this:

    //If the WRITE_READ operation has reached the end of its WRITE
    // portion, AND there are bytes to receive, AND the last byte
    // was ACKed, then the operation is set to READ and a ReStart
    // is begun.
    else if ( (!lBufferObj->transferSize)
    && (lBufferObj->readtransferSize)
    && (dObj->I2CACKStatus == M_ACK)
    && (lBufferObj->operation == DRV_I2C_OP_WRITE_READ) )
    {
    lBufferObj->operation = DRV_I2C_OP_WRITE_READ;
    dObj->i2cState = I2C_SDA_HIGH_RESTART;
    dObj->I2CSWCounter = 9;
    }
    // Even if there are no more bytes to transfer,
    // if the last byte was NACKED then begin the
    // STOP signal sequence.
    else if ( (dObj->I2CACKStatus == M_NACK) )
    {
    // NOTE This is a HACK!!! Do not release!!!
    // This essentially tricks the stop sequence into thinking
    // it was triggered before the transaction was complete.
    // As a result, it will set an error code after the bus has returned to idle.
    // It is likely to have other unwanted consequences.
    // Seriously, don't release this.
    // Ideally, a new flag should be added so that the transaction can complete
    // but still report an error.
    lBufferObj->transferSize = 1;
    dObj->i2cState = I2C_SCL_SDA_LOW_STOP;
    // Finally, if the last clock was the last clock in our operation,
    // then begin the STOP signal sequence.
    } else {
    dObj->i2cState = I2C_SCL_SDA_LOW_STOP;
    }

     
     
    Please, please take this with a grain of salt.  I don't have a setup to test this and as I said, I am not even sure I am looking at the same files as you.
     
    Having said that, I think this will cause the Bit Bang driver to report an error after a successful transaction where the final byte was NACKed.
     
    I'm thinking that I might need to add some conditions to check if the byte NACKED was the address byte.  The way the code flows, in a transaction which sends or receives more than 0 bytes, a NACKED address is already reported as an error.  This happens because AFTER the stop sequence, the driver checks for any remaining bytes to send or receive.  Thus the hack I added above.
     
    So, allow me to reiterate the caveats above.  But I hope this helps.
    #12
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/20 17:07:12 (permalink)
    0
    This is certainly worth a try.  I'm wondering though, if the if statement falls through because of something other than the NACK, will that cause a problem?  Would it be better to test for the ACK inside that if block, like this?

    //If the WRITE_READ operation has reached the end of its WRITE
    // portion, AND there are bytes to receive, AND the last byte
    // was ACKed, then the operation is set to READ and a ReStart
    // is begun.
    else if ( (!lBufferObj->transferSize)
     && (lBufferObj->readtransferSize)
     && (lBufferObj->operation == DRV_I2C_OP_WRITE_READ) )
    {
      if (dObj->I2CACKStatus == M_ACK)
      {
       lBufferObj->operation = DRV_I2C_OP_WRITE_READ;
       dObj->i2cState = I2C_SDA_HIGH_RESTART;
       dObj->I2CSWCounter = 9;
      }
      else
    {
    // Even if there are no more bytes to transfer, 
    // if the last byte was NACKED then begin the
    // STOP signal sequence.
     // NOTE This is a HACK!!! Do not release!!!
     // This essentially tricks the stop sequence into thinking
     // it was triggered before the transaction was complete.
     // As a result, it will set an error code after the bus has returned to idle.
     // It is likely to have other unwanted consequences.
     // Seriously, don't release this.
     // Ideally, a new flag should be added so that the transaction can complete
     // but still report an error.
     lBufferObj->transferSize = 1;
     dObj->i2cState = I2C_SCL_SDA_LOW_STOP;
    }
    // Finally, if the last clock was the last clock in our operation,
    // then begin the STOP signal sequence.
    else {
     dObj->i2cState = I2C_SCL_SDA_LOW_STOP;
    }
     
     
    #13
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/21 10:23:42 (permalink)
    0
    ONE of the reasons I marked it as a hack  is that I have not looked at all permutations.
     
    Having said that, the way you wrote it, it won't see your ACK check unless
    "&& (lBufferObj->readtransferSize)
    && (lBufferObj->operation == DRV_I2C_OP_WRITE_READ)"
     
    is true.
     
    Those conditions indicate that the receive buffer is not full AND that the operation is a Write then Read.
     
    You are correct, however, that other potential "states" could cause the evaluation of the "else if ( (dObj->I2CACKStatus == M_NACK) )" line. The condition probably has to be increased to make sure it does not trigger an error if a master legally NACKs the last byte of a slave's transmission. 
     
    Or, perhaps it would be better to make sure that the error is only generated on the address byte regardless of the need to continue after the address (regardless of any subsequent transmission or receptions. 
     
    Or perhaps it would be better to restructure the whole "if (dObj->I2CSWCounter == 0)" block to generate the error on any NACK (except a master requesting the end to its reception).  Even before checking for the other potential states.
     
    Or probably something else that I have not thought of.  ;)
     
    I'm sorry, I am not certain.
     
    I hope this helps.
     
     
     
    #14
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/21 10:32:22 (permalink)
    5 (1)
    Gary, I really appreciate all the help and all the thought you've put into this but I think it may be safest at this point to abandon I2C.  I'll use a SPI EEPROM instead. 
     
    When I originally posted I figured I surely must be missing something...
    #15
    GDA
    Junior Member
    • Total Posts : 82
    • Reward points : 0
    • Joined: 2015/08/31 16:46:40
    • Location: 0
    • Status: offline
    Re: I2C Check for NACK? 2019/06/21 15:05:47 (permalink)
    0
    I'll make sure a bug gets filed for Harmoy.  I don't have visibility into the schedule or anything, but I will make sure the issue gets recorded.
     
    You are probably right that another design is the safest path.  It would take several hours to really dig into the code and generate a reliable fix.
     
    I'm sorry for your inconvenience.
    #16
    billreinhold
    Super Member
    • Total Posts : 189
    • Reward points : 0
    • Joined: 2011/02/10 16:35:50
    • Location: Atlanta, GA
    • Status: offline
    Re: I2C Check for NACK? 2019/06/21 15:50:49 (permalink)
    0
    No worries, I learned quite a bit going through this.  Again I appreciate the help.
    #17
    Jump to:
    © 2019 APG vNext Commercial Version 4.5