K65 & SDKv2.3
This issue is similiar to bug in i2c driver for kinetis? but refers to the interrupt implementation.
My K65 device is an I2C Master talking to an I2C Slave. The slave device that I am communicating with has a command message that contains a slave address and a sub-address with no data. When I use the SDKv2.3 I2C driver, the code hangs waiting for the interrupt callback after an I2C Write. I traced the problem to I2C_MasterTransferRunStateMachine() in fsl_i2c.c which assumes that there will be at least one byte of data.
In the state machine the following code is found:
/* Run state machine. */ switch (handle->state) { /* Send I2C command. */ case kSendCommandState: if (handle->transfer.subaddressSize) { handle->transfer.subaddressSize--; base->D = ((handle->transfer.subaddress) >> (8 * handle->transfer.subaddressSize)); } else { if (handle->transfer.direction == kI2C_Write) { /* Next state, send data. */ handle->state = kSendDataState; /* Send first byte of data. */ if (handle->transfer.dataSize > 0) { base->D = *handle->transfer.data; handle->transfer.data++; handle->transfer.dataSize--; } } else { /* Send repeated start and slave address. */ result = I2C_MasterRepeatedStart(base, handle->transfer.slaveAddress, kI2C_Read); /* Next state, receive data begin. */ handle->state = kReceiveDataBeginState; } } break; /* Send I2C data. */ case kSendDataState: /* Send one byte of data. */ if (handle->transfer.dataSize > 0) { base->D = *handle->transfer.data; handle->transfer.data++; handle->transfer.dataSize--; } else { *isDone = true; } break;
The kSendCommandState case is where the first data byte would be sent. If there is no data to send, *isDone is never set to true and when I2C_MasterTransferRunStateMachine() returns to I2C_MasterTransferHandleIRQ() the following code is never executed:
/* Check transfer complete flag. */ result = I2C_MasterTransferRunStateMachine(base, handle, &isDone); if (isDone || result) { /* Send stop command if transfer done or received Nak. */ if ((!(handle->transfer.flags & kI2C_TransferNoStopFlag)) || (result == kStatus_I2C_Nak) || (result == kStatus_I2C_Addr_Nak)) { /* Ensure stop command is a need. */ if ((base->C1 & I2C_C1_MST_MASK)) { if (I2C_MasterStop(base) != kStatus_Success) { result = kStatus_I2C_Timeout; } } } /* Restore handle to idle state. */ handle->state = kIdleState; /* Disable interrupt. */ I2C_DisableInterrupts(base, kI2C_GlobalInterruptEnable); /* Call the callback function after the function has completed. */ if (handle->completionCallback) { handle->completionCallback(base, handle, result, handle->userData); } }
This code sends the stop bit and calls the completionCallback..functionality needed to complete the I2C write. By adding 'else *isDone = true' (like seen in the kSendDataState) when there is not a first byte of data to send in the kSendCommandState case, the driver works as expected. Please let me know if I am correct in my conclusion and if later versions of the SDK will correct this.
Thanks for your feedback, this workaround seems OK, we will fix this bug in future release.
Regards
Daniel