This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Polling I2C devices with a 2803x?



Are there any good examples of I2C polling floating around?  There's only one I2C example included for the 2803x and it's based on interrupts.  I know the application would probably work fine with interrupts, but I've never really used them before and now isn't a good time to learn.  My project due date is approaching fast.

You see, I'm running a real-time flight application that needs to sample a few devices at every nth loop iteration.  Since the control servos are controlled by such a slow signal (50 Hz), I can do each poll sequentially and simply wait for the data to come in each time.

Beyond that, I also need to do a few sequential writes at boot up.  Similarly to the reads, I can transfer each message sequentially and simply wait for the transfer to finish on each one.

To be clear, this is a breakdown of what I want to do:

1.) Device boot up

2.) Send messages to each device containing the initialization data for various registers.  Each transfer is expected to complete before the next one begins.  No other logic is executed during this time.

3.) Once all initialization data has been sent, the device begins its main flight loop.

4.) Device 1 is polled.  The flight loop pauses until the data transfer is complete.  Same thing with Devices 2 and 3.

5.) The data from each device is used to adjust the servo controls appropriately.

6.) Return to step 3 to iterate through the loop once again.

If anyone could point me to some specific code examples that could shed some light on a good way to do this, I would very much appreciate it.

Thanks in advance!

--Daniel

  • I could really use help with this, so if anyone has any good ideas please respond!  Thanks!

  • Okay I think I've got part of it.

    do {

    clear the NACK bit;

    while(BB bit is not cleared);  // wait until the bus is ready

    I2cRegs.I2CSAR = slave address of device 1, 2, or 3

    I2caRegs.I2CCNT = the total number of bytes I need to send (address and data)

    for(i = 0; i < total bytes I need to send; i++) {

    I2caRegs.I2CDXR = bytes[i]; // send the register address followed by the data

    }

    I2caRegs.I2CMDR.all = the proper start condition to have multiple bytes transmitted with a stop condition at the end

    while(the STP bit is not cleared) {

    if(a NACK occurs) {

    issue a manual STOP command

    }

    }

    } while(the message is NACK'd);

    With subsequent write commands following the same format but with different messages, slave addresses, etc.

    Am I going about this right?

  • Hi Daniel

    You're on the right path

    Daniel Mayben said:

    for(i = 0; i < total bytes I need to send; i++) {

    I2caRegs.I2CDXR = bytes[i]; // send the register address followed by the data

    }

    During transmission poll the XRDY bit before you write to I2CDXR

       //Wait till data has been shifted out of DXR to XSR
       while(I2caRegs.I2CSTR.bit.XRDY == 0);

    Also set the mode register I2CMDR before you start writing to the I2CDXR register.

  • Okay, cool.  Good to know I'm at least getting closer.  This was something I've been struggling with for a couple of weeks now (before I realized polling was an option over interrupts).  Also, the I2CMDR is initialized before any transfers take place.

    Now I just need to devise a method of reading from each device as well.  Now, obviously the first step is to send the register address I want to read to the proper device.

    I notice that this request does not send a STOP condition when it is done.  Can I instead poll the ARDY bit to determine when I'm done sending the register address and am safe to edit the I2CRegs and send a restart?

  • Here's what I wrote for a transfer-bytes function:

    void InitIMU_Offsets(void) {
    do {
    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT; // clear the NACK bit
    while(I2caRegs.I2CSTR.bit.BB == 1); // wait until the bus is no longer busy
    I2caRegs.I2CSAR = IMU_I2C_Address; // tell the I2C peripheral where we're transmitting
    I2caRegs.I2CCNT = IMU_NUMBYTES_OFFSETS; // tell the I2C peripheral how many bytes we're transmitting

    for(i = 0; i < IMU_NUMBYTES_OFFSETS; i++) {
    while(I2caRegs.I2CSTR.bit.XRDY == 0); // wait until the previous byte has moved to the shift register
    // before sending the next one
    switch(i) {
    case 0: I2CaRegs.I2CDXR = IMU_ADDR_X_OFFS_USRH; break;
    case 1: I2CaRegs.I2CDXR = IMU_INIT_X_OFFS_USRH; break;
    case 2: I2CaRegs.I2CDXR = IMU_INIT_X_OFFS_USRL; break;
    case 3: I2CaRegs.I2CDXR = IMU_INIT_Y_OFFS_USRH; break;
    case 4: I2CaRegs.I2CDXR = IMU_INIT_Y_OFFS_USRL; break;
    case 5: I2CaRegs.I2CDXR = IMU_INIT_Z_OFFS_USRH; break;
    case 6: I2CaRegs.I2CDXR = IMU_INIT_Z_OFFS_USRL; break;
    default: return;
    }
    }

    I2CaRegs.I2CMDR.all = 0x6E20; // send the START condition

    while(I2caRegs.I2CMDR.bit.STP == 1); // wait for the transmission to finish
    // or for a NACK to occur

    } while(I2caRegs.I2CSTR.bit.NACK == 1); // if there was a NACK, send the message again
    return;
    }

    Please let me know if I'm on the right track!

    One more thing I'm worried about: I noticed that the hardware response to a NACK is to clear the STP bit, hold SCL low, and THEN set the NACK bit.  Is the NACK bit going to be set in time to test it for the do-while loop do you think?

    ///////////////////////////////////////////////////////////////////////////////

    Here's a read function as well.  I wrote it while sleep-deprived so there's likely errors:

    void IMU_read_all(Uint16 * results) {
    int i;
    do {
    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    while(I2caRegs.I2CSTR.bit.BB == 1);
    I2caRegs.I2CSAR = IMU_I2C_Address;
    I2caRegs.I2CCNT = IMU_NUMBYTES_POLL;

    while(I2caRegs.I2CSTR.bit.XRDY == 0);
    I2CaRegs.I2CDXR = IMU_ADDR_TEMP_OUT_H;

    I2CaRegs.I2CMDR.all = 0x2620; // issue a START command (no stop)

    while(I2CaRegs.I2CSTR.bit.ARDY); // wait for the transmission to finish
    // or for a NACK to occur

    } while(I2caRegs.I2CSTR.bit.NACK == 1);

    do {
    while(I2caRegs.I2CSTR.bit.BB == 1);
    I2caRegs.I2CSAR = IMU_I2C_Address;
    I2caRegs.I2CCNT = IMU_NUMBYTES_READ_ALL;
    I2CaRegs.I2CMDR.all = 0x2C20; // issue a restart command

    while(I2caRegs.I2CMDR.bit.STP == 1); // wait for the slave to finish transmitting
    } while(I2caRegs.I2CSTR.bit.NACK == 1);

    for(i = 0; i < IMU_NUMBYTES_READ_ALL; i++) {
    while(I2caRegs.I2CSTR.bit.RRDY == 0);
    results[i] = I2caRegs.I2CDRR;
    }

    }

  • Daniel Mayben said:

    for(i = 0; i < IMU_NUMBYTES_OFFSETS; i++) {
    while(I2caRegs.I2CSTR.bit.XRDY == 0); // wait until the previous byte has moved to the shift register
    // before sending the next one
    switch(i) {
    case 0: I2CaRegs.I2CDXR = IMU_ADDR_X_OFFS_USRH; break;
    case 1: I2CaRegs.I2CDXR = IMU_INIT_X_OFFS_USRH; break;
    case 2: I2CaRegs.I2CDXR = IMU_INIT_X_OFFS_USRL; break;
    case 3: I2CaRegs.I2CDXR = IMU_INIT_Y_OFFS_USRH; break;
    case 4: I2CaRegs.I2CDXR = IMU_INIT_Y_OFFS_USRL; break;
    case 5: I2CaRegs.I2CDXR = IMU_INIT_Z_OFFS_USRH; break;
    case 6: I2CaRegs.I2CDXR = IMU_INIT_Z_OFFS_USRL; break;
    default: return;
    }
    }

    I2CaRegs.I2CMDR.all = 0x6E20; // send the START condition

    while(I2caRegs.I2CMDR.bit.STP == 1); // wait for the transmission to finish
    // or for a NACK to occur

    } while(I2caRegs.I2CSTR.bit.NACK == 1); // if there was a NACK, send the message ag

    What you are doing is transmitting all N bytes and then finally checking for a NACK after the STP is sent. This would only check the last byte for a NACK. I would poll for a NACK after each transmit. Remember a NACK is reset by a subsequent ACK so you need to check each ACK slot after a transmission

    The READ has the same issue with the do-while loop. You are only checking the last ACK/NACK received. Other than that I think you are ok.

  • The documentation for the MCU said that the STP bit is cleared when a NACK is sent.  I figured that would break the loop immediately so that we could retransmit.

  • I think thats only true if the master(c2000) wants to generate a NACK and send it to the slave.

    Im looking through the manual to see if thats true of NACKs received from the slaves.

  • I added a few lines just in case:

    void IMU_read_all(Uint16 * results) {
    int i;
    do {
    I2caRegs.I2CSTR.all = I2C_CLR_NACK_BIT;
    while(I2caRegs.I2CSTR.bit.BB == 1);
    I2caRegs.I2CSAR = IMU_I2C_Address;
    I2caRegs.I2CCNT = IMU_NUMBYTES_POLL;

    while(I2caRegs.I2CSTR.bit.XRDY == 0);
    I2caRegs.I2CDXR = IMU_ADDR_TEMP_OUT_H;

    I2caRegs.I2CMDR.all = 0x2620; // issue a START command (no stop)

    while(I2caRegs.I2CSTR.bit.ARDY) { // wait for the transmission to finish
    if(I2caRegs.I2CSTR.bit.NACK == 1) { // if we get a NACK during this transmission
    I2caRegs.I2CMDR.bit.STP = 1; // issue a STOP
    break;
    }
    }

    } while(I2caRegs.I2CSTR.bit.NACK == 1);


    do {
    while(I2caRegs.I2CSTR.bit.BB == 1);
    I2caRegs.I2CSAR = IMU_I2C_Address;
    I2caRegs.I2CCNT = IMU_NUMBYTES_READ_ALL;
    I2caRegs.I2CMDR.all = 0x2C20; // issue a restart command

    while(I2caRegs.I2CMDR.bit.STP == 1) { // wait for the slave to finish transmitting
    if(I2caRegs.I2CSTR.bit.NACK == 1) { // if we get a NACK during this transmission
    break;
    }
    }
    } while(I2caRegs.I2CSTR.bit.NACK == 1);

    for(i = 0; i < IMU_NUMBYTES_READ_ALL; i++) {
    while(I2caRegs.I2CSTR.bit.RRDY == 0);
    results[i] = I2caRegs.I2CDRR;
    }
    }