Merge pull request #10406 from mikeller/fix_bitbanged_dshot_commands
[betaflight.git] / src / main / drivers / motor.c
blobe08cd39d3ea6b6d562a16c63fd6b4d24df30e269
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/>.
20 * Author: jflyper
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <string.h>
27 #include "platform.h"
29 #ifdef USE_MOTOR
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
43 #include "motor.h"
45 static FAST_RAM_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)
61 #ifdef USE_PWM_OUTPUT
62 if (motorDevice->enabled) {
63 #if defined(USE_DSHOT) && defined(USE_DSHOT_TELEMETRY)
64 if (!motorDevice->vTable.updateStart()) {
65 return;
67 #endif
68 for (int i = 0; i < motorDevice->count; i++) {
69 motorDevice->vTable.write(i, values[i]);
71 motorDevice->vTable.updateComplete();
73 #endif
76 unsigned motorDeviceCount(void)
78 return motorDevice->count;
81 motorVTable_t motorGetVTable(void)
83 return motorDevice->vTable;
86 // This is not motor generic anymore; should be moved to analog pwm module
87 static void analogInitEndpoints(const motorConfig_t *motorConfig, float outputLimit, float *outputLow, float *outputHigh, float *disarm, float *deadbandMotor3dHigh, float *deadbandMotor3dLow) {
88 if (featureIsEnabled(FEATURE_3D)) {
89 float outputLimitOffset = (flight3DConfig()->limit3d_high - flight3DConfig()->limit3d_low) * (1 - outputLimit) / 2;
90 *disarm = flight3DConfig()->neutral3d;
91 *outputLow = flight3DConfig()->limit3d_low + outputLimitOffset;
92 *outputHigh = flight3DConfig()->limit3d_high - outputLimitOffset;
93 *deadbandMotor3dHigh = flight3DConfig()->deadband3d_high;
94 *deadbandMotor3dLow = flight3DConfig()->deadband3d_low;
95 } else {
96 *disarm = motorConfig->mincommand;
97 *outputLow = motorConfig->minthrottle;
98 *outputHigh = motorConfig->maxthrottle - ((motorConfig->maxthrottle - motorConfig->minthrottle) * (1 - outputLimit));
102 bool checkMotorProtocolEnabled(const motorDevConfig_t *motorDevConfig, bool *isProtocolDshot)
104 bool enabled = false;
105 bool isDshot = false;
107 switch (motorDevConfig->motorPwmProtocol) {
108 case PWM_TYPE_STANDARD:
109 case PWM_TYPE_ONESHOT125:
110 case PWM_TYPE_ONESHOT42:
111 case PWM_TYPE_MULTISHOT:
112 case PWM_TYPE_BRUSHED:
113 enabled = true;
115 break;
117 #ifdef USE_DSHOT
118 case PWM_TYPE_DSHOT150:
119 case PWM_TYPE_DSHOT300:
120 case PWM_TYPE_DSHOT600:
121 case PWM_TYPE_PROSHOT1000:
122 enabled = true;
123 isDshot = true;
125 break;
126 #endif
127 default:
129 break;
132 if (isProtocolDshot) {
133 *isProtocolDshot = isDshot;
136 return enabled;
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);
153 #ifdef USE_DSHOT
154 else {
155 dshotInitEndpoints(motorConfig, outputLimit, outputLow, outputHigh, disarm, deadbandMotor3dHigh, deadbandMotor3dLow);
157 #endif
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()
173 motorDevice->vTable.postInit();
176 void motorPostInitNull(void)
180 static bool motorEnableNull(void)
182 return false;
185 static void motorDisableNull(void)
189 static bool motorIsEnabledNull(uint8_t index)
191 UNUSED(index);
193 return false;
196 bool motorUpdateStartNull(void)
198 return true;
201 void motorWriteNull(uint8_t index, float value)
203 UNUSED(index);
204 UNUSED(value);
207 static void motorWriteIntNull(uint8_t index, uint16_t value)
209 UNUSED(index);
210 UNUSED(value);
213 void motorUpdateCompleteNull(void)
217 static void motorShutdownNull(void)
221 static float motorConvertFromExternalNull(uint16_t value)
223 UNUSED(value);
224 return 0.0f ;
227 static uint16_t motorConvertToExternalNull(float value)
229 UNUSED(value);
230 return 0;
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,
249 .enabled = 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) {
263 memset(motors, 0, sizeof(motors));
265 bool useUnsyncedPwm = motorDevConfig->useUnsyncedPwm;
267 if (isMotorProtocolEnabled()) {
268 if (!isMotorProtocolDshot()) {
269 motorDevice = motorPwmDevInit(motorDevConfig, idlePulse, motorCount, useUnsyncedPwm);
271 #ifdef USE_DSHOT
272 else {
273 #ifdef USE_DSHOT_BITBANG
274 if (isDshotBitbangActive(motorDevConfig)) {
275 motorDevice = dshotBitbangDevInit(motorDevConfig, motorCount);
276 } else
277 #endif
279 motorDevice = dshotPwmDevInit(motorDevConfig, idlePulse, motorCount, useUnsyncedPwm);
282 #endif
285 if (motorDevice) {
286 motorDevice->count = motorCount;
287 motorDevice->initialized = true;
288 motorDevice->motorEnableTimeMs = 0;
289 motorDevice->enabled = false;
290 } else {
291 motorNullDevice.vTable = motorNullVTable;
292 motorDevice = &motorNullDevice;
296 void motorDisable(void)
298 motorDevice->vTable.disable();
299 motorDevice->enabled = false;
300 motorDevice->motorEnableTimeMs = 0;
303 void motorEnable(void)
305 if (motorDevice->initialized && motorDevice->vTable.enable()) {
306 motorDevice->enabled = true;
307 motorDevice->motorEnableTimeMs = millis();
311 bool motorIsEnabled(void)
313 return motorDevice->enabled;
316 bool motorIsMotorEnabled(uint8_t index)
318 return motorDevice->vTable.isMotorEnabled(index);
321 #ifdef USE_DSHOT
322 timeMs_t motorGetMotorEnableTimeMs(void)
324 return motorDevice->motorEnableTimeMs;
326 #endif
328 #ifdef USE_DSHOT_BITBANG
329 bool isDshotBitbangActive(const motorDevConfig_t *motorDevConfig) {
330 return motorDevConfig->useDshotBitbang == DSHOT_BITBANG_ON ||
331 (motorDevConfig->useDshotBitbang == DSHOT_BITBANG_AUTO && motorDevConfig->useDshotTelemetry && motorDevConfig->motorPwmProtocol != PWM_TYPE_PROSHOT1000);
333 #endif
334 #endif // USE_MOTOR