1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-19 06:15:16 +03:00
betaflight/src/main/drivers/dshot.c
iso9660 0dd6d4d47a Unified all eRPM calculations into one single function
Fixed some review findings

Unified dshot average rpm calculations into one single function

Renamed calcEscRpm to erpmToRpm, and moved function to dshot.c

Removed unused esc_sensor.h header file from dshot.c

Removed esc_sensor.h header from modules that no longer needs it

Average RPM calculated by demmand only when rpm data is updated

Renamed rpm to averageRpm and fixed a bug

Update average rpm when telemetry data is received

Removed blank line

Fixed review findings

Fixed return values for erpmToRpm and getDshotAverageRpm so rpm value doesn't truncate

Restored osd_esc_rmp_alarm setting. This setting is used to set an alarm to notify when rpm  goes down a specified threshold. Rpm can go over 109krpm (1s 26kv motors setup), but a low rpm alarm doesn't have to be set to a so high value so at this time [0-32767] seems an acceptable range

Rebased to master
2022-09-17 00:26:17 +02:00

402 lines
12 KiB
C

/*
* This file is part of Cleanflight and Betaflight.
*
* Cleanflight and Betaflight are free software. You can redistribute
* this software and/or modify this software under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* Cleanflight and Betaflight are distributed in the hope that they
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*
* Author: jflyper
*/
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "platform.h"
#ifdef USE_DSHOT
#include "build/atomic.h"
#include "common/maths.h"
#include "config/feature.h"
#include "drivers/motor.h"
#include "drivers/timer.h"
#include "drivers/dshot_command.h"
#include "drivers/nvic.h"
#include "flight/mixer.h"
#include "rx/rx.h"
#include "dshot.h"
void dshotInitEndpoints(const motorConfig_t *motorConfig, float outputLimit, float *outputLow, float *outputHigh, float *disarm, float *deadbandMotor3dHigh, float *deadbandMotor3dLow) {
float outputLimitOffset = DSHOT_RANGE * (1 - outputLimit);
*disarm = DSHOT_CMD_MOTOR_STOP;
if (featureIsEnabled(FEATURE_3D)) {
*outputLow = DSHOT_MIN_THROTTLE + getDigitalIdleOffset(motorConfig) * (DSHOT_3D_FORWARD_MIN_THROTTLE - 1 - DSHOT_MIN_THROTTLE);
*outputHigh = DSHOT_MAX_THROTTLE - outputLimitOffset / 2;
*deadbandMotor3dHigh = DSHOT_3D_FORWARD_MIN_THROTTLE + getDigitalIdleOffset(motorConfig) * (DSHOT_MAX_THROTTLE - DSHOT_3D_FORWARD_MIN_THROTTLE);
*deadbandMotor3dLow = DSHOT_3D_FORWARD_MIN_THROTTLE - 1 - outputLimitOffset / 2;
} else {
*outputLow = DSHOT_MIN_THROTTLE + getDigitalIdleOffset(motorConfig) * DSHOT_RANGE;
*outputHigh = DSHOT_MAX_THROTTLE - outputLimitOffset;
}
}
float dshotConvertFromExternal(uint16_t externalValue)
{
float motorValue;
externalValue = constrain(externalValue, PWM_RANGE_MIN, PWM_RANGE_MAX);
if (featureIsEnabled(FEATURE_3D)) {
if (externalValue == PWM_RANGE_MIDDLE) {
motorValue = DSHOT_CMD_MOTOR_STOP;
} else if (externalValue < PWM_RANGE_MIDDLE) {
motorValue = scaleRangef(externalValue, PWM_RANGE_MIN, PWM_RANGE_MIDDLE - 1, DSHOT_3D_FORWARD_MIN_THROTTLE - 1, DSHOT_MIN_THROTTLE);
} else {
motorValue = scaleRangef(externalValue, PWM_RANGE_MIDDLE + 1, PWM_RANGE_MAX, DSHOT_3D_FORWARD_MIN_THROTTLE, DSHOT_MAX_THROTTLE);
}
} else {
motorValue = (externalValue == PWM_RANGE_MIN) ? DSHOT_CMD_MOTOR_STOP : scaleRangef(externalValue, PWM_RANGE_MIN + 1, PWM_RANGE_MAX, DSHOT_MIN_THROTTLE, DSHOT_MAX_THROTTLE);
}
return motorValue;
}
uint16_t dshotConvertToExternal(float motorValue)
{
float externalValue;
if (featureIsEnabled(FEATURE_3D)) {
if (motorValue == DSHOT_CMD_MOTOR_STOP || motorValue < DSHOT_MIN_THROTTLE) {
externalValue = PWM_RANGE_MIDDLE;
} else if (motorValue <= DSHOT_3D_FORWARD_MIN_THROTTLE - 1) {
externalValue = scaleRangef(motorValue, DSHOT_MIN_THROTTLE, DSHOT_3D_FORWARD_MIN_THROTTLE - 1, PWM_RANGE_MIDDLE - 1, PWM_RANGE_MIN);
} else {
externalValue = scaleRangef(motorValue, DSHOT_3D_FORWARD_MIN_THROTTLE, DSHOT_MAX_THROTTLE, PWM_RANGE_MIDDLE + 1, PWM_RANGE_MAX);
}
} else {
externalValue = (motorValue < DSHOT_MIN_THROTTLE) ? PWM_RANGE_MIN : scaleRangef(motorValue, DSHOT_MIN_THROTTLE, DSHOT_MAX_THROTTLE, PWM_RANGE_MIN + 1, PWM_RANGE_MAX);
}
return lrintf(externalValue);
}
FAST_CODE uint16_t prepareDshotPacket(dshotProtocolControl_t *pcb)
{
uint16_t packet;
ATOMIC_BLOCK(NVIC_PRIO_DSHOT_DMA) {
packet = (pcb->value << 1) | (pcb->requestTelemetry ? 1 : 0);
pcb->requestTelemetry = false; // reset telemetry request to make sure it's triggered only once in a row
}
// compute checksum
unsigned csum = 0;
unsigned csum_data = packet;
for (int i = 0; i < 3; i++) {
csum ^= csum_data; // xor data by nibbles
csum_data >>= 4;
}
// append checksum
#ifdef USE_DSHOT_TELEMETRY
if (useDshotTelemetry) {
csum = ~csum;
}
#endif
csum &= 0xf;
packet = (packet << 4) | csum;
return packet;
}
#ifdef USE_DSHOT_TELEMETRY
FAST_DATA_ZERO_INIT dshotTelemetryState_t dshotTelemetryState;
uint16_t getDshotTelemetry(uint8_t index)
{
return dshotTelemetryState.motorState[index].telemetryData[DSHOT_TELEMETRY_TYPE_eRPM];
}
bool isDshotMotorTelemetryActive(uint8_t motorIndex)
{
return (dshotTelemetryState.motorState[motorIndex].telemetryTypes & (1 << DSHOT_TELEMETRY_TYPE_eRPM)) != 0;
}
bool isDshotTelemetryActive(void)
{
const unsigned motorCount = motorDeviceCount();
if (motorCount) {
for (unsigned i = 0; i < motorCount; i++) {
if (!isDshotMotorTelemetryActive(i)) {
return false;
}
}
return true;
}
return false;
}
dshotTelemetryType_t dshot_get_telemetry_type_to_decode(uint8_t motorIndex)
{
dshotTelemetryType_t type;
// Prepare the allowed telemetry to be read
if ((dshotTelemetryState.motorState[motorIndex].telemetryTypes & DSHOT_EXTENDED_TELEMETRY_MASK) != 0) {
// Allow decoding all kind of telemetry frames
type = DSHOT_TELEMETRY_TYPE_COUNT;
} else if (dshotCommandGetCurrent(motorIndex) == DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE) {
// No empty command queue check needed because responses are always originated after a request
// Always checking the current existing request
// Allow decoding only extended telemetry enable frame (during arming)
type = DSHOT_TELEMETRY_TYPE_STATE_EVENTS;
} else {
// Allow decoding only eRPM telemetry frame
type = DSHOT_TELEMETRY_TYPE_eRPM;
}
return type;
}
FAST_CODE void dshotUpdateTelemetryData(uint8_t motorIndex, dshotTelemetryType_t type, uint16_t value)
{
// Update telemetry data
dshotTelemetryState.motorState[motorIndex].telemetryData[type] = value;
dshotTelemetryState.motorState[motorIndex].telemetryTypes |= (1 << type);
// Update max temp
if ((type == DSHOT_TELEMETRY_TYPE_TEMPERATURE) && (value > dshotTelemetryState.motorState[motorIndex].maxTemp)) {
dshotTelemetryState.motorState[motorIndex].maxTemp = value;
}
}
uint32_t erpmToRpm(uint16_t erpm)
{
// rpm = (erpm * 100) / (motorConfig()->motorPoleCount / 2)
return (erpm * 200) / motorConfig()->motorPoleCount;
}
uint32_t getDshotAverageRpm(void)
{
return dshotTelemetryState.averageRpm;
}
#endif // USE_DSHOT_TELEMETRY
#ifdef USE_DSHOT_TELEMETRY_STATS
FAST_DATA_ZERO_INIT dshotTelemetryQuality_t dshotTelemetryQuality[MAX_SUPPORTED_MOTORS];
int16_t getDshotTelemetryMotorInvalidPercent(uint8_t motorIndex)
{
int16_t invalidPercent = 0;
if (isDshotMotorTelemetryActive(motorIndex)) {
const uint32_t totalCount = dshotTelemetryQuality[motorIndex].packetCountSum;
const uint32_t invalidCount = dshotTelemetryQuality[motorIndex].invalidCountSum;
if (totalCount > 0) {
invalidPercent = lrintf(invalidCount * 10000.0f / totalCount);
}
} else {
invalidPercent = 10000; // 100.00%
}
return invalidPercent;
}
void updateDshotTelemetryQuality(dshotTelemetryQuality_t *qualityStats, bool packetValid, timeMs_t currentTimeMs)
{
uint8_t statsBucketIndex = (currentTimeMs / DSHOT_TELEMETRY_QUALITY_BUCKET_MS) % DSHOT_TELEMETRY_QUALITY_BUCKET_COUNT;
if (statsBucketIndex != qualityStats->lastBucketIndex) {
qualityStats->packetCountSum -= qualityStats->packetCountArray[statsBucketIndex];
qualityStats->invalidCountSum -= qualityStats->invalidCountArray[statsBucketIndex];
qualityStats->packetCountArray[statsBucketIndex] = 0;
qualityStats->invalidCountArray[statsBucketIndex] = 0;
qualityStats->lastBucketIndex = statsBucketIndex;
}
qualityStats->packetCountSum++;
qualityStats->packetCountArray[statsBucketIndex]++;
if (!packetValid) {
qualityStats->invalidCountSum++;
qualityStats->invalidCountArray[statsBucketIndex]++;
}
}
#endif // USE_DSHOT_TELEMETRY_STATS
#endif // USE_DSHOT
// temporarily here, needs to be moved during refactoring
void validateAndfixMotorOutputReordering(uint8_t *array, const unsigned size)
{
bool invalid = false;
for (unsigned i = 0; i < size; i++) {
if (array[i] >= size) {
invalid = true;
break;
}
}
int valuesAsIndexes[size];
for (unsigned i = 0; i < size; i++) {
valuesAsIndexes[i] = -1;
}
if (!invalid) {
for (unsigned i = 0; i < size; i++) {
if (-1 != valuesAsIndexes[array[i]]) {
invalid = true;
break;
}
valuesAsIndexes[array[i]] = array[i];
}
}
if (invalid) {
for (unsigned i = 0; i < size; i++) {
array[i] = i;
}
}
}
static uint32_t dshot_decode_eRPM_telemetry_value(uint32_t value)
{
// eRPM range
if (value == 0x0fff) {
return 0;
}
// Convert value to 16 bit from the GCR telemetry format (eeem mmmm mmmm)
value = (value & 0x000001ff) << ((value & 0xfffffe00) >> 9);
if (!value) {
return DSHOT_TELEMETRY_INVALID;
}
// Convert period to erpm * 100
return (1000000 * 60 / 100 + value / 2) / value;
}
uint32_t dshot_decode_telemetry_value(uint32_t value, dshotTelemetryType_t *type)
{
uint32_t decoded;
switch (*type) {
case DSHOT_TELEMETRY_TYPE_eRPM:
// Expect only eRPM telemetry
decoded = dshot_decode_eRPM_telemetry_value(value);
break;
case DSHOT_TELEMETRY_TYPE_STATE_EVENTS:
// Expect an extended telemetry enable frame
if (value == 0x0E00) {
// Decode
decoded = 0;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_STATE_EVENTS;
} else {
// Unexpected frame
decoded = DSHOT_TELEMETRY_INVALID;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_eRPM;
}
break;
default:
// Extended DSHOT telemetry
switch (value & 0x0f00) {
case 0x0200:
// Temperature range (in degree Celsius, just like Blheli_32 and KISS)
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_TEMPERATURE;
break;
case 0x0400:
// Voltage range (0-63,75V step 0,25V)
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_VOLTAGE;
break;
case 0x0600:
// Current range (0-255A step 1A)
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_CURRENT;
break;
case 0x0800:
// Debug 1 value
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_DEBUG1;
break;
case 0x0A00:
// Debug 2 value
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_DEBUG2;
break;
case 0x0C00:
// Debug 3 value
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_DEBUG3;
break;
case 0x0E00:
// State / events
decoded = value & 0x00ff;
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_STATE_EVENTS;
break;
default:
// Decode as eRPM
decoded = dshot_decode_eRPM_telemetry_value(value);
// Set telemetry type
*type = DSHOT_TELEMETRY_TYPE_eRPM;
break;
}
break;
}
return decoded;
}