cancel
Showing results for 
Search instead for 
Did you mean: 

Issues with HAL_SPI_TransmitReceive_DMA

crwper
Senior

Hi all--

I am using HAL_SPI_TransmitReceive_DMA to retrieve data periodically from an inertial sensor. I receive an EXTI interrupt when the sensor indicates that data is ready, and in response to this I call HAL_SPI_TransmitReceive_DMA to retrieve 15 bytes of data from the sensor, as follows:

HAL_StatusTypeDef res;
 
/* Register address with read flag */
dataBuf[0] = LSM6DS3H_OUT_TEMP_L_REG | 0x80;
 
/* Begin data transfer */
CS_LOW();
res = HAL_SPI_TransmitReceive_DMA(&hspi1, dataBuf, dataBuf, 15);

In response to HAL_SPI_TxRxCpltCallback, I complete the transfer:

CS_HIGH();
 
/* Translate the contents of dataBuf here */

However, once in a while, when I receive the signal from the sensor, I go to retrieve data only to have HAL_SPI_TransmitReceive_DMA return HAL_BUSY.

Looking into this, I noticed that HAL_SPI_TxRxCpltCallback is not being called. In fact, if I chase this all the way back to HAL_DMA_IRQHandler, I see that the IRQ handler is never called to indicate that the transfer is complete.

Looking into it a little further, I had a look at the DMA state just before the failed call to HAL_SPI_TransmitReceive_DMA. This showed me:

  • hspi1.hdmarx->Instance->CNDTR is equal to 1
  • hspi1.hdmatx->Instance->CNDTR is equal to 0

This strikes me as really odd. Under normal conditions, both of these are equal to 0 when the transfer is complete. Since the processor is generating the SPI clock, it's not clear to me how the transmitted and received byte counts could be different.

I am using an STM32WB55RG processor and STM32Cube_FW_WB_V1.8.0.

Does anyone have an ideas? Thanks for any help you can offer!

5 REPLIES 5

Rx overrun?

Do you have an SPI error interior in place? I don't Cube.

Try to decrease SPI clock rate as the first experiment.

JW

crwper
Senior

I've got a bit more information on the issue. I ran the test again with a logic analyzer in place. Just before the place where HAL_SPI_TransmitReceive_DMA would return HAL_BUSY, I break and look at the contents of the buffer and the output of the logic analyzer. The following table compares the two:

0693W000004JfTDQA0.png 

Here is the output of the logic analyzer:

0693W000004JfTmQAK.png 

A couple of observations:

  1. At position 0, the IMU transmits 0xff, but instead we see the value from MOSI, 0xa0, in the buffer (debug).
  2. At position 9, the IMU transmits 0x0b, but this is not received. Here, we see an omission in the buffer.
  3. Finally, because of the omission at position 9, the buffer is never filled, which is why we don't get an end of transfer interrupt.

To me, this doesn't seem like a clock issue, since we're not seeing individual bits missing, but rather one complete byte which is definitely being transmitted by the IMU but is never moved from the SPI peripheral into memory by DMA. Interestingly, the next byte starts exactly where it should, which suggests to me that the SPI peripheral received the byte correctly but DMA failed to move it to the buffer.

Here is a closeup of the missed byte:

0693W000004JfULQA0.png 

To my eye, there are no obvious issues with the signal here.

What could cause DMA to miss a single byte?

Michael

crwper
Senior

I think I may have a partial answer to my own question. My DMA priority list looks like this:

0693W000004JfanQAC.png 

SPI1 is what's being used for the IMU, so SPI1_RX would have the lowest priority (high channel number and low priority), while SPI1_TX would have very high priority (being the only channel used in DMA2). What I'm imagining could be happening is that the higher priority peripherals keep DMA1 busy for the entire duration between between two bytes received on SPI1, so that the byte sitting in SPI1 at that point never gets transferred to from SPI1 to memory.

Here's roughly how the peripherals listed above are being used:

  • SPI2 is an interface with a microSD card used for logging sensor data
  • SAI1 is used for audio output
  • USART1 is used to receive GNSS data
  • I2C3 is used to receive data from several I2C sensors
  • SPI1 is used to receive data from the IMU

I am at a bit of a loss to say how these could be divided between the two DMA peripherals and assigned priorities in such a way as to avoid the kind of conflict described above. It seems like, no matter how I arrange the peripherals, higher-priority peripherals could always conspire to prevent a byte being received properly by a lower-priority peripheral.

That said, it seems like I could reduce the likelihood of this kind of collision, e.g., by making better use of the two DMA peripherals, or perhaps by giving higher-speed transfers a higher priority. This leads to a question: I can't seem to find a way in STM32CubeMX to assign the DMA peripheral/channel manually. It seems like the system simply assigns the next available peripheral/channel to whatever I add next. Is it possible to adjust these things manually in STM32CubeMX?

Also, I would love to hear from others how they have dealt with this kind of conflict. Is there a solution which would ensure that the conflict never occurs, instead of just becoming very rare?

Michael

alister
Lead

> once in a while, when I receive the signal from the sensor, I go to retrieve data only to have HAL_SPI_TransmitReceive_DMA return HAL_BUSY.

Can you prove your low-DMA-priority theory by keeping the other DMA activity steady, half the SPI1 frequency and confirm the incidence of missed-write disappears or is significantly reduced?

I can do even better, I think. I modified the firmware so that SPI1_RX is on DMA2 instead of DMA1. With only this change, the firmware goes from failing after about 30 minutes of runtime to running more than 19 hours without failure. Reverting back to the original firmware, just to be sure, I again get failure after about 30 minutes of runtime.

Now, the big question for me is, what are best practices for setting DMA priority etc.? Is it possible to ensure that this situation can't happen?

Michael