I am testing I2C error recovery (pulling SDA to zero externally - simulated noise) and I am using this code to read data from I2C each 500 ms:
Code: Select all
void getL3G4200GyroData(L3G4200Handle *handle, L3G4200GyroData *data){
uint8_t reg_addr = L3G4200_GYRO_DATA;
msg_t msg = i2cMasterTransmitTimeout(handle->i2cd, L3G4200_I2C_ADDRESS, ®_addr, 1, (uint8_t*)data, 6, I2C_TIMEOUT);
if (msg!=RDY_OK) {
chprintf((BaseChannel *)&SD2, "I2C failed");
i2cStop(&I2CD1);
I2C1->CR1 |= I2C_CR1_SWRST;
i2cStart(&I2CD1, &i2cCFG);
}
}
Sometimes the i2cMasterTransmitTimeout function returns error as I expected, but sometimes the MCU gets stuck in I2C ISR. This stuck state of I2C is captured on attached debug screenshot. I think that i2c_lld_serve_event_interrupt() function should include some error handling options. In error state there is set BERR bit in I2C_SR1 register.
Reference manual says:
Bit 8 BERR: Bus error
0: No misplaced Start or Stop condition
1: Misplaced Start or Stop condition
–Set by hardware when the interface detects an SDA rising or falling edge while SCL is high,
occurring in a non-valid position during a byte transfer.
–Cleared by software writing 0, or by hardware when PE=0.
As the i2c_lld_serve_event_interrupt() function doesn't perform any action to clean this bit, the interrupt is fired again and again. ITERREN in I2C_CR2 is set and refman says:
Bit 8 ITERREN: Error interrupt enable
0: Error interrupt disabled
1: Error interrupt enabled
This interrupt is generated when:
– BERR = 1
– ARLO = 1
– AF = 1
– OVR = 1
– PECERR = 1
– TIMEOUT = 1
– SMBALERT = 1
I think that i2c error should be serviced and information should be passed back to "user space". I know that this sort of bus errors should never occur, but in real world I have to be prepared for everything and MCU freeze is not an option in any case.
I would appreciate any advice to make the I2C code "bulletproof".
Environment: ChibiOs 2.4.2, STM32F100x6