AnsweredAssumed Answered

STM32F4 timer driven DMA

Question asked by jd on Jul 19, 2016
Latest reply on Jul 19, 2016 by waclawek.jan
Hi,

Relative STM32 newbie, be gentle :)

I'm trying to adapt a DMA driven driver for WS2812 on STM32F4 (https://github.com/g4lvanix/0xWS2812)

I verified the clock by driving the output on CC1 but I cannot seem to trigger the DMA. I've looked at the datasheet for STM32F411CE and the timer, DMA channel/stream mapping seems to be correct.

I'm overlooking something very obvious, would appreciate any help.
001.#include <stdio.h>
002.#include <stdlib.h>
003.#include <stdint.h>
004.#include <string.h>
005.#include <stdbool.h>
006.#include <stddef.h>
007.#include <sys/types.h>
008. 
009.#include "stm32f4xx.h"
010.#include "stm32f4xx_tim.h"
011.#include "stm32f4xx_dma.h"
012.#include "stm32f4xx_rcc.h"
013.#include "system_stm32f4xx.h"
014. 
015.#define  WS2812_CLOCK_FREQUENCY      (100000000)                                                    // 100MHz <- clock
016.#define  WS2812_TIMER_FREQUENCY      (800000)                                                       // 800KHz <- Tim3
017.#define  WS2812_TIMER_PERIOD         (uint16_t)((WS2812_CLOCK_FREQUENCY / WS2812_TIMER_FREQUENCY))  // 1.25us
018.#define  WS2812_TIMER_PERIOD_LOW     (uint16_t)(WS2812_TIMER_PERIOD * 0.28)                         // 0.35us
019.#define  WS2812_TIMER_PERIOD_HIGH    (uint16_t)(WS2812_TIMER_PERIOD * 0.72)                         // 0.90us
020.#define  WS2812_TIMER_DP_OVERFLOW    (40)                                                           // 50us dead period, 1.25us overflow 40x
021. 
022.#define  WS2812_COUNT                (1)
023.#define  WS2812_BITS_PER_LED         (24)
024.#define  WS2812_FRAMEBUFFER_SIZE     (WS2812_COUNT * WS2812_BITS_PER_LED)
025. 
026.static struct {
027.  // frame buffer, 2 bytes per actual bit.
028.  uint16_t framebuffer[WS2812_FRAMEBUFFER_SIZE];
029.  // gpio.
030.  GPIO_TypeDef *port;
031.  // pin.
032.  uint32_t     pin;
033.  // io pin mask = (1 << pin)
034.  uint16_t     iomask;
035. 
036.  // overflows
037.  size_t overflows;
038.  size_t cc1;
039.  size_t cc3;
040. 
041.  size_t dma0, dma1, dma2;
042.} ws2812;
043. 
044.void DMA1_Stream2_irq(void) {
045.  if (DMA_GetITStatus(DMA1_Stream2, DMA_IT_TCIF2)) {
046.    DMA_ClearITPendingBit(DMA1_Stream2, DMA_IT_TCIF2);
047.    ws2812.dma0++;
048.  }
049.}
050. 
051.void DMA1_Stream4_irq(void) {
052.  if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4)) {
053.    DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
054.    ws2812.dma1++;
055.  }
056.}
057. 
058.void DMA1_Stream7_irq(void) {
059.  if (DMA_GetITStatus(DMA1_Stream7, DMA_IT_TCIF7)) {
060.    DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7);
061.    ws2812.dma2++;
062. 
063.    // Enable dead period hold.
064.    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
065. 
066.    // Disable DMA channels.
067.    DMA_Cmd(DMA1_Stream2, DISABLE);
068.    DMA_Cmd(DMA1_Stream4, DISABLE);
069.    DMA_Cmd(DMA1_Stream7, DISABLE);
070. 
071.    // Disable TIM3 DMA requests.
072.    TIM_DMACmd(TIM3, TIM_DMA_CC1,    DISABLE);
073.    TIM_DMACmd(TIM3, TIM_DMA_CC3,    DISABLE);
074.    TIM_DMACmd(TIM3, TIM_DMA_Update, DISABLE);
075.  }
076.}
077. 
078.void TIM3_irq(void) {
079.  if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) {
080.    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
081.    ws2812.cc1++;
082.  }
083. 
084.  if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET) {
085.    TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
086.    ws2812.cc3++;
087.  }
088. 
089.  if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
090.    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
091.    ws2812.overflows++;
092.  }
093. 
094.  if (ws2812.overflows > WS2812_TIMER_DP_OVERFLOW) {
095.    TIM_Cmd(TIM3, DISABLE);
096.    TIM_ITConfig(TIM3, TIM_IT_Update|TIM_IT_CC1|TIM_IT_CC3, DISABLE);
097.  }
098.}
099. 
100.static void timer_initialize(void) {
101.  TIM_TimeBaseInitTypeDef timer_base_config;
102.  memset(&timer_base_config, 0, sizeof(timer_base_config));
103. 
104.  // enable clock
105.  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
106. 
107.  // TIM3 @800KHz
108.  timer_base_config.TIM_Period      = WS2812_TIMER_PERIOD - 1;
109.  timer_base_config.TIM_Prescaler   = 0;
110.  timer_base_config.TIM_CounterMode = TIM_CounterMode_Up;
111.  TIM_TimeBaseInit(TIM3, &timer_base_config);
112. 
113.  // Enable preload.
114.  TIM_ARRPreloadConfig(TIM3, ENABLE);
115. 
116.  TIM_OCInitTypeDef timer_oc_config;
117.  memset(&timer_oc_config, 0, sizeof(timer_oc_config));
118. 
119.  // Low / SET @ 0.35us
120.  timer_oc_config.TIM_OCMode      = TIM_OCMode_Timing;
121.  timer_oc_config.TIM_OutputState = TIM_OutputState_Disable;
122.  timer_oc_config.TIM_Pulse       = WS2812_TIMER_PERIOD_LOW - 1;
123. 
124.  TIM_OC1Init(TIM3, &timer_oc_config);
125.  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
126. 
127.  // High / RST @ 0.9us
128.  timer_oc_config.TIM_OCMode      = TIM_OCMode_PWM1;
129.  timer_oc_config.TIM_OutputState = TIM_OutputState_Disable;
130.  timer_oc_config.TIM_Pulse       = WS2812_TIMER_PERIOD_HIGH - 1;
131. 
132.  TIM_OC3Init(TIM3, &timer_oc_config);
133.  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
134. 
135.  // Enable DMA transfer complete interrupt.
136.  NVIC_InitTypeDef nvic_config;
137.  memset(&nvic_config, 0, sizeof(nvic_config));
138.  nvic_config.NVIC_IRQChannel                   = TIM3_IRQn;
139.  nvic_config.NVIC_IRQChannelPreemptionPriority = 0;
140.  nvic_config.NVIC_IRQChannelSubPriority        = 1;
141.  nvic_config.NVIC_IRQChannelCmd                = ENABLE;
142.  NVIC_Init(&nvic_config);
143.}
144. 
145.static void gpio_initialize(void) {
146.  GPIO_InitTypeDef gpio_init_config;
147.  memset(&gpio_init_config, 0, sizeof(gpio_init_config));
148. 
149.  gpio_init_config.GPIO_Pin   = (1 << ws2812.pin);
150.  gpio_init_config.GPIO_Mode  = GPIO_Mode_OUT;
151.  gpio_init_config.GPIO_OType = GPIO_OType_PP;
152.  gpio_init_config.GPIO_PuPd  = GPIO_PuPd_NO_PULL;
153.  gpio_init_config.GPIO_Speed = GPIO_Speed_50MHz;
154. 
155.  switch ((uint32_t)ws2812.port) {
156.    case GPIOA_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); break;
157.    case GPIOB_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); break;
158.    case GPIOC_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); break;
159.    case GPIOD_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); break;
160.    case GPIOE_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); break;
161.    case GPIOF_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); break;
162.    case GPIOG_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); break;
163.    case GPIOH_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE); break;
164.    case GPIOI_BASE: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI, ENABLE); break;
165.    default:
166.      printf("Invalid GPIO port\n");
167.      while(1);
168.  }
169. 
170.  GPIO_Init(ws2812.port, &gpio_init_config);
171.}
172. 
173.static void dma_initialize(void) {
174.  DMA_InitTypeDef dma_init_config;
175.  memset(&dma_init_config, 0, sizeof(dma_init_config));
176. 
177.  // Enable DMA1 clock.
178.  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
179. 
180.  // TIM3 UP
181.  dma_init_config.DMA_Channel            = DMA_Channel_5;
182.  dma_init_config.DMA_PeripheralBaseAddr = (uint32_t)&ws2812.port->BSRRL;
183.  dma_init_config.DMA_Memory0BaseAddr    = (uint32_t)&ws2812.iomask;
184.  dma_init_config.DMA_DIR                = DMA_DIR_MemoryToPeripheral;
185.  dma_init_config.DMA_BufferSize         = 0;
186.  dma_init_config.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
187.  dma_init_config.DMA_MemoryInc          = DMA_MemoryInc_Disable;
188.  dma_init_config.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
189.  dma_init_config.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
190.  dma_init_config.DMA_Mode               = DMA_Mode_Normal;
191.  dma_init_config.DMA_Priority           = DMA_Priority_High;
192.  dma_init_config.DMA_FIFOMode           = DMA_FIFOMode_Enable;
193.  dma_init_config.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
194.  dma_init_config.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
195.  dma_init_config.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
196. 
197.  DMA_Cmd(DMA1_Stream2, DISABLE);
198.  DMA_DeInit(DMA1_Stream2);
199.  DMA_Init(DMA1_Stream2, &dma_init_config);
200. 
201.  // TIM3 CC1
202.  dma_init_config.DMA_Channel            = DMA_Channel_5;
203.  dma_init_config.DMA_PeripheralBaseAddr = (uint32_t)&ws2812.port->BSRRH;
204.  dma_init_config.DMA_Memory0BaseAddr    = (uint32_t)&ws2812.framebuffer;
205.  dma_init_config.DMA_DIR                = DMA_DIR_MemoryToPeripheral;
206.  dma_init_config.DMA_BufferSize         = 0;
207.  dma_init_config.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
208.  dma_init_config.DMA_MemoryInc          = DMA_MemoryInc_Enable;
209.  dma_init_config.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
210.  dma_init_config.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
211.  dma_init_config.DMA_Mode               = DMA_Mode_Normal;
212.  dma_init_config.DMA_Priority           = DMA_Priority_High;
213.  dma_init_config.DMA_FIFOMode           = DMA_FIFOMode_Enable;
214.  dma_init_config.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
215.  dma_init_config.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
216.  dma_init_config.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
217. 
218.  DMA_Cmd(DMA1_Stream4, DISABLE);
219.  DMA_DeInit(DMA1_Stream4);
220.  DMA_Init(DMA1_Stream4, &dma_init_config);
221. 
222.  // TIM3 CC3
223.  dma_init_config.DMA_Channel            = DMA_Channel_5;
224.  dma_init_config.DMA_PeripheralBaseAddr = (uint32_t)&ws2812.port->BSRRH;
225.  dma_init_config.DMA_Memory0BaseAddr    = (uint32_t)&ws2812.iomask;
226.  dma_init_config.DMA_DIR                = DMA_DIR_MemoryToPeripheral;
227.  dma_init_config.DMA_BufferSize         = 0;
228.  dma_init_config.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
229.  dma_init_config.DMA_MemoryInc          = DMA_MemoryInc_Disable;
230.  dma_init_config.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
231.  dma_init_config.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
232.  dma_init_config.DMA_Mode               = DMA_Mode_Normal;
233.  dma_init_config.DMA_Priority           = DMA_Priority_High;
234.  dma_init_config.DMA_FIFOMode           = DMA_FIFOMode_Enable;
235.  dma_init_config.DMA_FIFOThreshold      = DMA_FIFOThreshold_HalfFull;
236.  dma_init_config.DMA_MemoryBurst        = DMA_MemoryBurst_Single;
237.  dma_init_config.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;
238. 
239.  DMA_Cmd(DMA1_Stream7, DISABLE);
240.  DMA_DeInit(DMA1_Stream7);
241.  DMA_Init(DMA1_Stream7, &dma_init_config);
242. 
243.  // Enable DMA transfer complete interrupt for all streams.
244.  NVIC_InitTypeDef dma_nvic_config;
245.  memset(&dma_nvic_config, 0, sizeof(dma_nvic_config));
246. 
247.  dma_nvic_config.NVIC_IRQChannelPreemptionPriority = 0;
248.  dma_nvic_config.NVIC_IRQChannelSubPriority        = 0;
249.  dma_nvic_config.NVIC_IRQChannelCmd                = ENABLE;
250. 
251.  dma_nvic_config.NVIC_IRQChannel = DMA1_Stream2_IRQn;
252.  NVIC_Init(&dma_nvic_config);
253. 
254.  dma_nvic_config.NVIC_IRQChannel = DMA1_Stream4_IRQn;
255.  NVIC_Init(&dma_nvic_config);
256. 
257.  dma_nvic_config.NVIC_IRQChannel = DMA1_Stream7_IRQn;
258.  NVIC_Init(&dma_nvic_config);
259. 
260.  DMA_ITConfig(DMA1_Stream2,  DMA_IT_TC, ENABLE);
261.  DMA_ITConfig(DMA1_Stream4,  DMA_IT_TC, ENABLE);
262.  DMA_ITConfig(DMA1_Stream7,  DMA_IT_TC, ENABLE);
263.}
264. 
265.void ws2812_initialize(GPIO_TypeDef *port, uint32_t pin) {
266.  memset(&ws2812, 0, sizeof(ws2812));
267.  ws2812.port   = port;
268.  ws2812.pin    = pin;
269.  ws2812.iomask = (1 << pin);
270. 
271.  gpio_initialize();
272.  dma_initialize();
273.  timer_initialize();
274.}
275. 
276.bool ws2812_set_rgb(size_t led, uint8_t r, uint8_t g, uint8_t b) {
277.  if (led >= WS2812_COUNT)
278.    return false;
279. 
280.  uint16_t *base = &ws2812.framebuffer[led * WS2812_BITS_PER_LED];
281.  for (size_t i = 0; i < 8; i++) {
282.    if (r & 80)
283.      *base = ws2812.iomask;
284.    else
285.      *base = 0;
286.    base++;
287.    r <<= 1;
288.  }
289. 
290.  for (size_t i = 0; i < 8; i++) {
291.    if (g & 80)
292.      *base = ws2812.iomask;
293.    else
294.      *base = 0;
295.    base++;
296.    g <<= 1;
297.  }
298. 
299.  for (size_t i = 0; i < 8; i++) {
300.    if (b & 80)
301.      *base = ws2812.iomask;
302.    else
303.      *base = 0;
304.    base++;
305.    b <<= 1;
306.  }
307. 
308.  return true;
309.}
310. 
311.void ws2812_flush(void) {
312.  ws2812.overflows = 0;
313. 
314.  // clear all relevant DMA flags
315.  DMA_ClearFlag(DMA1_Stream2,  DMA_FLAG_FEIF2 | DMA_FLAG_DMEIF2 | DMA_FLAG_TEIF2 | DMA_FLAG_HTIF2 | DMA_FLAG_TCIF2);
316.  DMA_ClearFlag(DMA1_Stream4,  DMA_FLAG_FEIF4 | DMA_FLAG_DMEIF4 | DMA_FLAG_TEIF4 | DMA_FLAG_HTIF4 | DMA_FLAG_TCIF4);
317.  DMA_ClearFlag(DMA1_Stream7,  DMA_FLAG_FEIF7 | DMA_FLAG_DMEIF7 | DMA_FLAG_TEIF7 | DMA_FLAG_HTIF7 | DMA_FLAG_TCIF7);
318. 
319.  DMA_SetCurrDataCounter(DMA1_Stream2, WS2812_FRAMEBUFFER_SIZE);
320.  DMA_SetCurrDataCounter(DMA1_Stream4, WS2812_FRAMEBUFFER_SIZE);
321.  DMA_SetCurrDataCounter(DMA1_Stream7, WS2812_FRAMEBUFFER_SIZE);
322. 
323.  DMA_Cmd(DMA1_Stream2, ENABLE);
324.  DMA_Cmd(DMA1_Stream4, ENABLE);
325.  DMA_Cmd(DMA1_Stream7, ENABLE);
326. 
327.  TIM3->SR = 0;
328. 
329.  TIM_DMACmd(TIM3, TIM_DMA_CC1,    ENABLE);
330.  TIM_DMACmd(TIM3, TIM_DMA_CC3,    ENABLE);
331.  TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);
332. 
333.  TIM_SetCounter(TIM3, WS2812_TIMER_PERIOD - 1);
334.  TIM_Cmd(TIM3, ENABLE);
335.}


Outcomes