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 "build/atomic.h"
33 #include "common/maths.h"
34 #include "common/time.h"
36 #include "config/feature.h"
38 #include "drivers/motor.h"
39 #include "drivers/timer.h"
41 #include "drivers/dshot_dpwm.h" // for motorDmaOutput_t, should be gone
42 #include "drivers/dshot_command.h"
43 #include "drivers/nvic.h"
44 #include "drivers/pwm_output.h" // for PWM_TYPE_* and others
46 #include "fc/rc_controls.h" // for flight3DConfig_t
52 void dshotInitEndpoints(const motorConfig_t
*motorConfig
, float outputLimit
, float *outputLow
, float *outputHigh
, float *disarm
, float *deadbandMotor3dHigh
, float *deadbandMotor3dLow
) {
53 float outputLimitOffset
= DSHOT_RANGE
* (1 - outputLimit
);
54 *disarm
= DSHOT_CMD_MOTOR_STOP
;
55 if (featureIsEnabled(FEATURE_3D
)) {
56 *outputLow
= DSHOT_MIN_THROTTLE
+ getDigitalIdleOffset(motorConfig
) * (DSHOT_3D_FORWARD_MIN_THROTTLE
- 1 - DSHOT_MIN_THROTTLE
);
57 *outputHigh
= DSHOT_MAX_THROTTLE
- outputLimitOffset
/ 2;
58 *deadbandMotor3dHigh
= DSHOT_3D_FORWARD_MIN_THROTTLE
+ getDigitalIdleOffset(motorConfig
) * (DSHOT_MAX_THROTTLE
- DSHOT_3D_FORWARD_MIN_THROTTLE
);
59 *deadbandMotor3dLow
= DSHOT_3D_FORWARD_MIN_THROTTLE
- 1 - outputLimitOffset
/ 2;
61 *outputLow
= DSHOT_MIN_THROTTLE
+ getDigitalIdleOffset(motorConfig
) * DSHOT_RANGE
;
62 *outputHigh
= DSHOT_MAX_THROTTLE
- outputLimitOffset
;
66 float dshotConvertFromExternal(uint16_t externalValue
)
70 externalValue
= constrain(externalValue
, PWM_RANGE_MIN
, PWM_RANGE_MAX
);
72 if (featureIsEnabled(FEATURE_3D
)) {
73 if (externalValue
== PWM_RANGE_MIDDLE
) {
74 motorValue
= DSHOT_CMD_MOTOR_STOP
;
75 } else if (externalValue
< PWM_RANGE_MIDDLE
) {
76 motorValue
= scaleRangef(externalValue
, PWM_RANGE_MIN
, PWM_RANGE_MIDDLE
- 1, DSHOT_3D_FORWARD_MIN_THROTTLE
- 1, DSHOT_MIN_THROTTLE
);
78 motorValue
= scaleRangef(externalValue
, PWM_RANGE_MIDDLE
+ 1, PWM_RANGE_MAX
, DSHOT_3D_FORWARD_MIN_THROTTLE
, DSHOT_MAX_THROTTLE
);
81 motorValue
= (externalValue
== PWM_RANGE_MIN
) ? DSHOT_CMD_MOTOR_STOP
: scaleRangef(externalValue
, PWM_RANGE_MIN
+ 1, PWM_RANGE_MAX
, DSHOT_MIN_THROTTLE
, DSHOT_MAX_THROTTLE
);
87 uint16_t dshotConvertToExternal(float motorValue
)
91 if (featureIsEnabled(FEATURE_3D
)) {
92 if (motorValue
== DSHOT_CMD_MOTOR_STOP
|| motorValue
< DSHOT_MIN_THROTTLE
) {
93 externalValue
= PWM_RANGE_MIDDLE
;
94 } else if (motorValue
<= DSHOT_3D_FORWARD_MIN_THROTTLE
- 1) {
95 externalValue
= scaleRangef(motorValue
, DSHOT_MIN_THROTTLE
, DSHOT_3D_FORWARD_MIN_THROTTLE
- 1, PWM_RANGE_MIDDLE
- 1, PWM_RANGE_MIN
);
97 externalValue
= scaleRangef(motorValue
, DSHOT_3D_FORWARD_MIN_THROTTLE
, DSHOT_MAX_THROTTLE
, PWM_RANGE_MIDDLE
+ 1, PWM_RANGE_MAX
);
100 externalValue
= (motorValue
< DSHOT_MIN_THROTTLE
) ? PWM_RANGE_MIN
: scaleRangef(motorValue
, DSHOT_MIN_THROTTLE
, DSHOT_MAX_THROTTLE
, PWM_RANGE_MIN
+ 1, PWM_RANGE_MAX
);
103 return lrintf(externalValue
);
106 FAST_CODE
uint16_t prepareDshotPacket(dshotProtocolControl_t
*pcb
)
110 ATOMIC_BLOCK(NVIC_PRIO_DSHOT_DMA
) {
111 packet
= (pcb
->value
<< 1) | (pcb
->requestTelemetry
? 1 : 0);
112 pcb
->requestTelemetry
= false; // reset telemetry request to make sure it's triggered only once in a row
117 unsigned csum_data
= packet
;
118 for (int i
= 0; i
< 3; i
++) {
119 csum
^= csum_data
; // xor data by nibbles
123 #ifdef USE_DSHOT_TELEMETRY
124 if (useDshotTelemetry
) {
129 packet
= (packet
<< 4) | csum
;
134 #ifdef USE_DSHOT_TELEMETRY
135 FAST_DATA_ZERO_INIT dshotTelemetryState_t dshotTelemetryState
;
137 uint16_t getDshotTelemetry(uint8_t index
)
139 return dshotTelemetryState
.motorState
[index
].telemetryValue
;
144 #ifdef USE_DSHOT_TELEMETRY_STATS
145 FAST_DATA_ZERO_INIT dshotTelemetryQuality_t dshotTelemetryQuality
[MAX_SUPPORTED_MOTORS
];
147 void updateDshotTelemetryQuality(dshotTelemetryQuality_t
*qualityStats
, bool packetValid
, timeMs_t currentTimeMs
)
149 uint8_t statsBucketIndex
= (currentTimeMs
/ DSHOT_TELEMETRY_QUALITY_BUCKET_MS
) % DSHOT_TELEMETRY_QUALITY_BUCKET_COUNT
;
150 if (statsBucketIndex
!= qualityStats
->lastBucketIndex
) {
151 qualityStats
->packetCountSum
-= qualityStats
->packetCountArray
[statsBucketIndex
];
152 qualityStats
->invalidCountSum
-= qualityStats
->invalidCountArray
[statsBucketIndex
];
153 qualityStats
->packetCountArray
[statsBucketIndex
] = 0;
154 qualityStats
->invalidCountArray
[statsBucketIndex
] = 0;
155 qualityStats
->lastBucketIndex
= statsBucketIndex
;
157 qualityStats
->packetCountSum
++;
158 qualityStats
->packetCountArray
[statsBucketIndex
]++;
160 qualityStats
->invalidCountSum
++;
161 qualityStats
->invalidCountArray
[statsBucketIndex
]++;
164 #endif // USE_DSHOT_TELEMETRY_STATS
168 // temporarily here, needs to be moved during refactoring
169 void validateAndfixMotorOutputReordering(uint8_t *array
, const unsigned size
)
171 bool invalid
= false;
173 for (unsigned i
= 0; i
< size
; i
++) {
174 if (array
[i
] >= size
) {
180 int valuesAsIndexes
[size
];
182 for (unsigned i
= 0; i
< size
; i
++) {
183 valuesAsIndexes
[i
] = -1;
187 for (unsigned i
= 0; i
< size
; i
++) {
188 if (-1 != valuesAsIndexes
[array
[i
]]) {
193 valuesAsIndexes
[array
[i
]] = array
[i
];
198 for (unsigned i
= 0; i
< size
; i
++) {