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.

Strange issue with Tivaware Sensorlib's I2C master driver (i2cm_drv.c)



Dear All,

I am 

The Sensorlib's I2C master driver documentation says that the driver has a queue buffer for I2C commands (10 commands organized in a ring-buffer).

In my interpretation this means that especially for the register write operations the code does not have to wait for the end-of-transaction callback before issuing another command (fire and forget), just needs to make sure not to overload the buffer, which is clearly indicated by the return value of the command. I am sure this was the original intention, and it would be nice as I wouldn't have to delay the code execution or create states for all writes).

However here are my experiences: I am trying to interface a common Invensense MPU6050 sensor (but I guess any will do).

For this example let's take three subsequent register write operations from my code:

MPU_I2CWrite(psInst, MPU_RA_SMPLRT_DIV, 9);
MPU_I2CWrite(psInst, MPU_RA_GYRO_CONFIG, (MPU_GYRO_FS_500<<3));
MPU_I2CWrite(psInst, MPU_RA_ACCEL_CONFIG, (MPU_ACCEL_FS_2<<3));
while(1);

The register values (third argument) of these three commands are 0x09, 0x08, 0x00.

Here is the write function:

void MPU_I2CWrite(tMPUInst *psInst, uint8_t ui8Reg, uint8_t ui8Value) {
	psInst->pui8Buffer[0] = ui8Reg;
	psInst->pui8Buffer[1] = ui8Value;
	while(!(I2CMCommand(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Buffer, 
			2, 0, 0, 0, 0, MPU_write_callback, psInst)));
}

And its callback function:

void MPU_write_callback(void *pvCallbackData, uint_fast8_t ui8Status) {
	if(ui8Status != I2CM_STATUS_SUCCESS) g_sErrorCounters.ui16I2CMErrorCount++;
}

The callback does not do anything, but checks if the operation was completed successfully.

