cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L476 PWM generation through DMA transfer for WS2812

angeletti
Associate II
Posted on January 26, 2016 at 01:36

Hi guys, I am here again because I have a problem. I'm trying to control a WS2812 16xLED ring using TIM2 on the Nucleo-L476 board. I successfully communicated to the ring and for a single time I can control it. The problem is that after the first usage of ''HAL_TIM_PWM_Start_DMA'' function, the MCU stucks. I tried the debug functionality in Keil with no luck (be aware that I am not good in debug however). I attach the code, hoping to find a solution.

#include ''main.h''
/* Timer handler declaration */
TIM_HandleTypeDef TimHandle;
/* Timer Output Compare Configuration Structure declaration */
TIM_OC_InitTypeDef sConfig;
/* Capture Compare buffer */
//uint32_t aCCValue_Buffer[25] = {14,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0};
uint32_t aCCValue_Buffer[49] = {6,6,6,6,6,14,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,14,6,6,6,0};
uint32_t aCCReset_Buffer[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* Timer Period*/
uint32_t uwTimerPeriod = 0;
/* Definition of TIM instance */
#define TIMx TIM2
/* Definition for TIMx clock resources */
#define TIMx_CLK_ENABLE __HAL_RCC_TIM2_CLK_ENABLE
#define DMAx_CLK_ENABLE __HAL_RCC_DMA1_CLK_ENABLE
/* Definition for TIMx Pins */
#define TIMx_CHANNEL1_GPIO_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE
#define TIMx_GPIO_CHANNEL1_PORT GPIOA
#define GPIO_PIN_CHANNEL1 GPIO_PIN_5
#define GPIO_AF_TIMx GPIO_AF1_TIM2
/* Definition for TIMx's DMA */
#define TIMx_CC1_DMA_REQUEST DMA_REQUEST_4
#define TIMx_CC1_DMA_INST DMA1_Channel5
/* Definition for DMAx's NVIC */
#define TIMx_DMA_IRQn DMA1_Channel5_IRQn
#define TIMx_DMA_IRQHandler DMA1_Channel5_IRQHandle
extern
void
Error_Handler(
void
);
/**
* @brief TIM MSP Initialization
* This function configures the hardware resources used in this example:
* - Peripheral's clock enable
* - Peripheral's GPIO Configuration
* - DMA configuration for transmission request by peripheral
* @param htim: TIM handle pointer
* @retval None
*/
void
HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_HandleTypeDef hdma_tim;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* TIMx clock enable */
TIMx_CLK_ENABLE();
/* Enable GPIO Channel3/3N Clocks */
TIMx_CHANNEL1_GPIO_CLK_ENABLE();
/* Enable DMA clock */
DMAx_CLK_ENABLE();
/* Configure TIM1_Channel3 in output, push-pull & alternate function mode */
GPIO_InitStruct.Pin = GPIO_PIN_CHANNEL1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF_TIMx;
HAL_GPIO_Init(TIMx_GPIO_CHANNEL1_PORT, &GPIO_InitStruct);
/* Set the parameters to be configured */
hdma_tim.Init.Request = TIMx_CC1_DMA_REQUEST;
hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD ;
hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD ;
hdma_tim.Init.Mode = DMA_NORMAL;
hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;
/* Set hdma_tim instance */
hdma_tim.Instance = TIMx_CC1_DMA_INST;
/* Link hdma_tim to hdma[TIM_DMA_ID_CC1] (channel1) */
__HAL_LINKDMA(htim, hdma[TIM_DMA_ID_CC1], hdma_tim);
/* Initialize TIMx DMA handle */
HAL_DMA_Init(htim->hdma[TIM_DMA_ID_CC1]);
/*##-2- Configure the NVIC for DMA #########################################*/
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(TIMx_DMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIMx_DMA_IRQn);
}
void
ws2812_init(
void
)
{
TimHandle.Instance = TIMx;
TimHandle.Init.Period = 20 - 1; 
// 20 parts -> 16MHz / 20 = 800kHz
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.Prescaler = (uint32_t)((SystemCoreClock / 16000000) - 1); 
//16MHz
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&TimHandle);
/*##-2- Configure the PWM channel 3 ########################################*/
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.Pulse = 0;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_ENABLE;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
HAL_Delay(1);
/*##-3- Start PWM signal generation in DMA mode ############################*/
HAL_TIM_PWM_Start_DMA(&TimHandle, TIM_CHANNEL_1, aCCValue_Buffer, 49);
//while(HAL_TIM_PWM_GetState(&TimHandle) != HAL_TIM_STATE_READY);
//HAL_Delay(1);
//HAL_TIM_PWM_Stop_DMA(&TimHandle, TIM_CHANNEL_1);
}

