• AVR Freaks

Hot!18F27K42 - I2C check if Device Present.

Page: 12 > Showing page 1 of 2
Author
Doubletop
Starting Member
  • Total Posts : 43
  • Reward points : 0
  • Joined: 2019/03/07 21:46:09
  • Location: 0
  • Status: offline
2021/02/16 20:21:09 (permalink)
0

18F27K42 - I2C check if Device Present.

I’m trying to find a way to determine whether a device is present or not to stop the system hanging if it hasn’t been installed. My system has optional devices that may or may not be connected at start up. For now I’m not interested in the case where devices are disconnected from a running system.

I’m also aware that if the master breaks comms with a slave the slave can hang on the bus and hold it in a state that the master can’t recover from. Apparently a way out of this is to send (say) 16 SCL pulses and do a stop sequence.

I’ve been trying to address the slaves in turn and check if I get an ACK then pass that back to the main code to flag whether the device is present or not.

The code below is where I’m at, attached devices reply and I appear to get the ACK and the bus doesn’t hang. The problem arises when a device isn’t present and despite the lines

   while (!I2C1CON1bits.ACKT);         // Wait for ACK time
     
    ACK = !I2C1CON1bits.ACKSTAT;       // Read recent ACK status

ACKSTAT still indicates that the ACK has occurred so drops into the tidy up routine and the bus locks up.

Any clues please on how to correctly read  the NACK?

(BTW – I’m aware that all these while(xx.yy) loops are not good practice but they come from the MCC code and the construct is used in more than just the I2C modules so somebody in Microchip thinks the practice is acceptable.)
 
Pete


static inline void wait4Stop(void)
{
    while(!I2C1PIRbits.PCIF); // Wait for Stop condition to be recognized
    
    I2C1PIRbits.PCIF = 0; // Clear Stop condition IF
    I2C1PIRbits.SCIF = 0; // Clear Start condition IF
    I2C1STAT1bits.CLRBF = 1; // Ensure all data is removed from buffers
}

 bool I2C1_DevicePresent(uint8_t address)
 {
     uint8_t ACK;
      
    I2C1ADB1 = (uint8_t)(address << 1); // Load slave address and shift
    I2C1TXB = 0;
    I2C1CNT = 1;
     I2C1CON0bits.S = 1;
    
    while(!I2C1STAT0bits.MMA);
    while (!I2C1CON1bits.ACKT); // Wait for ACK time
     
    ACK = !I2C1CON1bits.ACKSTAT; // Read recent ACK status
    
    if(ACK) { // had an ACK so complete sequence
    
    while(I2C1CNT) // While count is true
    {
        while(!I2C1STAT1bits.TXBE); // Wait until buffer is empty
    }
    wait4Stop(); //Tidy up bus status to stop slave hanging
    }
    return ACK;
}



#1

