2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
29 #include "build/debug.h"
31 #include "drivers/dma.h"
32 #include "drivers/dma_reqmap.h"
33 #include "drivers/io.h"
34 #include "drivers/nvic.h"
35 #include "drivers/rcc.h"
36 #include "drivers/time.h"
37 #include "drivers/timer.h"
38 #include "drivers/system.h"
40 #include "stm32f4xx.h"
41 #elif defined(STM32F3)
42 #include "stm32f30x.h"
45 #include "pwm_output.h"
46 #include "drivers/dshot.h"
47 #include "drivers/dshot_dpwm.h"
48 #include "drivers/dshot_command.h"
50 #include "pwm_output_dshot_shared.h"
52 #ifdef USE_DSHOT_TELEMETRY
54 void dshotEnableChannels(uint8_t motorCount
)
56 for (int i
= 0; i
< motorCount
; i
++) {
57 if (dmaMotors
[i
].output
& TIMER_OUTPUT_N_CHANNEL
) {
58 TIM_CCxNCmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerHardware
->channel
, TIM_CCxN_Enable
);
60 TIM_CCxCmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerHardware
->channel
, TIM_CCx_Enable
);
67 FAST_CODE
void pwmDshotSetDirectionOutput(
68 motorDmaOutput_t
* const motor
69 #ifndef USE_DSHOT_TELEMETRY
70 ,TIM_OCInitTypeDef
*pOcInit
, DMA_InitTypeDef
* pDmaInit
74 #ifdef USE_DSHOT_TELEMETRY
75 TIM_OCInitTypeDef
* pOcInit
= &motor
->ocInitStruct
;
76 DMA_InitTypeDef
* pDmaInit
= &motor
->dmaInitStruct
;
79 const timerHardware_t
* const timerHardware
= motor
->timerHardware
;
80 TIM_TypeDef
*timer
= timerHardware
->tim
;
82 dmaResource_t
*dmaRef
= motor
->dmaRef
;
84 #if defined(USE_DSHOT_DMAR) && !defined(USE_DSHOT_TELEMETRY)
86 dmaRef
= timerHardware
->dmaTimUPRef
;
92 #ifdef USE_DSHOT_TELEMETRY
93 motor
->isInput
= false;
95 timerOCPreloadConfig(timer
, timerHardware
->channel
, TIM_OCPreload_Disable
);
96 timerOCInit(timer
, timerHardware
->channel
, pOcInit
);
97 timerOCPreloadConfig(timer
, timerHardware
->channel
, TIM_OCPreload_Enable
);
102 pDmaInit
->DMA_DIR
= DMA_DIR_PeripheralDST
;
104 pDmaInit
->DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
110 pDmaInit
->DMA_DIR
= DMA_DIR_PeripheralDST
;
111 pDmaInit
->DMA_M2M
= DMA_M2M_Disable
;
112 #elif defined(STM32F4)
113 pDmaInit
->DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
117 xDMA_Init(dmaRef
, pDmaInit
);
118 xDMA_ITConfig(dmaRef
, DMA_IT_TC
, ENABLE
);
122 #ifdef USE_DSHOT_TELEMETRY
128 static void pwmDshotSetDirectionInput(
129 motorDmaOutput_t
* const motor
132 DMA_InitTypeDef
* pDmaInit
= &motor
->dmaInitStruct
;
134 const timerHardware_t
* const timerHardware
= motor
->timerHardware
;
135 TIM_TypeDef
*timer
= timerHardware
->tim
;
137 dmaResource_t
*dmaRef
= motor
->dmaRef
;
141 motor
->isInput
= true;
143 inputStampUs
= micros();
145 TIM_ARRPreloadConfig(timer
, ENABLE
);
146 timer
->ARR
= 0xffffffff;
148 TIM_ICInit(timer
, &motor
->icInitStruct
);
151 motor
->dmaInitStruct
.DMA_DIR
= DMA_DIR_PeripheralSRC
;
152 motor
->dmaInitStruct
.DMA_M2M
= DMA_M2M_Disable
;
153 #elif defined(STM32F4)
154 motor
->dmaInitStruct
.DMA_DIR
= DMA_DIR_PeripheralToMemory
;
157 xDMA_Init(dmaRef
, pDmaInit
);
162 void pwmCompleteDshotMotorUpdate(void)
164 /* If there is a dshot command loaded up, time it correctly with motor update*/
165 if (!dshotCommandQueueEmpty()) {
166 if (!dshotCommandOutputIsEnabled(dshotPwmDevice
.count
)) {
171 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
172 #ifdef USE_DSHOT_DMAR
174 xDMA_SetCurrDataCounter(dmaMotorTimers
[i
].dmaBurstRef
, dmaMotorTimers
[i
].dmaBurstLength
);
175 xDMA_Cmd(dmaMotorTimers
[i
].dmaBurstRef
, ENABLE
);
176 TIM_DMAConfig(dmaMotorTimers
[i
].timer
, TIM_DMABase_CCR1
, TIM_DMABurstLength_4Transfers
);
177 TIM_DMACmd(dmaMotorTimers
[i
].timer
, TIM_DMA_Update
, ENABLE
);
181 TIM_ARRPreloadConfig(dmaMotorTimers
[i
].timer
, DISABLE
);
182 dmaMotorTimers
[i
].timer
->ARR
= dmaMotorTimers
[i
].outputPeriod
;
183 TIM_ARRPreloadConfig(dmaMotorTimers
[i
].timer
, ENABLE
);
184 TIM_SetCounter(dmaMotorTimers
[i
].timer
, 0);
185 TIM_DMACmd(dmaMotorTimers
[i
].timer
, dmaMotorTimers
[i
].timerDmaSources
, ENABLE
);
186 dmaMotorTimers
[i
].timerDmaSources
= 0;
196 static void motor_DMA_IRQHandler(dmaChannelDescriptor_t
*descriptor
)
198 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
199 motorDmaOutput_t
* const motor
= &dmaMotors
[descriptor
->userParam
];
200 #ifdef USE_DSHOT_TELEMETRY
201 dshotDMAHandlerCycleCounters
.irqAt
= getCycleCounter();
203 #ifdef USE_DSHOT_DMAR
205 xDMA_Cmd(motor
->timerHardware
->dmaTimUPRef
, DISABLE
);
206 TIM_DMACmd(motor
->timerHardware
->tim
, TIM_DMA_Update
, DISABLE
);
210 xDMA_Cmd(motor
->dmaRef
, DISABLE
);
211 TIM_DMACmd(motor
->timerHardware
->tim
, motor
->timerDmaSource
, DISABLE
);
214 #ifdef USE_DSHOT_TELEMETRY
215 if (useDshotTelemetry
) {
216 pwmDshotSetDirectionInput(motor
);
217 xDMA_SetCurrDataCounter(motor
->dmaRef
, GCR_TELEMETRY_INPUT_LEN
);
218 xDMA_Cmd(motor
->dmaRef
, ENABLE
);
219 TIM_DMACmd(motor
->timerHardware
->tim
, motor
->timerDmaSource
, ENABLE
);
220 dshotDMAHandlerCycleCounters
.changeDirectionCompletedAt
= getCycleCounter();
223 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
227 bool pwmDshotMotorHardwareConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint8_t reorderedMotorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
229 #ifdef USE_DSHOT_TELEMETRY
230 #define OCINIT motor->ocInitStruct
231 #define DMAINIT motor->dmaInitStruct
233 TIM_OCInitTypeDef ocInitStruct
;
234 DMA_InitTypeDef dmaInitStruct
;
235 #define OCINIT ocInitStruct
236 #define DMAINIT dmaInitStruct
239 dmaResource_t
*dmaRef
= NULL
;
241 uint32_t dmaChannel
= 0;
243 #if defined(USE_DMA_SPEC)
244 const dmaChannelSpec_t
*dmaSpec
= dmaGetChannelSpecByTimer(timerHardware
);
246 if (dmaSpec
!= NULL
) {
247 dmaRef
= dmaSpec
->ref
;
249 dmaChannel
= dmaSpec
->channel
;
253 dmaRef
= timerHardware
->dmaRef
;
255 dmaChannel
= timerHardware
->dmaChannel
;
259 #ifdef USE_DSHOT_DMAR
261 dmaRef
= timerHardware
->dmaTimUPRef
;
263 dmaChannel
= timerHardware
->dmaTimUPChannel
;
268 if (dmaRef
== NULL
) {
272 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(dmaRef
);
274 bool dmaIsConfigured
= false;
275 #ifdef USE_DSHOT_DMAR
277 const resourceOwner_t
*owner
= dmaGetOwner(dmaIdentifier
);
278 if (owner
->owner
== OWNER_TIMUP
&& owner
->resourceIndex
== timerGetTIMNumber(timerHardware
->tim
)) {
279 dmaIsConfigured
= true;
280 } else if (!dmaAllocate(dmaIdentifier
, OWNER_TIMUP
, timerGetTIMNumber(timerHardware
->tim
))) {
286 if (!dmaAllocate(dmaIdentifier
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
))) {
291 motorDmaOutput_t
* const motor
= &dmaMotors
[motorIndex
];
292 TIM_TypeDef
*timer
= timerHardware
->tim
;
294 // Boolean configureTimer is always true when different channels of the same timer are processed in sequence,
295 // causing the timer and the associated DMA initialized more than once.
296 // To fix this, getTimerIndex must be expanded to return if a new timer has been requested.
297 // However, since the initialization is idempotent, it is left as is in a favor of flash space (for now).
298 const uint8_t timerIndex
= getTimerIndex(timer
);
299 const bool configureTimer
= (timerIndex
== dmaMotorTimerCount
-1);
301 motor
->timer
= &dmaMotorTimers
[timerIndex
];
302 motor
->index
= motorIndex
;
303 motor
->timerHardware
= timerHardware
;
305 const IO_t motorIO
= IOGetByTag(timerHardware
->tag
);
308 pupMode
= (output
& TIMER_OUTPUT_INVERTED
) ? GPIO_PuPd_DOWN
: GPIO_PuPd_UP
;
309 #ifdef USE_DSHOT_TELEMETRY
310 if (useDshotTelemetry
) {
311 output
^= TIMER_OUTPUT_INVERTED
;
315 motor
->iocfg
= IO_CONFIG(GPIO_Mode_AF
, GPIO_Speed_50MHz
, GPIO_OType_PP
, pupMode
);
316 IOConfigGPIOAF(motorIO
, motor
->iocfg
, timerHardware
->alternateFunction
);
318 if (configureTimer
) {
319 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure
;
320 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure
);
322 RCC_ClockCmd(timerRCC(timer
), ENABLE
);
323 TIM_Cmd(timer
, DISABLE
);
325 TIM_TimeBaseStructure
.TIM_Prescaler
= (uint16_t)(lrintf((float) timerClock(timer
) / getDshotHz(pwmProtocolType
) + 0.01f
) - 1);
326 TIM_TimeBaseStructure
.TIM_Period
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
? (MOTOR_NIBBLE_LENGTH_PROSHOT
) : MOTOR_BITLENGTH
) - 1;
327 TIM_TimeBaseStructure
.TIM_ClockDivision
= TIM_CKD_DIV1
;
328 TIM_TimeBaseStructure
.TIM_RepetitionCounter
= 0;
329 TIM_TimeBaseStructure
.TIM_CounterMode
= TIM_CounterMode_Up
;
330 TIM_TimeBaseInit(timer
, &TIM_TimeBaseStructure
);
333 TIM_OCStructInit(&OCINIT
);
334 OCINIT
.TIM_OCMode
= TIM_OCMode_PWM1
;
335 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
336 OCINIT
.TIM_OutputNState
= TIM_OutputNState_Enable
;
337 OCINIT
.TIM_OCNIdleState
= TIM_OCNIdleState_Reset
;
338 OCINIT
.TIM_OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPolarity_Low
: TIM_OCNPolarity_High
;
340 OCINIT
.TIM_OutputState
= TIM_OutputState_Enable
;
341 OCINIT
.TIM_OCIdleState
= TIM_OCIdleState_Set
;
342 OCINIT
.TIM_OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPolarity_Low
: TIM_OCPolarity_High
;
344 OCINIT
.TIM_Pulse
= 0;
346 #ifdef USE_DSHOT_TELEMETRY
347 TIM_ICStructInit(&motor
->icInitStruct
);
348 motor
->icInitStruct
.TIM_ICSelection
= TIM_ICSelection_DirectTI
;
349 motor
->icInitStruct
.TIM_ICPolarity
= TIM_ICPolarity_BothEdge
;
350 motor
->icInitStruct
.TIM_ICPrescaler
= TIM_ICPSC_DIV1
;
351 motor
->icInitStruct
.TIM_Channel
= timerHardware
->channel
;
352 motor
->icInitStruct
.TIM_ICFilter
= 2;
356 #ifdef USE_DSHOT_DMAR
358 motor
->timer
->dmaBurstRef
= dmaRef
;
362 motor
->timerDmaSource
= timerDmaSource(timerHardware
->channel
);
363 motor
->timer
->timerDmaSources
&= ~motor
->timerDmaSource
;
366 xDMA_Cmd(dmaRef
, DISABLE
);
369 if (!dmaIsConfigured
) {
370 dmaEnable(dmaIdentifier
);
373 DMA_StructInit(&DMAINIT
);
374 #ifdef USE_DSHOT_DMAR
376 motor
->timer
->dmaBurstBuffer
= &dshotBurstDmaBuffer
[timerIndex
][0];
379 DMAINIT
.DMA_MemoryBaseAddr
= (uint32_t)motor
->timer
->dmaBurstBuffer
;
380 DMAINIT
.DMA_DIR
= DMA_DIR_PeripheralDST
;
382 DMAINIT
.DMA_Channel
= timerHardware
->dmaTimUPChannel
;
383 DMAINIT
.DMA_Memory0BaseAddr
= (uint32_t)motor
->timer
->dmaBurstBuffer
;
384 DMAINIT
.DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
385 DMAINIT
.DMA_FIFOMode
= DMA_FIFOMode_Enable
;
386 DMAINIT
.DMA_FIFOThreshold
= DMA_FIFOThreshold_Full
;
387 DMAINIT
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
388 DMAINIT
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
390 DMAINIT
.DMA_PeripheralBaseAddr
= (uint32_t)&timerHardware
->tim
->DMAR
;
391 DMAINIT
.DMA_BufferSize
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
) ? PROSHOT_DMA_BUFFER_SIZE
: DSHOT_DMA_BUFFER_SIZE
; // XXX
392 DMAINIT
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
393 DMAINIT
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
394 DMAINIT
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Word
;
395 DMAINIT
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Word
;
396 DMAINIT
.DMA_Mode
= DMA_Mode_Normal
;
397 DMAINIT
.DMA_Priority
= DMA_Priority_High
;
401 motor
->dmaBuffer
= &dshotDmaBuffer
[motorIndex
][0];
404 DMAINIT
.DMA_MemoryBaseAddr
= (uint32_t)motor
->dmaBuffer
;
405 DMAINIT
.DMA_DIR
= DMA_DIR_PeripheralDST
;
406 DMAINIT
.DMA_M2M
= DMA_M2M_Disable
;
407 #elif defined(STM32F4)
408 DMAINIT
.DMA_Channel
= dmaChannel
;
409 DMAINIT
.DMA_Memory0BaseAddr
= (uint32_t)motor
->dmaBuffer
;
410 DMAINIT
.DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
411 DMAINIT
.DMA_FIFOMode
= DMA_FIFOMode_Enable
;
412 DMAINIT
.DMA_FIFOThreshold
= DMA_FIFOThreshold_1QuarterFull
;
413 DMAINIT
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
414 DMAINIT
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
416 DMAINIT
.DMA_PeripheralBaseAddr
= (uint32_t)timerChCCR(timerHardware
);
417 DMAINIT
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
418 DMAINIT
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
419 DMAINIT
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Word
;
420 DMAINIT
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Word
;
421 DMAINIT
.DMA_Mode
= DMA_Mode_Normal
;
422 DMAINIT
.DMA_Priority
= DMA_Priority_High
;
425 // XXX Consolidate common settings in the next refactor
427 motor
->dmaRef
= dmaRef
;
429 #ifdef USE_DSHOT_TELEMETRY
430 motor
->dshotTelemetryDeadtimeUs
= DSHOT_TELEMETRY_DEADTIME_US
+ 1000000 *
431 (16 * MOTOR_BITLENGTH
) / getDshotHz(pwmProtocolType
);
432 motor
->timer
->outputPeriod
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
? (MOTOR_NIBBLE_LENGTH_PROSHOT
) : MOTOR_BITLENGTH
) - 1;
433 pwmDshotSetDirectionOutput(motor
);
435 pwmDshotSetDirectionOutput(motor
, &OCINIT
, &DMAINIT
);
438 #ifdef USE_DSHOT_DMAR
440 if (!dmaIsConfigured
) {
441 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motor
->index
);
446 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motor
->index
);
449 TIM_Cmd(timer
, ENABLE
);
450 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
451 TIM_CCxNCmd(timer
, timerHardware
->channel
, TIM_CCxN_Enable
);
453 TIM_CCxCmd(timer
, timerHardware
->channel
, TIM_CCx_Enable
);
455 if (configureTimer
) {
456 TIM_ARRPreloadConfig(timer
, ENABLE
);
457 TIM_CtrlPWMOutputs(timer
, ENABLE
);
458 TIM_Cmd(timer
, ENABLE
);
460 #ifdef USE_DSHOT_TELEMETRY
461 if (useDshotTelemetry
) {
462 // avoid high line during startup to prevent bootloader activation
463 *timerChCCR(timerHardware
) = 0xffff;
466 motor
->configured
= true;