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/>.
32 #include "drivers/pwm_output.h"
33 #include "drivers/dshot.h"
34 #include "drivers/dshot_dpwm.h"
35 #include "drivers/motor.h"
39 // XXX TODO: Share a single region among dshotDmaBuffer and dshotBurstDmaBuffer
41 DSHOT_DMA_BUFFER_ATTRIBUTE DSHOT_DMA_BUFFER_UNIT dshotDmaBuffer
[MAX_SUPPORTED_MOTORS
][DSHOT_DMA_BUFFER_ALLOC_SIZE
];
44 DSHOT_DMA_BUFFER_ATTRIBUTE DSHOT_DMA_BUFFER_UNIT dshotBurstDmaBuffer
[MAX_DMA_TIMERS
][DSHOT_DMA_BUFFER_SIZE
* 4];
48 FAST_DATA_ZERO_INIT
bool useBurstDshot
= false;
50 #ifdef USE_DSHOT_TELEMETRY
51 FAST_DATA_ZERO_INIT
bool useDshotTelemetry
= false;
54 FAST_DATA_ZERO_INIT loadDmaBufferFn
*loadDmaBuffer
;
56 FAST_CODE_NOINLINE
uint8_t loadDmaBufferDshot(uint32_t *dmaBuffer
, int stride
, uint16_t packet
)
59 for (i
= 0; i
< 16; i
++) {
60 dmaBuffer
[i
* stride
] = (packet
& 0x8000) ? MOTOR_BIT_1
: MOTOR_BIT_0
; // MSB first
63 dmaBuffer
[i
++ * stride
] = 0;
64 dmaBuffer
[i
++ * stride
] = 0;
66 return DSHOT_DMA_BUFFER_SIZE
;
69 FAST_CODE_NOINLINE
uint8_t loadDmaBufferProshot(uint32_t *dmaBuffer
, int stride
, uint16_t packet
)
72 for (i
= 0; i
< 4; i
++) {
73 dmaBuffer
[i
* stride
] = PROSHOT_BASE_SYMBOL
+ ((packet
& 0xF000) >> 12) * PROSHOT_BIT_WIDTH
; // Most significant nibble first
74 packet
<<= 4; // Shift 4 bits
76 dmaBuffer
[i
++ * stride
] = 0;
77 dmaBuffer
[i
++ * stride
] = 0;
79 return PROSHOT_DMA_BUFFER_SIZE
;
82 uint32_t getDshotHz(motorPwmProtocolTypes_e pwmProtocolType
)
84 switch (pwmProtocolType
) {
85 case(PWM_TYPE_PROSHOT1000
):
86 return MOTOR_PROSHOT1000_HZ
;
87 case(PWM_TYPE_DSHOT600
):
88 return MOTOR_DSHOT600_HZ
;
89 case(PWM_TYPE_DSHOT300
):
90 return MOTOR_DSHOT300_HZ
;
92 case(PWM_TYPE_DSHOT150
):
93 return MOTOR_DSHOT150_HZ
;
97 static void dshotPwmShutdown(void)
99 // DShot signal is only generated if write to motors happen,
100 // and that is prevented by enabled checking at write.
101 // Hence there's no special processing required here.
105 static void dshotPwmDisableMotors(void)
107 // No special processing required
111 static bool dshotPwmEnableMotors(void)
113 for (int i
= 0; i
< dshotPwmDevice
.count
; i
++) {
114 motorDmaOutput_t
*motor
= getMotorDmaOutput(i
);
115 const IO_t motorIO
= IOGetByTag(motor
->timerHardware
->tag
);
116 IOConfigGPIOAF(motorIO
, motor
->iocfg
, motor
->timerHardware
->alternateFunction
);
119 // No special processing required
123 static bool dshotPwmIsMotorEnabled(uint8_t index
)
125 return motors
[index
].enabled
;
128 static FAST_CODE
void dshotWriteInt(uint8_t index
, uint16_t value
)
130 pwmWriteDshotInt(index
, value
);
133 static FAST_CODE
void dshotWrite(uint8_t index
, float value
)
135 pwmWriteDshotInt(index
, lrintf(value
));
138 static motorVTable_t dshotPwmVTable
= {
139 .postInit
= motorPostInitNull
,
140 .enable
= dshotPwmEnableMotors
,
141 .disable
= dshotPwmDisableMotors
,
142 .isMotorEnabled
= dshotPwmIsMotorEnabled
,
143 .updateStart
= motorUpdateStartNull
, // May be updated after copying
145 .writeInt
= dshotWriteInt
,
146 .updateComplete
= pwmCompleteDshotMotorUpdate
,
147 .convertExternalToMotor
= dshotConvertFromExternal
,
148 .convertMotorToExternal
= dshotConvertToExternal
,
149 .shutdown
= dshotPwmShutdown
,
152 FAST_DATA_ZERO_INIT motorDevice_t dshotPwmDevice
;
154 motorDevice_t
*dshotPwmDevInit(const motorDevConfig_t
*motorConfig
, uint16_t idlePulse
, uint8_t motorCount
, bool useUnsyncedPwm
)
157 UNUSED(useUnsyncedPwm
);
159 dshotPwmDevice
.vTable
= dshotPwmVTable
;
161 #ifdef USE_DSHOT_TELEMETRY
162 useDshotTelemetry
= motorConfig
->useDshotTelemetry
;
163 dshotPwmDevice
.vTable
.updateStart
= pwmStartDshotMotorUpdate
;
166 switch (motorConfig
->motorPwmProtocol
) {
167 case PWM_TYPE_PROSHOT1000
:
168 loadDmaBuffer
= loadDmaBufferProshot
;
170 case PWM_TYPE_DSHOT600
:
171 case PWM_TYPE_DSHOT300
:
172 case PWM_TYPE_DSHOT150
:
173 loadDmaBuffer
= loadDmaBufferDshot
;
174 #ifdef USE_DSHOT_DMAR
175 useBurstDshot
= motorConfig
->useBurstDshot
== DSHOT_DMAR_ON
||
176 (motorConfig
->useBurstDshot
== DSHOT_DMAR_AUTO
&& !motorConfig
->useDshotTelemetry
);
181 for (int motorIndex
= 0; motorIndex
< MAX_SUPPORTED_MOTORS
&& motorIndex
< motorCount
; motorIndex
++) {
182 const unsigned reorderedMotorIndex
= motorConfig
->motorOutputReordering
[motorIndex
];
183 const ioTag_t tag
= motorConfig
->ioTags
[reorderedMotorIndex
];
184 const timerHardware_t
*timerHardware
= timerAllocate(tag
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
186 if (timerHardware
!= NULL
) {
187 motors
[motorIndex
].io
= IOGetByTag(tag
);
188 IOInit(motors
[motorIndex
].io
, OWNER_MOTOR
, RESOURCE_INDEX(reorderedMotorIndex
));
190 if (pwmDshotMotorHardwareConfig(timerHardware
,
193 motorConfig
->motorPwmProtocol
,
194 motorConfig
->motorPwmInversion
? timerHardware
->output
^ TIMER_OUTPUT_INVERTED
: timerHardware
->output
)) {
195 motors
[motorIndex
].enabled
= true;
201 /* not enough motors initialised for the mixer or a break in the motors */
202 dshotPwmDevice
.vTable
.write
= motorWriteNull
;
203 dshotPwmDevice
.vTable
.updateComplete
= motorUpdateCompleteNull
;
205 /* TODO: block arming and add reason system cannot arm */
209 return &dshotPwmDevice
;