cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 SPI DMA Timings

G�bor V�rkonyi
Associate
Posted on February 13, 2017 at 16:38

Hello all,

I'm trying to setup the STM32L432 MCU for SPI communication (24 MHz, 16 bit data size, master transmit only), using DMA.

The slave device is a 32 channel amplifier chip. I need to send a specific string of bits only once at the start, to setup the registers on the slave device, and then continuously send another string of bits to let the slave know to keep sampling and cycling through its 32 channels, until it receives a stop command or powers down.

I've generated a code using CubeMx, with the appropriate parameters setup. I've been using the HAL_SPI_Transmit_DMA command to send the data. I'm getting the correct SCLK and MOSI signals however am not getting the correct timings between successive SCLK bursts. My two questions are:

  1. The timing between the successive SCLK bursts is too short when Circular DMA transmission is enabled (shown in screenshot)

    https://i.stack.imgur.com/xLHNE.png

    . I would need it to be roughly twice as big (about ~200 ns) Where can I control the timing of this? If I enable Normal DMA and put the HAL_SPI_Transmit_DMA command within a while loop I get the opposite problem, the gap between the successive SCLK bursts become too big, about 5 us.
  2. How could I setup two different DMA profiles to operate withing one script, and call them up when required? I would need this as the bit stream dataTx2 only needs to be sent once so would require DMA to be set to normal, while dataTx would needed to be sent continuously using circular DMA.

The code can be found here:

https://github.com/varkong/SPI-DMA-Ver-C/tree/master/Src

Thank you in advance,

varkong

4 REPLIES 4
Oliver Beirne
Senior
Posted on February 13, 2017 at 17:40

Hello

I have moved your question to the

https://community.st.com/community/stm32-community/stm32-forum?sr=search&searchId=bde084e7-e05c-42ff-8111-b449e817ff22&searchIndex=0

‌ where someone should be able to assist you.

Thanks

Oli

Posted on February 13, 2017 at 18:13

I don't Cube/CubeMX, but if you used the NSS pulse mode (see 37.4.12  NSS pulse mode in RM0394) and you feed the SPI continuously from DMA, the pulse is one SPI clock long.

A simple trick to achieve longer spacing between frames is to trigger the DMA not from SPI itself, but from a timer. As I said I don't Cube, but I don't believe this is something simply clickable in CubeMX so you might need to dig deeper. That then will solve you q.2 too.

JW

Posted on July 24, 2017 at 17:58

Hello Jan,

Thank you for your response. I am revisiting this problem, as I got sidetracked by some other tasks. I contacted ST regarding your proposed solution and they said that: 'There is no way to have a link between a SPI peripheral and a DMA triggered by a timer.' Could you please elaborate on what you had in mind?

All the best,

GV

Posted on July 24, 2017 at 23:45

I assume you know how to enable relevant modules' clocks in RCC.

Set up SPI completely (including enable) as you've set it up previously, except that don't enable its DMA requests (don't set SPIx_CR2.TXDMAEN). Duration of SPI frame is now T1=(16+1)xSPI_bit_time, and you want it to be more, say T2>T1. Take a timer TIMx and set its TIMx_ARR and TIMx_PSC so that its overflow (i.e. 'update' in ST's TIM terminology) rate is T2. Set TIMx_DIER.UDE to enable the timer to output DMA requests upon update. In DMA chapter, find, to which DMA, which channel and which request number maps the given TIMx_UP request. For that DMA, in its DMAx_CSELR register, set the channel request mapping so for the given channel it maps to TIMx_UP. Clear the flags relevant to given channel through DMA_IFCR. Set DMA_CPARx to the address of SPIx_DR, set DMA_CMARx to the address of array of halfwords in memory (make sure it's properly aligned) which are to be transmitted, DMA_CNDTRx to the number of halfwords to be transmitted. Now set DMA_CCRx to transfer halfwords, from memory to peripheral, noncircular, choose if you want any interrupts to be fired, and enable. Finally, enable the timer, by setting TIMx_CR1.CEN.

Easy-peasy 😉

JW