Hello All,
Just to share my findings, there are a couple of issues with the LPCOpen I2CM_8xx example code.
1. In the SetupXferRecAndExecute function, the i2c is setup and then there is a wiat for transfer to complete followed by the disabling of the Interrupts. This only works if the interrupts get turned off before within about 15us of the last none-idle interrupt. (This is the case if there isn't much else going on, but if you have other ISR for other things then if they run just after the i2c final ISR this may well occur). If the Interrrupt is not turned off in time, the ISR continuously executes and the main code will never get chance to run. The Data sheet says for the MSTPENDING flag "If the master is in the idle state, and no communication is needed, mask this interrupt." The masking of this interrupt must therefore be done in the ISR itself to ensure it gets turned off..
void I2C_IRQHandler(void)
{
/* Call I2CM ISR function with the I2C device and transfer rec */
if (Chip_I2CM_XferHandler(LPC_I2C, &i2cmXferRec) != 0)
{
if(Chip_I2CM_GetMasterState(LPC_I2C) == I2C_STAT_MSTCODE_IDLE)
{
/* Clear all Interrupts */
Chip_I2C_ClearInt(LPC_I2C, I2C_INTENSET_MSTPENDING | I2C_INTENSET_MSTRARBLOSS | I2C_INTENSET_MSTSTSTPERR);
}
}
}
I imagine this is the way the interrupt should be disabled for the ROM version, and that this is probably applicable to other device families too.
2. If optomisation is turned on (for size -Os), the WaitForI2cXferComplete loop check for busy is optomised out, and it becomes a while(1); forever loop. In order to fix this the xferRecPtr->status should be made volatile (to tell the compiler this variable may change external to the loop) but this means changing the LPCOpen code.
The WaitForI2cXferComplete function can also me modified (i.e. so the LPCOpen code doesn't need changing):
/* Function to wait for I2CM transfer completion */
static void WaitForI2cXferComplete(I2CM_XFER_T *xferRecPtr)
{
volatile uint16_t *status;
status = (volatile uint16_t *)&xferRecPtr->status;
/* Test for still transferring data */
while (*status == I2CM_STATUS_BUSY)
{
/* Sleep until next interrupt */
__WFI();
}
}
I hope someone else finds these useful, or even NXP implment these findings....
Sincerely,
Adrian
Dear All,
I have found more issues with the i2cm_8xx.c code and highly optomised code.
In the Chip_I2CM_XferHandler the xfer->status is set to OK (i.e. finished) when the last data TX'd or RX'd, at this point the I2C Stop is sent. If the code is optomised then the transfer will be seen as completed before the stop condition is complete on the I2C Bus, a new transfer will cause interference with the previous one.
If the status is left as BUSY when the stop is sent, there there will be another Interrupt at the end fo the STOP condition and th Master State will be IDLE - it is at this point that the stusts should be changed to OK..
Here is the complete function:
/* Master transfer state change handler handler */
uint32_t Chip_I2CM_XferHandler(LPC_I2C_T *pI2C, I2CM_XFER_T *xfer)
{
uint32_t status = Chip_I2CM_GetStatus(pI2C);
/* Master Lost Arbitration */
if (status & I2C_STAT_MSTRARBLOSS) {
/* Set transfer status as Arbitration Lost */
xfer->status = I2CM_STATUS_ARBLOST;
/* Clear Status Flags */
Chip_I2CM_ClearStatus(pI2C, I2C_STAT_MSTRARBLOSS);
}
/* Master Start Stop Error */
else if (status & I2C_STAT_MSTSTSTPERR) {
/* Set transfer status as Bus Error */
xfer->status = I2CM_STATUS_BUS_ERROR;
/* Clear Status Flags */
Chip_I2CM_ClearStatus(pI2C, I2C_STAT_MSTSTSTPERR);
}
/* Master is Pending */
else if (status & I2C_STAT_MSTPENDING) {
/* Branch based on Master State Code */
switch (Chip_I2CM_GetMasterState(pI2C)) {
/* Master idle */
case I2C_STAT_MSTCODE_IDLE:
/* set transfer status as OK */
xfer->status = I2CM_STATUS_OK;
break;
/* Receive data is available */
case I2C_STAT_MSTCODE_RXREADY:
/* Read Data */
*xfer->rxBuff++ = pI2C->MSTDAT;
xfer->rxSz--;
if (xfer->rxSz) {
/* Set Continue if there is more data to read */
Chip_I2CM_MasterContinue(pI2C);
}
else {
/* No data to read send Stop */
Chip_I2CM_SendStop(pI2C);
}
break;
/* Master Transmit available */
case I2C_STAT_MSTCODE_TXREADY:
if (xfer->txSz) {
/* If Tx data available transmit data and continue */
pI2C->MSTDAT = *xfer->txBuff++;
xfer->txSz--;
Chip_I2CM_MasterContinue(pI2C);
}
else {
/* If receive queued after transmit then initiate master receive transfer*/
if (xfer->rxSz) {
/* Write Address and RW bit to data register */
Chip_I2CM_WriteByte(pI2C, (xfer->slaveAddr << 1) | 0x1);
/* Enter to Master Transmitter mode */
Chip_I2CM_SendStart(pI2C);
}
else {
/* Send Stop */
Chip_I2CM_SendStop(pI2C);
}
}
break;
case I2C_STAT_MSTCODE_NACKADR:
/* Set transfer status as NACK on address */
xfer->status = I2CM_STATUS_NAK_ADR;
Chip_I2CM_SendStop(pI2C);
break;
case I2C_STAT_MSTCODE_NACKDAT:
/* Set transfer status as NACK on data */
xfer->status = I2CM_STATUS_NAK_DAT;
Chip_I2CM_SendStop(pI2C);
break;
default:
/* Default case should not occur*/
xfer->status = I2CM_STATUS_ERROR;
break;
}
}
else {
/* Default case should not occur */
xfer->status = I2CM_STATUS_ERROR;
}
return xfer->status != I2CM_STATUS_BUSY;
}