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
);
87 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC2
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR2
, Length
);
88 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC2
);
92 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC3
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR3
,Length
);
93 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC3
);
97 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_CC4
], (uint32_t)pData
, (uint32_t)&htim
->Instance
->CCR4
, Length
);
98 __HAL_TIM_ENABLE_DMA(htim
, TIM_DMA_CC4
);
104 // A variant of HAL_TIM_PWM_Stop_DMA/HAL_TIMEx_PWMN_Stop_DMA that only disables DMA on a timer channel
105 // 1. Disable the TIM Capture/Compare 1 DMA request
107 void pwmChannelDMAStop(TIM_HandleTypeDef
*htim
, uint32_t Channel
)
111 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC1
);
115 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC2
);
119 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC3
);
123 __HAL_TIM_DISABLE_DMA(htim
, TIM_DMA_CC4
);
129 // A variant of HAL_TIM_DMABurst_WriteStart that can handle multiple bursts.
130 // (HAL_TIM_DMABurst_WriteStart can only handle single burst)
132 void pwmBurstDMAStart(TIM_HandleTypeDef
*htim
, uint32_t BurstBaseAddress
, uint32_t BurstRequestSrc
, uint32_t BurstUnit
, uint32_t* BurstBuffer
, uint32_t BurstLength
)
135 HAL_DMA_Start_IT(htim
->hdma
[TIM_DMA_ID_UPDATE
], (uint32_t)BurstBuffer
, (uint32_t)&htim
->Instance
->DMAR
, BurstLength
);
137 // Configure burst mode DMA */
138 htim
->Instance
->DCR
= BurstBaseAddress
| BurstUnit
;
140 // Enable burst mode DMA
141 __HAL_TIM_ENABLE_DMA(htim
, BurstRequestSrc
);
144 FAST_CODE
void pwmWriteDshotInt(uint8_t index
, uint16_t value
)
146 motorDmaOutput_t
*const motor
= &dmaMotors
[index
];
148 if (!motor
->configured
) {
152 /*If there is a command ready to go overwrite the value and send that instead*/
153 if (dshotCommandIsProcessing()) {
154 value
= dshotCommandGetCurrent(index
);
156 motor
->protocolControl
.requestTelemetry
= true;
160 if (!motor
->timerHardware
162 // When USE_DMA_SPEC is in effect, motor->timerHardware remains NULL if valid DMA is not assigned.
163 || !motor
->timerHardware
->dmaRef
170 motor
->protocolControl
.value
= value
;
172 uint16_t packet
= prepareDshotPacket(&motor
->protocolControl
);
175 #ifdef USE_DSHOT_DMAR
177 bufferSize
= loadDmaBuffer(&motor
->timer
->dmaBurstBuffer
[timerLookupChannelIndex(motor
->timerHardware
->channel
)], 4, packet
);
178 motor
->timer
->dmaBurstLength
= bufferSize
* 4;
182 bufferSize
= loadDmaBuffer(motor
->dmaBuffer
, 1, packet
);
184 pwmChannelDMAStart(&motor
->TimHandle
, motor
->timerHardware
->channel
, motor
->dmaBuffer
, bufferSize
);
188 void pwmCompleteDshotMotorUpdate(void)
190 // If there is a dshot command loaded up, time it correctly with motor update
192 if (!dshotCommandQueueEmpty() && !dshotCommandOutputIsEnabled(dshotPwmDevice
.count
)) {
196 #ifdef USE_DSHOT_DMAR
198 for (int i
= 0; i
< dmaMotorTimerCount
; i
++) {
199 motorDmaTimer_t
*burstDmaTimer
= &dmaMotorTimers
[i
];
201 // Transfer CCR1 through CCR4 for each burst
202 pwmBurstDMAStart(&burstDmaTimer
->timHandle
,
203 TIM_DMABASE_CCR1
, TIM_DMA_UPDATE
, TIM_DMABURSTLENGTH_4TRANSFERS
,
204 (uint32_t*)burstDmaTimer
->dmaBurstBuffer
, burstDmaTimer
->dmaBurstLength
);
209 // XXX Empty for non-burst?
213 FAST_IRQ_HANDLER
static void motor_DMA_IRQHandler(dmaChannelDescriptor_t
* descriptor
)
215 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
217 #ifdef USE_DSHOT_DMAR
219 motorDmaTimer_t
*burstDmaTimer
= &dmaMotorTimers
[descriptor
->userParam
];
221 HAL_TIM_DMABurst_WriteStop(&burstDmaTimer
->timHandle
, TIM_DMA_UPDATE
);
222 HAL_DMA_IRQHandler(&burstDmaTimer
->hdma_tim
);
226 motorDmaOutput_t
* const motor
= &dmaMotors
[descriptor
->userParam
];
228 pwmChannelDMAStop(&motor
->TimHandle
,motor
->timerHardware
->channel
);
229 HAL_DMA_IRQHandler(motor
->TimHandle
.hdma
[motor
->timerDmaIndex
]);
232 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
236 bool pwmDshotMotorHardwareConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint8_t reorderedMotorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
238 dmaResource_t
*dmaRef
= NULL
;
242 const dmaChannelSpec_t
*dmaSpec
= dmaGetChannelSpecByTimer(timerHardware
);
245 dmaRef
= dmaSpec
->ref
;
246 dmaChannel
= dmaSpec
->channel
;
249 dmaRef
= timerHardware
->dmaRef
;
250 dmaChannel
= timerHardware
->dmaChannel
;
253 #ifdef USE_DSHOT_DMAR
256 uint8_t timnum
= timerGetTIMNumber(timerHardware
->tim
);
257 dmaoptValue_t dmaopt
= timerUpConfig(timnum
- 1)->dmaopt
;
258 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByPeripheral(DMA_PERIPH_TIMUP
, timnum
- 1, dmaopt
);
259 dmaRef
= dmaChannelSpec
->ref
;
260 dmaChannel
= dmaChannelSpec
->channel
;
262 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 motor
->timerHardware
= timerHardware
;
294 TIM_TypeDef
*timer
= timerHardware
->tim
; // "timer" is confusing; "tim"?
295 const IO_t motorIO
= IOGetByTag(timerHardware
->tag
);
296 uint8_t pupMode
= (output
& TIMER_OUTPUT_INVERTED
) ? GPIO_PULLDOWN
: GPIO_PULLUP
;
297 #ifdef USE_DSHOT_TELEMETRY
298 if (useDshotTelemetry
) {
299 output
^= TIMER_OUTPUT_INVERTED
;
303 motor
->iocfg
= IO_CONFIG(GPIO_MODE_AF_PP
, GPIO_SPEED_FREQ_VERY_HIGH
, pupMode
);
304 const uint8_t timerIndex
= getTimerIndex(timer
);
305 const bool configureTimer
= (timerIndex
== dmaMotorTimerCount
- 1);
307 IOInit(motorIO
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
308 IOConfigGPIOAF(motorIO
, motor
->iocfg
, timerHardware
->alternateFunction
);
310 // Configure time base
312 if (configureTimer
) {
313 RCC_ClockCmd(timerRCC(timer
), ENABLE
);
315 motor
->TimHandle
.Instance
= timerHardware
->tim
; // timer
316 motor
->TimHandle
.Init
.Prescaler
= (uint16_t)(lrintf((float) timerClock(timer
) / getDshotHz(pwmProtocolType
) + 0.01f
) - 1);
317 motor
->TimHandle
.Init
.Period
= pwmProtocolType
== PWM_TYPE_PROSHOT1000
? MOTOR_NIBBLE_LENGTH_PROSHOT
: MOTOR_BITLENGTH
;
318 motor
->TimHandle
.Init
.RepetitionCounter
= 0;
319 motor
->TimHandle
.Init
.ClockDivision
= TIM_CLOCKDIVISION_DIV1
;
320 motor
->TimHandle
.Init
.CounterMode
= TIM_COUNTERMODE_UP
;
321 motor
->TimHandle
.Init
.AutoReloadPreload
= TIM_AUTORELOAD_PRELOAD_DISABLE
;
323 result
= HAL_TIM_PWM_Init(&motor
->TimHandle
);
325 if (result
!= HAL_OK
) {
326 /* Initialization Error */
333 chan oinv IDLE NIDLE POL NPOL
340 /* PWM mode 1 configuration */
342 TIM_OC_InitTypeDef TIM_OCInitStructure
;
343 TIM_OCInitStructure
.OCMode
= TIM_OCMODE_PWM1
;
345 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
346 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_RESET
;
347 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_HIGH
: TIM_OCPOLARITY_LOW
;
348 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_RESET
;
349 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_HIGH
: TIM_OCNPOLARITY_LOW
;
351 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_SET
;
352 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_LOW
: TIM_OCPOLARITY_HIGH
;
353 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_SET
;
354 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_LOW
: TIM_OCNPOLARITY_HIGH
;
356 TIM_OCInitStructure
.OCFastMode
= TIM_OCFAST_DISABLE
;
357 TIM_OCInitStructure
.Pulse
= 0;
359 result
= HAL_TIM_PWM_ConfigChannel(&motor
->TimHandle
, &TIM_OCInitStructure
, motor
->timerHardware
->channel
);
361 if (result
!= HAL_OK
) {
362 /* Configuration Error */
368 motor
->timer
= &dmaMotorTimers
[timerIndex
];
370 #ifdef USE_DSHOT_DMAR
372 motor
->timer
->dmaBurstRef
= dmaRef
;
374 if (!configureTimer
) {
375 motor
->configured
= true;
381 motor
->timerDmaSource
= timerDmaSource(timerHardware
->channel
);
382 motor
->timer
->timerDmaSources
|= motor
->timerDmaSource
;
383 motor
->timerDmaIndex
= timerDmaIndex(timerHardware
->channel
);
386 if (!dmaIsConfigured
) {
387 dmaEnable(dmaIdentifier
);
388 #ifdef USE_DSHOT_DMAR
390 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, timerIndex
);
394 dmaSetHandler(dmaIdentifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motorIndex
);
398 #ifdef USE_DSHOT_DMAR
400 motor
->timer
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
401 motor
->timer
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
402 motor
->timer
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
403 motor
->timer
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
404 motor
->timer
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
405 motor
->timer
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
406 motor
->timer
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
407 #if !defined(STM32G4)
408 motor
->timer
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
409 motor
->timer
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
410 motor
->timer
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
411 motor
->timer
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
414 motor
->timer
->dmaBurstBuffer
= &dshotBurstDmaBuffer
[timerIndex
][0];
415 motor
->timer
->timHandle
= motor
->TimHandle
;
416 memset(motor
->timer
->dmaBurstBuffer
, 0, DSHOT_DMA_BUFFER_SIZE
* 4 * sizeof(uint32_t));
418 /* Set hdma_tim instance */
419 motor
->timer
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
420 motor
->timer
->hdma_tim
.Init
.Request
= dmaChannel
;
422 /* Link hdma_tim to hdma[TIM_DMA_ID_UPDATE] (update) */
423 __HAL_LINKDMA(&motor
->timer
->timHandle
, hdma
[TIM_DMA_ID_UPDATE
], motor
->timer
->hdma_tim
);
425 if (!dmaIsConfigured
) {
426 /* Initialize TIMx DMA handle */
427 result
= HAL_DMA_Init(motor
->timer
->timHandle
.hdma
[TIM_DMA_ID_UPDATE
]);
434 motor
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
435 motor
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
436 motor
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
437 motor
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
438 motor
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
439 motor
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
440 motor
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
441 #if !defined(STM32G4)
442 motor
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
443 motor
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
444 motor
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
445 motor
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
448 motor
->dmaBuffer
= &dshotDmaBuffer
[motorIndex
][0];
449 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-2] = 0; // XXX Is this necessary? -> probably.
450 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-1] = 0; // XXX Is this necessary?
452 motor
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
453 motor
->hdma_tim
.Init
.Request
= dmaChannel
;
455 /* Link hdma_tim to hdma[x] (channelx) */
456 __HAL_LINKDMA(&motor
->TimHandle
, hdma
[motor
->timerDmaIndex
], motor
->hdma_tim
);
458 /* Initialize TIMx DMA handle */
459 result
= HAL_DMA_Init(motor
->TimHandle
.hdma
[motor
->timerDmaIndex
]);
463 if (result
!= HAL_OK
) {
464 /* Initialization Error */
468 // Start the timer channel now.
469 // Enabling/disabling DMA request can restart a new cycle without PWM start/stop.
471 if (motor
->timerHardware
->output
& TIMER_OUTPUT_N_CHANNEL
) {
472 result
= HAL_TIMEx_PWMN_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
474 result
= HAL_TIM_PWM_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
477 if (result
!= HAL_OK
) {
478 /* Starting PWM generation Error */
482 motor
->configured
= true;