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 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 motorDmaOutput_t
* const motor
= &dmaMotors
[motorIndex
];
273 motor
->timerHardware
= timerHardware
;
275 TIM_TypeDef
*timer
= timerHardware
->tim
; // "timer" is confusing; "tim"?
276 const IO_t motorIO
= IOGetByTag(timerHardware
->tag
);
277 uint8_t pupMode
= (output
& TIMER_OUTPUT_INVERTED
) ? GPIO_PULLDOWN
: GPIO_PULLUP
;
278 #ifdef USE_DSHOT_TELEMETRY
279 if (useDshotTelemetry
) {
280 output
^= TIMER_OUTPUT_INVERTED
;
284 motor
->iocfg
= IO_CONFIG(GPIO_MODE_AF_PP
, GPIO_SPEED_FREQ_VERY_HIGH
, pupMode
);
285 const uint8_t timerIndex
= getTimerIndex(timer
);
286 const bool configureTimer
= (timerIndex
== dmaMotorTimerCount
- 1);
288 IOInit(motorIO
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
289 IOConfigGPIOAF(motorIO
, motor
->iocfg
, timerHardware
->alternateFunction
);
291 // Configure time base
293 if (configureTimer
) {
294 RCC_ClockCmd(timerRCC(timer
), ENABLE
);
296 motor
->TimHandle
.Instance
= timerHardware
->tim
; // timer
297 motor
->TimHandle
.Init
.Prescaler
= (uint16_t)(lrintf((float) timerClock(timer
) / getDshotHz(pwmProtocolType
) + 0.01f
) - 1);
298 motor
->TimHandle
.Init
.Period
= pwmProtocolType
== PWM_TYPE_PROSHOT1000
? MOTOR_NIBBLE_LENGTH_PROSHOT
: MOTOR_BITLENGTH
;
299 motor
->TimHandle
.Init
.RepetitionCounter
= 0;
300 motor
->TimHandle
.Init
.ClockDivision
= TIM_CLOCKDIVISION_DIV1
;
301 motor
->TimHandle
.Init
.CounterMode
= TIM_COUNTERMODE_UP
;
302 motor
->TimHandle
.Init
.AutoReloadPreload
= TIM_AUTORELOAD_PRELOAD_DISABLE
;
304 result
= HAL_TIM_PWM_Init(&motor
->TimHandle
);
306 if (result
!= HAL_OK
) {
307 /* Initialization Error */
314 chan oinv IDLE NIDLE POL NPOL
321 /* PWM mode 1 configuration */
323 TIM_OC_InitTypeDef TIM_OCInitStructure
;
324 TIM_OCInitStructure
.OCMode
= TIM_OCMODE_PWM1
;
326 if (output
& TIMER_OUTPUT_N_CHANNEL
) {
327 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_RESET
;
328 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_HIGH
: TIM_OCPOLARITY_LOW
;
329 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_RESET
;
330 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_HIGH
: TIM_OCNPOLARITY_LOW
;
332 TIM_OCInitStructure
.OCIdleState
= TIM_OCIDLESTATE_SET
;
333 TIM_OCInitStructure
.OCPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCPOLARITY_LOW
: TIM_OCPOLARITY_HIGH
;
334 TIM_OCInitStructure
.OCNIdleState
= TIM_OCNIDLESTATE_SET
;
335 TIM_OCInitStructure
.OCNPolarity
= (output
& TIMER_OUTPUT_INVERTED
) ? TIM_OCNPOLARITY_LOW
: TIM_OCNPOLARITY_HIGH
;
337 TIM_OCInitStructure
.OCFastMode
= TIM_OCFAST_DISABLE
;
338 TIM_OCInitStructure
.Pulse
= 0;
340 result
= HAL_TIM_PWM_ConfigChannel(&motor
->TimHandle
, &TIM_OCInitStructure
, motor
->timerHardware
->channel
);
342 if (result
!= HAL_OK
) {
343 /* Configuration Error */
349 motor
->timer
= &dmaMotorTimers
[timerIndex
];
351 #ifdef USE_DSHOT_DMAR
353 motor
->timer
->dmaBurstRef
= dmaRef
;
355 if (!configureTimer
) {
356 motor
->configured
= true;
362 motor
->timerDmaSource
= timerDmaSource(timerHardware
->channel
);
363 motor
->timer
->timerDmaSources
|= motor
->timerDmaSource
;
364 motor
->timerDmaIndex
= timerDmaIndex(timerHardware
->channel
);
367 dmaIdentifier_e identifier
= dmaGetIdentifier(dmaRef
);
369 #ifdef USE_DSHOT_DMAR
371 dmaInit(identifier
, OWNER_TIMUP
, timerGetTIMNumber(timerHardware
->tim
));
372 dmaSetHandler(identifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, timerIndex
);
376 dmaInit(identifier
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
377 dmaSetHandler(identifier
, motor_DMA_IRQHandler
, NVIC_PRIO_DSHOT_DMA
, motorIndex
);
380 #ifdef USE_DSHOT_DMAR
382 motor
->timer
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
383 motor
->timer
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
384 motor
->timer
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
385 motor
->timer
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
386 motor
->timer
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
387 motor
->timer
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
388 motor
->timer
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
389 #if !defined(STM32G4)
390 motor
->timer
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
391 motor
->timer
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
392 motor
->timer
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
393 motor
->timer
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
396 motor
->timer
->dmaBurstBuffer
= &dshotBurstDmaBuffer
[timerIndex
][0];
397 motor
->timer
->timHandle
= motor
->TimHandle
;
398 memset(motor
->timer
->dmaBurstBuffer
, 0, DSHOT_DMA_BUFFER_SIZE
* 4 * sizeof(uint32_t));
400 /* Set hdma_tim instance */
401 motor
->timer
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
402 motor
->timer
->hdma_tim
.Init
.Request
= dmaChannel
;
404 /* Link hdma_tim to hdma[TIM_DMA_ID_UPDATE] (update) */
405 __HAL_LINKDMA(&motor
->timer
->timHandle
, hdma
[TIM_DMA_ID_UPDATE
], motor
->timer
->hdma_tim
);
407 /* Initialize TIMx DMA handle */
408 result
= HAL_DMA_Init(motor
->timer
->timHandle
.hdma
[TIM_DMA_ID_UPDATE
]);
413 motor
->hdma_tim
.Init
.Direction
= DMA_MEMORY_TO_PERIPH
;
414 motor
->hdma_tim
.Init
.PeriphInc
= DMA_PINC_DISABLE
;
415 motor
->hdma_tim
.Init
.MemInc
= DMA_MINC_ENABLE
;
416 motor
->hdma_tim
.Init
.PeriphDataAlignment
= DMA_PDATAALIGN_WORD
;
417 motor
->hdma_tim
.Init
.MemDataAlignment
= DMA_MDATAALIGN_WORD
;
418 motor
->hdma_tim
.Init
.Mode
= DMA_NORMAL
;
419 motor
->hdma_tim
.Init
.Priority
= DMA_PRIORITY_HIGH
;
420 #if !defined(STM32G4)
421 motor
->hdma_tim
.Init
.FIFOMode
= DMA_FIFOMODE_DISABLE
;
422 motor
->hdma_tim
.Init
.PeriphBurst
= DMA_PBURST_SINGLE
;
423 motor
->hdma_tim
.Init
.MemBurst
= DMA_MBURST_SINGLE
;
424 motor
->hdma_tim
.Init
.FIFOThreshold
= DMA_FIFO_THRESHOLD_FULL
;
427 motor
->dmaBuffer
= &dshotDmaBuffer
[motorIndex
][0];
428 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-2] = 0; // XXX Is this necessary? -> probably.
429 motor
->dmaBuffer
[DSHOT_DMA_BUFFER_SIZE
-1] = 0; // XXX Is this necessary?
431 motor
->hdma_tim
.Instance
= (DMA_ARCH_TYPE
*)dmaRef
;
432 motor
->hdma_tim
.Init
.Request
= dmaChannel
;
434 /* Link hdma_tim to hdma[x] (channelx) */
435 __HAL_LINKDMA(&motor
->TimHandle
, hdma
[motor
->timerDmaIndex
], motor
->hdma_tim
);
437 /* Initialize TIMx DMA handle */
438 result
= HAL_DMA_Init(motor
->TimHandle
.hdma
[motor
->timerDmaIndex
]);
442 if (result
!= HAL_OK
) {
443 /* Initialization Error */
447 // Start the timer channel now.
448 // Enabling/disabling DMA request can restart a new cycle without PWM start/stop.
450 if (motor
->timerHardware
->output
& TIMER_OUTPUT_N_CHANNEL
) {
451 result
= HAL_TIMEx_PWMN_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
453 result
= HAL_TIM_PWM_Start(&motor
->TimHandle
, motor
->timerHardware
->channel
);
456 if (result
!= HAL_OK
) {
457 /* Starting PWM generation Error */
461 motor
->configured
= true;