cancel
Showing results for 
Search instead for 
Did you mean: 

FreeRTOS stops running after starting timer with DMA to GPIO

luuk
Associate II

I'm working on a project where I need to control two WS2812B serial RGB LED strips. These strips are connected to 2 GPIOs which are configured to output modulated PWM to generatate zeros and ones, by letting TIM2 run with DMA configured on CC1 and CC2.

The code works well in a bare metal version of the firmware.

I am now porting this code to a FreeRTOS based version of the firmware. For some reason, as soon as I start timer 2 (after being configured with DMA), FreeRTOS directly stops running. I do see the GPIO output data, but the callback functions XferCpltCallback and XferHalfCpltCallback are never entered.

I'm kinda lost in why everything halts here and am not sure how to debug this.

Configuration of timer 2:

void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
 
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 0;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 80;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 26;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.Pulse = 54;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
 
  if(tim_baseHandle->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */
 
  /* USER CODE END TIM2_MspInit 0 */
    /* TIM2 clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();
 
    /* TIM2 interrupt Init */
    HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
  /* USER CODE BEGIN TIM2_MspInit 1 */
 
  /* USER CODE END TIM2_MspInit 1 */
  }

Initialization of DMA:

void LED_Driver_Init(uint8_t Mode, uint8_t Count)
{
    LEDDriver.LEDCount          = Count;
    LEDDriver.Mode              = LED_MODE_RGB;
    LEDDriver.TransferComplete  = 1;
 
    tim_period = SystemCoreClock / 800000;
 
    //DMA controller clock enable
    __HAL_RCC_DMA1_CLK_ENABLE();
 
    dmaUpdate.Init.Direction            = DMA_MEMORY_TO_PERIPH;
    dmaUpdate.Init.PeriphInc            = DMA_PINC_DISABLE;
    dmaUpdate.Init.MemInc               = DMA_MINC_DISABLE;
    dmaUpdate.Init.PeriphDataAlignment  = DMA_PDATAALIGN_WORD;
    dmaUpdate.Init.MemDataAlignment     = DMA_MDATAALIGN_WORD;
    dmaUpdate.Init.Mode                 = DMA_CIRCULAR;
    dmaUpdate.Init.Priority             = DMA_PRIORITY_VERY_HIGH;
    dmaUpdate.Instance                  = DMA1_Channel2;
    HAL_DMA_Init(&dmaUpdate);
    HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
 
 
    // TIM2 CC1 event
    dmaCC1.Init.Direction               = DMA_MEMORY_TO_PERIPH;
    dmaCC1.Init.PeriphInc               = DMA_PINC_DISABLE;
    dmaCC1.Init.MemInc                  = DMA_MINC_ENABLE;
    dmaCC1.Init.PeriphDataAlignment     = DMA_PDATAALIGN_WORD;
    dmaCC1.Init.MemDataAlignment        = DMA_MDATAALIGN_HALFWORD;
    dmaCC1.Init.Mode                    = DMA_CIRCULAR;
    dmaCC1.Init.Priority                = DMA_PRIORITY_VERY_HIGH;
    dmaCC1.Instance                     = DMA1_Channel5;
    HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
    HAL_DMA_Init(&dmaCC1);
    HAL_DMA_Start(&dmaCC1, reinterpret_cast<uint32_t>(DMABitBuffer),
                  reinterpret_cast<uint32_t>(&LED_DRIVER_PORT->BRR),
                  sizeof(DMABitBuffer) / sizeof(uint16_t));
 
    // TIM2 CC2 event
    dmaCC2.Init.Direction               = DMA_MEMORY_TO_PERIPH;
    dmaCC2.Init.PeriphInc               = DMA_PINC_DISABLE;
    dmaCC2.Init.MemInc                  = DMA_MINC_DISABLE;
    dmaCC2.Init.PeriphDataAlignment     = DMA_PDATAALIGN_WORD;
    dmaCC2.Init.MemDataAlignment        = DMA_MDATAALIGN_WORD;
    dmaCC2.Init.Mode                    = DMA_CIRCULAR;
    dmaCC2.Init.Priority                = DMA_PRIORITY_VERY_HIGH;
    dmaCC2.Instance                     = DMA1_Channel7;
    dmaCC2.XferCpltCallback             = DMA_TransferCompleteHandler;
    dmaCC2.XferHalfCpltCallback         = DMA_TransferHalfHandler;
    HAL_DMA_Init(&dmaCC2);
    HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
 
    //Test write some data to DMABitBuffer
    DMABitBuffer[0] = 0b0101010101010101;
    DMABitBuffer[1] = 0xFFFF;
    DMABitBuffer[2] = 0b110011011001100;
    DMABitBuffer[3] = 0xFFFF;
    DMABitBuffer[4] = 0;
 
    //Test start DMA
    HAL_DMA_Start(&dmaUpdate, reinterpret_cast<uint32_t>(&LED_IO_High),
                  reinterpret_cast<uint32_t>(&LED_DRIVER_PORT->BSRR),
                  sizeof(DMABitBuffer) / sizeof(uint16_t));
    HAL_DMA_Start_IT(&dmaCC2, reinterpret_cast<uint32_t>(&LED_IO_Low),
                     reinterpret_cast<uint32_t>(&LED_DRIVER_PORT->BSRR),
                     sizeof(DMABitBuffer) / sizeof(uint16_t));
}