But if check the communication the results show that although the device and register addresses are right, except the register values take the last value for all unsent commands (sorry I don't have a logic analyser at hand, just a lousy scope):

I know it's not much fun decoding this, but I confirm the all the device and register addresses are right, but in all three cases the register value is 0 (which is easy to check).

And if I limit the code to two subsequent writes, then -- again -- the value for both writes will be the value for the last issued command (0x08 in my case).

Do I do something wrong? Do I overlook anything?

I would do appreciate your comments, thanks,

Marton

  • Hello Marton,

    is this TM4C129 device that you are using?

    Regards
    Amit
  • My apologies. It's a TM4C123G Launchpad with Tivaware 2.1.0.12573, TI 5.1.10 compiler and CCS 6.0.1. Thank you.
  • Hello Marton,

    It was a tough read of the scope plot, but it seems that the last 2 transactions are the same (unless you can create 3 snapshots one for each transaction).
    Since I see only a small code snippet, it is tough to say how exactly is the sensorlib State Machine being used!!!

    Regards
    Amit
  • Dear Amit,

    Your efforts are highly appreciated.

    I fully agree with you on both your points (I enclosed only a snippet and also made a mistake in the result interpretation).

    I tried to do my best to produce the smallest standalone example, and I still get the some strange results.

    #define TARGET_IS_TM4C123_RB1
    #define F_CPU 40000000
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_ints.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/i2c.h"
    #include "sensorlib/i2cm_drv.h"
    
    void I2C1MasterIntHandler(void);
    void I2CWrite(uint8_t ui8Addr, uint8_t ui8Reg, uint8_t ui8Value);
    void I2CWrite_callback(void *pvCallbackData, uint_fast8_t ui8Status);
    
    tI2CMInstance g_sI2CM1Inst;
    uint8_t ui8Buffer[2];
    uint8_t ui8I2CMErrorCount;
    
    int main(void) {
    	ROM_SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C1);
    	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    	ROM_GPIOPinConfigure(GPIO_PA7_I2C1SDA);
    	ROM_GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_7);
    	ROM_GPIOPinConfigure(GPIO_PA6_I2C1SCL);
    	ROM_GPIOPinTypeI2CSCL(GPIO_PORTA_BASE, GPIO_PIN_6);
    
    	I2CMInit(&g_sI2CM1Inst, I2C1_BASE, INT_I2C1, 0xFF, 0xFF, F_CPU);
    	IntRegister(INT_I2C1, I2C1MasterIntHandler);
    
    	I2CWrite(0x69, 0x6B, 0x80); // reset;
    	ROM_SysCtlDelay(F_CPU);
    	I2CWrite(0x69, 0x6B, 0x01); // set PLL;
    	ROM_SysCtlDelay(F_CPU);
    	I2CWrite(0x69, 0x19, 0x09); // command 1;
    	I2CWrite(0x69, 0x1B, 0x08); // command 2;
    	I2CWrite(0x69, 0x1C, 0x00); // command 3;
    	while(1);
    }
    
    void I2CWrite(uint8_t ui8Addr, uint8_t ui8Reg, uint8_t ui8Value) {
    	ui8Buffer[0] = ui8Reg;
    	ui8Buffer[1] = ui8Value;
    	while(!(I2CMCommand(&g_sI2CM1Inst, ui8Addr, ui8Buffer,
    			2, 0, 0, 0, 0, I2CWrite_callback, 0)));
    }
    
    void I2CWrite_callback(void *pvCallbackData, uint_fast8_t ui8Status) {
    	if(ui8Status != I2CM_STATUS_SUCCESS) ui8I2CMErrorCount++;
    }
    
    void I2C1MasterIntHandler(void) {
    	I2CMIntHandler(&g_sI2CM1Inst);
    }
    

    I used the same MPU6050 hooked onto I2C1, but for this test those are identical to the Sensorhub's MPU9150.

    I guess I have left enough time for the sensor to get out of reset and also to stabilize its PLL, I was interested only in the final three I2C transactions.

    The expected communication is:

    0xD2, 0x19, 0x09 -- 0xD2, 0x1B, 0x08 -- 0xD2, 0x1C, 0x00

    This is what I get:

    0xD2, 0x19, 0x00 -- 0xD2, 0x1C, 0x00 -- 0xD2, 0x1C, 0x00

    Let's add one more command to the queue:

    0xD2, 0x19, 0x09 -- 0xD2, 0x1B, 0x08 -- 0xD2, 0x1C, 0x00 -- 0xD2, 0x1A, 0x02

    The results:

    0xD2, 0x19, 0x02 -- 0xD2, 0x1A, 0x02 -- 0xD2, 0x1A, 0x02 -- 0xD2, 0x1A, 0x02

    No I2C errors were occured (although these commands are not necessary valid).

    Please let me know if I can provide any more information.

    With kindest regards,

    Marton

  • Hello Marton,

    Clearly the issue is not with the sensor as the Master Is Initiating all the transactions. However a closer look at the code structure tells that

    while(!(I2CMCommand(&g_sI2CM1Inst, ui8Addr, ui8Buffer,
    2, 0, 0, 0, 0, I2CWrite_callback, 0)));

    if the return is false then it will register the same command multiple times and thereby affect the pipe. The only place that it does return false is if the read and write pointers indicate a full message queue. Having said that if you put a delay of say 1000 between the command then does it behave the same way or does it start sending the commands correctly?
    It would be quite some time before I can come to debug this on a LaunchPad+SensorHub.

    Regards
    Amit
  • Hi Amit,

    Removing the while loop itself alone did not change anything, however adding the delay seems to fix the issue.
    If you do not think it is uncommon to send burst commands the way I did, please consider reviewing the code in the next Tivaware release -- it took me a day of head-scratching until I started to investigate the communication itself.

    Thank you for your quick fix, I am really grateful.

    With best regards,

    Marton
  • Hello Marton,

    Sure we would look into the same and see what is actually going on.

    Regards
    Amit
  • Hi Amit,

    In the meanwhile I checked the i2cm_drv.c code and now I see it was not intended to work like this. It just queues pointers to read/write data, so I cannot reuse them until the transaction has been completed. Nevertheless, it is very useful for different devices sharing the same I2C bus.

    Thanks again for your help,

    Marton