Fix pt2 and pt3 filter initialization in gyro_init.c (#13960)
[betaflight.git] / src / main / drivers / mcu / at32 / pwm_output_dshot.c
blob96528e74c11d3be850ab467cb95262743d29c163
1 /*
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)
8 * any later version.
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/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <math.h>
25 #include "platform.h"
27 #ifdef USE_DSHOT
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 "drivers/pwm_output.h"
41 #include "drivers/dshot.h"
42 #include "drivers/dshot_dpwm.h"
43 #include "drivers/dshot_command.h"
44 #include "drivers/pwm_output_dshot_shared.h"
46 /**
47 * Convert from BF channel to AT32 constants for timer output channels
49 * The AT and ST apis take a different approach to naming channels, so just passing the bf
50 * channel number to the AT calls doesn't work. This function maps between them.
52 * @param bfChannel a channel number as used in timerHardware->channel (1 based)
53 * @param useNChannel indicates that the desired channel should be the complementary output (only available for 1 through 3)
54 * @return an AT32 tmr_channel_select_type constant
55 * XXX what to return for invalid inputs? The tmr_channel_select_type enum doesn't have a suitable value
57 * @see TIM_CH_TO_SELCHANNEL macro
59 tmr_channel_select_type toCHSelectType(const uint8_t bfChannel, const bool useNChannel)
61 tmr_channel_select_type result = TMR_SELECT_CHANNEL_1; // XXX I don't like using ch 1 as a default result, but what to do?
62 if (useNChannel)
64 // Complementary channels only available for 1 through 3
65 switch (bfChannel) {
66 case 1:
67 result = TMR_SELECT_CHANNEL_1C;
68 break;
69 case 2:
70 result = TMR_SELECT_CHANNEL_2C;
71 break;
72 case 3:
73 result = TMR_SELECT_CHANNEL_3C;
74 break;
75 default:
76 // bad input
77 #ifdef HANG_ON_ERRORS
78 while(true) {}
79 #endif
80 break;
83 } else {
84 switch (bfChannel) {
85 case 1:
86 result = TMR_SELECT_CHANNEL_1;
87 break;
88 case 2:
89 result = TMR_SELECT_CHANNEL_2;
90 break;
91 case 3:
92 result = TMR_SELECT_CHANNEL_3;
93 break;
94 case 4:
95 result = TMR_SELECT_CHANNEL_4;
96 break;
97 default:
98 // bad input
99 #ifdef HANG_ON_ERRORS
100 while(true) {}
101 #endif
102 break;
106 return result;
109 #ifdef USE_DSHOT_TELEMETRY
112 * Enable the timer channels for all motors
114 * Called once for every dshot update if telemetry is being used (not just enabled by #def)
115 * Called from pwm_output_dshot_shared.c pwmTelemetryDecode
117 void dshotEnableChannels(uint8_t motorCount)
119 for (int i = 0; i < motorCount; i++) {
120 tmr_primary_mode_select(dmaMotors[i].timerHardware->tim, TMR_PRIMARY_SEL_COMPARE);
122 tmr_channel_select_type atCh = toCHSelectType(dmaMotors[i].timerHardware->channel, dmaMotors[i].output & TIMER_OUTPUT_N_CHANNEL);
123 tmr_channel_enable(dmaMotors[i].timerHardware->tim, atCh, TRUE);
127 #endif // USE_DSHOT_TELEMETRY
130 * Set the timer and dma of the specified motor for use as an output
132 * Called from pwmDshotMotorHardwareConfig in this file and also from
133 * pwmTelemetryDecode in src/main/drivers/pwm_output_dshot_shared.c
135 FAST_CODE void pwmDshotSetDirectionOutput(
136 motorDmaOutput_t * const motor
137 #ifndef USE_DSHOT_TELEMETRY
138 ,TIM_OCInitTypeDef *pOcInit, DMA_InitTypeDef* pDmaInit
139 #endif
142 #ifdef USE_DSHOT_TELEMETRY
143 TIM_OCInitTypeDef* pOcInit = &motor->ocInitStruct;
144 DMA_InitTypeDef* pDmaInit = &motor->dmaInitStruct;
145 #endif
147 const timerHardware_t * const timerHardware = motor->timerHardware;
148 TIM_TypeDef *timer = timerHardware->tim;
149 const uint8_t channel = timerHardware->channel; // BF channel index (1 based)
150 const bool useCompOut = (timerHardware->output & TIMER_OUTPUT_N_CHANNEL) != 0;
152 dmaResource_t *dmaRef = motor->dmaRef;
154 #if defined(USE_DSHOT_DMAR) && !defined(USE_DSHOT_TELEMETRY)
155 if (useBurstDshot) {
156 dmaRef = timerHardware->dmaTimUPRef;
158 #endif
160 xDMA_DeInit(dmaRef);
162 #ifdef USE_DSHOT_TELEMETRY
163 motor->isInput = false;
164 #endif
166 // Disable the preload buffer so that we can write to the comparison registers (CxDT) immediately
167 // This call has the channel -> AT32 channel selector mapping built in
168 timerOCPreloadConfig(timer, channel, FALSE);
170 tmr_channel_enable(timer, toCHSelectType(channel, useCompOut), FALSE);
172 // The at32 apis do everything except actually put the channel in output mode, so we have to do that ourselves
173 // This is probably a bug in the at32 sdk, so the need for this code may go away in future releases
174 const uint8_t CH_OUTPUT = 0;
175 switch (channel) {
176 case 1:
177 timer->cm1_output_bit.c1c = CH_OUTPUT;
178 break;
179 case 2:
180 timer->cm1_output_bit.c2c = CH_OUTPUT;
181 break;
182 case 3:
183 timer->cm2_output_bit.c3c = CH_OUTPUT;
184 break;
185 case 4:
186 timer->cm2_output_bit.c4c = CH_OUTPUT;
187 break;
190 timerOCInit(timer, channel, pOcInit);
192 // On ST mcu this would be part of the ocInit struct, but on AT we have to do it seperately
193 { // local scope for variables
194 const bool useNChannel = false; // tmr_channel_value_set only supports the normal channels
195 const tmr_channel_select_type atChannel = toCHSelectType(timerHardware->channel, useNChannel);
196 tmr_channel_value_set(timer, atChannel, 0);
199 tmr_channel_enable(timer, toCHSelectType(channel, useCompOut), TRUE);
200 timerOCPreloadConfig(timer, channel, TRUE);
202 pDmaInit->direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
203 xDMA_Init(dmaRef, pDmaInit);
205 // Generate an interrupt when the transfer is complete
206 xDMA_ITConfig(dmaRef, DMA_FDT_INT, TRUE);
211 #ifdef USE_DSHOT_TELEMETRY
213 * Set the timer and dma of the specified motor for use as an input
215 FAST_CODE
216 static void pwmDshotSetDirectionInput(motorDmaOutput_t * const motor)
218 DMA_InitTypeDef* pDmaInit = &motor->dmaInitStruct;
219 const timerHardware_t * const timerHardware = motor->timerHardware;
220 TIM_TypeDef *timer = timerHardware->tim;
221 dmaResource_t *dmaRef = motor->dmaRef;
223 xDMA_DeInit(dmaRef);
225 motor->isInput = true;
226 if (!inputStampUs) {
227 inputStampUs = micros();
230 tmr_period_buffer_enable(timer, FALSE);
232 timer->pr = 0xffffffff;
234 tmr_input_channel_init(timer, &motor->icInitStruct, TMR_CHANNEL_INPUT_DIV_1);
236 pDmaInit->direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
238 xDMA_Init(dmaRef, pDmaInit);
240 #endif // USE_DSHOT_TELEMETRY
243 * Start the timers and dma requests to send dshot data to all motors
245 * Called after pwm_output_dshot_shared.c has finished setting up the buffers that represent the dshot packets.
246 * Iterates over all the timers needed (note that there may be less timers than motors since a single timer can run
247 * multiple motors) and enables each one.
249 void pwmCompleteDshotMotorUpdate(void)
251 // If there is a dshot command loaded up, time it correctly with motor update
252 if (!dshotCommandQueueEmpty()) {
253 if (!dshotCommandOutputIsEnabled(dshotPwmDevice.count)) {
254 return;
258 for (int i = 0; i < dmaMotorTimerCount; i++) { // dmaMotorTimerCount is a global declared in pwm_output_dshot_shared.c
259 #ifdef USE_DSHOT_DMAR
260 // NB burst mode not tested
261 if (useBurstDshot) {
262 xDMA_SetCurrDataCounter(dmaMotorTimers[i].dmaBurstRef, dmaMotorTimers[i].dmaBurstLength);
263 xDMA_Cmd(dmaMotorTimers[i].dmaBurstRef, TRUE);
265 // TIM_DMAConfig(dmaMotorTimers[i].timer, TIM_DMABase_CCR1, TIM_DMABurstLength_4Transfers);
266 tmr_dma_control_config(dmaMotorTimers[i].timer, TMR_DMA_TRANSFER_4BYTES, TMR_CTRL1_ADDRESS);
268 // XXX TODO - what is the equivalent of TIM_DMA_Update in AT32?
269 // TIM_DMACmd(dmaMotorTimers[i].timer, TIM_DMA_Update, ENABLE);
270 // tmr_dma_request_enable(dmaMotorTimers[i].timer, )
271 } else
272 #endif // USE_DSHOT_DMAR
274 // I think the counter is reset here to ensure that the first pulse is the correct width,
275 // and maybe also to reduce the chance of an interrupt firing prematurely
276 tmr_counter_value_set(dmaMotorTimers[i].timer, 0);
278 // Allows setting the period with immediate effect
279 tmr_period_buffer_enable(dmaMotorTimers[i].timer, FALSE);
281 #ifdef USE_DSHOT_TELEMETRY
282 // NB outputPeriod isn't set when telemetry is not #def'd
283 tmr_period_value_set(dmaMotorTimers[i].timer, dmaMotorTimers[i].outputPeriod);
284 #endif
286 tmr_period_buffer_enable(dmaMotorTimers[i].timer, TRUE);
288 // Ensure that overflow events are enabled. This event may affect both period and duty cycle
289 tmr_overflow_event_disable(dmaMotorTimers[i].timer, FALSE);
291 // Generate requests from the timer to one or more DMA channels
292 tmr_dma_request_enable(dmaMotorTimers[i].timer, dmaMotorTimers[i].timerDmaSources, TRUE);
294 dmaMotorTimers[i].timerDmaSources = 0;
300 * Interrupt handler called at the end of each packet
302 * Responsible for switching the dshot direction after sending a dshot command so that we
303 * can receive dshot telemetry. If telemetry is not enabled, disables the dma and request generation.
305 FAST_CODE static void motor_DMA_IRQHandler(dmaChannelDescriptor_t *descriptor)
307 if (DMA_GET_FLAG_STATUS(descriptor, DMA_IT_TCIF))
309 motorDmaOutput_t * const motor = &dmaMotors[descriptor->userParam];
311 #ifdef USE_DSHOT_TELEMETRY
312 dshotDMAHandlerCycleCounters.irqAt = getCycleCounter();
313 #endif
315 // Disable timers and dma
317 #ifdef USE_DSHOT_DMAR
318 if (useBurstDshot) {
319 xDMA_Cmd(motor->timerHardware->dmaTimUPRef, DISABLE);
320 TIM_DMACmd(motor->timerHardware->tim, TIM_DMA_Update, DISABLE);
321 } else
322 #endif
323 { // block for the 'else' in the #ifdef above
325 // Important - disable requests before disabling the dma channel, otherwise it's possible to
326 // have a pending request that will fire the moment the dma channel is re-enabled, which
327 // causes fake pulses to be sent at the start of the next packet.
328 // This may be the problem described in the errata 1.10.1. The full work-around sounds a bit
329 // heavyweight, but we should keep it in mind in case it's needed.
330 // ADVTM
331 // How to clear TMR-triggered DAM requests
332 // Description:
333 // TMR-induced DMA request cannot be cleared by resetting/setting the corresponding DMA
334 // request enable bit in the TMRx_IDEN register.
335 // Workaround:
336 // Before enabling DMA channel, reset TMR (reset CRM clock of TMR) and initialize TMR to
337 // clear pending DMA requests.
339 // disable request generation
340 tmr_dma_request_enable(motor->timerHardware->tim, motor->timerDmaSource, FALSE);
342 // disable the dma channel, (gets re-enabled in pwm_output_dshot_shared.c from pwmWriteDshotInt)
343 xDMA_Cmd(motor->dmaRef, FALSE);
346 // If we're expecting telem, flip direction and re-enable
347 #ifdef USE_DSHOT_TELEMETRY
348 if (useDshotTelemetry) {
349 pwmDshotSetDirectionInput(motor);
350 xDMA_SetCurrDataCounter(motor->dmaRef, GCR_TELEMETRY_INPUT_LEN);
351 xDMA_Cmd(motor->dmaRef, TRUE);
352 tmr_dma_request_enable(motor->timerHardware->tim, motor->timerDmaSource, TRUE);
354 dshotDMAHandlerCycleCounters.changeDirectionCompletedAt = getCycleCounter();
356 #endif
358 DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF);
360 } // if DMA_IT_TCIF
363 bool pwmDshotMotorHardwareConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint8_t reorderedMotorIndex,
364 motorPwmProtocolTypes_e pwmProtocolType, uint8_t output)
366 #ifdef USE_DSHOT_TELEMETRY
367 #define OCINIT motor->ocInitStruct
368 #define DMAINIT motor->dmaInitStruct
369 #else
370 TIM_OCInitTypeDef ocInitStruct;
371 DMA_InitTypeDef dmaInitStruct;
372 #define OCINIT ocInitStruct
373 #define DMAINIT dmaInitStruct
374 #endif
376 dmaResource_t *dmaRef = NULL;
377 uint32_t dmaMuxId = 0;
379 #if defined(USE_DMA_SPEC)
380 const dmaChannelSpec_t *dmaSpec = dmaGetChannelSpecByTimer(timerHardware);
382 if (dmaSpec != NULL) {
383 dmaRef = dmaSpec->ref;
384 dmaMuxId = dmaSpec->dmaMuxId;
386 #else // not defined USE_DMA_SPEC
387 dmaRef = timerHardware->dmaRef;
388 #endif // USE_DMA_SPEC
390 #ifdef USE_DSHOT_DMAR
391 if (useBurstDshot) {
392 dmaRef = timerHardware->dmaTimUPRef;
394 #endif // USE_DSHOT_DMAR
396 if (dmaRef == NULL) {
397 return false;
400 dmaIdentifier_e dmaIdentifier = dmaGetIdentifier(dmaRef);
402 bool dmaIsConfigured = false;
403 #ifdef USE_DSHOT_DMAR
404 if (useBurstDshot) {
405 const resourceOwner_t *owner = dmaGetOwner(dmaIdentifier);
406 if (owner->owner == OWNER_TIMUP && owner->resourceIndex == timerGetTIMNumber(timerHardware->tim)) {
407 dmaIsConfigured = true;
408 } else if (!dmaAllocate(dmaIdentifier, OWNER_TIMUP, timerGetTIMNumber(timerHardware->tim))) {
409 return false;
411 } else
412 #endif
413 { // 'else' block
414 if (!dmaAllocate(dmaIdentifier, OWNER_MOTOR, RESOURCE_INDEX(reorderedMotorIndex))) {
415 return false;
419 motorDmaOutput_t * const motor = &dmaMotors[motorIndex];
420 TIM_TypeDef *timer = timerHardware->tim;
422 // Boolean configureTimer is always true when different channels of the same timer are processed in sequence,
423 // causing the timer and the associated DMA initialized more than once.
424 // To fix this, getTimerIndex must be expanded to return if a new timer has been requested.
425 // However, since the initialization is idempotent (can be applied multiple times without changing the outcome),
426 // it is left as is in a favor of flash space (for now).
427 const uint8_t timerIndex = getTimerIndex(timer);
428 const bool configureTimer = (timerIndex == dmaMotorTimerCount-1);
430 motor->timer = &dmaMotorTimers[timerIndex];
431 motor->index = motorIndex;
432 motor->timerHardware = timerHardware;
434 const IO_t motorIO = IOGetByTag(timerHardware->tag);
436 uint8_t pupMode = (output & TIMER_OUTPUT_INVERTED) ? GPIO_PULL_DOWN : GPIO_PULL_UP;
438 #ifdef USE_DSHOT_TELEMETRY
439 if (useDshotTelemetry) {
440 output ^= TIMER_OUTPUT_INVERTED;
442 #endif
444 motor->iocfg = IO_CONFIG(GPIO_MODE_MUX, GPIO_DRIVE_STRENGTH_MODERATE, GPIO_OUTPUT_PUSH_PULL, pupMode);
446 IOConfigGPIOAF(motorIO, motor->iocfg, timerHardware->alternateFunction);
448 if (configureTimer) {
449 RCC_ClockCmd(timerRCC(timer), ENABLE);
451 tmr_counter_enable(timer, FALSE);
453 uint32_t prescaler = (uint16_t)(lrintf((float) timerClock(timer) / getDshotHz(pwmProtocolType) + 0.01f) - 1);
454 uint32_t period = (pwmProtocolType == PWM_TYPE_PROSHOT1000 ? (MOTOR_NIBBLE_LENGTH_PROSHOT) : MOTOR_BITLENGTH) - 1;
456 tmr_clock_source_div_set(timer, TMR_CLOCK_DIV1);
457 tmr_repetition_counter_set(timer, 0);
458 tmr_cnt_dir_set(timer, TMR_COUNT_UP);
459 tmr_base_init(timer, period, prescaler);
462 tmr_output_config_type * ocConfig = &OCINIT;
463 tmr_output_default_para_init(ocConfig);
465 ocConfig->oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
466 if (output & TIMER_OUTPUT_N_CHANNEL) {
467 // XXX N channels not yet tested, comments are the stm32 code
468 // OCINIT.TIM_OutputNState = TIM_OutputNState_Enable;
469 ocConfig->occ_output_state = TRUE;
470 // OCINIT.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
471 ocConfig->occ_idle_state = FALSE;
472 // OCINIT.TIM_OCNPolarity = (output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPolarity_Low : TIM_OCNPolarity_High;
473 ocConfig->occ_polarity = (output & TIMER_OUTPUT_INVERTED) ? TMR_OUTPUT_ACTIVE_LOW : TMR_OUTPUT_ACTIVE_HIGH;
474 } else {
475 ocConfig->oc_output_state = TRUE;
476 ocConfig->oc_idle_state = FALSE;
477 ocConfig->oc_polarity = (output & TIMER_OUTPUT_INVERTED) ? TMR_OUTPUT_ACTIVE_LOW : TMR_OUTPUT_ACTIVE_HIGH;
480 #ifdef USE_DSHOT_TELEMETRY
481 tmr_input_config_type * icConfig = &motor->icInitStruct;
482 tmr_input_default_para_init(icConfig);
483 icConfig->input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT;
484 icConfig->input_polarity_select = TMR_INPUT_BOTH_EDGE;
485 const bool useNChannel = output & TIMER_OUTPUT_N_CHANNEL;
486 icConfig->input_channel_select = toCHSelectType(timerHardware->channel, useNChannel);
487 icConfig->input_filter_value = 2;
488 #endif // USE_DSHOT_TELEMETRY
490 #ifdef USE_DSHOT_DMAR
491 if (useBurstDshot) {
492 motor->timer->dmaBurstRef = dmaRef;
493 } else
494 #endif
495 { // 'else' block
496 motor->timerDmaSource = timerDmaSource(timerHardware->channel);
498 // clear that bit from timerDmaSources
499 // timerDmaSources can have more than one source set in it if multiple motors share a common timer,
500 motor->timer->timerDmaSources &= ~motor->timerDmaSource;
503 xDMA_Cmd(dmaRef, FALSE);
504 xDMA_DeInit(dmaRef);
506 if (!dmaIsConfigured) {
507 dmaEnable(dmaIdentifier);
508 dmaMuxEnable(dmaIdentifier, dmaMuxId);
511 dma_default_para_init(&DMAINIT);
513 #ifdef USE_DSHOT_DMAR
514 if (useBurstDshot) {
515 motor->timer->dmaBurstBuffer = &dshotBurstDmaBuffer[timerIndex][0];
517 DMAINIT.DMA_Channel = timerHardware->dmaTimUPChannel;
518 DMAINIT.DMA_Memory0BaseAddr = (uint32_t)motor->timer->dmaBurstBuffer;
519 DMAINIT.DMA_DIR = DMA_DIR_MemoryToPeripheral;
520 DMAINIT.DMA_FIFOMode = DMA_FIFOMode_Enable;
521 DMAINIT.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
522 DMAINIT.DMA_MemoryBurst = DMA_MemoryBurst_Single;
523 DMAINIT.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
525 DMAINIT.DMA_PeripheralBaseAddr = (uint32_t)&timerHardware->tim->DMAR;
526 DMAINIT.DMA_BufferSize = (pwmProtocolType == PWM_TYPE_PROSHOT1000) ? PROSHOT_DMA_BUFFER_SIZE : DSHOT_DMA_BUFFER_SIZE; // XXX
527 DMAINIT.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
528 DMAINIT.DMA_MemoryInc = DMA_MemoryInc_Enable;
529 DMAINIT.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
530 DMAINIT.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
531 DMAINIT.DMA_Mode = DMA_Mode_Normal;
532 DMAINIT.DMA_Priority = DMA_Priority_High;
533 } else
534 #endif // USE_DSHOT_DMAR
535 { // 'else' block
536 motor->dmaBuffer = &dshotDmaBuffer[motorIndex][0];
537 DMAINIT.memory_base_addr = (uint32_t)motor->dmaBuffer;
538 DMAINIT.memory_inc_enable = TRUE;
539 DMAINIT.memory_data_width = DMA_MEMORY_DATA_WIDTH_WORD;
540 DMAINIT.loop_mode_enable = FALSE;
541 DMAINIT.buffer_size = DSHOT_DMA_BUFFER_SIZE;
542 DMAINIT.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
543 DMAINIT.peripheral_base_addr = (uint32_t)timerChCCR(timerHardware); // returns the address of CxDT for timerHardware->channel
544 DMAINIT.peripheral_inc_enable = FALSE;
545 DMAINIT.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_WORD;
546 DMAINIT.priority = DMA_PRIORITY_HIGH;
549 motor->dmaRef = dmaRef;
552 #ifdef USE_DSHOT_TELEMETRY
553 motor->dshotTelemetryDeadtimeUs = DSHOT_TELEMETRY_DEADTIME_US + 1000000 *
554 (16 * MOTOR_BITLENGTH) / getDshotHz(pwmProtocolType);
555 motor->timer->outputPeriod = (pwmProtocolType == PWM_TYPE_PROSHOT1000 ? (MOTOR_NIBBLE_LENGTH_PROSHOT) : MOTOR_BITLENGTH) - 1;
556 pwmDshotSetDirectionOutput(motor);
557 #else
558 pwmDshotSetDirectionOutput(motor, &OCINIT, &DMAINIT);
559 #endif
561 #ifdef USE_DSHOT_DMAR
562 if (useBurstDshot) {
563 if (!dmaIsConfigured) {
564 dmaSetHandler(dmaIdentifier, motor_DMA_IRQHandler, NVIC_PRIO_DSHOT_DMA, motor->index);
566 } else
567 #endif
568 { // 'else' block
569 dmaSetHandler(dmaIdentifier, motor_DMA_IRQHandler, NVIC_PRIO_DSHOT_DMA, motor->index);
572 { // local scope
573 const tmr_channel_select_type chSel = toCHSelectType(timerHardware->channel, output & TIMER_OUTPUT_N_CHANNEL);
574 tmr_channel_enable(timer, chSel, TRUE);
577 if (configureTimer) {
578 // Changes to the period register are deferred until the next counter overflow
579 tmr_period_buffer_enable(timer, TRUE);
580 tmr_output_enable(timer, TRUE);
581 tmr_counter_enable(timer, TRUE);
584 #ifdef USE_DSHOT_TELEMETRY
585 if (useDshotTelemetry) {
586 // avoid high line during startup to prevent bootloader activation
587 *timerChCCR(timerHardware) = 0xffff;
589 #endif
591 motor->configured = true;
593 return true;
596 #endif