From fc432d93edc73f9d951ccbd0b33c6751d94de90f Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 26 May 2026 02:07:36 -0500 Subject: [PATCH] drivers: fix DMA request disable ordering in timer IRQ handlers and stop functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STM32 RM (RM0090 ยง10.3.17) requires disabling the timer DMA request before disabling the DMA stream. The previous code had this backwards in five places across three driver files, creating a race window where the timer could issue a DMA request to an already-disabled stream. The incorrect ordering: DMA_Cmd(stream, DISABLE); // stream disabled first TIM_DMACmd(tim, source, DISABLE); // timer request disabled second Corrected ordering: TIM_DMACmd(tim, source, DISABLE); // timer request disabled first DMA_Cmd(stream, DISABLE); // stream disabled second Affected locations: - timer_impl_stdperiph.c: impl_timerPWMStopDMA, impl_timerPWMPrepareDMA, DMA TC IRQ handler - timer_impl_stdperiph_at32.c: impl_timerPWMStopDMA - timer_impl_hal.c: DMA TC IRQ handler The correct ordering was already used in impl_timerPWMSetDMACircular (both stdperiph files) and impl_timerPWMStopDMA (HAL file), which serve as the reference implementation. --- src/main/drivers/timer_impl_hal.c | 2 +- src/main/drivers/timer_impl_stdperiph.c | 6 +++--- src/main/drivers/timer_impl_stdperiph_at32.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 9c4093bafa8..a24875eec08 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -330,8 +330,8 @@ static void impl_timerDMA_IRQHandler(DMA_t descriptor) tch->dmaState = TCH_DMA_IDLE; } - LL_DMA_DisableStream(tch->dma->dma, lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]); LL_TIM_DisableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]); + LL_DMA_DisableStream(tch->dma->dma, lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]); DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF); } diff --git a/src/main/drivers/timer_impl_stdperiph.c b/src/main/drivers/timer_impl_stdperiph.c index 5b52cb862a4..13b38cdd2b3 100644 --- a/src/main/drivers/timer_impl_stdperiph.c +++ b/src/main/drivers/timer_impl_stdperiph.c @@ -279,8 +279,8 @@ static void impl_timerDMA_IRQHandler(DMA_t descriptor) tch->dmaState = TCH_DMA_IDLE; - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF); } @@ -518,8 +518,8 @@ void impl_timerPWMPrepareDMA(TCH_t * tch, uint32_t dmaBufferElementCount) // Clear the flag as well, so even if DMA transfer finishes while within ATOMIC_BLOCK // the resulting IRQ won't mess up the DMA state ATOMIC_BLOCK(NVIC_PRIO_MAX) { - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); DMA_CLEAR_FLAG(tch->dma, DMA_IT_TCIF); } @@ -561,8 +561,8 @@ void impl_timerPWMStartDMA(TCH_t * tch) void impl_timerPWMStopDMA(TCH_t * tch) { - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); tch->dmaState = TCH_DMA_IDLE; TIM_Cmd(tch->timHw->tim, ENABLE); } diff --git a/src/main/drivers/timer_impl_stdperiph_at32.c b/src/main/drivers/timer_impl_stdperiph_at32.c index 782943ab25d..54c6d257078 100644 --- a/src/main/drivers/timer_impl_stdperiph_at32.c +++ b/src/main/drivers/timer_impl_stdperiph_at32.c @@ -409,8 +409,8 @@ void impl_timerPWMStartDMA(TCH_t * tch) void impl_timerPWMStopDMA(TCH_t * tch) { - dma_channel_enable(tch->dma->ref,FALSE); tmr_dma_request_enable(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], FALSE); + dma_channel_enable(tch->dma->ref,FALSE); tch->dmaState = TCH_DMA_IDLE; tmr_counter_enable(tch->timHw->tim, TRUE); }