To use it I simply call ws2812_init() inside the main function. It stucks just microseconds after this call (I know because after wa2812_init() I try to print a string and only the first character is outputted - it stucks also without printf). Anyone can help about it? Also some redirection to useful documentation (for use HAL drivers with PWM and DMA) would be greatly appreciated. I also installed the interrupt

void
TIMx_DMA_IRQHandler(
void
)
{
HAL_DMA_IRQHandler(TimHandle.hdma[TIM_DMA_ID_CC1]);
}

#pwm #dma #led #ws2812
6 REPLIES 6
angeletti
Associate II
Posted on January 26, 2016 at 13:43

I made a progress. With the following code I can change the colours using ws2812_send() function but only if after that I also call a printf. I noticed that the interruptTIMx_DMA_IRQHandler is never called, is it not strange?

#include ''main.h''
/* Timer handler declaration */
TIM_HandleTypeDef TimHandle;
/* Timer Output Compare Configuration Structure declaration */
TIM_OC_InitTypeDef sConfig;
/* Timer Period*/
uint32_t uwTimerPeriod = 0;
/* Definition of TIM instance */
#define TIMx TIM2
/* Definition for TIMx clock resources */
#define TIMx_CLK_ENABLE __HAL_RCC_TIM2_CLK_ENABLE
#define DMAx_CLK_ENABLE __HAL_RCC_DMA1_CLK_ENABLE
/* Definition for TIMx Pins */
#define TIMx_CHANNEL1_GPIO_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE
#define TIMx_GPIO_CHANNEL1_PORT GPIOA
#define GPIO_PIN_CHANNEL1 GPIO_PIN_5
#define GPIO_AF_TIMx GPIO_AF1_TIM2
/* Definition for TIMx's DMA */
#define TIMx_CC1_DMA_REQUEST DMA_REQUEST_4
#define TIMx_CC1_DMA_INST DMA1_Channel5
/* Definition for DMAx's NVIC */
#define TIMx_DMA_IRQn DMA1_Channel2_IRQn
#define TIMx_DMA_IRQHandler DMA1_Channel2_IRQHandle
//WS2812
#define LED_NUMBER (16)
#define LED_DATA_SIZE (LED_NUMBER * 24)
#define RESET_SLOTS_BEGIN (0)
#define RESET_SLOTS_END (1)
#define LED_BUFFER_SIZE (RESET_SLOTS_BEGIN + LED_DATA_SIZE + RESET_SLOTS_END)
#define WS2812_0 (6)
#define WS2812_1 (14)
uint32_t LEDbuffer[LED_BUFFER_SIZE];
extern
void
Error_Handler(
void
);
/**
* @brief TIM MSP Initialization
* This function configures the hardware resources used in this example:
* - Peripheral's clock enable
* - Peripheral's GPIO Configuration
* - DMA configuration for transmission request by peripheral
* @param htim: TIM handle pointer
* @retval None
*/
void
HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_HandleTypeDef hdma_tim;
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* TIMx clock enable */
TIMx_CLK_ENABLE();
/* Enable GPIO Channel3/3N Clocks */
TIMx_CHANNEL1_GPIO_CLK_ENABLE();
/* Enable DMA clock */
DMAx_CLK_ENABLE();
/* Configure TIM1_Channel3 in output, push-pull & alternate function mode */
GPIO_InitStruct.Pin = GPIO_PIN_CHANNEL1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF_TIMx;
HAL_GPIO_Init(TIMx_GPIO_CHANNEL1_PORT, &GPIO_InitStruct);
/* Set the parameters to be configured */
hdma_tim.Init.Request = TIMx_CC1_DMA_REQUEST;
hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD ;
hdma_tim.Init.MemDataAlignment = DMA_MDATAALIGN_WORD ;
hdma_tim.Init.Mode = DMA_NORMAL;
hdma_tim.Init.Priority = DMA_PRIORITY_HIGH;
/* Set hdma_tim instance */
hdma_tim.Instance = TIMx_CC1_DMA_INST;
/* Link hdma_tim to hdma[TIM_DMA_ID_CC1] (channel1) */
__HAL_LINKDMA(htim, hdma[TIM_DMA_ID_CC1], hdma_tim);
/* Initialize TIMx DMA handle */
HAL_DMA_Init(htim->hdma[TIM_DMA_ID_CC1]);
/*##-2- Configure the NVIC for DMA #########################################*/
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(TIMx_DMA_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIMx_DMA_IRQn);
}
void
ws2812_init(
void
)
{
/*Fill LED buffer - ALL OFF*/
uint32_t index, buffIndex;
buffIndex = 0;
for
(index=0;index<RESET_SLOTS_BEGIN;index++)
{
LEDbuffer[buffIndex] = 0;
buffIndex++;
}
for
(index=0;index<LED_DATA_SIZE;index++)
{
LEDbuffer[buffIndex] = WS2812_0;
buffIndex++;
}
for
(index=0;index<RESET_SLOTS_END;index++)
{
LEDbuffer[buffIndex] = 0;
buffIndex++;
}
TimHandle.Instance = TIMx;
TimHandle.Init.Period = 20 - 1; 
// 20 parts -> 16MHz / 20 = 800kHz
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.Prescaler = (uint32_t)((SystemCoreClock / 16000000) - 1); 
//16MHz
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&TimHandle);
/*##-2- Configure the PWM channel 3 ########################################*/
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.Pulse = 0;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_ENABLE;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
HAL_Delay(1);
/*##-3- Start PWM signal generation in DMA mode ############################*/
HAL_TIM_PWM_Start_DMA(&TimHandle, TIM_CHANNEL_1, LEDbuffer, LED_BUFFER_SIZE);
//while(HAL_TIM_PWM_GetState(&TimHandle) != HAL_TIM_STATE_READY);
HAL_Delay(1);
HAL_TIM_PWM_Stop_DMA(&TimHandle, TIM_CHANNEL_1);
}
void
ws2812_send(
void
)
{
HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1);
HAL_Delay(1);
HAL_TIM_PWM_Start_DMA(&TimHandle, TIM_CHANNEL_1, LEDbuffer, LED_BUFFER_SIZE);
HAL_Delay(1);
HAL_TIM_PWM_Stop_DMA(&TimHandle, TIM_CHANNEL_1);
}
void
setLEDcolor(uint32_t LEDnumber, uint8_t RED, uint8_t GREEN, uint8_t BLUE)
{
uint8_t tempBuffer[24];
int
i;
for
(i=0; i<8; i++) 
// GREEN data
tempBuffer[i] = ((GREEN<<i) & 0x80)?WS2812_1:WS2812_0;
for
(i=0; i<8; i++) 
// RED
tempBuffer[8+i] = ((RED<<i) & 0x80)?WS2812_1:WS2812_0;
for
(i=0; i<8; i++) 
// BLUE
tempBuffer[16+i] = ((BLUE<<i) & 0x80)?WS2812_1:WS2812_0;
for
(i=0; i<24; i++)
LEDbuffer[RESET_SLOTS_BEGIN + LEDnumber * 24 + i] = tempBuffer[i];
}

Posted on January 26, 2016 at 15:41

IRQHandler not IRQHandle

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
angeletti
Associate II
Posted on January 26, 2016 at 16:07

Solved. Thank you clive1. However everytime you reply to my questions, I feel suddenly dumb.

angeletti
Associate II
Posted on January 26, 2016 at 16:17

Since I need to remove every call to HAL_Delay. How can I poll for a flag that signals that the last PWM is generated? I'm not sure where and what to search.

Posted on January 26, 2016 at 17:40

However everytime you reply to my questions, I feel suddenly dumb.

I wouldn't take it personally, I've got this savant/clarity thing going on where I understand the rules and fit the facts around them, you told me enough and presented enough code for me to see it quickly, I just perceive things differently. The hints are brief because I only need to break your focus from the symptoms to the cause.

This is why I tend to probe for context, because what is happening tends to lead to why it is happening.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
angeletti
Associate II
Posted on January 26, 2016 at 21:44

Well, it's not the end of the world but I spent some hours tryin to figuring it out with no success. Seems that I need give more attention on the details. Moreover I struggle to get familiar with the new HAL libraries.