2 * This file is part of INAV.
4 * INAV is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * INAV is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with INAV. If not, see <http://www.gnu.org/licenses/>.
25 #include "build/atomic.h"
26 #include "build/debug.h"
28 #include "common/utils.h"
30 #include "drivers/io.h"
31 #include "drivers/rcc.h"
32 #include "drivers/time.h"
33 #include "drivers/nvic.h"
34 #include "drivers/timer.h"
35 #include "drivers/timer_impl.h"
37 extern uint32_t timerClock(TIM_TypeDef
*tim
);
39 const uint16_t lookupDMASourceTable
[] = { TIM_DMA_CC1
, TIM_DMA_CC2
, TIM_DMA_CC3
, TIM_DMA_CC4
};
40 const uint8_t lookupTIMChannelTable
[] = { TIM_CHANNEL_1
, TIM_CHANNEL_2
, TIM_CHANNEL_3
, TIM_CHANNEL_4
};
42 static const uint32_t lookupDMALLStreamTable
[] = { LL_DMA_STREAM_0
, LL_DMA_STREAM_1
, LL_DMA_STREAM_2
, LL_DMA_STREAM_3
, LL_DMA_STREAM_4
, LL_DMA_STREAM_5
, LL_DMA_STREAM_6
, LL_DMA_STREAM_7
};
44 #if !(defined(STM32H7) || defined(STM32G4))
45 static const uint32_t lookupDMALLChannelTable
[] = { LL_DMA_CHANNEL_0
, LL_DMA_CHANNEL_1
, LL_DMA_CHANNEL_2
, LL_DMA_CHANNEL_3
, LL_DMA_CHANNEL_4
, LL_DMA_CHANNEL_5
, LL_DMA_CHANNEL_6
, LL_DMA_CHANNEL_7
};
48 static TIM_HandleTypeDef timerHandle
[HARDWARE_TIMER_DEFINITION_COUNT
];
50 static TIM_HandleTypeDef
* timerFindTimerHandle(TIM_TypeDef
*tim
)
52 uint8_t timerIndex
= lookupTimerIndex(tim
);
53 if (timerIndex
>= HARDWARE_TIMER_DEFINITION_COUNT
) {
57 return &timerHandle
[timerIndex
];
60 void impl_timerInitContext(timHardwareContext_t
* timCtx
)
62 timCtx
->timHandle
= timerFindTimerHandle(timCtx
->timDef
->tim
);
65 void impl_timerNVICConfigure(TCH_t
* tch
, int irqPriority
)
67 if (tch
->timCtx
->timDef
->irq
) {
68 HAL_NVIC_SetPriority(tch
->timCtx
->timDef
->irq
, irqPriority
, 0);
69 HAL_NVIC_EnableIRQ(tch
->timCtx
->timDef
->irq
);
72 if (tch
->timCtx
->timDef
->secondIrq
) {
73 HAL_NVIC_SetPriority(tch
->timCtx
->timDef
->secondIrq
, irqPriority
, 0);
74 HAL_NVIC_EnableIRQ(tch
->timCtx
->timDef
->secondIrq
);
78 void impl_timerConfigBase(TCH_t
* tch
, uint16_t period
, uint32_t hz
)
80 // Get and verify HAL TIM_Handle object
81 TIM_HandleTypeDef
* timHandle
= tch
->timCtx
->timHandle
;
82 TIM_TypeDef
* timer
= tch
->timCtx
->timDef
->tim
;
84 uint16_t period1
= (period
- 1) & 0xffff;
85 uint16_t prescaler1
= lrintf((float)timerGetBaseClock(tch
) / hz
+ 0.01f
) - 1;
87 if (timHandle
->Instance
== timer
&& timHandle
->Init
.Prescaler
== prescaler1
&& timHandle
->Init
.Period
== period1
) {
91 timHandle
->Instance
= timer
;
92 timHandle
->Init
.Prescaler
= prescaler1
;
93 timHandle
->Init
.Period
= period1
; // AKA TIMx_ARR
94 timHandle
->Init
.RepetitionCounter
= 0;
95 timHandle
->Init
.ClockDivision
= TIM_CLOCKDIVISION_DIV1
;
96 timHandle
->Init
.CounterMode
= TIM_COUNTERMODE_UP
;
97 timHandle
->Init
.AutoReloadPreload
= TIM_AUTORELOAD_PRELOAD_ENABLE
;
99 HAL_TIM_Base_Init(timHandle
);
101 #if defined(STM32H7) || defined(STM32G4)
102 if (timer
== TIM1
|| timer
== TIM2
|| timer
== TIM3
|| timer
== TIM4
|| timer
== TIM5
|| timer
== TIM8
) {
104 if (timer
== TIM1
|| timer
== TIM2
|| timer
== TIM3
|| timer
== TIM4
|| timer
== TIM5
|| timer
== TIM8
|| timer
== TIM9
) {
106 TIM_ClockConfigTypeDef sClockSourceConfig
;
107 memset(&sClockSourceConfig
, 0, sizeof(sClockSourceConfig
));
108 sClockSourceConfig
.ClockSource
= TIM_CLOCKSOURCE_INTERNAL
;
109 if (HAL_TIM_ConfigClockSource(timHandle
, &sClockSourceConfig
) != HAL_OK
) {
114 if (timer
== TIM1
|| timer
== TIM2
|| timer
== TIM3
|| timer
== TIM4
|| timer
== TIM5
|| timer
== TIM8
) {
115 TIM_MasterConfigTypeDef sMasterConfig
;
116 memset(&sMasterConfig
, 0, sizeof(sMasterConfig
));
117 sMasterConfig
.MasterSlaveMode
= TIM_MASTERSLAVEMODE_DISABLE
;
118 if (HAL_TIMEx_MasterConfigSynchronization(timHandle
, &sMasterConfig
) != HAL_OK
) {
124 void impl_timerPWMConfigChannel(TCH_t
* tch
, uint16_t value
)
126 const bool inverted
= tch
->timHw
->output
& TIMER_OUTPUT_INVERTED
;
128 TIM_OC_InitTypeDef TIM_OCInitStructure
;
130 TIM_OCInitStructure
.OCMode
= TIM_OCMODE_PWM1
;
131 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_SET
;
132 TIM_OCInitStructure
.OCPolarity
= inverted
? TIM_OCPOLARITY_LOW
: TIM_OCPOLARITY_HIGH
;
133 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_SET
;
134 TIM_OCInitStructure
.OCNPolarity
= inverted
? TIM_OCNPOLARITY_LOW
: TIM_OCNPOLARITY_HIGH
;
135 TIM_OCInitStructure
.Pulse
= value
;
136 TIM_OCInitStructure
.OCFastMode
= TIM_OCFAST_DISABLE
;
138 HAL_TIM_PWM_ConfigChannel(tch
->timCtx
->timHandle
, &TIM_OCInitStructure
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
141 volatile timCCR_t
* impl_timerCCR(TCH_t
* tch
)
143 switch (tch
->timHw
->channelIndex
) {
145 return &tch
->timHw
->tim
->CCR1
;
148 return &tch
->timHw
->tim
->CCR2
;
151 return &tch
->timHw
->tim
->CCR3
;
154 return &tch
->timHw
->tim
->CCR4
;
160 void impl_enableTimer(TCH_t
* tch
)
162 HAL_TIM_Base_Start(tch
->timCtx
->timHandle
);
165 void impl_timerPWMStart(TCH_t
* tch
)
167 if (tch
->timHw
->output
& TIMER_OUTPUT_N_CHANNEL
) {
168 HAL_TIMEx_PWMN_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
171 HAL_TIM_PWM_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
175 void impl_timerEnableIT(TCH_t
* tch
, uint32_t interrupt
)
177 __HAL_TIM_ENABLE_IT(tch
->timCtx
->timHandle
, interrupt
);
180 void impl_timerDisableIT(TCH_t
* tch
, uint32_t interrupt
)
182 __HAL_TIM_DISABLE_IT(tch
->timCtx
->timHandle
, interrupt
);
185 void impl_timerClearFlag(TCH_t
* tch
, uint32_t flag
)
187 __HAL_TIM_CLEAR_FLAG(tch
->timCtx
->timHandle
, flag
);
190 // calculate input filter constant
191 static unsigned getFilter(unsigned ticks
)
193 static const unsigned ftab
[16] = {
195 1*2, 1*4, 1*8, // fCK_INT
203 for (unsigned i
= 1; i
< ARRAYLEN(ftab
); i
++) {
204 if (ftab
[i
] > ticks
) {
212 void impl_timerChConfigIC(TCH_t
* tch
, bool polarityRising
, unsigned inputFilterTicks
)
214 TIM_IC_InitTypeDef TIM_ICInitStructure
;
216 TIM_ICInitStructure
.ICPolarity
= polarityRising
? TIM_ICPOLARITY_RISING
: TIM_ICPOLARITY_FALLING
;
217 TIM_ICInitStructure
.ICSelection
= TIM_ICSELECTION_DIRECTTI
;
218 TIM_ICInitStructure
.ICPrescaler
= TIM_ICPSC_DIV1
;
219 TIM_ICInitStructure
.ICFilter
= getFilter(inputFilterTicks
);
220 HAL_TIM_IC_ConfigChannel(tch
->timCtx
->timHandle
, &TIM_ICInitStructure
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
223 void impl_timerCaptureCompareHandler(TIM_TypeDef
*tim
, timHardwareContext_t
*timerCtx
)
225 unsigned tim_status
= tim
->SR
& tim
->DIER
;
228 // flags will be cleared by reading CCR in dual capture, make sure we call handler correctly
229 // currrent order is highest bit first. Code should not rely on specific order (it will introduce race conditions anyway)
230 unsigned bit
= __builtin_clz(tim_status
);
231 unsigned mask
= ~(0x80000000 >> bit
);
237 case __builtin_clz(TIM_IT_UPDATE
): {
238 const uint16_t capture
= tim
->ARR
;
239 if (timerCtx
->ch
[0].cb
&& timerCtx
->ch
[0].cb
->callbackOvr
) {
240 timerCtx
->ch
[0].cb
->callbackOvr(&timerCtx
->ch
[0], capture
);
242 if (timerCtx
->ch
[1].cb
&& timerCtx
->ch
[1].cb
->callbackOvr
) {
243 timerCtx
->ch
[1].cb
->callbackOvr(&timerCtx
->ch
[1], capture
);
245 if (timerCtx
->ch
[2].cb
&& timerCtx
->ch
[2].cb
->callbackOvr
) {
246 timerCtx
->ch
[2].cb
->callbackOvr(&timerCtx
->ch
[2], capture
);
248 if (timerCtx
->ch
[3].cb
&& timerCtx
->ch
[3].cb
->callbackOvr
) {
249 timerCtx
->ch
[3].cb
->callbackOvr(&timerCtx
->ch
[3], capture
);
253 case __builtin_clz(TIM_IT_CC1
):
254 timerCtx
->ch
[0].cb
->callbackEdge(&timerCtx
->ch
[0], tim
->CCR1
);
256 case __builtin_clz(TIM_IT_CC2
):
257 timerCtx
->ch
[1].cb
->callbackEdge(&timerCtx
->ch
[1], tim
->CCR2
);
259 case __builtin_clz(TIM_IT_CC3
):
260 timerCtx
->ch
[2].cb
->callbackEdge(&timerCtx
->ch
[2], tim
->CCR3
);
262 case __builtin_clz(TIM_IT_CC4
):
263 timerCtx
->ch
[3].cb
->callbackEdge(&timerCtx
->ch
[3], tim
->CCR4
);
268 // timerConfig == NULL
269 volatile uint32_t tmp
;
272 case __builtin_clz(TIM_IT_UPDATE
):
275 case __builtin_clz(TIM_IT_CC1
):
278 case __builtin_clz(TIM_IT_CC2
):
281 case __builtin_clz(TIM_IT_CC3
):
284 case __builtin_clz(TIM_IT_CC4
):
294 void impl_timerChCaptureCompareEnable(TCH_t
* tch
, bool enable
)
296 static const uint32_t lookupTIMLLChannelTable
[] = { LL_TIM_CHANNEL_CH1
, LL_TIM_CHANNEL_CH2
, LL_TIM_CHANNEL_CH3
, LL_TIM_CHANNEL_CH4
};
299 LL_TIM_CC_EnableChannel(tch
->timHw
->tim
, lookupTIMLLChannelTable
[tch
->timHw
->channelIndex
]);
302 LL_TIM_CC_DisableChannel(tch
->timHw
->tim
, lookupTIMLLChannelTable
[tch
->timHw
->channelIndex
]);
306 // HAL_LL additionan implementation for enabling multiple DMA channels in one operation
307 static inline void LL_TIM_EnableDMAReq_CCx(TIM_TypeDef
* TIMx
, uint16_t dmaSources
)
309 SET_BIT(TIMx
->DIER
, dmaSources
& (TIM_DMA_CC1
| TIM_DMA_CC2
| TIM_DMA_CC3
| TIM_DMA_CC4
));
312 static inline void LL_TIM_DisableDMAReq_CCx(TIM_TypeDef
* TIMx
, uint16_t dmaSources
)
314 CLEAR_BIT(TIMx
->DIER
, dmaSources
& (TIM_DMA_CC1
| TIM_DMA_CC2
| TIM_DMA_CC3
| TIM_DMA_CC4
));
317 static void impl_timerDMA_IRQHandler(DMA_t descriptor
)
319 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
320 TCH_t
* tch
= (TCH_t
*)descriptor
->userParam
;
322 // If it was ACTIVE - switch to IDLE
323 if (tch
->dmaState
== TCH_DMA_ACTIVE
) {
324 tch
->dmaState
= TCH_DMA_IDLE
;
327 LL_DMA_DisableStream(tch
->dma
->dma
, lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)]);
328 LL_TIM_DisableDMAReq_CCx(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
]);
330 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
334 bool impl_timerPWMConfigChannelDMA(TCH_t
* tch
, void * dmaBuffer
, uint8_t dmaBufferElementSize
, uint32_t dmaBufferElementCount
)
336 tch
->dma
= dmaGetByTag(tch
->timHw
->dmaTag
);
337 tch
->dmaBuffer
= dmaBuffer
;
338 if (tch
->dma
== NULL
) {
342 // If DMA is already in use - abort
343 if (dmaGetOwner(tch
->dma
) != OWNER_FREE
) {
347 // We assume that timer channels are already initialized by calls to:
349 // timerPWMConfigChannel
350 const uint32_t streamLL
= lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)];
352 LL_DMA_DeInit(tch
->dma
->dma
, streamLL
);
354 LL_DMA_InitTypeDef init
;
355 LL_DMA_StructInit(&init
);
357 #if defined(STM32H7) || defined(STM32G4)
358 // For H7 the DMA periphRequest is encoded in the DMA tag
359 init
.PeriphRequest
= DMATAG_GET_CHANNEL(tch
->timHw
->dmaTag
);
361 init
.Channel
= lookupDMALLChannelTable
[DMATAG_GET_CHANNEL(tch
->timHw
->dmaTag
)];
364 init
.PeriphOrM2MSrcAddress
= (uint32_t)impl_timerCCR(tch
);
365 init
.PeriphOrM2MSrcIncMode
= LL_DMA_PERIPH_NOINCREMENT
;
367 switch (dmaBufferElementSize
) {
369 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_BYTE
;
370 init
.PeriphOrM2MSrcDataSize
= LL_DMA_MDATAALIGN_BYTE
;
373 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_HALFWORD
;
374 init
.PeriphOrM2MSrcDataSize
= LL_DMA_MDATAALIGN_HALFWORD
;
377 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
378 init
.PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_WORD
;
387 init
.MemoryOrM2MDstAddress
= (uint32_t)dmaBuffer
;
388 init
.MemoryOrM2MDstIncMode
= LL_DMA_MEMORY_INCREMENT
;
389 init
.NbData
= dmaBufferElementCount
;
390 init
.Direction
= LL_DMA_DIRECTION_MEMORY_TO_PERIPH
;
391 init
.Mode
= LL_DMA_MODE_NORMAL
;
392 init
.Priority
= LL_DMA_PRIORITY_HIGH
;
393 init
.FIFOMode
= LL_DMA_FIFOMODE_ENABLE
;
394 init
.FIFOThreshold
= LL_DMA_FIFOTHRESHOLD_FULL
;
395 init
.MemBurst
= LL_DMA_MBURST_SINGLE
;
396 init
.PeriphBurst
= LL_DMA_PBURST_SINGLE
;
398 dmaInit(tch
->dma
, OWNER_TIMER
, 0);
399 dmaSetHandler(tch
->dma
, impl_timerDMA_IRQHandler
, NVIC_PRIO_TIMER_DMA
, (uint32_t)tch
);
401 LL_DMA_Init(tch
->dma
->dma
, streamLL
, &init
);
403 // Start PWM generation
404 if (tch
->timHw
->output
& TIMER_OUTPUT_N_CHANNEL
) {
405 HAL_TIMEx_PWMN_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
408 HAL_TIM_PWM_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
414 #ifdef USE_DSHOT_DMAR
415 bool impl_timerPWMConfigDMABurst(burstDmaTimer_t
*burstDmaTimer
, TCH_t
* tch
, void * dmaBuffer
, uint8_t dmaBufferElementSize
, uint32_t dmaBufferElementCount
)
417 tch
->dma
= dmaGetByTag(tch
->timHw
->dmaTag
);
418 tch
->dmaBuffer
= dmaBuffer
;
419 if (tch
->dma
== NULL
) {
423 // If DMA is already in use - abort
424 if (dmaGetOwner(tch
->dma
) != OWNER_FREE
) {
428 if (!tch
->timCtx
->dmaBurstRef
) {
429 // We assume that timer channels are already initialized by calls to:
431 // timerPWMConfigChannel
432 const uint32_t streamLL
= lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)];
434 LL_DMA_DeInit(tch
->dma
->dma
, streamLL
);
436 LL_DMA_InitTypeDef init
;
437 LL_DMA_StructInit(&init
);
439 #if defined(STM32H7) || defined(STM32G4)
440 // For H7 the DMA periphRequest is encoded in the DMA tag
441 init
.PeriphRequest
= DMATAG_GET_CHANNEL(tch
->timHw
->dmaTag
);
443 init
.Channel
= lookupDMALLChannelTable
[DMATAG_GET_CHANNEL(tch
->timHw
->dmaTag
)];
446 init
.PeriphOrM2MSrcAddress
= (uint32_t)&tch
->timHw
->tim
->DMAR
;
447 init
.PeriphOrM2MSrcIncMode
= LL_DMA_PERIPH_NOINCREMENT
;
449 switch (dmaBufferElementSize
) {
451 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_BYTE
;
452 init
.PeriphOrM2MSrcDataSize
= LL_DMA_MDATAALIGN_BYTE
;
455 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_HALFWORD
;
456 init
.PeriphOrM2MSrcDataSize
= LL_DMA_MDATAALIGN_HALFWORD
;
459 init
.MemoryOrM2MDstDataSize
= LL_DMA_MDATAALIGN_WORD
;
460 init
.PeriphOrM2MSrcDataSize
= LL_DMA_PDATAALIGN_WORD
;
469 init
.MemoryOrM2MDstAddress
= (uint32_t)dmaBuffer
;
470 init
.MemoryOrM2MDstIncMode
= LL_DMA_MEMORY_INCREMENT
;
471 init
.NbData
= dmaBufferElementCount
;
472 init
.Direction
= LL_DMA_DIRECTION_MEMORY_TO_PERIPH
;
473 init
.Mode
= LL_DMA_MODE_NORMAL
;
474 init
.Priority
= LL_DMA_PRIORITY_HIGH
;
475 init
.FIFOMode
= LL_DMA_FIFOMODE_ENABLE
;
476 init
.FIFOThreshold
= LL_DMA_FIFOTHRESHOLD_FULL
;
477 init
.MemBurst
= LL_DMA_MBURST_SINGLE
;
478 init
.PeriphBurst
= LL_DMA_PBURST_SINGLE
;
480 dmaInit(tch
->dma
, OWNER_TIMER
, 0);
481 dmaSetHandler(tch
->dma
, impl_timerDMA_IRQHandler
, NVIC_PRIO_TIMER_DMA
, (uint32_t)tch
);
483 LL_DMA_Init(tch
->dma
->dma
, streamLL
, &init
);
485 tch
->timCtx
->dmaBurstRef
= tch
->dma
;
486 burstDmaTimer
->burstRequestSource
= lookupDMASourceTable
[tch
->timHw
->channelIndex
];
487 burstDmaTimer
->streamLL
= lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)];
488 burstDmaTimer
->dma
= tch
->dma
->dma
;
490 tch
->dmaState
= TCH_DMA_READY
;
493 // Start PWM generation
494 if (tch
->timHw
->output
& TIMER_OUTPUT_N_CHANNEL
) {
495 HAL_TIMEx_PWMN_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
498 HAL_TIM_PWM_Start(tch
->timCtx
->timHandle
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
]);
504 void impl_pwmBurstDMAStart(burstDmaTimer_t
* burstDmaTimer
, uint32_t BurstLength
)
506 LL_DMA_SetDataLength(burstDmaTimer
->dma
, burstDmaTimer
->streamLL
, BurstLength
);
507 LL_DMA_EnableIT_TC(burstDmaTimer
->dma
, burstDmaTimer
->streamLL
);
508 LL_DMA_EnableStream(burstDmaTimer
->dma
, burstDmaTimer
->streamLL
);
509 /* configure the DMA Burst Mode */
510 LL_TIM_ConfigDMABurst(burstDmaTimer
->timer
, LL_TIM_DMABURST_BASEADDR_CCR1
, LL_TIM_DMABURST_LENGTH_4TRANSFERS
);
511 /* Enable the TIM DMA Request */
512 //LL_TIM_EnableDMAReq_UPDATE(burstDmaTimer->timer);
513 LL_TIM_EnableDMAReq_CCx(burstDmaTimer
->timer
, burstDmaTimer
->burstRequestSource
);
517 void impl_timerPWMPrepareDMA(TCH_t
* tch
, uint32_t dmaBufferElementCount
)
519 const uint32_t streamLL
= lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)];
520 DMA_TypeDef
*dmaBase
= tch
->dma
->dma
;
522 // Make sure we terminate any DMA transaction currently in progress
523 // Clear the flag as well, so even if DMA transfer finishes while within ATOMIC_BLOCK
524 // the resulting IRQ won't mess up the DMA state
525 ATOMIC_BLOCK(NVIC_PRIO_MAX
) {
526 LL_TIM_DisableDMAReq_CCx(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
]);
527 LL_DMA_DisableStream(dmaBase
, streamLL
);
528 DMA_CLEAR_FLAG(tch
->dma
, DMA_IT_TCIF
);
531 LL_DMA_SetDataLength(dmaBase
, streamLL
, dmaBufferElementCount
);
532 LL_DMA_ConfigAddresses(dmaBase
, streamLL
, (uint32_t)tch
->dmaBuffer
, (uint32_t)impl_timerCCR(tch
), LL_DMA_DIRECTION_MEMORY_TO_PERIPH
);
533 LL_DMA_EnableIT_TC(dmaBase
, streamLL
);
534 LL_DMA_EnableStream(dmaBase
, streamLL
);
535 tch
->dmaState
= TCH_DMA_READY
;
538 void impl_timerPWMStartDMA(TCH_t
* tch
)
540 uint16_t dmaSources
= 0;
541 timHardwareContext_t
* timCtx
= tch
->timCtx
;
543 if (timCtx
->ch
[0].dmaState
== TCH_DMA_READY
) {
544 timCtx
->ch
[0].dmaState
= TCH_DMA_ACTIVE
;
545 dmaSources
|= TIM_DMA_CC1
;
548 if (timCtx
->ch
[1].dmaState
== TCH_DMA_READY
) {
549 timCtx
->ch
[1].dmaState
= TCH_DMA_ACTIVE
;
550 dmaSources
|= TIM_DMA_CC2
;
553 if (timCtx
->ch
[2].dmaState
== TCH_DMA_READY
) {
554 timCtx
->ch
[2].dmaState
= TCH_DMA_ACTIVE
;
555 dmaSources
|= TIM_DMA_CC3
;
558 if (timCtx
->ch
[3].dmaState
== TCH_DMA_READY
) {
559 timCtx
->ch
[3].dmaState
= TCH_DMA_ACTIVE
;
560 dmaSources
|= TIM_DMA_CC4
;
564 LL_TIM_SetCounter(timCtx
->timDef
->tim
, 0);
565 LL_TIM_EnableDMAReq_CCx(timCtx
->timDef
->tim
, dmaSources
);
569 void impl_timerPWMStopDMA(TCH_t
* tch
)
571 const uint32_t streamLL
= lookupDMALLStreamTable
[DMATAG_GET_STREAM(tch
->timHw
->dmaTag
)];
572 DMA_TypeDef
*dmaBase
= tch
->dma
->dma
;
574 ATOMIC_BLOCK(NVIC_PRIO_MAX
) {
575 LL_TIM_DisableDMAReq_CCx(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
]);
576 LL_DMA_DisableStream(dmaBase
, streamLL
);
577 DMA_CLEAR_FLAG(tch
->dma
, DMA_IT_TCIF
);
579 tch
->dmaState
= TCH_DMA_IDLE
;
581 HAL_TIM_Base_Start(tch
->timCtx
->timHandle
);