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/>.
30 #include "drivers/dma_reqmap.h"
31 #include "drivers/io.h"
33 #include "pwm_output.h"
34 #include "drivers/dshot.h"
35 #include "drivers/dshot_dpwm.h"
36 #include "drivers/dshot_command.h"
37 #include "drivers/nvic.h"
40 #include "pg/timerup.h"
42 static HAL_StatusTypeDef result
;
44 static uint8_t dmaMotorTimerCount
= 0;
45 static motorDmaTimer_t dmaMotorTimers
[MAX_DMA_TIMERS
];
46 static motorDmaOutput_t dmaMotors
[MAX_SUPPORTED_MOTORS
];
48 motorDmaOutput_t
*getMotorDmaOutput(uint8_t index
)
50 return &dmaMotors
[index
];
53 uint8_t getTimerIndex(TIM_TypeDef
*timer
)
55 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
56 if (dmaMotorTimers
[i
].timer
== timer
) {
60 dmaMotorTimers
[dmaMotorTimerCount
++].timer
= timer
;
61 return dmaMotorTimerCount
- 1;
64 // TIM_CHANNEL_x TIM_CHANNEL_x/4 TIM_DMA_ID_CCx TIM_DMA_CCx
70 // TIM_CHANNEL_TO_TIM_DMA_ID_CC = (TIM_CHANNEL_x / 4) + 1
71 // TIM_CHANNEL_TO_TIM_DMA_CC = 0x200 << (TIM_CHANNEL_x / 4)
74 // A variant of HAL_TIM_PWM_Start_DMA/HAL_TIMEx_PWMN_Start_DMA that only disables DMA on a timer channel:
75 // 1. Configure and enable DMA Stream
76 // 2. Enable DMA request on a timer channel
78 void pwmChannelDMAStart(TIM_HandleTypeDef
*htim
, uint32_t Channel
, uint32_t *pData
, uint16_t Length
)
82 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC1
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR1
, Length
);
83 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC1
);
86 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC2
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR2
, Length
);
87 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC2
);
90 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC3
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR3
,Length
);
91 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC3
);
94 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC4
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR4
, Length
);
95 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC4
);
101 // A variant of HAL_TIM_PWM_Stop_DMA/HAL_TIMEx_PWMN_Stop_DMA that only disables DMA on a timer channel
102 // 1. Disable the TIM Capture/Compare 1 DMA request
104 void pwmChannelDMAStop(TIM_HandleTypeDef
*htim
, uint32_t Channel
)
108 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC1
);
111 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC2
);
114 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC3
);
117 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC4
);
123 // A variant of HAL_TIM_DMABurst_WriteStart that can handle multiple bursts.
124 // (HAL_TIM_DMABurst_WriteStart can only handle single burst)
126 void pwmBurstDMAStart(TIM_HandleTypeDef
*htim
, uint32_t BurstBaseAddress
, uint32_t BurstRequestSrc
, uint32_t BurstUnit
, uint32_t* BurstBuffer
, uint32_t BurstLength
)
129 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_UPDATE
], (uint32_t)BurstBuffer
, (uint32_t)&htim
->Instance
->DMAR
, BurstLength
);
131 // Configure burst mode DMA */
132 htim
->Instance
->DCR
= BurstBaseAddress
| BurstUnit
;
134 // Enable burst mode DMA
135 __HAL_TIM_ENABLE_DMA(htim
, BurstRequestSrc
);
138 FAST_CODE
void pwmWriteDshotInt(uint8_t index
, uint16_t value
)
140 motorDmaOutput_t
*const motor
= &dmaMotors
[index
];
142 if (!motor
->configured
) {
146 /*If there is a command ready to go overwrite the value and send that instead*/
147 if (dshotCommandIsProcessing()) {
148 value
= dshotCommandGetCurrent(index
);
150 motor
->protocolControl
.requestTelemetry
= true;
154 if (!motor
->timerHardware
156 // When USE_DMA_SPEC is in effect, motor->timerHardware remains NULL if valid DMA is not assigned.
157 || !motor
->timerHardware
->dmaRef
164 motor
->protocolControl
.value
= value
;
166 uint16_t packet
= prepareDshotPacket(&motor
->protocolControl
);
169 #ifdef USE_DSHOT_DMAR
171 bufferSize
= loadDmaBuffer(&motor
->timer
->dmaBurstBuffer
[timerLookupChannelIndex(motor
->timerHardware
->channel
)], 4, packet
);
172 motor
->timer
->dmaBurstLength
= bufferSize
* 4;
176 bufferSize
= loadDmaBuffer(motor
->dmaBuffer
, 1, packet
);
178 pwmChannelDMAStart(&motor
->TimHandle
, motor
->timerHardware
->channel
, motor
->dmaBuffer
, bufferSize
);
182 void pwmCompleteDshotMotorUpdate(void)
184 // If there is a dshot command loaded up, time it correctly with motor update
186 if (!dshotCommandQueueEmpty() && !dshotCommandOutputIsEnabled(dshotPwmDevice
.count
)) {
190 #ifdef USE_DSHOT_DMAR
192 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
193 motorDmaTimer_t
*burstDmaTimer
= &dmaMotorTimers
[i
];
195 // Transfer CCR1 through CCR4 for each burst
196 pwmBurstDMAStart(&burstDmaTimer
->timHandle
,
197 TIM_DMABASE_CCR1
, TIM_DMA_UPDATE
, TIM_DMABURSTLENGTH_4TRANSFERS
,
198 (uint32_t*)burstDmaTimer
->dmaBurstBuffer
, burstDmaTimer
->dmaBurstLength
);
203 // XXX Empty for non-burst?
207 FAST_IRQ_HANDLER
static void motor_DMA_IRQHandler(dmaChannelDescriptor_t
* descriptor
)
209 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
211 #ifdef USE_DSHOT_DMAR
213 motorDmaTimer_t
*burstDmaTimer
= &dmaMotorTimers
[descriptor
->userParam
];
215 HAL_TIM_DMABurst_WriteStop(&burstDmaTimer
->timHandle
, TIM_DMA_UPDATE
);
216 HAL_DMA_IRQHandler(&burstDmaTimer
->hdma_tim
);
220 motorDmaOutput_t
* const motor
= &dmaMotors
[descriptor
->userParam
];
222 pwmChannelDMAStop(&motor
->TimHandle
,motor
->timerHardware
->channel
);
223 HAL_DMA_IRQHandler(motor
->TimHandle
.hdma
[motor
->timerDmaIndex
]);
226 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
230 bool pwmDshotMotorHardwareConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint8_t reorderedMotorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
232 dmaResource_t
*dmaRef
= NULL
;
236 const dmaChannelSpec_t
*dmaSpec
= dmaGetChannelSpecByTimer(timerHardware
);
239 dmaRef
= dmaSpec
->ref
;
240 dmaChannel
= dmaSpec
->channel
;
243 dmaRef
= timerHardware
->dmaRef
;
244 dmaChannel
= timerHardware
->dmaChannel
;
247 #ifdef USE_DSHOT_DMAR
250 uint8_t timnum
= timerGetTIMNumber(timerHardware
->tim
);
251 dmaoptValue_t dmaopt
= timerUpConfig(timnum
- 1)->dmaopt
;
252 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByPeripheral(DMA_PERIPH_TIMUP
, timnum
- 1, dmaopt
);
253 dmaRef
= dmaChannelSpec
->ref
;
254 dmaChannel
= dmaChannelSpec
->channel
;
256 dmaRef
= timerHardware
->dmaTimUPRef
;
257 dmaChannel
= timerHardware
->dmaTimUPChannel
;
262 if (dmaRef
== NULL
) {
266 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(dmaRef
);
268 bool dmaIsConfigured
= false;
269 #ifdef USE_DSHOT_DMAR
271 const resourceOwner_t
*owner
= dmaGetOwner(dmaIdentifier
);
272 if (owner
->owner
== OWNER_TIMUP
&& owner
->resourceIndex
== timerGetTIMNumber(timerHardware
->tim
)) {
273 dmaIsConfigured
= true;
274 } else if (!dmaAllocate(dmaIdentifier
, OWNER_TIMUP
, timerGetTIMNumber(timerHardware
->tim
))) {
280 if (!dmaAllocate(dmaIdentifier
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
))) {
285 motorDmaOutput_t
* const motor
= &dmaMotors
[motorIndex
];
286 motor
->timerHardware
= timerHardware
;
288 TIM_TypeDef
*timer
= timerHardware
->tim
; // "timer" is confusing; "tim"?
289 const IO_t motorIO
= IOGetByTag(timerHardware
->tag
);
290 uint8_t pupMode
= (output
& TIMER_OUTPUT_INVERTED
) ? GPIO_PULLDOWN
: GPIO_PULLUP
;
291 #ifdef USE_DSHOT_TELEMETRY
292 if (useDshotTelemetry
) {
293 output
^= TIMER_OUTPUT_INVERTED
;
297 motor
->iocfg
= IO_CONFIG(GPIO_MODE_AF_PP
, GPIO_SPEED_FREQ_LOW
, pupMode
);
298 const uint8_t timerIndex
= getTimerIndex(timer
);
299 const bool configureTimer
= (timerIndex
== dmaMotorTimerCount
- 1);
301 IOInit(motorIO
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
302 IOConfigGPIOAF(motorIO
, motor
->iocfg
, timerHardware
->alternateFunction
);
304 // Configure time base
306 if (configureTimer
) {
307 RCC_ClockCmd(timerRCC(timer
), ENABLE
);
309 motor
->TimHandle
.Instance
= timerHardware
->tim
; // timer
310 motor
->TimHandle
.Init
.Prescaler
= (uint16_t)(lrintf((float) timerClock(timer
) / getDshotHz(pwmProtocolType
) + 0.01f
) - 1);
311 motor
->TimHandle
.Init
.Period
= pwmProtocolType
== PWM_TYPE_PROSHOT1000
? MOTOR_NIBBLE_LENGTH_PROSHOT
: MOTOR_BITLENGTH
;
312 motor
->TimHandle
.Init
.RepetitionCounter
= 0;
313 motor
->TimHandle
.Init
.ClockDivision
= TIM_CLOCKDIVISION_DIV1
;
314 motor
->TimHandle
.Init
.CounterMode
= TIM_COUNTERMODE_UP
;
315 motor
->TimHandle
.Init
.AutoReloadPreload
= TIM_AUTORELOAD_PRELOAD_DISABLE
;
317 result
= HAL_TIM_PWM_Init(&motor
->TimHandle
);
319 if (result
!= HAL_OK
) {
320 /* Initialization Error */
327 chan oinv IDLE NIDLE POL NPOL
334 /* PWM mode 1 configuration */
336 TIM_OC_InitTypeDef TIM_OCInitStructure
;
337 TIM_OCInitStructure
.OCMode
= TIM_OCMODE_PWM1
;
339 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
340 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_RESET
;
341 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_HIGH
: TIM_OCPOLARITY_LOW
;
342 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_RESET
;
343 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_HIGH
: TIM_OCNPOLARITY_LOW
;
345 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_SET
;
346 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_LOW
: TIM_OCPOLARITY_HIGH
;
347 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_SET
;
348 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_LOW
: TIM_OCNPOLARITY_HIGH
;
350 TIM_OCInitStructure
.OCFastMode
= TIM_OCFAST_DISABLE
;
351 TIM_OCInitStructure
.Pulse
= 0;
353 result
= HAL_TIM_PWM_ConfigChannel(&motor
->TimHandle
, &TIM_OCInitStructure
, motor
->timerHardware
->channel
);
355 if (result
!= HAL_OK
) {
356 /* Configuration Error */
362 motor
->timer
= &dmaMotorTimers
[timerIndex
];
364 #ifdef USE_DSHOT_DMAR
366 motor
->timer
->dmaBurstRef
= dmaRef
;
368 if (!configureTimer
) {
369 motor
->configured
= true;
375 motor
->timerDmaSource
= timerDmaSource(timerHardware
->channel
);
376 motor
->timer
->timerDmaSources
|= motor
->timerDmaSource
;
377 motor
->timerDmaIndex
= timerDmaIndex(timerHardware
->channel
);
380 if (!dmaIsConfigured
) {
381 dmaEnable(dmaIdentifier
);
382 #ifdef USE_DSHOT_DMAR
384 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, timerIndex
);
388 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motorIndex
);
392 #ifdef USE_DSHOT_DMAR
394 motor
->timer
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
395 motor
->timer
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
396 motor
->timer
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
397 motor
->timer
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
398 motor
->timer
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
399 motor
->timer
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
400 motor
->timer
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
401 #if !defined(STM32G4)
402 motor
->timer
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
403 motor
->timer
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
404 motor
->timer
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
405 motor
->timer
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
408 motor
->timer
->dmaBurstBuffer
= &dshotBurstDmaBuffer
[timerIndex
][0];
409 motor
->timer
->timHandle
= motor
->TimHandle
;
410 memset(motor
->timer
->dmaBurstBuffer
, 0, DSHOT_DMA_BUFFER_SIZE
* 4 * sizeof(uint32_t));
412 /* Set hdma_tim instance */
413 motor
->timer
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
414 motor
->timer
->hdma_tim
.Init
.Request
= dmaChannel
;
416 /* Link hdma_tim to hdma[TIM_DMA_ID_UPDATE] (update) */
417 __HAL_LINKDMA(&motor
->timer
->timHandle
, hdma
[TIM_DMA_ID_UPDATE
], motor
->timer
->hdma_tim
);
419 if (!dmaIsConfigured
) {
420 /* Initialize TIMx DMA handle */
421 result
= HAL_DMA_Init(motor
->timer
->timHandle
.hdma
[TIM_DMA_ID_UPDATE
]);
428 motor
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
429 motor
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
430 motor
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
431 motor
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
432 motor
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
433 motor
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
434 motor
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
435 #if !defined(STM32G4)
436 motor
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
437 motor
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
438 motor
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
439 motor
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
442 motor
->dmaBuffer
= &dshotDmaBuffer
[motorIndex
][0];
443 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-2] = 0; // XXX Is this necessary? -> probably.
444 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-1] = 0; // XXX Is this necessary?
446 motor
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
447 motor
->hdma_tim
.Init
.Request
= dmaChannel
;
449 /* Link hdma_tim to hdma[x] (channelx) */
450 __HAL_LINKDMA(&motor
->TimHandle
, hdma
[motor
->timerDmaIndex
], motor
->hdma_tim
);
452 /* Initialize TIMx DMA handle */
453 result
= HAL_DMA_Init(motor
->TimHandle
.hdma
[motor
->timerDmaIndex
]);
457 if (result
!= HAL_OK
) {
458 /* Initialization Error */
462 // Start the timer channel now.
463 // Enabling/disabling DMA request can restart a new cycle without PWM start/stop.
465 if (motor
->timerHardware
->output
& TIMER_OUTPUT_N_CHANNEL
) {
466 result
= HAL_TIMEx_PWMN_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
468 result
= HAL_TIM_PWM_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
471 if (result
!= HAL_OK
) {
472 /* Starting PWM generation Error */
476 motor
->configured
= true;