2025-05-19 7:16 AM
On an STM32G4, if an I2C error occurs (e.g. a bus error) during a DMA driven I2C transaction, is the intention that the HAL I2C driver abort the DMA transfer?
Starting at line 6599 in stm32g4xx_hal_i2c.c (v1.2.5), I see code in the I2C_ITError function that looks like it's intending to cancel an ongoing DMA transfer. However, when using the HAL_I2C_Master_Seq_*_DMA functions, it does not appear that it actually does.
Consider the case of a HAL_I2C_Master_Seq_Transmit_DMA, followed by a HAL_I2C_Master_Seq_Receive_DMA (a basic I2C write-read transaction with repeated start). At the end of the initial transmit, the HAL_I2C_EV_IRQHandler will call I2C_Master_ISR_DMA, which in turn calls I2C_ITMasterSeqCplt (line 5477), which will set the PreviousState variable of the I2C handle to I2C_STATE_MASTER_BUSY_TX (line 6014). Then, if an I2C error occurs during the receive DMA transfer, the HAL_I2C_ER_IRQHandler will call the I2C_ITError function (line 4699), which will fail to abort the receive DMA. That is because it checks PreviousState, which was set to I2C_STATE_MASTER_BUSY_TX, and then tries to abort the transmit DMA that is already complete rather than the receive DMA actually in progress (line 6602).
Note that this is not entirely hypothetical. While I can't be sure the sequence is exactly as I described above, I have encountered the case that an I2C error occurs during a DMA receive initiated by HAL_I2C_Master_Seq_Receive_DMA and the I2C_ITError function is called with hi2c->State = HAL_I2C_STATE_BUSY_RX and hi2c->PreviousState = I2C_STATE_MASTER_BUSY_TX, which by inspection will not abort the receive DMA.
I believe that this is the source (or at least a source) of an I2C interrupt storm that I have been tracking down. Because the receive DMA transfer is not aborted, the DMA complete interrupt fires after I2C_ITError, calling the I2C_DMAMasterReceiveCplt callback. That function re-enables the I2C interrupts (line 6823), specifically the STOP interrupt. The I2C event interrupt then fires due to the STOP flag, calling HAL_I2C_EV_IRQHandler. However, since hi2c->XferISR was set to NULL by the I2C_ITError function (line 6596), it immediately returns without clearing any interrupt flags, thus resulting in the interrupt firing again immediately, hanging the processor.
To me, this looks like a bug with the HAL, but maybe I'm misunderstanding the intention of the DMA abort code in I2C_ITError.
2025-05-19 7:31 AM
Hello @mjones37
How you can performe HAL_I2C_Master_Seq_Receive_DMA() after a transmit process not completed?
2025-05-19 8:43 AM
The transmit process does complete. From the application code, the sequence is this:
- HAL_I2C_Master_Seq_Transmit_DMA(hi2c, addr, data, size, I2C_FIRST_FRAME)
- wait for completion
- HAL_I2C_Master_Seq_Receive_DMA(hi2c, addr, data, size, I2C_LAST_FRAME)
2025-05-19 9:10 AM
Hello @mjones37
Thank you for bringing this issue to our attention.
I reported this internally.
Internal ticket number: 210078 (This is an internal tracking number and is not accessible or usable by customers).