STM32L476 PWM generation through DMA transfer for WS2812
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