cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA transmit complete interrupt to early

Nickelgrass
Senior II

Hello,

I have an STM32G441 and using DMA to transmit some SPI data. All works so far. The problem is the chipselect. I am using the software chipselect. So before I start the DMA I pull CS low. Then when the DMA complete interrupt fires I pull it high again. The problem is, it cuts off about 3 or 4 bytes. This is obvious because the DMA is done while there is still SPI data in the buffer. 

What is the go-to solution for this? How can I reliably get an interrupt when the DMA is done and the SPI is done transmitting all data? At the moment I simply start a timer when the DMA is done that triggers a second interrupt after a certain time that I measure with a logic analyzer. Seems a bit makeshift and has to be adjusted each time the baudrate is changed. I also noticed that all the SPI interrupts fire premature. So is it the only solution to waste a timer on the SPI?

1 ACCEPTED SOLUTION

Accepted Solutions
waclawek.jan
Super User

Handle the CS in the Rx DMA interrupt. That won't happen before all data are shifted out, too (there's a marginal hazard with certain CPOL/CPHA combinations but that often can be solved by relatively simple delays; or use other CPOL/CPHA combination).

It doesn't matter that you don't need Rx. You can DMA into a "dummy" single memory address (i.e. no memory increment), and you don't need to assign any pin in GPIO to it. This is just to provide the timing.

JW

View solution in original post

6 REPLIES 6
TDK
Super User

You could use the HAL library which handles this correctly. Set CS high in the transmit complete callback.

If you want to do it yourself, you will need to poll the BSY flag to determine when the transfer is complete. The reference manual discusses this in some detail.

TDK_0-1770227148567.png

It's not an elegant or efficient solution, but that is what is available on this chip. Later families have better handling of the CS line and better ways to do this without polling.

If you feel a post has answered your question, please click "Accept as Solution".
Andrei Chichak
Lead II

Assume that the DMA complete interrupt happens when the DMA is complete. The SPI device transmit register got loaded and is in the process of shifting out the data, but the DMA is done, and DMA likely deposited a 32-bit pile of data, say, 3 or 4 bytes. DMA done, SPI busy.

You'll have to query the SPI device to determine when it is finished shifting out/in the data.

waclawek.jan
Super User

Handle the CS in the Rx DMA interrupt. That won't happen before all data are shifted out, too (there's a marginal hazard with certain CPOL/CPHA combinations but that often can be solved by relatively simple delays; or use other CPOL/CPHA combination).

It doesn't matter that you don't need Rx. You can DMA into a "dummy" single memory address (i.e. no memory increment), and you don't need to assign any pin in GPIO to it. This is just to provide the timing.

JW

Thanks, that's a nifty solution. Although it does sacrifice a DMA channel. But that is not so expensive as a timer as I don't need many DMAs. I will check it out and report the result. 

Hello,

so it worked but there seems to be a slight detail that the RX DMA quit to early. Reading the SPI DR seems to solve this. Strange though it would do that. 

waclawek.jan
Super User

Try to use the other CPHA setting, i.e. if you've used mode 0 (CPOL=0, CPHA=0), try mode 3 (CPOL=1, CPHA=1).

This may or may not suit your slaves' requirements, though.

JW