There is a problem in the STM32F4 HAL I2C module driver. Current version (1.6.0), Cube Firmware 1.14.0.
When performing an interrupt mode IO MEM read, the ITBUFEN register in the I2C peripheral is not cleared before (or during) the period that the peripheral is clocking out the last byte of the peripheral's register address we are looking to read from. This causes the TxE event to continually generate an unnecessary interrupt for the complete duration this byte is being clocked out.
Attached are several photos of the event occurring on my oscilloscope. The lines are as follows:
1 (dark blue): SCL
2 (teal): SDA
3: Set to go high before HAL_I2C_EV_IRQHandler() is called, and set back low after HAL_I2C_EV_IRQHandler() returns.
4. The state of the ITBUFFEN flag. It is updated in HAL_I2C_EV_IRQHandler() right after the SR and CR registers are read. Before HAL_I2C_EV_IRQHandler() returns the value is updated from the registers.
As you can see, the interrupt is constantly triggering from the time the last data (register address) byte has been taken out of the DR, until the re-start condition. The same issue occurs when writing a 16bit register address, but it only presents itself in the last byte being written.
A patch is attached which will set low the ITBUFFEN register after the last byte to write is copied out of the DR, and will set high the ITBUFFEN register after the restart condition is clocked out (triggered by the SB = 1 interrupt.)
--- stm32f4xx_hal_i2c.c 2016-11-09 15:17:10.000000000 -0400
+++ stm32f4xx_hal_i2c_mbfix.c 2017-02-28 16:12:39.768615300 -0400
@@ -3835,6 +3835,8 @@
/* Generate Restart */
hi2c->Instance->CR1 |= I2C_CR1_START;
+ /* Disable BUF interrupt */
+ __HAL_I2C_DISABLE_IT(hi2c, I2C_IT_BUF);
else if(hi2c->State == HAL_I2C_STATE_BUSY_TX)
@@ -4092,6 +4094,8 @@
+ //Re-enable the buff interrupt
+ __HAL_I2C_ENABLE_IT(hi2c, I2C_IT_BUF);
hi2c->Instance->DR = I2C_7BIT_ADD_READ(hi2c->Devaddress);