2020-04-13 04:38 AM
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!
2020-04-13 05:24 AM
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.
2020-04-13 07:04 AM
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);
}
2020-04-13 08:09 AM