36 Replies Related Threads

    ric
    Super Member
    • Total Posts : 29918
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/16 20:31:32 (permalink)
    0
    I haven't used that I2C peripheral yet, but a quick look in the datasheet indicates the ACKT bit is for slave mode, not master mode, so I don't think you should be waiting on it.
     
    Edit: Plainly my reading was wrong, as the eventual solution does use ACKT.
     
     
    post edited by ric - 2021/02/17 17:40:00

    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!
    #2
    Bob White
    Super Member
    • Total Posts : 360
    • Reward points : 0
    • Joined: 2010/11/06 19:52:38
    • Location: Denver, Colorado
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/16 21:12:27 (permalink)
    +1 (1)
     
    One of the shortcomings of I²C is this detection and enumeration problem.  One can poll the bus and look for an ACK or NACK to the device's address and then immediately send a STOP condition.  The problem is that the I²C specification does not require an I²C device to always ACK its own address (SMBus does require that an SMBus device always ACK its own address).  That means if the master/controller receives an ACK from an I²C device in response to the device's address the master/controller cannot be absolutely sure there is no device at that address.
     
    #3
    ric
    Super Member
    • Total Posts : 29918
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/16 21:39:26 (permalink)
    +2 (2)
    robertvwhite
    That means if the master/controller receives an ACK from an I²C device in response to the device's address the master/controller cannot be absolutely sure there is no device at that address.

    I think you meant to say "NAK".
    If you get an ACK, you can be reasonably confident there IS a device there! ;)
    AFAIK, the only time a connected device would not ACK would be a memory device which was busy performing a write cycle, which is unlikely to be the case when you're doing a bus scan to find out what is there in the first place.
     

    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
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 01:44:46 (permalink)
    0
    Doubletop,
     
    Code logic should be like that ( example ) :
    #define I2C_1_IDLE  while ( ( SSP1CON2 & 0x1F ) | ( SSP1STATbits.R_W ) );
     
    #define I2C_1_START SSP1CON2bits.SEN = 1; while ( SSP1CON2bits.SEN );
     
    #define I2C_1_WRITE_DONE while ( SSP1STATbits.R_W );
     
     
        I2C_1_IDLE
        I2C_1_START
        SSP1BUF = DeviceAddress;
        I2C_1_WRITE_DONE // - waiting for write to accomplished
        may be some pause required ( depends on I2C speed )
        then your code should check but NOT to wait permanently for ACK state on I2C bus.
        if ACK present, then do regular I2C device data exchange.
        I2C_1_START
        ...
    Does your code working the same ?
    post edited by Kabak - 2021/02/17 01:53:22
    #5
    ric
    Super Member
    • Total Posts : 29918
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 01:54:09 (permalink)
    0
    Kabak, I think you have missed that the PIC18F27K42 has a very different I2C module to older devices.

    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
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:00:14 (permalink)
    0
    Hi Ric.
     
    Yes,  I just want to point on code logic should be like that ( my code - example )
    #7
    ric
    Super Member
    • Total Posts : 29918
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:04:50 (permalink)
    0
    and my point is that the new peripheral design means you have to rethink how to do it. It is much more high level.

    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!
    #8
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:09:14 (permalink)
    0
    Thank you. If my advice useless just ignore it.
     
    Sorry
    #9
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:13:11 (permalink)
    +1 (1)

    #10
    ric
    Super Member
    • Total Posts : 29918
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: online
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:17:42 (permalink)
    0
    That is the slave diagram, go to the Master one.
     

    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!
    #11
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:28:58 (permalink)
    0

     
    It almost the same for ACK checking
    #12
    Kabak
    Super Member
    • Total Posts : 250
    • Reward points : 0
    • Joined: 2014/07/30 03:47:55
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 02:34:54 (permalink)
    0

    #13
    paulfjujo
    paulfjujo
    • Total Posts : 113
    • Reward points : 0
    • Joined: 2011/03/08 05:33:46
    • Location: France 01700
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 05:26:27 (permalink)
    +1 (1)
    hello,
     
    i get some help from a friend to solve this problem ( alias Satinas , many thanks to him !)
    whis mikroC , but without using mikroC librarie I2C hardware ...
    uses only MCU registers..

     
    sbit I2C1CON0_EN at I2C1CON0.B7;
    sbit I2C1CON0_S at I2C1CON0.B5;
    sbit I2C1STAT0_BFRE at I2C1STAT0.B7;
    sbit I2C1STAT1_TXBE at I2C1STAT1.B5;
    sbit I2C1CON1_ACKSTAT at I2C1CON1.B5; //0 = Ack was received 1 = No Ack was not received
    sbit I2C1CON2_ADB at I2C1CON2.B4;

    //------------------------------------------------------
    void I2C1_hw_Init(void)
    //------------------------------------------------------
    {
    TRISC.B3 = 0; // C3 output
    TRISC.B4 = 0; // C4 output
    WPUC = 0x18; // C3 et C4 pull-up
    ODCONC = 0x18; // C3 et C4 open drain
    RC3PPS = 0x21; // i2c1, scl -> C3
    I2C1SCLPPS = 0x13; // i2c1, scl <- C3
    RC4PPS = 0x22; // i2c1, sda -> C4
    I2C1SDAPPS = 0x14; // i2c1, sda <- C4
    //CLKRCON=0b10010110; // EN=1 Duty=50% Div=64
    //CLKRCLK=0x01; //0001 = HFINTOSC
    // I2C1CLK = 0x06; // i2c1, 0110 TMR2 post scaled output BAD !!!
    I2C1CLK = 0x03; // i2c1, 0011 MFINTOSC 500Khz ..OK
    // I2C1CLK =0x01; // HFINTOSC 100KHz BAD ????
    // I2C1CLK =0x00; // FOSC/4 16MHz BAD
    // SLRCONC=0b11100111; //RC3 ,RC4 maximum rate
    I2C1CON0 = 0x04; // i2c1, master 7 bits
    I2C1CON1 = 0x80; // i2c1, envoi NAK sur dernier octet attendu
    I2C1CON2 = 0x01; // i2c1, envoi Start non auto, SDAHT 300ns hold time, BFRET 16 i2c clock pulses
    I2C1ADB1 = OLED_ADDR; // i2c1, 2 * adresse esclave
    I2C1CON0_EN = 1; // i2c1 on
    }

    //--------------------------------------------------------------------------------------------
    unsigned char I2cScan(void)
    //--------------------------------------------------------------------------------------------
    // Scan bus I2C, envoi Start + adresse + Stop
    {
    unsigned char addr;
    static unsigned char n ;
    n=0;
    for (addr=1; addr<127; addr++)
    {
    while (!I2C1STAT0_BFRE) { } ; // attente bus libre
    I2C1ADB1 = (unsigned char)(addr<<1); // adresse esclave+ bit R/W
    I2C1CNT = 0; // aucun octet data
    I2C1CON0_S = 1; // envoi Start et adresse
    Delay_ms(10);
    if (!I2C1CON1_ACKSTAT)
    {
    I2C_Adresses_Devices[n]=addr;
    n++;
    }
    if(n>7) break;
    }
    return n;
    }

    ---_ main ----------
    I2C1_hw_Init();

    #ifdef Test_Presence
    CPrint(" Test presence device(s) sur le bus I2C1 \r\n");
    Delay_ms(100);
    j=I2cScan();
    if (j>0)
    {
    CRLF1();
    CPrint(" Device(s) trouvés sur le bus\r\n");
    for (i=0;i<j;i++)
    { //0123456789012345678
    if( I2C_Adresses_Devices[i]==0) break;
    strConstRamCpy(CRam1," Device #1 at @xx \r\n");
    *(CRam1+9)=i+49;
    ByteToHex( I2C_Adresses_Devices[i],CRam1+15);
    *(CRam1+17)=' ';
    Print (CRam1);
    }
    }
    else
    {
    CRLF1();
    CPrint(" AUCUN Device(s) trouvés sur le bus\r\n");
    CRLF1();
    }



    result on display

    Test presence device(s) sur le bus I2C1
    Device(s) trouvés sur le bus
    Device #1 at @3C            <-- OLED
    Device #2 at @57            <-- eeprom on board RTC
    Device #3 at @68           <-- RTC DS3231

    OLED SSD1306 128x32 init

     
     
     
     
     
     
    #14
    Doubletop
    Starting Member
    • Total Posts : 43
    • Reward points : 0
    • Joined: 2019/03/07 21:46:09
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 14:16:26 (permalink)
    0
    paulfjujo
    hello,
     
    i get some help from a friend to solve this problem ( alias Satinas , many thanks to him !)
    whis mikroC , but without using mikroC librarie I2C hardware ...
    uses only MCU registers..

    // Scan bus I2C, envoi Start + adresse + Stop
    {
    unsigned char addr;
    static unsigned char n ;
    n=0;
    for (addr=1; addr<127; addr++)
    {
    while (!I2C1STAT0_BFRE) { } ; // attente bus libre
    I2C1ADB1 = (unsigned char)(addr<<1); // adresse esclave+ bit R/W
    I2C1CNT = 0; // aucun octet data
    I2C1CON0_S = 1; // envoi Start et adresse
    Delay_ms(10);
    if (!I2C1CON1_ACKSTAT)
    {
    I2C_Adresses_Devices[n]=addr;
    n++;
    }
    if(n>7) break;
    }
    return n;
    }




    Thank you. Your code is pretty similar to mine except the addition of the 10ms delay. It had crossed my mind that I may be reading ACKSTAT too early in the ACK period and a delay was required. I'd guess the delay should be around 1/2 clock period. I can't remember what clock rate I'm using so would need to check
     
    I'll go to my workbench now and try it out and let you know. 
     
    Regards
     
    Pete
    #15
    Doubletop
    Starting Member
    • Total Posts : 43
    • Reward points : 0
    • Joined: 2019/03/07 21:46:09
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 16:12:46 (permalink)
    0
    Well thanks to @paulfjujo and his friend we have a working device check that is remarkably simple. I was close and the key was the delay before reading ACKSTAT. As I am using the ACKT to identify the ACK 'window' the delay can be reduced to 1ms
     
     bool I2C1_DevicePresent(uint8_t address)
     {
           
        I2C1ADB1 = (uint8_t)(address << 1); // Load slave address and shift
        I2C1CNT = 0;
      
        I2C1CON0bits.S = 1;                          // Start send of address

        //while (!I2C1STAT0_BFRE);              // Not required as single master
        while (!I2C1CON1bits.ACKT);           // Wait until ACK interval
        __delay_ms(1);
     
        return !I2C1CON1bits.ACKSTAT;     // Return ACK as +ve logic
    }

    #16
    Doubletop
    Starting Member
    • Total Posts : 43
    • Reward points : 0
    • Joined: 2019/03/07 21:46:09
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/17 20:05:46 (permalink)
    0
    The only issue that I've since discovered is the MPU6050 gyro/accelerometer responds to the poll it gets itself into some unknown state if it is accessed immeadiately after it has been polled.
     
    The work around is to access another device before any read/writes are made to the MPU6050. Which really isn't a problem for me as I'm polling all the devices on the bus at startup and I just initialise the MPU6050 after the other devices.
     
    Pete
    #17
    Jerry Messina
    Super Member
    • Total Posts : 668
    • Reward points : 0
    • Joined: 2003/11/07 12:35:12
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/18 04:18:19 (permalink)
    +2 (2)

     while (!I2C1CON1bits.ACKT);           // Wait until ACK interval
     __delay_ms(1);


    Instead of waiting for ACKT, have you tried waiting for PCIF == 1 to signal the end of the STOP condition?
    You'd still have to wait at least TSCL after that before starting another probe (plus allowing some extra time for slaves to see the STOP), but that might be better than waiting a fixed 1ms after the end of the ACK interval.
     
    #18
    Doubletop
    Starting Member
    • Total Posts : 43
    • Reward points : 0
    • Joined: 2019/03/07 21:46:09
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/18 18:16:42 (permalink)
    0
    Jerry Messina

     

     while (!I2C1CON1bits.ACKT);           // Wait until ACK interval
     __delay_ms(1);


    Instead of waiting for ACKT, have you tried waiting for PCIF == 1 to signal the end of the STOP condition?You'd still have to wait at least TSCL after that before starting another probe (plus allowing some extra time for slaves to see the STOP), but that might be better than waiting a fixed 1ms after the end of the ACK interval. 

    Thanks Jerry
     
    I'll try that out next and report back.
     
    The sugestion to wait for the Stop to have been processed by the slaves may also be the reason the MPU6050 is hanging as it hasn't seen the Stop before the next command gets sent.
     
    regards
    Pete
    #19
    Doubletop
    Starting Member
    • Total Posts : 43
    • Reward points : 0
    • Joined: 2019/03/07 21:46:09
    • Location: 0
    • Status: offline
    Re: 18F27K42 - I2C check if Device Present. 2021/02/18 23:22:04 (permalink)
    0
    Well I've tested using PCIF instead of ACKT but it still seems to still required the 1ms delay before reading ACKSTAT so not much of a difference. I don't have a scope that allows me to investigate I2C so I can't really tell what is going on timing wise.
     
    It also didn't make any difference to the MPU6050 locking up. Even adding a delay after the ACK poll before the next command to the device didn't change things.
     
    Pete
     
    #20
    Page: 12 > Showing page 1 of 2
    Jump to:
    © 2021 APG vNext Commercial Version 4.5