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/>.
27 #ifdef USE_DSHOT_BITBANG
29 #include "build/debug.h"
31 #include "drivers/io.h"
32 #include "drivers/io_impl.h"
33 #include "drivers/dma.h"
34 #include "drivers/dma_reqmap.h"
35 #include "drivers/dshot.h"
36 #include "drivers/dshot_bitbang.h"
37 #include "drivers/dshot_bitbang_impl.h"
38 #include "drivers/dshot_command.h"
39 #include "drivers/motor.h"
40 #include "drivers/nvic.h"
41 #include "drivers/pwm_output.h" // XXX for pwmOutputPort_t motors[]; should go away with refactoring
42 #include "drivers/dshot_dpwm.h" // XXX for motorDmaOutput_t *getMotorDmaOutput(uint8_t index); should go away with refactoring
43 #include "drivers/dshot_bitbang_decode.h"
44 #include "drivers/time.h"
45 #include "drivers/timer.h"
49 #if defined(USE_DEBUG_PIN)
50 #include "build/debug_pin.h"
57 FAST_DATA_ZERO_INIT bbPacer_t bbPacers
[MAX_MOTOR_PACERS
]; // TIM1 or TIM8
58 FAST_DATA_ZERO_INIT
int usedMotorPacers
= 0;
60 FAST_DATA_ZERO_INIT bbPort_t bbPorts
[MAX_SUPPORTED_MOTOR_PORTS
];
61 FAST_DATA_ZERO_INIT
int usedMotorPorts
;
63 FAST_DATA_ZERO_INIT bbMotor_t bbMotors
[MAX_SUPPORTED_MOTORS
];
65 static FAST_DATA_ZERO_INIT
int motorCount
;
66 dshotBitbangStatus_e bbStatus
;
68 // For MCUs that use MPU to control DMA coherency, there might be a performance hit
69 // on manipulating input buffer content especially if it is read multiple times,
70 // as the buffer region is attributed as not cachable.
71 // If this is not desirable, we should use manual cache invalidation.
72 #ifdef USE_DSHOT_CACHE_MGMT
73 #define BB_OUTPUT_BUFFER_ATTRIBUTE DMA_RW_AXI __attribute__((aligned(32)))
74 #define BB_INPUT_BUFFER_ATTRIBUTE DMA_RW_AXI __attribute__((aligned(32)))
77 #define BB_OUTPUT_BUFFER_ATTRIBUTE
78 #define BB_INPUT_BUFFER_ATTRIBUTE
79 #elif defined(STM32F7)
80 #define BB_OUTPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
81 #define BB_INPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
82 #elif defined(STM32H7)
83 #define BB_OUTPUT_BUFFER_ATTRIBUTE DMA_RAM
84 #define BB_INPUT_BUFFER_ATTRIBUTE DMA_RAM
85 #elif defined(STM32G4)
86 #define BB_OUTPUT_BUFFER_ATTRIBUTE DMA_RAM_W
87 #define BB_INPUT_BUFFER_ATTRIBUTE DMA_RAM_R
89 #endif // USE_DSHOT_CACHE_MGMT
91 BB_OUTPUT_BUFFER_ATTRIBUTE
uint32_t bbOutputBuffer
[MOTOR_DSHOT_BUF_CACHE_ALIGN_LENGTH
* MAX_SUPPORTED_MOTOR_PORTS
];
92 BB_INPUT_BUFFER_ATTRIBUTE
uint16_t bbInputBuffer
[DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_LENGTH
* MAX_SUPPORTED_MOTOR_PORTS
];
95 FAST_DATA_ZERO_INIT timeUs_t dshotFrameUs
;
98 const timerHardware_t bbTimerHardware
[] = {
99 #if defined(STM32F4) || defined(STM32F7)
100 #if !defined(STM32F411xE)
101 DEF_TIM(TIM8
, CH1
, NONE
, TIM_USE_NONE
, 0, 1),
102 DEF_TIM(TIM8
, CH2
, NONE
, TIM_USE_NONE
, 0, 1),
103 DEF_TIM(TIM8
, CH3
, NONE
, TIM_USE_NONE
, 0, 1),
104 DEF_TIM(TIM8
, CH4
, NONE
, TIM_USE_NONE
, 0, 0),
106 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 1),
107 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 2),
108 DEF_TIM(TIM1
, CH2
, NONE
, TIM_USE_NONE
, 0, 1),
109 DEF_TIM(TIM1
, CH3
, NONE
, TIM_USE_NONE
, 0, 1),
110 DEF_TIM(TIM1
, CH4
, NONE
, TIM_USE_NONE
, 0, 0),
112 #elif defined(STM32G4) || defined(STM32H7)
113 // XXX TODO: STM32G4 and STM32H7 can use any timer for pacing
115 // DMA request numbers are duplicated for TIM1 and TIM8:
116 // - Any pacer can serve a GPIO port.
117 // - For quads (or less), 4 pacers can cover the worst case scenario of
118 // 4 motors scattered across 4 different GPIO ports.
119 // - For hexas (and larger), more channels may become necessary,
120 // in which case the DMA request numbers should be modified.
121 DEF_TIM(TIM8
, CH1
, NONE
, TIM_USE_NONE
, 0, 0, 0),
122 DEF_TIM(TIM8
, CH2
, NONE
, TIM_USE_NONE
, 0, 1, 0),
123 DEF_TIM(TIM8
, CH3
, NONE
, TIM_USE_NONE
, 0, 2, 0),
124 DEF_TIM(TIM8
, CH4
, NONE
, TIM_USE_NONE
, 0, 3, 0),
125 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 0, 0),
126 DEF_TIM(TIM1
, CH2
, NONE
, TIM_USE_NONE
, 0, 1, 0),
127 DEF_TIM(TIM1
, CH3
, NONE
, TIM_USE_NONE
, 0, 2, 0),
128 DEF_TIM(TIM1
, CH4
, NONE
, TIM_USE_NONE
, 0, 3, 0),
131 #error MCU dependent code required
135 static FAST_DATA_ZERO_INIT motorDevice_t bbDevice
;
136 static FAST_DATA_ZERO_INIT timeUs_t lastSendUs
;
138 static motorPwmProtocolTypes_e motorPwmProtocol
;
140 // DMA GPIO output buffer formatting
142 static void bbOutputDataInit(uint32_t *buffer
, uint16_t portMask
, bool inverted
)
148 resetMask
= portMask
;
149 setMask
= (portMask
<< 16);
151 resetMask
= (portMask
<< 16);
157 for (bitpos
= 0; bitpos
< 16; bitpos
++) {
158 buffer
[bitpos
* 3 + 0] |= setMask
; // Always set all ports
159 buffer
[bitpos
* 3 + 1] = 0; // Reset bits are port dependent
160 buffer
[bitpos
* 3 + 2] |= resetMask
; // Always reset all ports
164 static void bbOutputDataSet(uint32_t *buffer
, int pinNumber
, uint16_t value
, bool inverted
)
169 middleBit
= (1 << (pinNumber
+ 0));
171 middleBit
= (1 << (pinNumber
+ 16));
174 for (int pos
= 0; pos
< 16; pos
++) {
175 if (!(value
& 0x8000)) {
176 buffer
[pos
* 3 + 1] |= middleBit
;
182 static void bbOutputDataClear(uint32_t *buffer
)
184 // Middle position to no change
185 for (int bitpos
= 0; bitpos
< 16; bitpos
++) {
186 buffer
[bitpos
* 3 + 1] = 0;
190 // bbPacer management
192 static bbPacer_t
*bbFindMotorPacer(TIM_TypeDef
*tim
)
194 for (int i
= 0; i
< MAX_MOTOR_PACERS
; i
++) {
196 bbPacer_t
*bbPacer
= &bbPacers
[i
];
198 if (bbPacer
->tim
== NULL
) {
204 if (bbPacer
->tim
== tim
) {
214 static bbPort_t
*bbFindMotorPort(int portIndex
)
216 for (int i
= 0; i
< usedMotorPorts
; i
++) {
217 if (bbPorts
[i
].portIndex
== portIndex
) {
224 static bbPort_t
*bbAllocMotorPort(int portIndex
)
226 if (usedMotorPorts
>= MAX_SUPPORTED_MOTOR_PORTS
) {
227 bbStatus
= DSHOT_BITBANG_STATUS_TOO_MANY_PORTS
;
231 bbPort_t
*bbPort
= &bbPorts
[usedMotorPorts
];
233 if (!bbPort
->timhw
) {
234 // No more pacer channel available
235 bbStatus
= DSHOT_BITBANG_STATUS_NO_PACER
;
239 bbPort
->portIndex
= portIndex
;
240 bbPort
->owner
.owner
= OWNER_DSHOT_BITBANG
;
241 bbPort
->owner
.resourceIndex
= RESOURCE_INDEX(portIndex
);
248 const resourceOwner_t
*dshotBitbangTimerGetOwner(int8_t timerNumber
, uint16_t timerChannel
)
250 for (int index
= 0; index
< usedMotorPorts
; index
++) {
251 const timerHardware_t
*timer
= bbPorts
[index
].timhw
;
252 if (timerGetTIMNumber(timer
->tim
) == timerNumber
&& timer
->channel
== timerChannel
) {
253 return &bbPorts
[index
].owner
;
260 // Return frequency of smallest change [state/sec]
262 static uint32_t getDshotBaseFrequency(motorPwmProtocolTypes_e pwmProtocolType
)
264 switch (pwmProtocolType
) {
265 case(PWM_TYPE_DSHOT600
):
266 return MOTOR_DSHOT600_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
267 case(PWM_TYPE_DSHOT300
):
268 return MOTOR_DSHOT300_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
270 case(PWM_TYPE_DSHOT150
):
271 return MOTOR_DSHOT150_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
275 static void bbAllocDma(bbPort_t
*bbPort
)
277 const timerHardware_t
*timhw
= bbPort
->timhw
;
280 dmaoptValue_t dmaopt
= dmaGetOptionByTimer(timhw
);
281 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByTimerValue(timhw
->tim
, timhw
->channel
, dmaopt
);
282 bbPort
->dmaResource
= dmaChannelSpec
->ref
;
283 bbPort
->dmaChannel
= dmaChannelSpec
->channel
;
285 bbPort
->dmaResource
= timhw
->dmaRef
;
286 bbPort
->dmaChannel
= timhw
->dmaChannel
;
289 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(bbPort
->dmaResource
);
290 dmaInit(dmaIdentifier
, OWNER_DSHOT_BITBANG
, bbPort
->owner
.resourceIndex
);
291 bbPort
->dmaSource
= timerDmaSource(timhw
->channel
);
293 bbPacer_t
*bbPacer
= bbFindMotorPacer(timhw
->tim
);
294 bbPacer
->dmaSources
|= bbPort
->dmaSource
;
296 dmaSetHandler(dmaIdentifier
, bbDMAIrqHandler
, NVIC_BUILD_PRIORITY(2, 1), (uint32_t)bbPort
);
298 bbDMA_ITConfig(bbPort
);
301 void bbDMAIrqHandler(dmaChannelDescriptor_t
*descriptor
)
305 bbPort_t
*bbPort
= (bbPort_t
*)descriptor
->userParam
;
307 bbDMA_Cmd(bbPort
, DISABLE
);
309 bbTIM_DMACmd(bbPort
->timhw
->tim
, bbPort
->dmaSource
, DISABLE
);
311 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TEIF
)) {
315 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
317 #ifdef USE_DSHOT_TELEMETRY
318 if (useDshotTelemetry
) {
319 if (bbPort
->direction
== DSHOT_BITBANG_DIRECTION_INPUT
) {
320 #ifdef DEBUG_COUNT_INTERRUPT
324 #ifdef DEBUG_COUNT_INTERRUPT
330 bbSwitchToInput(bbPort
);
332 bbTIM_DMACmd(bbPort
->timhw
->tim
, bbPort
->dmaSource
, ENABLE
);
339 // Setup bbPorts array elements so that they each have a TIM1 or TIM8 channel
340 // in timerHardware array for BB-DShot.
342 static void bbFindPacerTimer(void)
344 for (int bbPortIndex
= 0; bbPortIndex
< MAX_SUPPORTED_MOTOR_PORTS
; bbPortIndex
++) {
345 for (unsigned timerIndex
= 0; timerIndex
< ARRAYLEN(bbTimerHardware
); timerIndex
++) {
346 const timerHardware_t
*timer
= &bbTimerHardware
[timerIndex
];
347 int timNumber
= timerGetTIMNumber(timer
->tim
);
348 if ((motorConfig()->dev
.useDshotBitbangedTimer
== DSHOT_BITBANGED_TIMER_TIM1
&& timNumber
!= 1)
349 || (motorConfig()->dev
.useDshotBitbangedTimer
== DSHOT_BITBANGED_TIMER_TIM8
&& timNumber
!= 8)) {
352 bool timerConflict
= false;
353 for (int channel
= 0; channel
< CC_CHANNELS_PER_TIMER
; channel
++) {
354 const resourceOwner_e timerOwner
= timerGetOwner(timNumber
, CC_CHANNEL_FROM_INDEX(channel
))->owner
;
355 if (timerOwner
!= OWNER_FREE
&& timerOwner
!= OWNER_DSHOT_BITBANG
) {
356 timerConflict
= true;
361 for (int index
= 0; index
< bbPortIndex
; index
++) {
362 const timerHardware_t
* t
= bbPorts
[index
].timhw
;
363 if (timerGetTIMNumber(t
->tim
) == timNumber
&& timer
->channel
== t
->channel
) {
364 timerConflict
= true;
374 dmaoptValue_t dmaopt
= dmaGetOptionByTimer(timer
);
375 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByTimerValue(timer
->tim
, timer
->channel
, dmaopt
);
376 dmaResource_t
*dma
= dmaChannelSpec
->ref
;
378 dmaResource_t
*dma
= timer
->dmaRef
;
380 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(dma
);
381 if (dmaGetOwner(dmaIdentifier
)->owner
== OWNER_FREE
) {
382 bbPorts
[bbPortIndex
].timhw
= timer
;
390 static void bbTimebaseSetup(bbPort_t
*bbPort
, motorPwmProtocolTypes_e dshotProtocolType
)
392 uint32_t timerclock
= timerClock(bbPort
->timhw
->tim
);
394 uint32_t outputFreq
= getDshotBaseFrequency(dshotProtocolType
);
395 dshotFrameUs
= 1000000 * 17 * 3 / outputFreq
;
396 bbPort
->outputARR
= timerclock
/ outputFreq
- 1;
398 // XXX Explain this formula
399 uint32_t inputFreq
= outputFreq
* 5 * 2 * DSHOT_BITBANG_TELEMETRY_OVER_SAMPLE
/ 24;
400 bbPort
->inputARR
= timerclock
/ inputFreq
- 1;
404 // bb only use pin info associated with timerHardware entry designated as TIM_USE_MOTOR;
405 // it does not use the timer channel associated with the pin.
408 static bool bbMotorConfig(IO_t io
, uint8_t motorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
410 int pinIndex
= IO_GPIOPinIdx(io
);
411 int portIndex
= IO_GPIOPortIdx(io
);
413 bbPort_t
*bbPort
= bbFindMotorPort(portIndex
);
419 bbPort
= bbAllocMotorPort(portIndex
);
421 bbDevice
.vTable
.write
= motorWriteNull
;
422 bbDevice
.vTable
.updateStart
= motorUpdateStartNull
;
423 bbDevice
.vTable
.updateComplete
= motorUpdateCompleteNull
;
428 bbPort
->gpio
= IO_GPIO(io
);
430 bbPort
->portOutputCount
= MOTOR_DSHOT_BUF_LENGTH
;
431 bbPort
->portOutputBuffer
= &bbOutputBuffer
[(bbPort
- bbPorts
) * MOTOR_DSHOT_BUF_CACHE_ALIGN_LENGTH
];
433 bbPort
->portInputCount
= DSHOT_BB_PORT_IP_BUF_LENGTH
;
434 bbPort
->portInputBuffer
= &bbInputBuffer
[(bbPort
- bbPorts
) * DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_LENGTH
];
436 bbTimebaseSetup(bbPort
, pwmProtocolType
);
437 bbTIM_TimeBaseInit(bbPort
, bbPort
->outputARR
);
438 bbTimerChannelInit(bbPort
);
441 bbDMAPreconfigure(bbPort
, DSHOT_BITBANG_DIRECTION_OUTPUT
);
442 bbDMAPreconfigure(bbPort
, DSHOT_BITBANG_DIRECTION_INPUT
);
444 bbDMA_ITConfig(bbPort
);
447 bbMotors
[motorIndex
].pinIndex
= pinIndex
;
448 bbMotors
[motorIndex
].io
= io
;
449 bbMotors
[motorIndex
].output
= output
;
450 bbMotors
[motorIndex
].bbPort
= bbPort
;
452 IOInit(io
, OWNER_MOTOR
, RESOURCE_INDEX(motorIndex
));
454 // Setup GPIO_MODER and GPIO_ODR register manipulation values
456 bbGpioSetup(&bbMotors
[motorIndex
]);
458 #ifdef USE_DSHOT_TELEMETRY
459 if (useDshotTelemetry
) {
460 bbOutputDataInit(bbPort
->portOutputBuffer
, (1 << pinIndex
), DSHOT_BITBANG_INVERTED
);
464 bbOutputDataInit(bbPort
->portOutputBuffer
, (1 << pinIndex
), DSHOT_BITBANG_NONINVERTED
);
467 bbSwitchToOutput(bbPort
);
469 bbMotors
[motorIndex
].configured
= true;
474 static bool bbUpdateStart(void)
476 #ifdef USE_DSHOT_TELEMETRY
477 if (useDshotTelemetry
) {
478 #ifdef USE_DSHOT_TELEMETRY_STATS
479 const timeMs_t currentTimeMs
= millis();
481 timeUs_t currentUs
= micros();
482 // don't send while telemetry frames might still be incoming
483 if (cmpTimeUs(currentUs
, lastSendUs
) < (timeDelta_t
)(40 + 2 * dshotFrameUs
)) {
487 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
488 #ifdef USE_DSHOT_CACHE_MGMT
489 // Only invalidate the buffer once. If all motors are on a common port they'll share a buffer.
490 bool invalidated
= false;
491 for (int i
= 0; i
< motorIndex
; i
++) {
492 if (bbMotors
[motorIndex
].bbPort
->portInputBuffer
== bbMotors
[i
].bbPort
->portInputBuffer
) {
497 SCB_InvalidateDCache_by_Addr((uint32_t *)bbMotors
[motorIndex
].bbPort
->portInputBuffer
,
498 DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_BYTES
);
503 uint32_t value
= decode_bb_bitband(
504 bbMotors
[motorIndex
].bbPort
->portInputBuffer
,
505 bbMotors
[motorIndex
].bbPort
->portInputCount
- bbDMA_Count(bbMotors
[motorIndex
].bbPort
),
506 bbMotors
[motorIndex
].pinIndex
);
508 uint32_t value
= decode_bb(
509 bbMotors
[motorIndex
].bbPort
->portInputBuffer
,
510 bbMotors
[motorIndex
].bbPort
->portInputCount
- bbDMA_Count(bbMotors
[motorIndex
].bbPort
),
511 bbMotors
[motorIndex
].pinIndex
);
513 if (value
== BB_NOEDGE
) {
516 dshotTelemetryState
.readCount
++;
518 if (value
!= BB_INVALID
) {
519 dshotTelemetryState
.motorState
[motorIndex
].telemetryValue
= value
;
520 dshotTelemetryState
.motorState
[motorIndex
].telemetryActive
= true;
521 if (motorIndex
< 4) {
522 DEBUG_SET(DEBUG_DSHOT_RPM_TELEMETRY
, motorIndex
, value
);
525 dshotTelemetryState
.invalidPacketCount
++;
527 #ifdef USE_DSHOT_TELEMETRY_STATS
528 updateDshotTelemetryQuality(&dshotTelemetryQuality
[motorIndex
], value
!= BB_INVALID
, currentTimeMs
);
533 for (int i
= 0; i
< usedMotorPorts
; i
++) {
534 bbDMA_Cmd(&bbPorts
[i
], DISABLE
);
535 bbOutputDataClear(bbPorts
[i
].portOutputBuffer
);
541 static void bbWriteInt(uint8_t motorIndex
, uint16_t value
)
543 bbMotor_t
*const bbmotor
= &bbMotors
[motorIndex
];
545 if (!bbmotor
->configured
) {
549 // fetch requestTelemetry from motors. Needs to be refactored.
550 motorDmaOutput_t
* const motor
= getMotorDmaOutput(motorIndex
);
551 bbmotor
->protocolControl
.requestTelemetry
= motor
->protocolControl
.requestTelemetry
;
552 motor
->protocolControl
.requestTelemetry
= false;
554 // If there is a command ready to go overwrite the value and send that instead
555 if (dshotCommandIsProcessing()) {
556 value
= dshotCommandGetCurrent(motorIndex
);
558 bbmotor
->protocolControl
.requestTelemetry
= true;
562 bbmotor
->protocolControl
.value
= value
;
564 uint16_t packet
= prepareDshotPacket(&bbmotor
->protocolControl
);
566 bbPort_t
*bbPort
= bbmotor
->bbPort
;
568 #ifdef USE_DSHOT_TELEMETRY
569 if (useDshotTelemetry
) {
570 bbOutputDataSet(bbPort
->portOutputBuffer
, bbmotor
->pinIndex
, packet
, DSHOT_BITBANG_INVERTED
);
574 bbOutputDataSet(bbPort
->portOutputBuffer
, bbmotor
->pinIndex
, packet
, DSHOT_BITBANG_NONINVERTED
);
578 static void bbWrite(uint8_t motorIndex
, float value
)
580 bbWriteInt(motorIndex
, value
);
583 static void bbUpdateComplete(void)
585 // If there is a dshot command loaded up, time it correctly with motor update
587 if (!dshotCommandQueueEmpty()) {
588 if (!dshotCommandOutputIsEnabled(bbDevice
.count
)) {
593 #ifdef USE_DSHOT_CACHE_MGMT
594 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
595 // Only clean the buffer once. If all motors are on a common port they'll share a buffer.
597 for (int i
= 0; i
< motorIndex
; i
++) {
598 if (bbMotors
[motorIndex
].bbPort
->portOutputBuffer
== bbMotors
[i
].bbPort
->portOutputBuffer
) {
603 SCB_CleanDCache_by_Addr(bbMotors
[motorIndex
].bbPort
->portOutputBuffer
, MOTOR_DSHOT_BUF_CACHE_ALIGN_BYTES
);
608 #ifdef USE_DSHOT_TELEMETRY
609 for (int i
= 0; i
< usedMotorPorts
; i
++) {
610 bbPort_t
*bbPort
= &bbPorts
[i
];
612 if (useDshotTelemetry
) {
613 if (bbPort
->direction
== DSHOT_BITBANG_DIRECTION_INPUT
) {
614 bbPort
->inputActive
= false;
615 bbSwitchToOutput(bbPort
);
619 bbDMA_Cmd(bbPort
, ENABLE
);
624 lastSendUs
= micros();
625 for (int i
= 0; i
< usedMotorPacers
; i
++) {
626 bbPacer_t
*bbPacer
= &bbPacers
[i
];
627 bbTIM_DMACmd(bbPacer
->tim
, bbPacer
->dmaSources
, ENABLE
);
631 static bool bbEnableMotors(void)
633 for (int i
= 0; i
< motorCount
; i
++) {
634 if (bbMotors
[i
].configured
) {
635 IOConfigGPIO(bbMotors
[i
].io
, bbMotors
[i
].iocfg
);
641 static void bbDisableMotors(void)
646 static void bbShutdown(void)
651 static bool bbIsMotorEnabled(uint8_t index
)
653 return bbMotors
[index
].enabled
;
656 static void bbPostInit()
660 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
662 if (!bbMotorConfig(bbMotors
[motorIndex
].io
, motorIndex
, motorPwmProtocol
, bbMotors
[motorIndex
].output
)) {
667 bbMotors
[motorIndex
].enabled
= true;
669 // Fill in motors structure for 4way access (XXX Should be refactored)
671 motors
[motorIndex
].enabled
= true;
675 static motorVTable_t bbVTable
= {
676 .postInit
= bbPostInit
,
677 .enable
= bbEnableMotors
,
678 .disable
= bbDisableMotors
,
679 .isMotorEnabled
= bbIsMotorEnabled
,
680 .updateStart
= bbUpdateStart
,
682 .writeInt
= bbWriteInt
,
683 .updateComplete
= bbUpdateComplete
,
684 .convertExternalToMotor
= dshotConvertFromExternal
,
685 .convertMotorToExternal
= dshotConvertToExternal
,
686 .shutdown
= bbShutdown
,
689 dshotBitbangStatus_e
dshotBitbangGetStatus()
694 motorDevice_t
*dshotBitbangDevInit(const motorDevConfig_t
*motorConfig
, uint8_t count
)
700 motorPwmProtocol
= motorConfig
->motorPwmProtocol
;
701 bbDevice
.vTable
= bbVTable
;
703 bbStatus
= DSHOT_BITBANG_STATUS_OK
;
705 #ifdef USE_DSHOT_TELEMETRY
706 useDshotTelemetry
= motorConfig
->useDshotTelemetry
;
709 memset(bbOutputBuffer
, 0, sizeof(bbOutputBuffer
));
711 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
712 const unsigned reorderedMotorIndex
= motorConfig
->motorOutputReordering
[motorIndex
];
713 const timerHardware_t
*timerHardware
= timerGetByTag(motorConfig
->ioTags
[reorderedMotorIndex
]);
714 const IO_t io
= IOGetByTag(motorConfig
->ioTags
[reorderedMotorIndex
]);
716 uint8_t output
= motorConfig
->motorPwmInversion
? timerHardware
->output
^ TIMER_OUTPUT_INVERTED
: timerHardware
->output
;
717 bbPuPdMode
= (output
& TIMER_OUTPUT_INVERTED
) ? BB_GPIO_PULLDOWN
: BB_GPIO_PULLUP
;
719 #ifdef USE_DSHOT_TELEMETRY
720 if (useDshotTelemetry
) {
721 output
^= TIMER_OUTPUT_INVERTED
;
725 if (!IOIsFreeOrPreinit(io
)) {
726 /* not enough motors initialised for the mixer or a break in the motors */
727 bbDevice
.vTable
.write
= motorWriteNull
;
728 bbDevice
.vTable
.updateStart
= motorUpdateStartNull
;
729 bbDevice
.vTable
.updateComplete
= motorUpdateCompleteNull
;
730 bbStatus
= DSHOT_BITBANG_STATUS_MOTOR_PIN_CONFLICT
;
734 int pinIndex
= IO_GPIOPinIdx(io
);
736 bbMotors
[motorIndex
].pinIndex
= pinIndex
;
737 bbMotors
[motorIndex
].io
= io
;
738 bbMotors
[motorIndex
].output
= output
;
739 #if defined(STM32F4) || defined(STM32F3)
740 bbMotors
[motorIndex
].iocfg
= IO_CONFIG(GPIO_Mode_OUT
, GPIO_Speed_50MHz
, GPIO_OType_PP
, bbPuPdMode
);
741 #elif defined(STM32F7) || defined(STM32G4) || defined(STM32H7)
742 bbMotors
[motorIndex
].iocfg
= IO_CONFIG(GPIO_MODE_OUTPUT_PP
, GPIO_SPEED_FREQ_VERY_HIGH
, bbPuPdMode
);
745 IOInit(io
, OWNER_MOTOR
, RESOURCE_INDEX(motorIndex
));
746 IOConfigGPIO(io
, bbMotors
[motorIndex
].iocfg
);
747 if (output
& TIMER_OUTPUT_INVERTED
) {
753 // Fill in motors structure for 4way access (XXX Should be refactored)
754 motors
[motorIndex
].io
= bbMotors
[motorIndex
].io
;
760 #endif // USE_DSHOT_BB