The code to send RGB buffers by DMA:

void LED_SendBuffer(uint8_t BufferCount)
{
    //Transmission complete flag
    LEDDriver.TransferComplete = 0;
 
    for(uint32_t Index = 0; Index < BufferCount; Index++ )
    {
        LEDDriver.Buffer[Index].Counter = 0;
        loadNextFramebufferData(&LEDDriver.Buffer[Index], 0); // ROW 0
        loadNextFramebufferData(&LEDDriver.Buffer[Index], 1); // ROW 1
    }
 
    //Clear all DMA flags
    __HAL_DMA_CLEAR_FLAG(&dmaUpdate, DMA_FLAG_TC2 | DMA_FLAG_HT2 | DMA_FLAG_TE2);
    __HAL_DMA_CLEAR_FLAG(&dmaCC1, DMA_FLAG_TC5 | DMA_FLAG_HT5 | DMA_FLAG_TE5);
    __HAL_DMA_CLEAR_FLAG(&dmaCC2, DMA_FLAG_TC7 | DMA_FLAG_HT7 | DMA_FLAG_TE7);
 
    //Configure the number of bytes to be transferred by the DMA controller
    dmaUpdate.Instance->CNDTR = sizeof(DMABitBuffer) / sizeof(uint16_t);
    dmaCC1.Instance->CNDTR = sizeof(DMABitBuffer) / sizeof(uint16_t);
    dmaCC2.Instance->CNDTR = sizeof(DMABitBuffer) / sizeof(uint16_t);
 
    //Clear all TIM2 flags
    __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE | TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4);
 
    //Enable DMA channels
    __HAL_DMA_ENABLE(&dmaUpdate);
    __HAL_DMA_ENABLE(&dmaCC1);
    __HAL_DMA_ENABLE(&dmaCC2);
 
    //IMPORTANT: enable the TIM2 DMA requests AFTER enabling the DMA channels!
    __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);
    __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1);
    __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC2);
 
    TIM2->CNT = tim_period - 1;
 
    //Start TIM2
    __HAL_TIM_ENABLE(&htim2);
}

Any help would be greatly appreciated!

3 REPLIES 3
luuk
Associate II

A little extra information, I just discovered that shortly after starting timer 2 the program enters the default handler infinite loop in the startup file "startup_stm32f103vetx.s"

    .section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b Infinite_Loop
  .size Default_Handler, .-Default_Handler

This would suggest some interrupt isn't handled properly. Not sure what I'm missing here.

luuk
Associate II

Of course, I should have thought of this earlier. I forgot to add the IRQ handler back in after starting over in a new branch. After adding the handler the RTOS keeps running like expected.

void DMA1_Channel7_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&dmaCC2);
}

RDura.1
Associate II
  1. Disable timer interrupt: /* TIM2 interrupt Init */
  2. HAL_NVIC_SetPriority(TIM2_IRQn, 6, 0);
  3. HAL_NVIC_EnableIRQ(TIM2_IRQn);