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"
43 #include "pwm_output.h"
44 #include "drivers/dshot.h"
45 #include "drivers/dshot_dpwm.h"
46 #include "drivers/dshot_command.h"
48 #include "pwm_output_dshot_shared.h"
50 #ifdef USE_DSHOT_TELEMETRY
52 void dshotEnableChannels(uint8_t motorCount
)
54 for (int i
= 0; i
< motorCount
; i
++) {
55 if (dmaMotors
[i
].output
& TIMER_OUTPUT_N_CHANNEL
) {
56 TIM_CCxNCmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerHardware
->channel
, TIM_CCxN_Enable
);
58 TIM_CCxCmd(dmaMotors
[i
].timerHardware
->tim
, dmaMotors
[i
].timerHardware
->channel
, TIM_CCx_Enable
);
65 FAST_CODE
void pwmDshotSetDirectionOutput(
66 motorDmaOutput_t
* const motor
67 #ifndef USE_DSHOT_TELEMETRY
68 ,TIM_OCInitTypeDef
*pOcInit
, DMA_InitTypeDef
* pDmaInit
72 #ifdef USE_DSHOT_TELEMETRY
73 TIM_OCInitTypeDef
* pOcInit
= &motor
->ocInitStruct
;
74 DMA_InitTypeDef
* pDmaInit
= &motor
->dmaInitStruct
;
77 const timerHardware_t
* const timerHardware
= motor
->timerHardware
;
78 TIM_TypeDef
*timer
= timerHardware
->tim
;
80 dmaResource_t
*dmaRef
= motor
->dmaRef
;
82 #if defined(USE_DSHOT_DMAR) && !defined(USE_DSHOT_TELEMETRY)
84 dmaRef
= timerHardware
->dmaTimUPRef
;
90 #ifdef USE_DSHOT_TELEMETRY
91 motor
->isInput
= false;
93 timerOCPreloadConfig(timer
, timerHardware
->channel
, TIM_OCPreload_Disable
);
94 timerOCInit(timer
, timerHardware
->channel
, pOcInit
);
95 timerOCPreloadConfig(timer
, timerHardware
->channel
, TIM_OCPreload_Enable
);
99 pDmaInit
->DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
104 pDmaInit
->DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
108 xDMA_Init(dmaRef
, pDmaInit
);
109 xDMA_ITConfig(dmaRef
, DMA_IT_TC
, ENABLE
);
113 #ifdef USE_DSHOT_TELEMETRY
115 static void pwmDshotSetDirectionInput(
116 motorDmaOutput_t
* const motor
119 DMA_InitTypeDef
* pDmaInit
= &motor
->dmaInitStruct
;
121 const timerHardware_t
* const timerHardware
= motor
->timerHardware
;
122 TIM_TypeDef
*timer
= timerHardware
->tim
;
124 dmaResource_t
*dmaRef
= motor
->dmaRef
;
128 motor
->isInput
= true;
130 inputStampUs
= micros();
132 TIM_ARRPreloadConfig(timer
, ENABLE
);
133 timer
->ARR
= 0xffffffff;
135 TIM_ICInit(timer
, &motor
->icInitStruct
);
138 motor
->dmaInitStruct
.DMA_DIR
= DMA_DIR_PeripheralToMemory
;
141 xDMA_Init(dmaRef
, pDmaInit
);
146 void pwmCompleteDshotMotorUpdate(void)
148 /* If there is a dshot command loaded up, time it correctly with motor update*/
149 if (!dshotCommandQueueEmpty()) {
150 if (!dshotCommandOutputIsEnabled(dshotPwmDevice
.count
)) {
155 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
156 #ifdef USE_DSHOT_DMAR
158 xDMA_SetCurrDataCounter(dmaMotorTimers
[i
].dmaBurstRef
, dmaMotorTimers
[i
].dmaBurstLength
);
159 xDMA_Cmd(dmaMotorTimers
[i
].dmaBurstRef
, ENABLE
);
160 TIM_DMAConfig(dmaMotorTimers
[i
].timer
, TIM_DMABase_CCR1
, TIM_DMABurstLength_4Transfers
);
161 TIM_DMACmd(dmaMotorTimers
[i
].timer
, TIM_DMA_Update
, ENABLE
);
165 TIM_ARRPreloadConfig(dmaMotorTimers
[i
].timer
, DISABLE
);
166 dmaMotorTimers
[i
].timer
->ARR
= dmaMotorTimers
[i
].outputPeriod
;
167 TIM_ARRPreloadConfig(dmaMotorTimers
[i
].timer
, ENABLE
);
168 TIM_SetCounter(dmaMotorTimers
[i
].timer
, 0);
169 TIM_DMACmd(dmaMotorTimers
[i
].timer
, dmaMotorTimers
[i
].timerDmaSources
, ENABLE
);
170 dmaMotorTimers
[i
].timerDmaSources
= 0;
175 FAST_CODE
static void motor_DMA_IRQHandler(dmaChannelDescriptor_t
*descriptor
)
177 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
178 motorDmaOutput_t
* const motor
= &dmaMotors
[descriptor
->userParam
];
179 #ifdef USE_DSHOT_TELEMETRY
180 dshotDMAHandlerCycleCounters
.irqAt
= getCycleCounter();
182 #ifdef USE_DSHOT_DMAR
184 xDMA_Cmd(motor
->timerHardware
->dmaTimUPRef
, DISABLE
);
185 TIM_DMACmd(motor
->timerHardware
->tim
, TIM_DMA_Update
, DISABLE
);
189 xDMA_Cmd(motor
->dmaRef
, DISABLE
);
190 TIM_DMACmd(motor
->timerHardware
->tim
, motor
->timerDmaSource
, DISABLE
);
193 #ifdef USE_DSHOT_TELEMETRY
194 if (useDshotTelemetry
) {
195 pwmDshotSetDirectionInput(motor
);
196 xDMA_SetCurrDataCounter(motor
->dmaRef
, GCR_TELEMETRY_INPUT_LEN
);
197 xDMA_Cmd(motor
->dmaRef
, ENABLE
);
198 TIM_DMACmd(motor
->timerHardware
->tim
, motor
->timerDmaSource
, ENABLE
);
199 dshotDMAHandlerCycleCounters
.changeDirectionCompletedAt
= getCycleCounter();
202 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
206 bool pwmDshotMotorHardwareConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint8_t reorderedMotorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
208 #ifdef USE_DSHOT_TELEMETRY
209 #define OCINIT motor->ocInitStruct
210 #define DMAINIT motor->dmaInitStruct
212 TIM_OCInitTypeDef ocInitStruct
;
213 DMA_InitTypeDef dmaInitStruct
;
214 #define OCINIT ocInitStruct
215 #define DMAINIT dmaInitStruct
218 dmaResource_t
*dmaRef
= NULL
;
220 uint32_t dmaChannel
= 0;
222 #if defined(USE_DMA_SPEC)
223 const dmaChannelSpec_t
*dmaSpec
= dmaGetChannelSpecByTimer(timerHardware
);
225 if (dmaSpec
!= NULL
) {
226 dmaRef
= dmaSpec
->ref
;
228 dmaChannel
= dmaSpec
->channel
;
232 dmaRef
= timerHardware
->dmaRef
;
234 dmaChannel
= timerHardware
->dmaChannel
;
238 #ifdef USE_DSHOT_DMAR
240 dmaRef
= timerHardware
->dmaTimUPRef
;
242 dmaChannel
= timerHardware
->dmaTimUPChannel
;
247 if (dmaRef
== NULL
) {
251 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(dmaRef
);
253 bool dmaIsConfigured
= false;
254 #ifdef USE_DSHOT_DMAR
256 const resourceOwner_t
*owner
= dmaGetOwner(dmaIdentifier
);
257 if (owner
->owner
== OWNER_TIMUP
&& owner
->resourceIndex
== timerGetTIMNumber(timerHardware
->tim
)) {
258 dmaIsConfigured
= true;
259 } else if (!dmaAllocate(dmaIdentifier
, OWNER_TIMUP
, timerGetTIMNumber(timerHardware
->tim
))) {
265 if (!dmaAllocate(dmaIdentifier
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
))) {
270 motorDmaOutput_t
* const motor
= &dmaMotors
[motorIndex
];
271 TIM_TypeDef
*timer
= timerHardware
->tim
;
273 // Boolean configureTimer is always true when different channels of the same timer are processed in sequence,
274 // causing the timer and the associated DMA initialized more than once.
275 // To fix this, getTimerIndex must be expanded to return if a new timer has been requested.
276 // However, since the initialization is idempotent, it is left as is in a favor of flash space (for now).
277 const uint8_t timerIndex
= getTimerIndex(timer
);
278 const bool configureTimer
= (timerIndex
== dmaMotorTimerCount
-1);
280 motor
->timer
= &dmaMotorTimers
[timerIndex
];
281 motor
->index
= motorIndex
;
282 motor
->timerHardware
= timerHardware
;
284 const IO_t motorIO
= IOGetByTag(timerHardware
->tag
);
287 pupMode
= (output
& TIMER_OUTPUT_INVERTED
) ? GPIO_PuPd_DOWN
: GPIO_PuPd_UP
;
288 #ifdef USE_DSHOT_TELEMETRY
289 if (useDshotTelemetry
) {
290 output
^= TIMER_OUTPUT_INVERTED
;
294 motor
->iocfg
= IO_CONFIG(GPIO_Mode_AF
, GPIO_Speed_50MHz
, GPIO_OType_PP
, pupMode
);
295 IOConfigGPIOAF(motorIO
, motor
->iocfg
, timerHardware
->alternateFunction
);
297 if (configureTimer
) {
298 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure
;
299 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure
);
301 RCC_ClockCmd(timerRCC(timer
), ENABLE
);
302 TIM_Cmd(timer
, DISABLE
);
304 TIM_TimeBaseStructure
.TIM_Prescaler
= (uint16_t)(lrintf((float) timerClock(timer
) / getDshotHz(pwmProtocolType
) + 0.01f
) - 1);
305 TIM_TimeBaseStructure
.TIM_Period
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
? (MOTOR_NIBBLE_LENGTH_PROSHOT
) : MOTOR_BITLENGTH
) - 1;
306 TIM_TimeBaseStructure
.TIM_ClockDivision
= TIM_CKD_DIV1
;
307 TIM_TimeBaseStructure
.TIM_RepetitionCounter
= 0;
308 TIM_TimeBaseStructure
.TIM_CounterMode
= TIM_CounterMode_Up
;
309 TIM_TimeBaseInit(timer
, &TIM_TimeBaseStructure
);
312 TIM_OCStructInit(&OCINIT
);
313 OCINIT
.TIM_OCMode
= TIM_OCMode_PWM1
;
314 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
315 OCINIT
.TIM_OutputNState
= TIM_OutputNState_Enable
;
316 OCINIT
.TIM_OCNIdleState
= TIM_OCNIdleState_Reset
;
317 OCINIT
.TIM_OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPolarity_Low
: TIM_OCNPolarity_High
;
319 OCINIT
.TIM_OutputState
= TIM_OutputState_Enable
;
320 OCINIT
.TIM_OCIdleState
= TIM_OCIdleState_Set
;
321 OCINIT
.TIM_OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPolarity_Low
: TIM_OCPolarity_High
;
323 OCINIT
.TIM_Pulse
= 0;
325 #ifdef USE_DSHOT_TELEMETRY
326 TIM_ICStructInit(&motor
->icInitStruct
);
327 motor
->icInitStruct
.TIM_ICSelection
= TIM_ICSelection_DirectTI
;
328 motor
->icInitStruct
.TIM_ICPolarity
= TIM_ICPolarity_BothEdge
;
329 motor
->icInitStruct
.TIM_ICPrescaler
= TIM_ICPSC_DIV1
;
330 motor
->icInitStruct
.TIM_Channel
= timerHardware
->channel
;
331 motor
->icInitStruct
.TIM_ICFilter
= 2;
335 #ifdef USE_DSHOT_DMAR
337 motor
->timer
->dmaBurstRef
= dmaRef
;
341 motor
->timerDmaSource
= timerDmaSource(timerHardware
->channel
);
342 motor
->timer
->timerDmaSources
&= ~motor
->timerDmaSource
;
345 xDMA_Cmd(dmaRef
, DISABLE
);
348 if (!dmaIsConfigured
) {
349 dmaEnable(dmaIdentifier
);
352 DMA_StructInit(&DMAINIT
);
353 #ifdef USE_DSHOT_DMAR
355 motor
->timer
->dmaBurstBuffer
= &dshotBurstDmaBuffer
[timerIndex
][0];
357 DMAINIT
.DMA_Channel
= timerHardware
->dmaTimUPChannel
;
358 DMAINIT
.DMA_Memory0BaseAddr
= (uint32_t)motor
->timer
->dmaBurstBuffer
;
359 DMAINIT
.DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
360 DMAINIT
.DMA_FIFOMode
= DMA_FIFOMode_Enable
;
361 DMAINIT
.DMA_FIFOThreshold
= DMA_FIFOThreshold_Full
;
362 DMAINIT
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
363 DMAINIT
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
365 DMAINIT
.DMA_PeripheralBaseAddr
= (uint32_t)&timerHardware
->tim
->DMAR
;
366 DMAINIT
.DMA_BufferSize
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
) ? PROSHOT_DMA_BUFFER_SIZE
: DSHOT_DMA_BUFFER_SIZE
; // XXX
367 DMAINIT
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
368 DMAINIT
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
369 DMAINIT
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Word
;
370 DMAINIT
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Word
;
371 DMAINIT
.DMA_Mode
= DMA_Mode_Normal
;
372 DMAINIT
.DMA_Priority
= DMA_Priority_High
;
376 motor
->dmaBuffer
= &dshotDmaBuffer
[motorIndex
][0];
379 DMAINIT
.DMA_Channel
= dmaChannel
;
380 DMAINIT
.DMA_Memory0BaseAddr
= (uint32_t)motor
->dmaBuffer
;
381 DMAINIT
.DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
382 DMAINIT
.DMA_FIFOMode
= DMA_FIFOMode_Enable
;
383 DMAINIT
.DMA_FIFOThreshold
= DMA_FIFOThreshold_1QuarterFull
;
384 DMAINIT
.DMA_MemoryBurst
= DMA_MemoryBurst_Single
;
385 DMAINIT
.DMA_PeripheralBurst
= DMA_PeripheralBurst_Single
;
387 DMAINIT
.DMA_PeripheralBaseAddr
= (uint32_t)timerChCCR(timerHardware
);
388 DMAINIT
.DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
389 DMAINIT
.DMA_MemoryInc
= DMA_MemoryInc_Enable
;
390 DMAINIT
.DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Word
;
391 DMAINIT
.DMA_MemoryDataSize
= DMA_MemoryDataSize_Word
;
392 DMAINIT
.DMA_Mode
= DMA_Mode_Normal
;
393 DMAINIT
.DMA_Priority
= DMA_Priority_High
;
396 // XXX Consolidate common settings in the next refactor
398 motor
->dmaRef
= dmaRef
;
400 #ifdef USE_DSHOT_TELEMETRY
401 motor
->dshotTelemetryDeadtimeUs
= DSHOT_TELEMETRY_DEADTIME_US
+ 1000000 *
402 (16 * MOTOR_BITLENGTH
) / getDshotHz(pwmProtocolType
);
403 motor
->timer
->outputPeriod
= (pwmProtocolType
== PWM_TYPE_PROSHOT1000
? (MOTOR_NIBBLE_LENGTH_PROSHOT
) : MOTOR_BITLENGTH
) - 1;
404 pwmDshotSetDirectionOutput(motor
);
406 pwmDshotSetDirectionOutput(motor
, &OCINIT
, &DMAINIT
);
409 #ifdef USE_DSHOT_DMAR
411 if (!dmaIsConfigured
) {
412 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motor
->index
);
417 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motor
->index
);
420 TIM_Cmd(timer
, ENABLE
);
421 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
422 TIM_CCxNCmd(timer
, timerHardware
->channel
, TIM_CCxN_Enable
);
424 TIM_CCxCmd(timer
, timerHardware
->channel
, TIM_CCx_Enable
);
426 if (configureTimer
) {
427 TIM_ARRPreloadConfig(timer
, ENABLE
);
428 TIM_CtrlPWMOutputs(timer
, ENABLE
);
429 TIM_Cmd(timer
, ENABLE
);
431 #ifdef USE_DSHOT_TELEMETRY
432 if (useDshotTelemetry
) {
433 // avoid high line during startup to prevent bootloader activation
434 *timerChCCR(timerHardware
) = 0xffff;
437 motor
->configured
= true;