There are two problems here with the M4 running at 204MHz. The first is the I2C state machine is not stable at this frequency and needs more time to settle. When changing from state 60 to state 80 it goes through state F8 before it gets to state 80. This can be captured by reading the states into an array. If you look at the code below you can see I captured the states in global array tmp[] so I could look at them after it crashes. You will see a for loop commented out at the beginning and the end. This will come in later. Transferring two bytes + address the states go as follows:
tmp[0] == 60
tmp[1] == F8
tmp[2] == 80
tmp[3] == F8
tmp[4] == 80
tmp[5] == F8
tmp[6] == A0
Ending in the Hard Fault Handler()
Uncommenting the for loops solves this problem, but you need one on enter from interrupt and you need one on exit back to interrupt. The F8 states go away and it runs normally.
tmp[0] == 60
tmp[1] == 80
tmp[2] == 80
tmp[3] == A0
Note there is a bug in the library in State 80 where it tests for: if (xfer->rxSz > 1). This must be changed to if (xfer->rxSz > 0) or you will never get your last byte.
/* Slave state machine handler */
int handleSlaveXferState(LPC_I2C_T *pI2C, I2C_XFER_T *xfer)
{
uint32_t cclr = I2C_CON_FLAGS;
int ret = RET_SLAVE_BUSY;
int State = 0;
int j;
/* There is a timing problem with slave mode and 204MHz, State machine needs more time to settle.
* Uncomment this to fix this problem.
*/
// for(j = 0; j < 50; j++);
xfer->status = I2C_STATUS_BUSY;
State = getCurState(pI2C);
tmp[indx++] = State; // Use for debugging State machine DJD 9-11-18
switch (State)
{
case 0x80: /* SLA: Data received + ACK sent */
case 0x90: /* GC: Data received + ACK sent */
*xfer->rxBuff++ = pI2C->DAT;
xfer->rxSz--;
ret = RET_SLAVE_RX;
if (xfer->rxSz > 0) { // Changed from (xfer->rxSz > 1) DJD 9-10-18
cclr &= ~I2C_CON_AA;
}
break;
case 0x60: /* Own SLA+W received */
case 0x68: /* Own SLA+W received after losing arbitration */
case 0x70: /* GC+W received */
case 0x78: /* GC+W received after losing arbitration */
xfer->slaveAddr = pI2C->DAT & ~1;
if (xfer->rxSz > 1) {
cclr &= ~I2C_CON_AA;
}
break;
case 0xA8: /* SLA+R received */
case 0xB0: /* SLA+R received after losing arbitration */
xfer->slaveAddr = pI2C->DAT & ~1;
case 0xB8: /* DATA sent and ACK received */
pI2C->DAT = *xfer->txBuff++;
xfer->txSz--;
if (xfer->txSz > 0) {
cclr &= ~I2C_CON_AA;
}
ret = RET_SLAVE_TX;
break;
case 0xC0: /* Data transmitted and NAK received */
case 0xC8: /* Last data transmitted and ACK received */
case 0x88: /* SLA: Data received + NAK sent */
case 0x98: /* GC: Data received + NAK sent */
case 0xA0: /* STOP/Repeated START condition received */
ret = RET_SLAVE_IDLE;
cclr &= ~I2C_CON_AA;
xfer->status = I2C_STATUS_DONE;
if (xfer->slaveAddr & 1) {
cclr &= ~I2C_CON_STA;
}
indx = 0; // Use for debugging DJD 9-11-18
break;
}
/* Set clear control flags */
pI2C->CONSET = cclr ^ I2C_CON_FLAGS;
pI2C->CONCLR = cclr & ~I2C_CON_STO;
/* There is a timing problem with slave mode and 204MHz, State machine needs more time
* to settle, otherwise it vectors to the Hard Fault Handler.
*/
// for(j = 0; j < 50; j++);
return ret;
}