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/>.
31 #include "common/maths.h"
33 #include "config/feature.h"
35 #include "drivers/dshot.h" // for DSHOT_ constants in initEscEndpoints; may be gone in the future
36 #include "drivers/pwm_output.h" // for PWM_TYPE_* and others
37 #include "drivers/time.h"
38 #include "drivers/dshot_bitbang.h"
39 #include "drivers/dshot_dpwm.h"
41 #include "fc/rc_controls.h" // for flight3DConfig_t
45 static FAST_DATA_ZERO_INIT motorDevice_t
*motorDevice
;
47 static bool motorProtocolEnabled
= false;
48 static bool motorProtocolDshot
= false;
50 void motorShutdown(void)
52 motorDevice
->vTable
.shutdown();
53 motorDevice
->enabled
= false;
54 motorDevice
->motorEnableTimeMs
= 0;
55 motorDevice
->initialized
= false;
56 delayMicroseconds(1500);
59 void motorWriteAll(float *values
)
62 if (motorDevice
->enabled
) {
63 #if defined(USE_DSHOT) && defined(USE_DSHOT_TELEMETRY)
64 if (!motorDevice
->vTable
.updateStart()) {
68 for (int i
= 0; i
< motorDevice
->count
; i
++) {
69 motorDevice
->vTable
.write(i
, values
[i
]);
71 motorDevice
->vTable
.updateComplete();
78 unsigned motorDeviceCount(void)
80 return motorDevice
->count
;
83 motorVTable_t
motorGetVTable(void)
85 return motorDevice
->vTable
;
88 // This is not motor generic anymore; should be moved to analog pwm module
89 static void analogInitEndpoints(const motorConfig_t
*motorConfig
, float outputLimit
, float *outputLow
, float *outputHigh
, float *disarm
, float *deadbandMotor3dHigh
, float *deadbandMotor3dLow
)
91 if (featureIsEnabled(FEATURE_3D
)) {
92 float outputLimitOffset
= (flight3DConfig()->limit3d_high
- flight3DConfig()->limit3d_low
) * (1 - outputLimit
) / 2;
93 *disarm
= flight3DConfig()->neutral3d
;
94 *outputLow
= flight3DConfig()->limit3d_low
+ outputLimitOffset
;
95 *outputHigh
= flight3DConfig()->limit3d_high
- outputLimitOffset
;
96 *deadbandMotor3dHigh
= flight3DConfig()->deadband3d_high
;
97 *deadbandMotor3dLow
= flight3DConfig()->deadband3d_low
;
99 *disarm
= motorConfig
->mincommand
;
100 *outputLow
= motorConfig
->minthrottle
;
101 *outputHigh
= motorConfig
->maxthrottle
- ((motorConfig
->maxthrottle
- motorConfig
->minthrottle
) * (1 - outputLimit
));
105 bool checkMotorProtocolEnabled(const motorDevConfig_t
*motorDevConfig
, bool *isProtocolDshot
)
107 bool enabled
= false;
108 bool isDshot
= false;
110 switch (motorDevConfig
->motorPwmProtocol
) {
111 case PWM_TYPE_STANDARD
:
112 case PWM_TYPE_ONESHOT125
:
113 case PWM_TYPE_ONESHOT42
:
114 case PWM_TYPE_MULTISHOT
:
115 case PWM_TYPE_BRUSHED
:
120 case PWM_TYPE_DSHOT150
:
121 case PWM_TYPE_DSHOT300
:
122 case PWM_TYPE_DSHOT600
:
123 case PWM_TYPE_PROSHOT1000
:
132 if (isProtocolDshot
) {
133 *isProtocolDshot
= isDshot
;
139 static void checkMotorProtocol(const motorDevConfig_t
*motorDevConfig
)
141 motorProtocolEnabled
= checkMotorProtocolEnabled(motorDevConfig
, &motorProtocolDshot
);
144 // End point initialization is called from mixerInit before motorDevInit; can't use vtable...
145 void motorInitEndpoints(const motorConfig_t
*motorConfig
, float outputLimit
, float *outputLow
, float *outputHigh
, float *disarm
, float *deadbandMotor3dHigh
, float *deadbandMotor3dLow
)
147 checkMotorProtocol(&motorConfig
->dev
);
149 if (isMotorProtocolEnabled()) {
150 if (!isMotorProtocolDshot()) {
151 analogInitEndpoints(motorConfig
, outputLimit
, outputLow
, outputHigh
, disarm
, deadbandMotor3dHigh
, deadbandMotor3dLow
);
155 dshotInitEndpoints(motorConfig
, outputLimit
, outputLow
, outputHigh
, disarm
, deadbandMotor3dHigh
, deadbandMotor3dLow
);
161 float motorConvertFromExternal(uint16_t externalValue
)
163 return motorDevice
->vTable
.convertExternalToMotor(externalValue
);
166 uint16_t motorConvertToExternal(float motorValue
)
168 return motorDevice
->vTable
.convertMotorToExternal(motorValue
);
171 void motorPostInit(void)
173 motorDevice
->vTable
.postInit();
176 void motorPostInitNull(void)
180 static bool motorEnableNull(void)
185 static void motorDisableNull(void)
189 static bool motorIsEnabledNull(uint8_t index
)
196 bool motorUpdateStartNull(void)
201 void motorWriteNull(uint8_t index
, float value
)
207 static void motorWriteIntNull(uint8_t index
, uint16_t value
)
213 void motorUpdateCompleteNull(void)
217 static void motorShutdownNull(void)
221 static float motorConvertFromExternalNull(uint16_t value
)
227 static uint16_t motorConvertToExternalNull(float value
)
233 static const motorVTable_t motorNullVTable
= {
234 .postInit
= motorPostInitNull
,
235 .enable
= motorEnableNull
,
236 .disable
= motorDisableNull
,
237 .isMotorEnabled
= motorIsEnabledNull
,
238 .updateStart
= motorUpdateStartNull
,
239 .write
= motorWriteNull
,
240 .writeInt
= motorWriteIntNull
,
241 .updateComplete
= motorUpdateCompleteNull
,
242 .convertExternalToMotor
= motorConvertFromExternalNull
,
243 .convertMotorToExternal
= motorConvertToExternalNull
,
244 .shutdown
= motorShutdownNull
,
247 static motorDevice_t motorNullDevice
= {
248 .initialized
= false,
252 bool isMotorProtocolEnabled(void)
254 return motorProtocolEnabled
;
257 bool isMotorProtocolDshot(void)
259 return motorProtocolDshot
;
262 void motorDevInit(const motorDevConfig_t
*motorDevConfig
, uint16_t idlePulse
, uint8_t motorCount
)
264 memset(motors
, 0, sizeof(motors
));
266 bool useUnsyncedPwm
= motorDevConfig
->useUnsyncedPwm
;
268 if (isMotorProtocolEnabled()) {
269 if (!isMotorProtocolDshot()) {
270 motorDevice
= motorPwmDevInit(motorDevConfig
, idlePulse
, motorCount
, useUnsyncedPwm
);
274 #ifdef USE_DSHOT_BITBANG
275 if (isDshotBitbangActive(motorDevConfig
)) {
276 motorDevice
= dshotBitbangDevInit(motorDevConfig
, motorCount
);
280 motorDevice
= dshotPwmDevInit(motorDevConfig
, idlePulse
, motorCount
, useUnsyncedPwm
);
287 motorDevice
->count
= motorCount
;
288 motorDevice
->initialized
= true;
289 motorDevice
->motorEnableTimeMs
= 0;
290 motorDevice
->enabled
= false;
292 motorNullDevice
.vTable
= motorNullVTable
;
293 motorDevice
= &motorNullDevice
;
297 void motorDisable(void)
299 motorDevice
->vTable
.disable();
300 motorDevice
->enabled
= false;
301 motorDevice
->motorEnableTimeMs
= 0;
304 void motorEnable(void)
306 if (motorDevice
->initialized
&& motorDevice
->vTable
.enable()) {
307 motorDevice
->enabled
= true;
308 motorDevice
->motorEnableTimeMs
= millis();
312 bool motorIsEnabled(void)
314 return motorDevice
->enabled
;
317 bool motorIsMotorEnabled(uint8_t index
)
319 return motorDevice
->vTable
.isMotorEnabled(index
);
323 timeMs_t
motorGetMotorEnableTimeMs(void)
325 return motorDevice
->motorEnableTimeMs
;
329 #ifdef USE_DSHOT_BITBANG
330 bool isDshotBitbangActive(const motorDevConfig_t
*motorDevConfig
)
333 return motorDevConfig
->useDshotBitbang
== DSHOT_BITBANG_ON
||
334 (motorDevConfig
->useDshotBitbang
== DSHOT_BITBANG_AUTO
&& motorDevConfig
->useDshotTelemetry
&& motorDevConfig
->motorPwmProtocol
!= PWM_TYPE_PROSHOT1000
);
336 return motorDevConfig
->useDshotBitbang
== DSHOT_BITBANG_ON
||
337 (motorDevConfig
->useDshotBitbang
== DSHOT_BITBANG_AUTO
&& motorDevConfig
->motorPwmProtocol
!= PWM_TYPE_PROSHOT1000
);
342 float getDigitalIdleOffset(const motorConfig_t
*motorConfig
)
344 return CONVERT_PARAMETER_TO_PERCENT(motorConfig
->digitalIdleOffsetValue
* 0.01f
);