cancel
Showing results for 
Search instead for 
Did you mean: 

HAL I2C abort DMA on I2C error

mjones37
Associate

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.

 

3 REPLIES 3
Saket_Om
ST Employee

Hello @mjones37 

How you can performe HAL_I2C_Master_Seq_Receive_DMA() after a transmit process not completed?

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
mjones37
Associate

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)

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).

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om