Disclaimer:
I have submitted a forum post on Microchip as well as the forum appears to have issues with me and to increase the pool of possible answers I thought of posting here with as much detail as I could.
Issue:
Using MPLABX 5.10, MCC (Microchip Code Configurator plugin) 3.75 with I2C Driver version 2.11, XC8 v 2.00
MCC generated I2C Slave code is incrementing data address pointer when it shouldn't
Reproduction:
Run MCC with PIC18F47K42 project and add I2C slave and generate provides the code below (I have added some debug uart stuff hence the queue_enqueue
calls):
void I2C1_ISR(void)
{
uint8_t I2C1_data = 0x55;
if ((I2C1STAT1bits.RXBF) || (PIR2bits.I2C1RXIF))
{
PIR2bits.I2C1RXIF = 0;
I2C1_data = I2C1RXB;
queue_enqueue(&uart_queue, "1");
}
if (1 == I2C1STAT0bits.R)
{
if (I2C1PIRbits.PCIF)
{
I2C1PIRbits.PCIF = 0;
PIR3bits.I2C1IF = 0;
I2C1STAT1bits.CLRBF = 1; //clear I2C1TXB and TXBE
I2C1CNT = 0xFF;
queue_enqueue(&uart_queue, "2");
}
if (I2C1ERRbits.NACKIF)
{
I2C1ERRbits.NACKIF = 0;
PIR3bits.I2C1EIF = 0;
I2C1STAT1bits.CLRBF = 1; //clear I2C1TXB and TXBE
I2C1_StatusCallback(I2C1_SLAVE_READ_COMPLETED);
queue_enqueue(&uart_queue, "3");
}
else if (PIR3bits.I2C1TXIF)
{
PIR3bits.I2C1TXIF = 0;
// callback routine should write data into I2C1TXB
I2C1_StatusCallback(I2C1_SLAVE_READ_REQUEST);
queue_enqueue(&uart_queue, "4");
}
if (I2C1PIRbits.ADRIF)
{
I2C1PIRbits.ADRIF = 0;
PIR3bits.I2C1IF = 0;
queue_enqueue(&uart_queue, "5");
}
}
else if ((I2C1PIRbits.ADRIF))
{
I2C1PIRbits.ADRIF = 0;
PIR3bits.I2C1IF = 0;
// callback routine should prepare to receive data from the master
I2C1_StatusCallback(I2C1_SLAVE_WRITE_REQUEST);
queue_enqueue(&uart_queue, "6");
}
else
{
I2C1_slaveWriteData = I2C1_data;
queue_enqueue(&uart_queue, "7");
if (I2C1PIRbits.PCIF)
{
I2C1PIR = 0;
PIR3bits.I2C1IF = 0;
I2C1STAT1bits.CLRBF = 1;
I2C1CNT = 0xFF;
queue_enqueue(&uart_queue, "8");
}
// callback routine should process I2C1_slaveWriteData from the master
I2C1_StatusCallback(I2C1_SLAVE_WRITE_COMPLETED);
}
I2C1CON0bits.CSTR = 0;
}
eeprom simulated code MCC also provides:
static uint8_t EEPROM_Buffer[] ={
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
};
static uint8_t eepromAddress = 0;
static uint8_t eepromData = 0x55;
static uint8_t slaveWriteType = SLAVE_NORMAL_DATA;
switch (i2c1_bus_state)
{
case I2C1_SLAVE_WRITE_REQUEST:
// the master will be sending the eeprom address next
slaveWriteType = SLAVE_DATA_ADDRESS;
break;
case I2C1_SLAVE_WRITE_COMPLETED:
switch (slaveWriteType)
{
case SLAVE_DATA_ADDRESS:
eepromAddress = I2C1_slaveWriteData;
break;
case SLAVE_NORMAL_DATA:
default:
// the master has written data to store in the eeprom
EEPROM_Buffer[eepromAddress++] = I2C1_slaveWriteData;
if (sizeof (EEPROM_Buffer) <= eepromAddress)
{
eepromAddress = 0; // wrap to start of eeprom page
}
break;
}
slaveWriteType = SLAVE_NORMAL_DATA;
break;
case I2C1_SLAVE_READ_REQUEST:
if (I2C1STAT1bits.TXBE)
{
I2C1TXB = EEPROM_Buffer[eepromAddress++];
}
if (sizeof (EEPROM_Buffer) <= eepromAddress)
{
eepromAddress = 0; // wrap to start of eeprom page
}
break;
case I2C1_SLAVE_READ_COMPLETED:
default:;
} // end switch(i2c1_bus_state)
}
Set the eepromAddress to 0x00 by writing one byte to slave
i.e.
[SLAVE ADDR WRITE] [0x00]
Then read 8 bytes supposedly starting from register 0x00
i.e.
[SLAVE ADDR READ] [B0] [B1] [B2] ...
As the "EEPROM" data should match the address I would expect the data 0x00, 0x01, 0x02 etc.... Instead I get 0x01, 0x02, 0x03 so it looks like eepromAddress has incremented somewhere by accident.
Looking at my UART output for [SLAVE ADDR WRITE] [0x00]
I got:
6
1
7
7
8
Which means
6 (I2C1_SLAVE_WRITE_REQUEST)
1 (I2C1_data = I2C1RXB)
7 (I2C1_StatusCallback(I2C1_SLAVE_WRITE_COMPLETED))
7 (I2C1_StatusCallback(I2C1_SLAVE_WRITE_COMPLETED))
8 (WRITE STOP)
What is see wrong is that it is calling I2C1_StatusCallback(I2C1_SLAVE_WRITE_COMPLETED) twice when it should only be once as I only want to set the eepromAddress and not set any data. I.e. it will be going into SLAVE_NORMAL_DATA when it should only go into SLAVE_DATA_ADDRESS, and therefore incrementing eepromAddress in the line EEPROM_Buffer[eepromAddress++] = I2C1_slaveWriteData
switch (slaveWriteType)
{
case SLAVE_DATA_ADDRESS:
eepromAddress = I2C1_slaveWriteData;
break;
case SLAVE_NORMAL_DATA:
default:
// the master has written data to store in the eeprom
EEPROM_Buffer[eepromAddress++] = I2C1_slaveWriteData;
if (sizeof (EEPROM_Buffer) <= eepromAddress)
{
eepromAddress = 0; // wrap to start of eeprom page
}
break;
}
The same thing also happens with continuous read
First read (8 bytes)
Second read (8 bytes)
Note that 0x09 is missing in the second read
Debugging with UART it appears that I2C1_StatusCallback(I2C1_SLAVE_READ_REQUEST) is called nine times instead of eight times.
Has any one else been able to confirm this? Is it a bug with MCC I2C driver for this PIC? Is there an easy work around to solve the issue?