Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dshot RPM Telemetry Refactoring #13012

Merged
merged 2 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/blackbox/blackbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ static void loadMainState(timeUs_t currentTimeUs)

#ifdef USE_DSHOT_TELEMETRY
for (int i = 0; i < motorCount; i++) {
blackboxCurrent->erpm[i] = getDshotTelemetry(i);
blackboxCurrent->erpm[i] = getDshotErpm(i);
}
#endif

Expand Down
2 changes: 0 additions & 2 deletions src/main/build/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
* If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdint.h>

#include "platform.h"

#include "debug.h"
Expand Down
4 changes: 2 additions & 2 deletions src/main/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -6119,8 +6119,8 @@ static void cliDshotTelemetryInfo(const char *cmdName, char *cmdline)
#endif

for (uint8_t i = 0; i < getMotorCount(); i++) {
const uint16_t erpm = getDshotTelemetry(i);
const uint16_t rpm = erpmToRpm(erpm);
const uint16_t erpm = getDshotErpm(i);
const uint16_t rpm = lrintf(getDshotRpm(i));

cliPrintf("%5d %c%c%c%c%c %6d %6d %6d",
i + 1,
Expand Down
5 changes: 4 additions & 1 deletion src/main/common/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ typedef uint32_t timeUs_t;
#define TIMEZONE_OFFSET_MINUTES_MIN -780 // -13 hours
#define TIMEZONE_OFFSET_MINUTES_MAX 780 // +13 hours

#define SECONDS_PER_MINUTE 60.0f

static inline timeDelta_t cmpTimeUs(timeUs_t a, timeUs_t b) { return (timeDelta_t)(a - b); }
static inline int32_t cmpTimeCycles(uint32_t a, uint32_t b) { return (int32_t)(a - b); }

Expand Down Expand Up @@ -101,4 +103,5 @@ bool rtcSetDateTime(dateTime_t *dt);

void rtcPersistWrite(int16_t offsetMinutes);
bool rtcPersistRead(rtcTime_t *t);
#endif

#endif // USE_RTC_TIME
138 changes: 98 additions & 40 deletions src/main/drivers/dshot.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
* Follows the extended dshot telemetry documentation found at https://github.com/bird-sanctuary/extended-dshot-telemetry
*/

#include <stdbool.h>
#include <stdint.h>
#include <float.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>

#include "platform.h"
Expand All @@ -34,6 +34,7 @@
#include "build/debug.h"
#include "build/atomic.h"

#include "common/filter.h"
#include "common/maths.h"

#include "config/feature.h"
Expand All @@ -46,9 +47,14 @@

#include "flight/mixer.h"

#include "pg/rpm_filter.h"

#include "rx/rx.h"

#include "dshot.h"

#define ERPM_PER_LSB 100.0f

void dshotInitEndpoints(const motorConfig_t *motorConfig, float outputLimit, float *outputLow, float *outputHigh, float *disarm, float *deadbandMotor3dHigh, float *deadbandMotor3dLow)
{
float outputLimitOffset = DSHOT_RANGE * (1 - outputLimit);
Expand Down Expand Up @@ -134,9 +140,30 @@ FAST_CODE uint16_t prepareDshotPacket(dshotProtocolControl_t *pcb)

#ifdef USE_DSHOT_TELEMETRY


FAST_DATA_ZERO_INIT dshotTelemetryState_t dshotTelemetryState;

FAST_DATA_ZERO_INIT static pt1Filter_t motorFreqLpf[MAX_SUPPORTED_MOTORS];
FAST_DATA_ZERO_INIT static float motorFrequencyHz[MAX_SUPPORTED_MOTORS];
FAST_DATA_ZERO_INIT static float minMotorFrequencyHz;
FAST_DATA_ZERO_INIT static float erpmToHz;
FAST_DATA_ZERO_INIT static float dshotRpmAverage;
FAST_DATA_ZERO_INIT static float dshotRpm[MAX_SUPPORTED_MOTORS];

void initDshotTelemetry(const timeUs_t looptimeUs)
{
// if bidirectional DShot is not available
if (!motorConfig()->dev.useDshotTelemetry) {
return;
}

// init LPFs for RPM data
for (int i = 0; i < getMotorCount(); i++) {
pt1FilterInit(&motorFreqLpf[i], pt1FilterGain(rpmFilterConfig()->rpm_filter_lpf_hz, looptimeUs * 1e-6f));
}

erpmToHz = ERPM_PER_LSB / SECONDS_PER_MINUTE / (motorConfig()->motorPoleCount / 2.0f);
}

static uint32_t dshot_decode_eRPM_telemetry_value(uint16_t value)
{
// eRPM range
Expand Down Expand Up @@ -259,41 +286,78 @@ static void dshotUpdateTelemetryData(uint8_t motorIndex, dshotTelemetryType_t ty
}
}

uint16_t getDshotTelemetry(uint8_t index)
FAST_CODE_NOINLINE void updateDshotTelemetry(void)
{
// Process telemetry in case it haven´t been processed yet
if (dshotTelemetryState.rawValueState == DSHOT_RAW_VALUE_STATE_NOT_PROCESSED) {
const unsigned motorCount = motorDeviceCount();
uint32_t erpmTotal = 0;
uint32_t rpmSamples = 0;

// Decode all telemetry data now to discharge interrupt from this task
for (uint8_t k = 0; k < motorCount; k++) {
dshotTelemetryType_t type;
uint32_t value;

dshot_decode_telemetry_value(k, &value, &type);

if (value != DSHOT_TELEMETRY_INVALID) {
dshotUpdateTelemetryData(k, type, value);

if (type == DSHOT_TELEMETRY_TYPE_eRPM) {
erpmTotal += value;
rpmSamples++;
}
if (!motorConfig()->dev.useDshotTelemetry) {
return;
}

// Only process telemetry in case it hasn´t been processed yet
if (dshotTelemetryState.rawValueState != DSHOT_RAW_VALUE_STATE_NOT_PROCESSED) {
return;
}

const unsigned motorCount = motorDeviceCount();
uint32_t erpmTotal = 0;
uint32_t rpmSamples = 0;

// Decode all telemetry data now to discharge interrupt from this task
for (uint8_t k = 0; k < motorCount; k++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use int here also ?

dshotTelemetryType_t type;
uint32_t value;

dshot_decode_telemetry_value(k, &value, &type);

if (value != DSHOT_TELEMETRY_INVALID) {
dshotUpdateTelemetryData(k, type, value);

if (type == DSHOT_TELEMETRY_TYPE_eRPM) {
dshotRpm[k] = erpmToRpm(value);
erpmTotal += value;
rpmSamples++;
}
}
}

// Update average
if (rpmSamples > 0) {
dshotTelemetryState.averageErpm = (uint16_t)(erpmTotal / rpmSamples);
}
// Update average
if (rpmSamples > 0) {
dshotRpmAverage = erpmToRpm(erpmTotal) / (float)rpmSamples;
}

// Set state to processed
dshotTelemetryState.rawValueState = DSHOT_RAW_VALUE_STATE_PROCESSED;
// update filtered rotation speed of motors for features (e.g. "RPM filter")
minMotorFrequencyHz = FLT_MAX;
for (int motor = 0; motor < getMotorCount(); motor++) {
motorFrequencyHz[motor] = pt1FilterApply(&motorFreqLpf[motor], erpmToHz * getDshotErpm(motor));
minMotorFrequencyHz = MIN(minMotorFrequencyHz, motorFrequencyHz[motor]);
}

return dshotTelemetryState.motorState[index].telemetryData[DSHOT_TELEMETRY_TYPE_eRPM];
// Set state to processed
dshotTelemetryState.rawValueState = DSHOT_RAW_VALUE_STATE_PROCESSED;
}

uint16_t getDshotErpm(uint8_t motorIndex)
{
return dshotTelemetryState.motorState[motorIndex].telemetryData[DSHOT_TELEMETRY_TYPE_eRPM];
}

float getDshotRpm(uint8_t motorIndex)
{
return dshotRpm[motorIndex];
}

float getDshotRpmAverage(void)
{
return dshotRpmAverage;
}

float getMotorFrequencyHz(uint8_t motorIndex)
{
return motorFrequencyHz[motorIndex];
}

float getMinMotorFrequencyHz(void)
{
return minMotorFrequencyHz;
}

bool isDshotMotorTelemetryActive(uint8_t motorIndex)
Expand All @@ -320,21 +384,15 @@ void dshotCleanTelemetryData(void)
memset(&dshotTelemetryState, 0, sizeof(dshotTelemetryState));
}


uint32_t getDshotAverageRpm(void)
{
return erpmToRpm(dshotTelemetryState.averageErpm);
}

#endif // USE_DSHOT_TELEMETRY

#if defined(USE_ESC_SENSOR) || defined(USE_DSHOT_TELEMETRY)

// Used with serial esc telem as well as dshot telem
uint32_t erpmToRpm(uint16_t erpm)
float erpmToRpm(uint32_t erpm)
{
// rpm = (erpm * 100) / (motorConfig()->motorPoleCount / 2)
return (erpm * 200) / motorConfig()->motorPoleCount;
// rpm = (erpm * ERPM_PER_LSB) / (motorConfig()->motorPoleCount / 2)
return erpm * erpmToHz * SECONDS_PER_MINUTE;
}

#endif // USE_ESC_SENSOR || USE_DSHOT_TELEMETRY
Expand Down
29 changes: 20 additions & 9 deletions src/main/drivers/dshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

#pragma once

#include <stdbool.h>
#include <stdint.h>

#include "common/time.h"

#include "pg/motor.h"
Expand Down Expand Up @@ -66,13 +69,14 @@ typedef enum dshotTelemetryType_e {
DSHOT_TELEMETRY_TYPE_DEBUG2 = 5,
DSHOT_TELEMETRY_TYPE_DEBUG3 = 6,
DSHOT_TELEMETRY_TYPE_STATE_EVENTS = 7,
DSHOT_TELEMETRY_TYPE_COUNT = 8
DSHOT_TELEMETRY_TYPE_COUNT
} dshotTelemetryType_t;

typedef enum dshotRawValueState_e {
DSHOT_RAW_VALUE_STATE_INVALID = 0,
DSHOT_RAW_VALUE_STATE_INVALID = 0,
DSHOT_RAW_VALUE_STATE_NOT_PROCESSED = 1,
DSHOT_RAW_VALUE_STATE_PROCESSED = 2
DSHOT_RAW_VALUE_STATE_PROCESSED = 2,
DSHOT_RAW_VALUE_STATE_COUNT
} dshotRawValueState_t;

typedef struct dshotProtocolControl_s {
Expand Down Expand Up @@ -103,7 +107,6 @@ typedef struct dshotTelemetryState_s {
uint32_t readCount;
dshotTelemetryMotorState_t motorState[MAX_SUPPORTED_MOTORS];
uint32_t inputBuffer[MAX_GCR_EDGES];
uint16_t averageErpm;
dshotRawValueState_t rawValueState;
} dshotTelemetryState_t;

Expand All @@ -112,15 +115,23 @@ extern dshotTelemetryState_t dshotTelemetryState;
#ifdef USE_DSHOT_TELEMETRY_STATS
void updateDshotTelemetryQuality(dshotTelemetryQuality_t *qualityStats, bool packetValid, timeMs_t currentTimeMs);
#endif
#endif
#endif // USE_DSHOT_TELEMETRY

void initDshotTelemetry(const timeUs_t looptimeUs);
void updateDshotTelemetry(void);

uint16_t getDshotErpm(uint8_t motorIndex);
float getDshotRpm(uint8_t motorIndex);
float getDshotRpmAverage(void);
float getMotorFrequencyHz(uint8_t motorIndex);
float getMinMotorFrequencyHz(void);

uint16_t getDshotTelemetry(uint8_t index);
uint32_t erpmToRpm(uint16_t erpm);
uint32_t getDshotAverageRpm(void);
bool isDshotMotorTelemetryActive(uint8_t motorIndex);
bool isDshotTelemetryActive(void);
void dshotCleanTelemetryData(void);

float erpmToRpm(uint32_t erpm);

int16_t getDshotTelemetryMotorInvalidPercent(uint8_t motorIndex);

void validateAndfixMotorOutputReordering(uint8_t *array, const unsigned size);
void dshotCleanTelemetryData(void);
4 changes: 3 additions & 1 deletion src/main/fc/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1270,8 +1270,10 @@ FAST_CODE bool pidLoopReady(void)

FAST_CODE void taskFiltering(timeUs_t currentTimeUs)
{
#ifdef USE_DSHOT_TELEMETRY
updateDshotTelemetry(); // decode and update Dshot telemetry
KarateBrot marked this conversation as resolved.
Show resolved Hide resolved
#endif
gyroFiltering(currentTimeUs);

}

// Function for loop trigger
Expand Down
6 changes: 6 additions & 0 deletions src/main/fc/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "drivers/camera_control_impl.h"
#include "drivers/compass/compass.h"
#include "drivers/dma.h"
#include "drivers/dshot.h"
#include "drivers/exti.h"
#include "drivers/flash.h"
#include "drivers/inverter.h"
Expand Down Expand Up @@ -694,6 +695,11 @@ void init(void)
// Now reset the targetLooptime as it's possible for the validation to change the pid_process_denom
gyroSetTargetLooptime(pidConfig()->pid_process_denom);

#ifdef USE_DSHOT_TELEMETRY
// Initialize the motor frequency filter now that we have a target looptime
initDshotTelemetry(gyro.targetLooptime);
#endif

// Finally initialize the gyro filtering
gyroInitFilters();

Expand Down
2 changes: 1 addition & 1 deletion src/main/flight/mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ static void calculateThrottleAndCurrentMotorEndpoints(timeUs_t currentTimeUs)
if (mixerRuntime.dynIdleMinRps > 0.0f) {
const float maxIncrease = isAirmodeActivated()
? mixerRuntime.dynIdleMaxIncrease : mixerRuntime.dynIdleStartIncrease;
float minRps = getMinMotorFrequency();
float minRps = getMinMotorFrequencyHz();
DEBUG_SET(DEBUG_DYN_IDLE, 3, lrintf(minRps * 10.0f));
float rpsError = mixerRuntime.dynIdleMinRps - minRps;
// PT1 type lowpass delay and smoothing for D
Expand Down