AnsweredAssumed Answered

STM32L476 PWM generation through DMA transfer for WS2812

Question asked by Fabio_IT on Jan 26, 2016
Latest reply on Jan 26, 2016 by Fabio_IT
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]);
}

Outcomes