Use the I2C in an M4 as slave

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Use the I2C in an M4 as slave

885 Views
dandahl
Contributor I

I am trying to get the I2C in an LPC4337 to work as a Slave receiver and having no luck. I have followed the recommended settings from the UM but still does not work. I have an LpcXpresso4337 and I am driving the I2C with an Aardvark I2C/SPI module from Total Phase. I am developing using MCUXpresso 10.2 using the LpcOpen 3.02 library. I have configured the pins in the SCU as I2C and turned on the input buffers. I have configured all 4 slave address registers to the same address and unused mask regs to zero. I am trying to run in interrupt mode. The interrupt is enabled in the NVIC. The CON has I2EN and AA set and all others cleared. When I send a packet I never get an interrupt and the status is stuck at F8. Is there any example code on how to do this?

Labels (3)
0 Kudos
5 Replies

570 Views
FelipeGarcia
NXP Employee
NXP Employee

Hi Dan,

I received some comments about this:

 I test periph_i2c example code in the lpcopen_3_02_lpcxpresso_xpresso4337 based LPCXpresso4337 board.

   Result: LPC4337 as slaver can success in transmission under 204MHz SystemCoreClock.

periph_i2c.PNG

The terminal show as below:

   

**************** I2C Demo Menu ****************
0: Exit Demo
1: Select I2C peripheral [I2C1]
2: Toggle mode POLLING/INTERRUPT [INTERRUPT]
3: Probe for Slave devices
4: Read slave data
5: Write slave data
6: Write/Read slave data

Select an option [0 - 6] :5
Enter 7-Bit Slave address : 0x5a
Enter number of bytes to write : 5
1:Enter Data: 0
2:Enter Data: 1
3:Enter Data: 2
4:Enter Data: 3
5:Enter Data: 4
Written 5 bytes of data to slave 0x5A.
**************** I2C Demo Menu ****************
0: Exit Demo
1: Select I2C peripheral [I2C1]
2: Toggle mode POLLING/INTERRUPT [INTERRUPT]
3: Probe for Slave devices
4: Read slave data
5: Write slave data
6: Write/Read slave data

Select an option [0 - 6] :6
Enter 7-Bit Slave address : 0x5a
Enter number of bytes to read : 64
Enter number of bytes to write : 1
1:Enter Data: 0
Master transfer : SUCCESS
Received 64 bytes from slave 0x5A

00: 01 02 03 04 FF FF FF FF FF FF FF FF FF FF FF FF
10: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
20: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
30: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
**************** I2C Demo Menu ****************
0: Exit Demo
1: Select I2C peripheral [I2C1]
2: Toggle mode POLLING/INTERRUPT [INTERRUPT]
3: Probe for Slave devices
4: Read slave data
5: Write slave data
6: Write/Read slave data

Select an option [0 - 6] :

Could you please try the example and see if you find the same issues?

Best regards,

Felipe.

0 Kudos

570 Views
FelipeGarcia
NXP Employee
NXP Employee

Hi Dan,

Thanks for reporting this issues.

I already made a report to the development team.

Best regards,

Felipe

0 Kudos

570 Views
dandahl
Contributor I

So it seems that the 7 bit address must be shifted up one before it is initialized the 4 slave address registers. Once you do this then you can get to state 60 and get an ACK. Next I am trying to write a byte after the address is accepted, but this is not working. After the address with the write flag is ACK'd the next byte gets NACK'd and the code goes the the hard fault handler. Now trying to sort this out.

0 Kudos

570 Views
dandahl
Contributor I

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;
}

0 Kudos

570 Views
dandahl
Contributor I

I forgot to mention that your interrupt handler should look like this:

// Interrupt handler for the I2C interrupt.
void I2C0_IRQHandler(void)
{
   if (Chip_I2C_IsMasterActive(I2C0))
   {
      Chip_I2C_MasterStateHandler(I2C0);
   }
   else
   {
      Chip_I2C_SlaveStateHandler(I2C0);
   }

} // end I2C0_IRQHandler()

Thanks

Dan Dahl

0 Kudos