cancel
Showing results for 
Search instead for 
Did you mean: 

DMA delay caused by Interrupts?

Arno Thiele
Associate III
Posted on September 04, 2017 at 12:33

On my STM32F411RE I send data to my SPI2 peripheral port using DMA and it works fine, but the moment I enable any kind of interrupts for the NVIC it completely scrambles up my stream of DMA data on my SPI2 - MOSI pin.

My basic configuration is rather simple. I use is an external DAC over SPI1 clocked by a TIM interrupt which is handled by the NVIC, plus an ADC on the SPI2 port trying to run it using DMA. Using an interrupt for the CS line on this ADC/SPI2/DMA port causes the same DMA data incoherency. It seems any interrupt that I enable does that.

I am really puzzled, since DMA is supposed to boost efficiency and handling things in hardware undisturbed by any processing cycles. If I leave out DMA the whole code works fine.

AN4031 makes some mention of DMA transfer delays and possible conditions that can cause it but it doesn’t seem to give much practical advice. My DMA init code is below and it works perfectly for sending 3 byte packages, as long as no interrupts are firing.

If anyone could point me into the right direction I would be more than grateful!

uint32_t var2Tx[4] ={0b00000000011111111111111111111111}; // has to be a 32bit array length 4 for some reason
 DMA_InitTypeDef dma_init;
 dma_init.DMA_BufferSize = 3;
dma_init.DMA_PeripheralBaseAddr = (uint32_t)(&(SPI2->DR));
dma_init.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable; // direct mode 
dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
/* Configure Tx DMA */
dma_init.DMA_Channel = DMA_Channel_0;
dma_init.DMA_DIR = DMA_DIR_MemoryToPeripheral;
dma_init.DMA_Memory0BaseAddr = (uint32_t) var2Tx;
dma_init.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Stream4, &dma_init);
DMA_Cmd(DMA1_Stream4, ENABLE); /* Enable the DMA SPI TX Stream */
/* Enable the SPI Tx DMA request */
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

3 REPLIES 3
Arno Thiele
Associate III
Posted on September 05, 2017 at 15:47

After some more testing I discovered this: When I move the DMA memory base address to RAM and copy the corresponding values from the flash memory (where my variable 'var2Tx' is automatically generated) to RAM the problem disappears. So it seems the CPU access to the vector table (which is also stored in flash) interferes with and blocks the DMA access to flash. 

I have to say in my project my flash memory is almost completely full with a huge wavetable that I have stored in there. I don’t know if that might be a reason for the conflicting access. But again, any enabled interrupt causes the DMA to dropout, not only interrupts that cause access to that wavetable in the flash, so the problem must be caused by access to the vector table in general.

I still don’t know if my solution just band aids a more fundamental problem so I am still happy about any insight from more experienced users about how to iron this out the best possible way. Thanks!

Arno Thiele
Associate III
Posted on September 12, 2017 at 23:35

It seems even if I get this to work it never works reliably without interfering too much with other functionalities that I need to reliably work in realtime. 

I am giving up on DMA. It seems the overhead it produces on the bus and interrupt system far outweighs the improved throughput and cpu relief. At least that is when using it in circular mode for a package of only 3 bytes. It doesn’t seem to be worth it. 

Plus it seems very sensitive for different base memory addresses. Some it seems to “likeâ€� others it doesn’t and the whole DMA stream collapses depending on where my compiler decides to place those addresses in memory. Also the length of the variable that I declare to read from seems to be extremely critical. I have to declare an array that is at least 5 bytes longer then the DMA is supposed to read from, otherwise the whole transfer shuts down as well. That just seems buggy and very unreliable to me. 

Posted on September 13, 2017 at 00:49

Well doing 3-5 seems to be overkill, doing a few 100 or 1000 bytes makes more sense given the overhead.

FLASH is slow, in the order of 35ns, the ART doesn't help DMA, so I'd expect byte reads to be expensive. Not sure what the data rate is here.

Can't say I've had any problem chaining DMA transactions out the USART, or via ADC or GPIO, so I don't believe there are fundamental issues. The DMA units have a relatively basic and uniform design. With TIM you have to be careful about the width of the data written to the registers.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..