ASFv4 I2C async slave bugs

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all,

 

I am posting this to document this issue for anyone else googling for it.

 

I currently have a platform setup as follows:

  • A non-atmel I2C Master setup with a message structure of write 4 bytes then read 4 bytes.
  • A SAMD21J18A setup as the I2C Slave to interface with the master.

 

During previous version of my platform, I was implementing the I2C Slave Callback system in ASF v3. During this time I am able to send and receive bytes with no issue.

 

However, once I migrated to ASFv4 I have found that the Slave transmits bytes out of order. For Example, If the I2C slave is setup to have a tx buffer of 0x01020304, The first time there is a master read, it will read 0x01020304 correctly. However, After successive reads , the bytes will be shifted by one. Example, 2nd read -> 0x02030401, 3rd read -> 0x03040102, 4th read -> 0x04010203, 5th read -> 0x01020304.

 

After looking under the hood, I have found that in  _sercom_i2c_s_irq_handler(hpl_sercom.c), the tx callback (device->cb.tx()) was being called 6 times. Within the associated internal callback to the bus "i2c_s_async_tx", the tx_por was operating correctly and once it got to 5 when the buffer_length was 4; it correctly cleared the por and buffer length and called the tx_complete callback. However, once it is called a 6th time, it re-indexes the por and so by the time the Master actually comes around again we are already incremented on the buffer by one so everything gets shifted off by one. Breaking on the interrupt flags in "_sercom_i2c_s_irq_handler" I have found that:

1) The wrong flags are being read. The following line is referencing the master instead of the slave flags.

  • uint32_t flags = hri_sercomi2cm_read_INTFLAG_reg(hw);
  • instead of uint32_t flags = hri_sercomi2cs_read_INTFLAG_reg(hw);

2) The DRDY interrupt seemed to not be getting cleared after a stop condition has been detected causing another interrupt.

 

After posting to Microchip I have received the following response:

Hi Paul,

 

These are indeed bugs in the ASFv4 i2c driver. I have already reported this to the concerned team, as the issue was identified while debugging a similar case.

The patch you have applied is correct.

Following is the fixed implementation.

static void _sercom_i2c_s_irq_handler(struct _i2c_s_async_device *device)
{
	void *  hw  = device->hw;
	uint32_t flags = hri_sercomi2cs_read_INTFLAG_reg(hw);

	if (flags & SERCOM_I2CS_INTFLAG_DRDY) {
		if (!hri_sercomi2cs_get_STATUS_DIR_bit(hw)) {
			ASSERT(device->cb.rx_done);
			device->cb.rx_done(device, hri_sercomi2cs_read_DATA_reg(hw));
		} else {
			ASSERT(device->cb.tx);
			device->cb.tx(device);
		}
		hri_sercomi2cs_clear_INTFLAG_DRDY_bit(hw);
	}
	// clear AMATCH interrupt flag
	else if (flags & SERCOM_I2CS_INTFLAG_AMATCH) {
		hri_sercomi2cs_clear_INTFLAG_AMATCH_bit(hw);
	}

	// clear OREC interrupt flag
	else if (flags & SERCOM_I2CS_INTFLAG_PREC) {
		hri_sercomi2cs_clear_INTFLAG_PREC_bit(hw);
	}
}

Also, the AMATCH interrupt is not enabled by the driver. This has to be done manually after the i2c enable.

i2c_s_async_enable(&I2C_0);
// Enable AMATCH interrupt
((Sercom *)I2C_0.device.hw)->I2CS.INTENSET.reg |= SERCOM_I2CS_INTENSET_AMATCH;

 

This seems to have cleared up the issue for me but if still having issues please also see the following:

 

Thanks