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"
30 #include "build/debug_pin.h"
32 #include "drivers/io.h"
33 #include "drivers/io_impl.h"
34 #include "drivers/dma.h"
35 #include "drivers/dma_reqmap.h"
36 #include "drivers/dshot.h"
37 #include "drivers/dshot_bitbang.h"
38 #include "drivers/dshot_bitbang_impl.h"
39 #include "drivers/dshot_command.h"
40 #include "drivers/motor.h"
41 #include "drivers/nvic.h"
42 #include "drivers/pwm_output.h" // XXX for pwmOutputPort_t motors[]; should go away with refactoring
43 #include "drivers/dshot_dpwm.h" // XXX for motorDmaOutput_t *getMotorDmaOutput(uint8_t index); should go away with refactoring
44 #include "drivers/dshot_bitbang_decode.h"
45 #include "drivers/time.h"
46 #include "drivers/timer.h"
51 // DEBUG_DSHOT_TELEMETRY_COUNTS
52 // 0 - Count of telemetry packets read
53 // 1 - Count of missing edge
54 // 2 - Count of reception not complete in time
55 // 3 - Number of high bits before telemetry start
57 // Maximum time to wait for telemetry reception to complete
58 #define DSHOT_TELEMETRY_TIMEOUT 2000
60 FAST_DATA_ZERO_INIT bbPacer_t bbPacers
[MAX_MOTOR_PACERS
]; // TIM1 or TIM8
61 FAST_DATA_ZERO_INIT
int usedMotorPacers
= 0;
63 FAST_DATA_ZERO_INIT bbPort_t bbPorts
[MAX_SUPPORTED_MOTOR_PORTS
];
64 FAST_DATA_ZERO_INIT
int usedMotorPorts
;
66 FAST_DATA_ZERO_INIT bbMotor_t bbMotors
[MAX_SUPPORTED_MOTORS
];
68 static FAST_DATA_ZERO_INIT
int motorCount
;
69 dshotBitbangStatus_e bbStatus
;
71 // For MCUs that use MPU to control DMA coherency, there might be a performance hit
72 // on manipulating input buffer content especially if it is read multiple times,
73 // as the buffer region is attributed as not cachable.
74 // If this is not desirable, we should use manual cache invalidation.
75 #ifdef USE_DSHOT_CACHE_MGMT
76 #define BB_OUTPUT_BUFFER_ATTRIBUTE DMA_RW_AXI __attribute__((aligned(32)))
77 #define BB_INPUT_BUFFER_ATTRIBUTE DMA_RW_AXI __attribute__((aligned(32)))
80 #define BB_OUTPUT_BUFFER_ATTRIBUTE
81 #define BB_INPUT_BUFFER_ATTRIBUTE
82 #elif defined(STM32F7)
83 #define BB_OUTPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
84 #define BB_INPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
85 #elif defined(STM32H7)
86 #define BB_OUTPUT_BUFFER_ATTRIBUTE DMA_RAM
87 #define BB_INPUT_BUFFER_ATTRIBUTE DMA_RAM
88 #elif defined(STM32G4)
89 #define BB_OUTPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
90 #define BB_INPUT_BUFFER_ATTRIBUTE FAST_DATA_ZERO_INIT
92 #endif // USE_DSHOT_CACHE_MGMT
94 BB_OUTPUT_BUFFER_ATTRIBUTE
uint32_t bbOutputBuffer
[MOTOR_DSHOT_BUF_CACHE_ALIGN_LENGTH
* MAX_SUPPORTED_MOTOR_PORTS
];
95 BB_INPUT_BUFFER_ATTRIBUTE
uint16_t bbInputBuffer
[DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_LENGTH
* MAX_SUPPORTED_MOTOR_PORTS
];
98 FAST_DATA_ZERO_INIT timeUs_t dshotFrameUs
;
101 const timerHardware_t bbTimerHardware
[] = {
102 #if defined(STM32F4) || defined(STM32F7)
103 #if !defined(STM32F411xE)
104 DEF_TIM(TIM8
, CH1
, NONE
, TIM_USE_NONE
, 0, 1),
105 DEF_TIM(TIM8
, CH2
, NONE
, TIM_USE_NONE
, 0, 1),
106 DEF_TIM(TIM8
, CH3
, NONE
, TIM_USE_NONE
, 0, 1),
107 DEF_TIM(TIM8
, CH4
, NONE
, TIM_USE_NONE
, 0, 0),
109 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 1),
110 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 2),
111 DEF_TIM(TIM1
, CH2
, NONE
, TIM_USE_NONE
, 0, 1),
112 DEF_TIM(TIM1
, CH3
, NONE
, TIM_USE_NONE
, 0, 1),
113 DEF_TIM(TIM1
, CH4
, NONE
, TIM_USE_NONE
, 0, 0),
115 #elif defined(STM32G4) || defined(STM32H7)
116 // XXX TODO: STM32G4 and STM32H7 can use any timer for pacing
118 // DMA request numbers are duplicated for TIM1 and TIM8:
119 // - Any pacer can serve a GPIO port.
120 // - For quads (or less), 4 pacers can cover the worst case scenario of
121 // 4 motors scattered across 4 different GPIO ports.
122 // - For hexas (and larger), more channels may become necessary,
123 // in which case the DMA request numbers should be modified.
124 DEF_TIM(TIM8
, CH1
, NONE
, TIM_USE_NONE
, 0, 0, 0),
125 DEF_TIM(TIM8
, CH2
, NONE
, TIM_USE_NONE
, 0, 1, 0),
126 DEF_TIM(TIM8
, CH3
, NONE
, TIM_USE_NONE
, 0, 2, 0),
127 DEF_TIM(TIM8
, CH4
, NONE
, TIM_USE_NONE
, 0, 3, 0),
128 DEF_TIM(TIM1
, CH1
, NONE
, TIM_USE_NONE
, 0, 0, 0),
129 DEF_TIM(TIM1
, CH2
, NONE
, TIM_USE_NONE
, 0, 1, 0),
130 DEF_TIM(TIM1
, CH3
, NONE
, TIM_USE_NONE
, 0, 2, 0),
131 DEF_TIM(TIM1
, CH4
, NONE
, TIM_USE_NONE
, 0, 3, 0),
134 #error MCU dependent code required
138 static FAST_DATA_ZERO_INIT motorDevice_t bbDevice
;
139 static FAST_DATA_ZERO_INIT timeUs_t lastSendUs
;
141 static motorPwmProtocolTypes_e motorPwmProtocol
;
143 // DMA GPIO output buffer formatting
145 static void bbOutputDataInit(uint32_t *buffer
, uint16_t portMask
, bool inverted
)
151 resetMask
= portMask
;
152 setMask
= (portMask
<< 16);
154 resetMask
= (portMask
<< 16);
160 for (symbol_index
= 0; symbol_index
< MOTOR_DSHOT_FRAME_BITS
; symbol_index
++) {
161 buffer
[symbol_index
* MOTOR_DSHOT_STATE_PER_SYMBOL
+ 0] |= setMask
; // Always set all ports
162 buffer
[symbol_index
* MOTOR_DSHOT_STATE_PER_SYMBOL
+ 1] = 0; // Reset bits are port dependent
163 buffer
[symbol_index
* MOTOR_DSHOT_STATE_PER_SYMBOL
+ 2] |= resetMask
; // Always reset all ports
167 // output one more 'bit' that keeps the line level at idle to allow the ESC to sample the last bit
169 // Avoid CRC errors in the case of bi-directional d-shot. CRC errors can occur if the output is
170 // transitioned to an input before the signal has been sampled by the ESC as the sampled voltage
171 // may be somewhere between logic-high and logic-low depending on how the motor output line is
172 // driven or floating. On some MCUs it's observed that the voltage momentarily drops low on transition
175 int hold_bit_index
= MOTOR_DSHOT_FRAME_BITS
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
176 buffer
[hold_bit_index
+ 0] |= resetMask
; // Always reset all ports
177 buffer
[hold_bit_index
+ 1] = 0; // Never any change
178 buffer
[hold_bit_index
+ 2] = 0; // Never any change
181 static void bbOutputDataSet(uint32_t *buffer
, int pinNumber
, uint16_t value
, bool inverted
)
186 middleBit
= (1 << (pinNumber
+ 0));
188 middleBit
= (1 << (pinNumber
+ 16));
191 for (int pos
= 0; pos
< 16; pos
++) {
192 if (!(value
& 0x8000)) {
193 buffer
[pos
* 3 + 1] |= middleBit
;
199 static void bbOutputDataClear(uint32_t *buffer
)
201 // Middle position to no change
202 for (int bitpos
= 0; bitpos
< 16; bitpos
++) {
203 buffer
[bitpos
* 3 + 1] = 0;
207 // bbPacer management
209 static bbPacer_t
*bbFindMotorPacer(TIM_TypeDef
*tim
)
211 for (int i
= 0; i
< MAX_MOTOR_PACERS
; i
++) {
213 bbPacer_t
*bbPacer
= &bbPacers
[i
];
215 if (bbPacer
->tim
== NULL
) {
221 if (bbPacer
->tim
== tim
) {
231 static bbPort_t
*bbFindMotorPort(int portIndex
)
233 for (int i
= 0; i
< usedMotorPorts
; i
++) {
234 if (bbPorts
[i
].portIndex
== portIndex
) {
241 static bbPort_t
*bbAllocateMotorPort(int portIndex
)
243 if (usedMotorPorts
>= MAX_SUPPORTED_MOTOR_PORTS
) {
244 bbStatus
= DSHOT_BITBANG_STATUS_TOO_MANY_PORTS
;
248 bbPort_t
*bbPort
= &bbPorts
[usedMotorPorts
];
250 if (!bbPort
->timhw
) {
251 // No more pacer channel available
252 bbStatus
= DSHOT_BITBANG_STATUS_NO_PACER
;
256 bbPort
->portIndex
= portIndex
;
257 bbPort
->owner
.owner
= OWNER_DSHOT_BITBANG
;
258 bbPort
->owner
.resourceIndex
= RESOURCE_INDEX(portIndex
);
265 const timerHardware_t
*dshotBitbangTimerGetAllocatedByNumberAndChannel(int8_t timerNumber
, uint16_t timerChannel
)
267 for (int index
= 0; index
< usedMotorPorts
; index
++) {
268 const timerHardware_t
*bitbangTimer
= bbPorts
[index
].timhw
;
269 if (bitbangTimer
&& timerGetTIMNumber(bitbangTimer
->tim
) == timerNumber
&& bitbangTimer
->channel
== timerChannel
&& bbPorts
[index
].owner
.owner
) {
277 const resourceOwner_t
*dshotBitbangTimerGetOwner(const timerHardware_t
*timer
)
279 for (int index
= 0; index
< usedMotorPorts
; index
++) {
280 const timerHardware_t
*bitbangTimer
= bbPorts
[index
].timhw
;
281 if (bitbangTimer
&& bitbangTimer
== timer
) {
282 return &bbPorts
[index
].owner
;
289 // Return frequency of smallest change [state/sec]
291 static uint32_t getDshotBaseFrequency(motorPwmProtocolTypes_e pwmProtocolType
)
293 switch (pwmProtocolType
) {
294 case(PWM_TYPE_DSHOT600
):
295 return MOTOR_DSHOT600_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
296 case(PWM_TYPE_DSHOT300
):
297 return MOTOR_DSHOT300_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
299 case(PWM_TYPE_DSHOT150
):
300 return MOTOR_DSHOT150_SYMBOL_RATE
* MOTOR_DSHOT_STATE_PER_SYMBOL
;
304 static void bbSetupDma(bbPort_t
*bbPort
)
306 const dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(bbPort
->dmaResource
);
307 dmaEnable(dmaIdentifier
);
308 bbPort
->dmaSource
= timerDmaSource(bbPort
->timhw
->channel
);
310 bbPacer_t
*bbPacer
= bbFindMotorPacer(bbPort
->timhw
->tim
);
311 bbPacer
->dmaSources
|= bbPort
->dmaSource
;
313 dmaSetHandler(dmaIdentifier
, bbDMAIrqHandler
, NVIC_BUILD_PRIORITY(2, 1), (uint32_t)bbPort
);
315 bbDMA_ITConfig(bbPort
);
318 FAST_IRQ_HANDLER
void bbDMAIrqHandler(dmaChannelDescriptor_t
*descriptor
)
322 bbPort_t
*bbPort
= (bbPort_t
*)descriptor
->userParam
;
324 bbDMA_Cmd(bbPort
, DISABLE
);
326 bbTIM_DMACmd(bbPort
->timhw
->tim
, bbPort
->dmaSource
, DISABLE
);
328 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TEIF
)) {
332 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
334 #ifdef USE_DSHOT_TELEMETRY
335 if (useDshotTelemetry
) {
336 if (bbPort
->direction
== DSHOT_BITBANG_DIRECTION_INPUT
) {
337 bbPort
->telemetryPending
= false;
338 #ifdef DEBUG_COUNT_INTERRUPT
341 // Disable DMA as telemetry reception is complete
342 bbDMA_Cmd(bbPort
, DISABLE
);
344 #ifdef DEBUG_COUNT_INTERRUPT
350 bbSwitchToInput(bbPort
);
351 bbPort
->telemetryPending
= true;
353 bbTIM_DMACmd(bbPort
->timhw
->tim
, bbPort
->dmaSource
, ENABLE
);
360 // Setup bbPorts array elements so that they each have a TIM1 or TIM8 channel
361 // in timerHardware array for BB-DShot.
363 static void bbFindPacerTimer(void)
365 for (int bbPortIndex
= 0; bbPortIndex
< MAX_SUPPORTED_MOTOR_PORTS
; bbPortIndex
++) {
366 for (unsigned timerIndex
= 0; timerIndex
< ARRAYLEN(bbTimerHardware
); timerIndex
++) {
367 const timerHardware_t
*timer
= &bbTimerHardware
[timerIndex
];
368 int timNumber
= timerGetTIMNumber(timer
->tim
);
369 if ((motorConfig()->dev
.useDshotBitbangedTimer
== DSHOT_BITBANGED_TIMER_TIM1
&& timNumber
!= 1)
370 || (motorConfig()->dev
.useDshotBitbangedTimer
== DSHOT_BITBANGED_TIMER_TIM8
&& timNumber
!= 8)) {
373 bool timerConflict
= false;
374 for (int channel
= 0; channel
< CC_CHANNELS_PER_TIMER
; channel
++) {
375 const timerHardware_t
*timer
= timerGetAllocatedByNumberAndChannel(timNumber
, CC_CHANNEL_FROM_INDEX(channel
));
376 const resourceOwner_e timerOwner
= timerGetOwner(timer
)->owner
;
377 if (timerOwner
!= OWNER_FREE
&& timerOwner
!= OWNER_DSHOT_BITBANG
) {
378 timerConflict
= true;
383 for (int index
= 0; index
< bbPortIndex
; index
++) {
384 const timerHardware_t
* t
= bbPorts
[index
].timhw
;
385 if (timerGetTIMNumber(t
->tim
) == timNumber
&& timer
->channel
== t
->channel
) {
386 timerConflict
= true;
396 dmaoptValue_t dmaopt
= dmaGetOptionByTimer(timer
);
397 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByTimerValue(timer
->tim
, timer
->channel
, dmaopt
);
398 dmaResource_t
*dma
= dmaChannelSpec
->ref
;
400 dmaResource_t
*dma
= timer
->dmaRef
;
402 dmaIdentifier_e dmaIdentifier
= dmaGetIdentifier(dma
);
403 if (dmaGetOwner(dmaIdentifier
)->owner
== OWNER_FREE
) {
404 bbPorts
[bbPortIndex
].timhw
= timer
;
412 static void bbTimebaseSetup(bbPort_t
*bbPort
, motorPwmProtocolTypes_e dshotProtocolType
)
414 uint32_t timerclock
= timerClock(bbPort
->timhw
->tim
);
416 uint32_t outputFreq
= getDshotBaseFrequency(dshotProtocolType
);
417 dshotFrameUs
= 1000000 * 17 * 3 / outputFreq
;
418 bbPort
->outputARR
= timerclock
/ outputFreq
- 1;
420 // XXX Explain this formula
421 uint32_t inputFreq
= outputFreq
* 5 * 2 * DSHOT_BITBANG_TELEMETRY_OVER_SAMPLE
/ 24;
422 bbPort
->inputARR
= timerclock
/ inputFreq
- 1;
426 // bb only use pin info associated with timerHardware entry designated as TIM_USE_MOTOR;
427 // it does not use the timer channel associated with the pin.
430 static bool bbMotorConfig(IO_t io
, uint8_t motorIndex
, motorPwmProtocolTypes_e pwmProtocolType
, uint8_t output
)
432 int pinIndex
= IO_GPIOPinIdx(io
);
433 int portIndex
= IO_GPIOPortIdx(io
);
435 bbPort_t
*bbPort
= bbFindMotorPort(portIndex
);
441 bbPort
= bbAllocateMotorPort(portIndex
);
444 const timerHardware_t
*timhw
= bbPort
->timhw
;
447 const dmaChannelSpec_t
*dmaChannelSpec
= dmaGetChannelSpecByTimerValue(timhw
->tim
, timhw
->channel
, dmaGetOptionByTimer(timhw
));
448 bbPort
->dmaResource
= dmaChannelSpec
->ref
;
449 bbPort
->dmaChannel
= dmaChannelSpec
->channel
;
451 bbPort
->dmaResource
= timhw
->dmaRef
;
452 bbPort
->dmaChannel
= timhw
->dmaChannel
;
456 if (!bbPort
|| !dmaAllocate(dmaGetIdentifier(bbPort
->dmaResource
), bbPort
->owner
.owner
, bbPort
->owner
.resourceIndex
)) {
457 bbDevice
.vTable
.write
= motorWriteNull
;
458 bbDevice
.vTable
.decodeTelemetry
= motorDecodeTelemetryNull
;
459 bbDevice
.vTable
.updateComplete
= motorUpdateCompleteNull
;
464 bbPort
->gpio
= IO_GPIO(io
);
466 bbPort
->portOutputCount
= MOTOR_DSHOT_BUF_LENGTH
;
467 bbPort
->portOutputBuffer
= &bbOutputBuffer
[(bbPort
- bbPorts
) * MOTOR_DSHOT_BUF_CACHE_ALIGN_LENGTH
];
469 bbPort
->portInputCount
= DSHOT_BB_PORT_IP_BUF_LENGTH
;
470 bbPort
->portInputBuffer
= &bbInputBuffer
[(bbPort
- bbPorts
) * DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_LENGTH
];
472 bbTimebaseSetup(bbPort
, pwmProtocolType
);
473 bbTIM_TimeBaseInit(bbPort
, bbPort
->outputARR
);
474 bbTimerChannelInit(bbPort
);
477 bbDMAPreconfigure(bbPort
, DSHOT_BITBANG_DIRECTION_OUTPUT
);
478 bbDMAPreconfigure(bbPort
, DSHOT_BITBANG_DIRECTION_INPUT
);
480 bbDMA_ITConfig(bbPort
);
483 bbMotors
[motorIndex
].pinIndex
= pinIndex
;
484 bbMotors
[motorIndex
].io
= io
;
485 bbMotors
[motorIndex
].output
= output
;
486 bbMotors
[motorIndex
].bbPort
= bbPort
;
488 IOInit(io
, OWNER_MOTOR
, RESOURCE_INDEX(motorIndex
));
490 // Setup GPIO_MODER and GPIO_ODR register manipulation values
492 bbGpioSetup(&bbMotors
[motorIndex
]);
494 #ifdef USE_DSHOT_TELEMETRY
495 if (useDshotTelemetry
) {
496 bbOutputDataInit(bbPort
->portOutputBuffer
, (1 << pinIndex
), DSHOT_BITBANG_INVERTED
);
500 bbOutputDataInit(bbPort
->portOutputBuffer
, (1 << pinIndex
), DSHOT_BITBANG_NONINVERTED
);
503 bbSwitchToOutput(bbPort
);
505 bbMotors
[motorIndex
].configured
= true;
510 static bool bbTelemetryWait(void)
512 // Wait for telemetry reception to complete
513 bool telemetryPending
;
514 bool telemetryWait
= false;
515 const timeUs_t startTimeUs
= micros();
518 telemetryPending
= false;
519 for (int i
= 0; i
< usedMotorPorts
; i
++) {
520 telemetryPending
|= bbPorts
[i
].telemetryPending
;
523 telemetryWait
|= telemetryPending
;
525 if (cmpTimeUs(micros(), startTimeUs
) > DSHOT_TELEMETRY_TIMEOUT
) {
528 } while (telemetryPending
);
531 DEBUG_SET(DEBUG_DSHOT_TELEMETRY_COUNTS
, 2, debug
[2] + 1);
534 return telemetryWait
;
537 static void bbUpdateInit(void)
539 for (int i
= 0; i
< usedMotorPorts
; i
++) {
540 bbOutputDataClear(bbPorts
[i
].portOutputBuffer
);
544 static bool bbDecodeTelemetry(void)
546 #ifdef USE_DSHOT_TELEMETRY
547 if (useDshotTelemetry
) {
548 #ifdef USE_DSHOT_TELEMETRY_STATS
549 const timeMs_t currentTimeMs
= millis();
552 #ifdef USE_DSHOT_CACHE_MGMT
553 for (int i
= 0; i
< usedMotorPorts
; i
++) {
554 bbPort_t
*bbPort
= &bbPorts
[i
];
555 SCB_InvalidateDCache_by_Addr((uint32_t *)bbPort
->portInputBuffer
, DSHOT_BB_PORT_IP_BUF_CACHE_ALIGN_BYTES
);
558 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
560 uint32_t rawValue
= decode_bb_bitband(
561 bbMotors
[motorIndex
].bbPort
->portInputBuffer
,
562 bbMotors
[motorIndex
].bbPort
->portInputCount
,
563 bbMotors
[motorIndex
].pinIndex
);
565 uint32_t rawValue
= decode_bb(
566 bbMotors
[motorIndex
].bbPort
->portInputBuffer
,
567 bbMotors
[motorIndex
].bbPort
->portInputCount
,
568 bbMotors
[motorIndex
].pinIndex
);
570 if (rawValue
== DSHOT_TELEMETRY_NOEDGE
) {
571 DEBUG_SET(DEBUG_DSHOT_TELEMETRY_COUNTS
, 1, debug
[1] + 1);
574 DEBUG_SET(DEBUG_DSHOT_TELEMETRY_COUNTS
, 0, debug
[0] + 1);
575 dshotTelemetryState
.readCount
++;
577 if (rawValue
!= DSHOT_TELEMETRY_INVALID
) {
578 // Check EDT enable or store raw value
579 if ((rawValue
== 0x0E00) && (dshotCommandGetCurrent(motorIndex
) == DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE
)) {
580 dshotTelemetryState
.motorState
[motorIndex
].telemetryTypes
= 1 << DSHOT_TELEMETRY_TYPE_STATE_EVENTS
;
582 dshotTelemetryState
.motorState
[motorIndex
].rawValue
= rawValue
;
585 dshotTelemetryState
.invalidPacketCount
++;
587 #ifdef USE_DSHOT_TELEMETRY_STATS
588 updateDshotTelemetryQuality(&dshotTelemetryQuality
[motorIndex
], rawValue
!= DSHOT_TELEMETRY_INVALID
, currentTimeMs
);
592 dshotTelemetryState
.rawValueState
= DSHOT_RAW_VALUE_STATE_NOT_PROCESSED
;
599 static void bbWriteInt(uint8_t motorIndex
, uint16_t value
)
601 bbMotor_t
*const bbmotor
= &bbMotors
[motorIndex
];
603 if (!bbmotor
->configured
) {
607 // fetch requestTelemetry from motors. Needs to be refactored.
608 motorDmaOutput_t
* const motor
= getMotorDmaOutput(motorIndex
);
609 bbmotor
->protocolControl
.requestTelemetry
= motor
->protocolControl
.requestTelemetry
;
610 motor
->protocolControl
.requestTelemetry
= false;
612 // If there is a command ready to go overwrite the value and send that instead
613 if (dshotCommandIsProcessing()) {
614 value
= dshotCommandGetCurrent(motorIndex
);
616 bbmotor
->protocolControl
.requestTelemetry
= true;
620 bbmotor
->protocolControl
.value
= value
;
622 uint16_t packet
= prepareDshotPacket(&bbmotor
->protocolControl
);
624 bbPort_t
*bbPort
= bbmotor
->bbPort
;
626 #ifdef USE_DSHOT_TELEMETRY
627 if (useDshotTelemetry
) {
628 bbOutputDataSet(bbPort
->portOutputBuffer
, bbmotor
->pinIndex
, packet
, DSHOT_BITBANG_INVERTED
);
632 bbOutputDataSet(bbPort
->portOutputBuffer
, bbmotor
->pinIndex
, packet
, DSHOT_BITBANG_NONINVERTED
);
636 static void bbWrite(uint8_t motorIndex
, float value
)
638 bbWriteInt(motorIndex
, lrintf(value
));
641 static void bbUpdateComplete(void)
643 // If there is a dshot command loaded up, time it correctly with motor update
645 if (!dshotCommandQueueEmpty()) {
646 if (!dshotCommandOutputIsEnabled(bbDevice
.count
)) {
651 for (int i
= 0; i
< usedMotorPorts
; i
++) {
652 bbPort_t
*bbPort
= &bbPorts
[i
];
653 #ifdef USE_DSHOT_CACHE_MGMT
654 SCB_CleanDCache_by_Addr(bbPort
->portOutputBuffer
, MOTOR_DSHOT_BUF_CACHE_ALIGN_BYTES
);
657 #ifdef USE_DSHOT_TELEMETRY
658 if (useDshotTelemetry
) {
659 if (bbPort
->direction
== DSHOT_BITBANG_DIRECTION_INPUT
) {
660 bbPort
->inputActive
= false;
661 bbSwitchToOutput(bbPort
);
667 // Using circular mode resets the counter one short, so explicitly reload
668 bbSwitchToOutput(bbPort
);
672 bbDMA_Cmd(bbPort
, ENABLE
);
675 lastSendUs
= micros();
676 for (int i
= 0; i
< usedMotorPacers
; i
++) {
677 bbPacer_t
*bbPacer
= &bbPacers
[i
];
678 bbTIM_DMACmd(bbPacer
->tim
, bbPacer
->dmaSources
, ENABLE
);
682 static bool bbEnableMotors(void)
684 for (int i
= 0; i
< motorCount
; i
++) {
685 if (bbMotors
[i
].configured
) {
686 IOConfigGPIO(bbMotors
[i
].io
, bbMotors
[i
].iocfg
);
692 static void bbDisableMotors(void)
697 static void bbShutdown(void)
702 static bool bbIsMotorEnabled(uint8_t index
)
704 return bbMotors
[index
].enabled
;
707 static void bbPostInit(void)
711 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
713 if (!bbMotorConfig(bbMotors
[motorIndex
].io
, motorIndex
, motorPwmProtocol
, bbMotors
[motorIndex
].output
)) {
718 bbMotors
[motorIndex
].enabled
= true;
720 // Fill in motors structure for 4way access (XXX Should be refactored)
722 motors
[motorIndex
].enabled
= true;
726 static motorVTable_t bbVTable
= {
727 .postInit
= bbPostInit
,
728 .enable
= bbEnableMotors
,
729 .disable
= bbDisableMotors
,
730 .isMotorEnabled
= bbIsMotorEnabled
,
731 .telemetryWait
= bbTelemetryWait
,
732 .decodeTelemetry
= bbDecodeTelemetry
,
733 .updateInit
= bbUpdateInit
,
735 .writeInt
= bbWriteInt
,
736 .updateComplete
= bbUpdateComplete
,
737 .convertExternalToMotor
= dshotConvertFromExternal
,
738 .convertMotorToExternal
= dshotConvertToExternal
,
739 .shutdown
= bbShutdown
,
742 dshotBitbangStatus_e
dshotBitbangGetStatus(void)
747 motorDevice_t
*dshotBitbangDevInit(const motorDevConfig_t
*motorConfig
, uint8_t count
)
752 motorPwmProtocol
= motorConfig
->motorPwmProtocol
;
753 bbDevice
.vTable
= bbVTable
;
755 bbStatus
= DSHOT_BITBANG_STATUS_OK
;
757 #ifdef USE_DSHOT_TELEMETRY
758 useDshotTelemetry
= motorConfig
->useDshotTelemetry
;
761 memset(bbOutputBuffer
, 0, sizeof(bbOutputBuffer
));
763 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
764 const unsigned reorderedMotorIndex
= motorConfig
->motorOutputReordering
[motorIndex
];
765 const timerHardware_t
*timerHardware
= timerGetConfiguredByTag(motorConfig
->ioTags
[reorderedMotorIndex
]);
766 const IO_t io
= IOGetByTag(motorConfig
->ioTags
[reorderedMotorIndex
]);
768 uint8_t output
= motorConfig
->motorPwmInversion
? timerHardware
->output
^ TIMER_OUTPUT_INVERTED
: timerHardware
->output
;
769 bbPuPdMode
= (output
& TIMER_OUTPUT_INVERTED
) ? BB_GPIO_PULLDOWN
: BB_GPIO_PULLUP
;
771 #ifdef USE_DSHOT_TELEMETRY
772 if (useDshotTelemetry
) {
773 output
^= TIMER_OUTPUT_INVERTED
;
777 if (!IOIsFreeOrPreinit(io
)) {
778 /* not enough motors initialised for the mixer or a break in the motors */
779 bbDevice
.vTable
.write
= motorWriteNull
;
780 bbDevice
.vTable
.decodeTelemetry
= motorDecodeTelemetryNull
;
781 bbDevice
.vTable
.updateComplete
= motorUpdateCompleteNull
;
782 bbStatus
= DSHOT_BITBANG_STATUS_MOTOR_PIN_CONFLICT
;
786 int pinIndex
= IO_GPIOPinIdx(io
);
788 bbMotors
[motorIndex
].pinIndex
= pinIndex
;
789 bbMotors
[motorIndex
].io
= io
;
790 bbMotors
[motorIndex
].output
= output
;
792 bbMotors
[motorIndex
].iocfg
= IO_CONFIG(GPIO_Mode_OUT
, GPIO_Speed_50MHz
, GPIO_OType_PP
, bbPuPdMode
);
793 #elif defined(STM32F7) || defined(STM32G4) || defined(STM32H7)
794 bbMotors
[motorIndex
].iocfg
= IO_CONFIG(GPIO_MODE_OUTPUT_PP
, GPIO_SPEED_FREQ_LOW
, bbPuPdMode
);
797 IOInit(io
, OWNER_MOTOR
, RESOURCE_INDEX(motorIndex
));
798 IOConfigGPIO(io
, bbMotors
[motorIndex
].iocfg
);
799 if (output
& TIMER_OUTPUT_INVERTED
) {
805 // Fill in motors structure for 4way access (XXX Should be refactored)
806 motors
[motorIndex
].io
= bbMotors
[motorIndex
].io
;
812 #endif // USE_DSHOT_BB