9 Replies Latest reply on Jan 7, 2020 4:42 PM by MoTa_728816

    How to use I2C Master non blocking functions

    AmCi_3754291

      Hey guys

       

      I'm trying to use the non-blocking I2C functions: I2C_MasterWriteBuf and I2C_MasterReadBuf so I can more efficiently use RTOS functions.

      The functions that I have look like this:

       

      void BNO080_IMU1_sendPacket(uint8_t channelNumber, uint8_t dataLength) {

         

          uint8 packetLength = dataLength+4; // adding the 4 header bytes

          //debugging tool

         

          //memset(rdHeader, 0, sizeof(rdHeader)); //making sure that read header is 0s

          uint8_t wrData[packetLength];

          wrData[0] = packetLength >> 8;

          wrData[1] = channelNumber;

          wrData[2] = sequenceNumber[channelNumber]++;

         

          for (uint8_t i=0; i < dataLength; i++){

              wrData[2+i] = shtpData[i];

          }

         

          //USBUART_PutString("I'm about to send data\n");

          //while(!USBUART_CDCIsReady()) {};

         

          I2C_IMU1_MasterClearWriteBuf();

          uint8_t mstrStatus = I2C_IMU1_MasterStatus();

          uint8_t errStatus = I2C_IMU1_MasterWriteBuf(IMU_Address,(uint8_t *) wrData, sizeof(wrData), I2C_IMU1_MODE_COMPLETE_XFER);

         

        

          //I2C_IMU1_MasterClearStatus();

          //debugging

          char buffer[100];

          sprintf(buffer, "write error = %d; master status = %d\n", errStatus, mstrStatus);

          USBUART_PutString(buffer);

          while(!USBUART_CDCIsReady()){};

         

          while (0u == (I2C_IMU1_MasterStatus() & I2C_IMU1_MSTAT_WR_CMPLT)) {

              uint8_t mstrStatus = I2C_IMU1_MasterStatus();

              sprintf(buffer, "write incomplete. Master Status = %d\n", mstrStatus);

              USBUART_PutString(buffer);

              while(!USBUART_CDCIsReady()){};

              //mstrStatus = I2C_IMU1_MasterClearStatus();

              //CyDelay(50);

          }

                 

       

      bool BNO080_IMU1_receivePacket() {

         

          USBUART_PutString("receive packet sequence started\n");

          while(!USBUART_CDCIsReady()) {};

         

          memset(rdHeader, 0, sizeof(rdHeader)); //making sure that read header is 0s

          I2C_IMU1_MasterClearReadBuf();

          uint8_t errStatus = I2C_IMU1_MasterReadBuf(IMU_Address, (uint8_t *) rdHeader, sizeof(rdHeader), I2C_IMU1_MODE_COMPLETE_XFER);

          //uint8_t mstrStatus = I2C_IMU1_MasterClearStatus();

          char buffer[100];

          sprintf(buffer, "read error = %d\n", errStatus);

          USBUART_PutString(buffer);

          while(!USBUART_CDCIsReady()){};

         

          while (0u == (I2C_IMU1_MasterStatus() & I2C_IMU1_MSTAT_RD_CMPLT)) {

              uint8_t mstrStatus = I2C_IMU1_MasterStatus();

              sprintf(buffer, "read incomplete. Read Master Status = %d\n", mstrStatus);

              USBUART_PutString(buffer);

              while(!USBUART_CDCIsReady()){};

              //mstrStatus = I2C_IMU1_MasterClearStatus();

              //CyDelay(50);

          }

       

      void BNO080_IMU1_getData(uint16_t bytesRemaining) {

         

          memset(rdHeader, 0, sizeof(rdHeader)); //making sure that read header is 0s

          //reading the header

          I2C_IMU1_MasterClearReadBuf();

          uint8_t errStatus = I2C_IMU1_MasterReadBuf(IMU_Address, (uint8_t *)rdHeader, sizeof(rdHeader), I2C_IMU1_MODE_COMPLETE_XFER);

         

          char buffer[100];

          sprintf(buffer, "get data read error = %d\n", errStatus);

          USBUART_PutString(buffer);

          while(!USBUART_CDCIsReady()){};

          //reading the data

          uint8_t rdData[bytesRemaining];

          I2C_IMU1_MasterClearReadBuf();

          I2C_IMU1_MasterReadBuf(IMU_Address, (uint8_t *) rdData, sizeof(rdData), I2C_IMU1_MODE_COMPLETE_XFER);

         

         while (0u == (I2C_IMU1_MasterStatus() & I2C_IMU1_MSTAT_RD_CMPLT)) {

              uint8_t mstrStatus = I2C_IMU1_MasterStatus();

              sprintf(buffer, "read incomplete. Master Status = %d\n", mstrStatus);

              USBUART_PutString(buffer);

              while(!USBUART_CDCIsReady()){};

              //mstrStatus = I2C_IMU1_MasterClearStatus();

              //CyDelay(50);

          }

          USBUART_PutString("get data read complete");

          while(!USBUART_CDCIsReady()){};

      }

       

      So it seems like I manage to write initially, read twice and the third time I try to read it breaks and the status error is something I don't recognize: 6 (as it can be seen in the image I attached.

       

      The bundle has a lot more information than necessary for what we're doing here. Here what I'm looking it are the functions in BNO080_IMU1.c and main.c.

       

      Any ideas as to what I might be doing wrong?

       

      Thanks in advance

       

      Amilcar

        • 1. Re: How to use I2C Master non blocking functions
          MoTa_728816

          Hi,

           

          In I2C_IMU1.h Statused of Master are defined as

          ========================

              /* Master status */

              #define I2C_IMU1_MSTAT_CLEAR            (0x00u) /* Clear (initialize) status value */

           

           

              #define I2C_IMU1_MSTAT_RD_CMPLT         (0x01u) /* Read complete */

              #define I2C_IMU1_MSTAT_WR_CMPLT         (0x02u) /* Write complete */

              #define I2C_IMU1_MSTAT_XFER_INP         (0x04u) /* Master transfer in progress */

              #define I2C_IMU1_MSTAT_XFER_HALT        (0x08u) /* Transfer is halted */

           

           

              #define I2C_IMU1_MSTAT_ERR_MASK         (0xF0u) /* Mask for all errors */

              #define I2C_IMU1_MSTAT_ERR_SHORT_XFER   (0x10u) /* Master NAKed before end of packet */

              #define I2C_IMU1_MSTAT_ERR_ADDR_NAK     (0x20u) /* Slave did not ACK */

              #define I2C_IMU1_MSTAT_ERR_ARB_LOST     (0x40u) /* Master lost arbitration during communication */

              #define I2C_IMU1_MSTAT_ERR_XFER         (0x80u) /* Error during transfer */

          ========================

           

          So status == 6 seems to be

          I2C_IMU1_MSTAT_WR_CMPLT (0x02) | I2C_IMU1_MSTAT_XFER_INP (0x04)

           

          I think that this happens when a byte transfer is completed during multi bytes transfer.

           

          So you may need to wait till

               I2C_IMU1_MSTAT_XFER_INP == 0

          and

               I2C_IMU1_MSTAT_WR_CMPLT == 1

           

          moto

          • 2. Re: How to use I2C Master non blocking functions
            AmCi_3754291

            Thanks Moto!

             

            So I've been poking around the code based on the info you gave me and managed to narrow down the issue.

             

            My void BNO080_IMU1_sendPacket(uint8_t channelNumber, uint8_t dataLength) works! I managed to send data...

             

            *********If I convert my "bool BNO080_IMU1_receivePacket()" to "void BNO080_IMU1_getData(uint16_t bytesRemaining)" into the blocking I2C (MasterSendStart(), MasterWriteByte(), MasterReadByte()) functions everything works fine.

             

            However, if I turn my "bool BNO080_IMU1_receivePacket()"  into non_blocking and let  "void BNO080_IMU1_getData(uint16_t bytesRemaining)" to remain blocking, I manage to send 1 data packet and the next time I go to read data using: "uint8_t errStatus = I2C_IMU1_MasterReadBuf(IMU_Address, (uint8_t *)rdHeader, sizeof(rdHeader), I2C_IMU1_MODE_COMPLETE_XFER);" I get the I2C_MSTR_BUS_BUSY error. However, when I check the status of my previous start and stop conditions these are 0.

             

            Have you experienced anything like this? I.e. Complete transfers occur but Bus is still busy?

             

            I added a picture of what it is I'm getting printed:

             

            write error is the output of MasterWriteBuf,

            read error is the output of MasterReadBuf and

            master status is the output of of MasterStatus()

             

            Amilcar

            • 3. Re: How to use I2C Master non blocking functions
              MoTa_728816

              Hi,

               

              In my experience, I have been making a couple of mistakes which causes I2C bus hang.

               

              (1) When reading multiple bytes, except the last byte, we need to answer with ACK,

              but at the last byte we need to answer with NAK.

              So please make sure if you are sending NAK at the last data read function.

               

              (2) Some I2C devices require certain amount of bytes to be transferred.

              For example some Vishay I2C sensor requires to have 2 bytes sent or received.

              So if we stop transaction at odd bytes, although from the view point of I2C transaction

              there should be no problem, the sensor will keep holding the line until it will receive or send one more byte.

              For this please refer to the datasheet of your device.

               

              moto

              • 4. Re: How to use I2C Master non blocking functions
                AmCi_3754291

                Hey Motoo

                 

                How do I send ACK or NAK signals when I'm using I2C_MasterReadBuf() function instead of I2C_MasterReadByte() function?

                 

                Thanks

                 

                Amilcar

                • 5. Re: How to use I2C Master non blocking functions
                  MoTa_728816

                  Dear Amilcar-san,

                   

                  From your screen shot, your program successfully read 276 bytes,

                  so I think that I2C_MasterReadBuf() is taking care of ACK/NAK.

                   

                  Then I would imagine that something after "FLUSHING DATA" is wrong.

                  But in your attached project, I could not find "FLUSHING DATA" string,

                  Please check your project after writing "FLUSHING DATA"

                  if there is/are protocol error(s) between PSoC and your I2C device.

                   

                  Best Regards,

                  4-Jan-2020

                  Motoo Tanaka

                  • 6. Re: How to use I2C Master non blocking functions
                    AmCi_3754291

                    I've managed to isolate the issue even further: What is happening is

                         1. I send a reset command to the IMU (this happens successfully)

                         2. The IMU attempts to flush random data (it attempts to clear 276 bytes), HOWEVER only 16 of those 276 bytes are sent;

                         3. Every other iteration of read attempt, successfully reads the first 4 bytes - that tell the PSoC how many bytes are left to be read (this is a BNO thing) but never manages to read any of the remaining 260 bytes.

                     

                    Is I2C_MasterReadBuf() limited to 16 bytes somehow? Could I be hitting some sort of memory limit? If so how do I check something like that?

                     

                    Thanks

                     

                    Amilcar

                    • 7. Re: How to use I2C Master non blocking functions
                      MoTa_728816

                      Hi,

                       

                      I just skimmed Sensor-Hub-Protocol-v1.7.pdf

                      https://cdn.sparkfun.com/assets/7/6/9/3/c/Sensor-Hub-Transport-Protocol-v1.7.pdf

                       

                      According to the document the header of the packet is

                      2.2.1 SHTP Header  Figure 2

                      ===========

                      Byte 0: Length LSB

                      Byte 1: Length MSB

                      Byte 2: Channel

                      Byte 3: SeqNum,

                      ===========

                       

                      But in your BNO080_IMU1.c

                      ===========

                          uint8_t wrData[packetLength];

                          wrData[0] = packetLength >> 8;

                          wrData[1] = channelNumber;

                          wrData[2] = sequenceNumber[channelNumber]++;

                      ===========

                       

                      I think that it should be

                      ===========

                          uint8_t wrData[packetLength];

                          wrData[0] = packetLength & 0xFF ; // LSB

                          wrData[1] = (packetLength >> 8) & 0xFF ; // MSB

                          wrData[2] = channelNumber;

                          wrData[3] = sequenceNumber[channelNumber]++;

                      ===========

                       

                      Although I can not afford time to read all BNO080 documents,

                      I'd recommend you to re-check

                      ===========

                      (1) Sensor Hub Transport Protocol

                      https://cdn.sparkfun.com/assets/7/6/9/3/c/Sensor-Hub-Transport-Protocol-v1.7.pdf

                       

                      (2) BNO080 Datasheet

                      https://cdn.sparkfun.com/assets/1/3/4/5/9/BNO080_Datasheet_v1.3.pdf

                      ===========

                       

                      moto

                      • 8. Re: How to use I2C Master non blocking functions
                        AmCi_3754291

                        Thanks Moto

                         

                        This worked for getting the data out of the IMU.

                         

                        What I had to do was do blocking i2c for the begin sequence and then use the non blocking functions to get the data.

                         

                        Are there limitations to how many bytes can be read using the non blocking functions? If so what is it? Is there a way to get around it?

                         

                        Thanks

                         

                        Amilcar

                        • 9. Re: How to use I2C Master non blocking functions
                          MoTa_728816

                          Dear Amilcar-san,

                           

                          I'm glad hearing that you could make it work.

                           

                          > Are there limitations to how many bytes can be read using the non blocking functions?

                          > If so what is it? Is there a way to get around it?

                           

                          I checked I2C_MASTER.c in the Generated_Source, I2C_MasterReadBuf() is defined as

                           

                          uint8 I2C_MasterReadBuf(uint8 slaveAddress, uint8 * rdData, uint8 cnt, uint8 mode)

                           

                          Since the cnt is uint8, with this definition, the maximum number is 0xFF = 255.

                          I think there was a similar question in the past, and at that time the person copied and modified the function with

                          uint16 cnt, so that it can handle up to 65535.

                          May be you can define it as uint32.

                           

                          But meantime, in my experience, if we get interrupt during an I2C transaction, it could corrupt the data and/or bus hang.

                          As it was an application which is required to work months without stopping, I protected each transaction of I2C

                          by using, CyEnterCriticalSection()  and CyExitCriticalSection() .

                           

                          As usual, your mileage may vary ;-)

                           

                          Best Regards,

                          8-Jan-2020

                          Motoo Tanaka