Hi,
I'm writing a simple application to read data from an I2C slave using a MSP430FR5969 as the master.
CCS v6.1, Compiler v4.4.5, TI-RTOS MSP43x v2.14.03.28, XDCtools v3.31.1.33_core
First I noticed that I2C_transfer() never returns in I2C_MODE_BLOCKING, so I searched for any clues on this support forum.
This post identifies the same problem and confirms my suspicions.
Question: "I have also noticed that if the I2C transaction fails then the task will block forever, shouldn't the drivers have a timeout?"
Response: "There is a bug ID for this, but it hasn't been prioritized as its possible to use the I2C driver in I2C_MODE_CALLBACK where you can add your own semaphore with a timeout. You'd block code execution after calling I2C_transfer() and then perform a Semaphore_post in its callback function."
So, it's clear that there's a known bug but nothing has been done to fix this because there is a possible workaround. It would be helpful (I think), for instance, for TI to provide an example of the proposed workaround on the wiki page or perhaps add a sticky post at the top of this forum with workarounds for known Bug IDs such as this scenario. It would save a lot of time and frustration for everyone.
I have some questions regarding my implementation of this workaround.
For my test there is no slave attached to the I2C bus and pull-ups. I'm using a MSP-EXP430FR5969 board.
Having the callback post the semaphore or it timing out while pending prevents the task lock-up but the problem lies when it times out rather than being posted by the callback.
If the callback is never called (and it isn't in my case) then the I2C transaction remains active and throws an assert error when attempting to close it using I2C_close(). My application will eventually be deployed in remote locations so it's crucial to ensure that it will not get stuck anywhere in the code under any circumstance.
There is no mechanism in the I2C driver to safely end the transaction if the transfer never completes. Is this what the unimplemented I2C_control() function is intended for in the future?
Reading through the driver source code I was able to identify what my application needs to do, but it it requires accessing I2CEUSCIB_Object member variables in the application code which is not ideal but I've tested it and it does work. There's a note in I2CEUSCIB.h on line 134 specifically stating that the application must not do this though.
- Is this a new bug?
- Is there any other way to safely end the current transaction if the driver was unable to perform this itself (via transferComplete)?
- Is my approach a valid one? I have placed any code which is modifying the driver variables within a critical section. See code below.
Void MyCallbackFxn(I2C_Handle handle, I2C_Transaction *msg, Bool transfer) { // Must check the transfer result. Not yet implemented if (msg->arg != NULL) { Semaphore_post((Semaphore_Handle)(msg->arg)); } } Void taskFxn(UArg arg0, UArg arg1) { uint8_t txBuffer[1]; I2C_Handle i2c; I2C_Params i2cParams; I2C_Transaction i2cTransaction; /* Create I2C for usage */ I2C_Params_init(&i2cParams); i2cParams.bitRate = I2C_100kHz; i2cParams.transferMode = I2C_MODE_CALLBACK; i2cParams.transferCallbackFxn = (I2C_CallbackFxn) MyCallbackFxn; i2c = I2C_open(Board_I2C_TMP, &i2cParams); if (i2c == NULL) { System_abort("Error Initializing I2C\n"); } else { System_printf("I2C Initialized!\n"); } txBuffer[0] = 0xE3; i2cTransaction.slaveAddress = (128 >> 1); i2cTransaction.writeBuf = txBuffer; i2cTransaction.writeCount = 1; i2cTransaction.arg = sem_i2cTimeout; // provide the callback with a semaphore I2C_transfer(i2c, &i2cTransaction); // always returns true in I2C_MODE_CALLBACK /* Pend on the I2C transactions to have completed, or to timeout */ if (Semaphore_pend(sem_i2cTimeout, 200) == FALSE) { /* * The current transaction is still active! * Never received the I2C transfer callback. * An active transaction cannot be closed using I2C_close() * without causing the an Assert error. * Two I2CEUSCIB_Object member variables need to be modified * as they would normally during a successful transaction. * The modification must be done within a critical section. */ unsigned int key; // Critical section: start key = Hwi_disable(); I2CEUSCIB_Object *object_i = i2c->object; object_i->currentTransaction = NULL; object_i->headPtr = NULL; Hwi_restore(key); // Critical section: end // Bus fault! Transaction never took place. System_printf("I2C Timeout!\n"); } else { // Handle I2C transaction errors... // Process data... } /* Deinitialised I2C */ I2C_close(i2c); System_printf("I2C closed!\n"); }
Thank you