2016-06-23 08:03 PM
I am working on a personal project and keep running into a problem. The I am trying to use the DMA in an STM32F4 to control a strip of LEDs that rely on the duty cycle of an output signal.
I want to update the duty cycle of TIM5 at the end of each TIM5 period, I want to create a timer with a repeating duty cycle pattern (ie. 10% 20% 30% 10% 20% 30%...).
Is there something I am missing in my code in order to get that functionality?void ws2811LedStripHardwareInit(void){ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; DMA_InitTypeDef DMA_InitStructure; TIM5->CCR2 = 106; uint16_t prescalerValue; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); /* GPIOA Configuration: TIM5 Channel 1 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5); // Stop timer TIM_Cmd(TIM5, DISABLE); /* Compute the prescaler value */ prescalerValue = (uint16_t) (SystemCoreClock / 2 / 84000000) - 1; /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 104; // 800kHz TIM_TimeBaseStructure.TIM_Prescaler = prescalerValue; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); /* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM5, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable); TIM_Cmd(TIM5, ENABLE); /* configure DMA */ /* DMA1 Channel Config */ DMA_Cmd(DMA1_Stream2, DISABLE); // disable DMA channel 6 DMA_DeInit(DMA1_Stream2); DMA_StructInit(&DMA_InitStructure); DMA_InitStructure.DMA_Channel = DMA_Channel_6; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(TIM5->CCR1); DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ledStripDMABuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; DMA_InitStructure.DMA_BufferSize = WS2811_DMA_BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA1_Stream2, &DMA_InitStructure); DMA_ITConfig(DMA1_Stream2, DMA_IT_TC, ENABLE); DMA_ClearITPendingBit(DMA1_Stream2, DMA_IT_TCIF2); // clear DMA1 Channel 6 transfer complete flag NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_BASE(NVIC_PRIO_WS2811_DMA); NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_PRIORITY_SUB(NVIC_PRIO_WS2811_DMA); NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); setStripColor(&hsv_white); ws2811UpdateStrip(); //sets LED strip to white, duty cycle of 68% //TIM5->CCR1 = 72;} #stm32f4-dma-timer2016-06-23 11:58 PM
I'm guessing that your problems are caused by the DMA to timer interface being a bit inconsistent compared with DMA to other peripherals.
You aren't meant to access the TIM5->CCRx registers directly from DMA; instead, use the TIM5->DCR and TIM5->DMAR registers to indirectly access the CCRx registers. You'll need to read the reference manual for an explanation of how these registers work. The underlying reason for this is that DMA1 can't access the full memory space on its peripheral port, just particular registers for the peripherals.