STM32 PWM DMA只有在我每次传输re-init时才能正常工作,否则会丢弃前几个脉冲
STM32 PWM DMA only works properly if I re-init every time I transfer, otherwise drops first few pulses
如标题所说,如果我不在 WS2812_DMA_Stop
中包含对 HAL_DMA_Init(&hdma_tim2_ch1)
的调用,我的第一次传输工作正常,但所有后续传输都缺少第一个(1-4,通常3)脉冲。
DMA 设置:
hdma_tim2_ch1.Instance = DMA1_Channel5;
hdma_tim2_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim2_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim2_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim2_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim2_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim2_ch1.Init.Mode = DMA_CIRCULAR;
hdma_tim2_ch1.Init.Priority = DMA_PRIORITY_HIGH;
下面附上完整代码:
void WS2812_DMA_Stop() {
__HAL_TIM_DISABLE_DMA(&htim2, TIM_DMA_CC1);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
__HAL_TIM_DISABLE(&htim2);
HAL_DMA_Init(&hdma_tim2_ch1);
ws2812_busy = 0;
}
void WS2812_DMA_ISR(uint8_t tc_flag) {
if (ws2812_current_led < WS2812_NUM_LEDS) {
if (tc_flag) {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[WS2812_DMA_LED_SIZE]);
} else {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[0]);
}
} else if (ws2812_current_led < (WS2812_NUM_LEDS + WS2812_RESET_PULSE_LEN)) {
if (tc_flag) {
memset(&ws2812_dma_buffer[WS2812_DMA_LED_SIZE], 0, sizeof(ws2812_dma_buffer[0]) * WS2812_DMA_LED_SIZE);
} else {
memset(&ws2812_dma_buffer[0], 0, sizeof(ws2812_dma_buffer[0]) * WS2812_DMA_LED_SIZE);
}
} else {
WS2812_DMA_Stop();
}
ws2812_current_led++;
}
void WS2812_DMA_HT(DMA_HandleTypeDef *hdma) {
WS2812_DMA_ISR(0);
}
void WS2812_DMA_TC(DMA_HandleTypeDef *hdma) {
WS2812_DMA_ISR(1);
}
void WS2812_DMA_Error(DMA_HandleTypeDef *hdma) {
_Error_Handler(__FILE__, __LINE__);
}
void WS2812_DMA_Start() {
ws2812_busy = 1;
for (ws2812_current_led = 0; ws2812_current_led < WS2812_DMA_NUM_LEDS; ws2812_current_led++) {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[ws2812_current_led * WS2812_DMA_LED_SIZE]);
}
htim2.hdma[TIM_DMA_ID_CC1]->XferCpltCallback = WS2812_DMA_TC;
htim2.hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = WS2812_DMA_HT;
htim2.hdma[TIM_DMA_ID_CC1]->XferErrorCallback = WS2812_DMA_Error;
HAL_DMA_Start_IT(htim2.hdma[TIM_DMA_ID_CC1], (uint32_t)(&ws2812_dma_buffer[0]), (uint32_t)(&(htim2.Instance->CCR1)), WS2812_DMA_BUF_LEN);
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
__HAL_TIM_ENABLE(&htim2);
}
嗯,仍然不能 100% 确定原因,但看起来 HAL_DMA_Init
中的关键行是 hdma->State = HAL_DMA_STATE_READY;
。仅用该行替换对 Init 的调用也可以解决问题。
如标题所说,如果我不在 WS2812_DMA_Stop
中包含对 HAL_DMA_Init(&hdma_tim2_ch1)
的调用,我的第一次传输工作正常,但所有后续传输都缺少第一个(1-4,通常3)脉冲。
DMA 设置:
hdma_tim2_ch1.Instance = DMA1_Channel5;
hdma_tim2_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim2_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim2_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim2_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim2_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_tim2_ch1.Init.Mode = DMA_CIRCULAR;
hdma_tim2_ch1.Init.Priority = DMA_PRIORITY_HIGH;
下面附上完整代码:
void WS2812_DMA_Stop() {
__HAL_TIM_DISABLE_DMA(&htim2, TIM_DMA_CC1);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
__HAL_TIM_DISABLE(&htim2);
HAL_DMA_Init(&hdma_tim2_ch1);
ws2812_busy = 0;
}
void WS2812_DMA_ISR(uint8_t tc_flag) {
if (ws2812_current_led < WS2812_NUM_LEDS) {
if (tc_flag) {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[WS2812_DMA_LED_SIZE]);
} else {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[0]);
}
} else if (ws2812_current_led < (WS2812_NUM_LEDS + WS2812_RESET_PULSE_LEN)) {
if (tc_flag) {
memset(&ws2812_dma_buffer[WS2812_DMA_LED_SIZE], 0, sizeof(ws2812_dma_buffer[0]) * WS2812_DMA_LED_SIZE);
} else {
memset(&ws2812_dma_buffer[0], 0, sizeof(ws2812_dma_buffer[0]) * WS2812_DMA_LED_SIZE);
}
} else {
WS2812_DMA_Stop();
}
ws2812_current_led++;
}
void WS2812_DMA_HT(DMA_HandleTypeDef *hdma) {
WS2812_DMA_ISR(0);
}
void WS2812_DMA_TC(DMA_HandleTypeDef *hdma) {
WS2812_DMA_ISR(1);
}
void WS2812_DMA_Error(DMA_HandleTypeDef *hdma) {
_Error_Handler(__FILE__, __LINE__);
}
void WS2812_DMA_Start() {
ws2812_busy = 1;
for (ws2812_current_led = 0; ws2812_current_led < WS2812_DMA_NUM_LEDS; ws2812_current_led++) {
WS2812_fill_buffer(ws2812_current_led, &ws2812_dma_buffer[ws2812_current_led * WS2812_DMA_LED_SIZE]);
}
htim2.hdma[TIM_DMA_ID_CC1]->XferCpltCallback = WS2812_DMA_TC;
htim2.hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = WS2812_DMA_HT;
htim2.hdma[TIM_DMA_ID_CC1]->XferErrorCallback = WS2812_DMA_Error;
HAL_DMA_Start_IT(htim2.hdma[TIM_DMA_ID_CC1], (uint32_t)(&ws2812_dma_buffer[0]), (uint32_t)(&(htim2.Instance->CCR1)), WS2812_DMA_BUF_LEN);
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
__HAL_TIM_ENABLE(&htim2);
}
嗯,仍然不能 100% 确定原因,但看起来 HAL_DMA_Init
中的关键行是 hdma->State = HAL_DMA_STATE_READY;
。仅用该行替换对 Init 的调用也可以解决问题。