cancel
Showing results for 
Search instead for 
Did you mean: 

Control DMA byte transfer speed to GPIO port with timer?

CanisSomnolentus
Associate III

Currently I'm playing with a STM32F030 board, but if this one cannot do it but another STM32 can ... I have a bunch of older, but bigger STM32's, laying around... feel free to mention such one!

I would like to write bytes to a GPIO port with a strictly controlled speed, and in an ongoing / circular fashion.

When looking for examples for DMAing to GPIO, I usually find the mention of memory-to-memory mode, but that one does not allow circular mode - so I guess I can't use that. Or can I achieve the same result, with stable overall timing, in another way?

Or, can I actually use the mem-to-periph mode for GPIO?

I have used DMA with regards to ADC and DAC before, and AFAIR, those peripherals would take care the data word timing.

A GPIO port of course does not do that.

Then I looked at timer, e.g. TIM17 general purpose, w.r.t. DMA, and it seems the only way these two can be married is with writing the timer value to memory, unless I misunderstood.

Is there actually a way to achieve the stated goal - write data to a GPIO with a fixed speed?

(not using a CPU loop, that'd be too slow, I need something like 5 MHz byte clock)

5 REPLIES 5

> Or, can I actually use the mem-to-periph mode for GPIO?

Yes.

Prepare into RAM or FLASH a table of values you want to write into GPIO port. You want to write them into GPIOx_BSRR, read its description in RM, so you need 32-bit values (words). Note, that that table needs to be word-aligned ("normally" allocated uint32_t[] arrays will be).

Look up the appropriate channel for selected timer in the channel assignment table in DMA chapter of RM, note that there are some remaps possible in some of the 'F0xx (note that variants of 'F030 differ in details like these). For given channel, set address of table in memory into DMA_CMARx, set GPIOx_BSRR address into DMA_CPARx, set required number of transfers (number of elements in the array in memory) into DMA_CNDTRx, and into the control register DMA_CCRx MEM2MEM zero, PL any, MSIZE and PSIZE 32-bit word, MINC one, PINC zero, CIRC one, DIR 1 for memory-to-peripheral, interrupt enables as you wish (not needed initially), EN one.

Set up the timer to roll over at the required pace, i.e. appropriate value to TIMx_ARR (start with slower frequencies, 5MHz will be close to the limits and you don't want to push it at the beginning). Enable DMA by setting TIMx_DIER.UDE and enable timer's counter by setting TIMx_CR1.CEN.

Lean back and enjoy.

JW

Circular mode works for both memory to peripheral(gpio) and gpio to memory, circular mode works too. Look at HAL_TIM_OC_Start_DMA source. If you replace &htim->Instance->CCR1 with &GPIOA->ODR, then it will output memory buffer to GPIO at output compare trigger speed. You can even use input capture mode to perform memory to gpio transfer using external clock (for example, input capture TIM2_CH1 pin). For reading gpio pins to memory you can use &GPIOA->IDR.

Memory to GPIO quick test: Use STM32CubeIDE configurator, cofigure timer with output compare + circular DMA mode, generate code, copy-paste HAL_TIM_OC_Start_DMA() to your code, rename it to MY_TIM_OC_Start_DMA(), replace CCR1 with ODR for required GPIO.

CanisSomnolentus
Associate III

Thanks, I'll try that. The 4byte alignment is a bit unfortunatem, requiring 4 times the buffer size, but if the GPIO accepts only 32 writes, ah well.

BSRR is more nuanced

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

You *can* use ODR, and then you can write 16-bits or even 8-bits, but as Clive said above, using BSRR allows to manipulate individual pins.

Please don't use "alignment" instead of the correct "transfer size". That Cube uses "alignment", that does not make it correct.

JW