RMT timing rewritten

This commit is contained in:
Wastl Kraus 2025-09-12 23:14:34 +02:00
parent 034bd59b64
commit 8d07812548
2 changed files with 79 additions and 66 deletions

View File

@ -8,14 +8,28 @@
#include "DShotRMT.h"
// --- HELPERS ---
void printDShotResult(dshot_result_t &result, Stream &output)
{
output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg);
// Print telemetry data if available
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
{
output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm);
}
output.println();
}
// Timing parameters for each DShot mode
// Format: {frame_length_us, ticks_per_bit, ticks_one_high, ticks_one_low, ticks_zero_high, ticks_zero_low}
static constexpr dshot_timing_t DSHOT_TIMINGS[] = {
{0, 0, 0, 0, 0, 0}, // DSHOT_OFF
{128, 64, 48, 16, 24, 40}, // DSHOT150
{64, 32, 24, 8, 12, 20}, // DSHOT300
{32, 16, 12, 4, 6, 10}, // DSHOT600
{16, 8, 6, 2, 3, 5} // DSHOT1200
// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks}
static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = {
{0.00, 0.00},
{6.67, 5.00},
{3.33, 2.50},
{1.67, 1.25},
{0.83, 0.67}
};
// Constructor with GPIO number
@ -26,9 +40,10 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
_last_erpm_atomic(0),
_telemetry_ready_flag(false),
_frame_timer_us(0),
_timing_config(DSHOT_TIMINGS[mode]),
_dshot_timing(DSHOT_TIMING_US[mode]),
_rmt_ticks{0},
_last_throttle(DSHOT_CMD_MOTOR_STOP),
_last_transmission_time(0),
_last_transmission_time_us(0),
_parsed_packet(0),
_packet{0},
_bitPositions{0},
@ -42,8 +57,15 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
_transmit_config{},
_receive_config{}
{
// Calculate frame timing including switch/pause time
_frame_timer_us = _timing_config.frame_length_us + DSHOT_PAUSE_US;
// Convert DShot timings (us) to RMT ticks
_rmt_ticks.ticks_per_bit = static_cast<uint16_t>(_dshot_timing.bit_length_us * RMT_TICKS_PER_US);
_rmt_ticks.t1h_ticks = static_cast<uint16_t>(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US);
_rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0
_rmt_ticks.t1l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t1h_ticks;
_rmt_ticks.t0l_ticks = _rmt_ticks.ticks_per_bit - _rmt_ticks.t0h_ticks;
// Pause between frames is frame time in us, some padding and about 30 us is added by hardware
_frame_timer_us = (static_cast<uint32_t>(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US;
// Double frame time for bidirectional mode (includes response time)
if (_is_bidirectional)
@ -54,7 +76,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
// Constructor using pin number
DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional)
: DShotRMT((gpio_num_t)pin_nr, mode, is_bidirectional)
: DShotRMT(static_cast<gpio_num_t>(pin_nr), mode, is_bidirectional)
{
// Delegates to primary constructor with type cast
}
@ -152,7 +174,6 @@ dshot_result_t DShotRMT::_initTXChannel()
// Init RMT RX channel
dshot_result_t DShotRMT::_initRXChannel()
{
// Direct RMT symbol processing - Performance optimized
_rx_event_callbacks.on_recv_done = _rmt_rx_done_callback;
@ -258,7 +279,7 @@ dshot_result_t DShotRMT::sendCommand(uint16_t command)
dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
{
// Result container with unified structure
dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_ERPM, NO_DSHOT_RPM};
dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
// Check if bidirectional mode is enabled
if (!_is_bidirectional)
@ -277,7 +298,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
//
if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1)
{
uint8_t pole_pairs = max(MIN_POLE_PAIRS, (magnet_count / MAGNETS_PER_POLE_PAIR));
uint8_t pole_pairs = max(POLE_PAIRS_MIN, (magnet_count / MAGNETS_PER_POLE_PAIR));
uint32_t motor_rpm = (erpm / pole_pairs);
result.success = true;
@ -426,9 +447,9 @@ bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t
bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001;
symbols[i].level0 = _level0;
symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high;
symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks;
symbols[i].level1 = _level1;
symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low;
symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks;
}
return DSHOT_OK;
@ -487,7 +508,7 @@ bool DShotRMT::_timer_signal()
uint64_t current_time = esp_timer_get_time();
// Handle potential overflow
uint64_t elapsed = current_time - _last_transmission_time;
uint64_t elapsed = current_time - _last_transmission_time_us;
return elapsed >= _frame_timer_us;
}
@ -495,7 +516,7 @@ bool DShotRMT::_timer_signal()
// Reset transmission timer to current time
bool DShotRMT::_timer_reset()
{
_last_transmission_time = esp_timer_get_time();
_last_transmission_time_us = esp_timer_get_time();
return DSHOT_OK;
}
@ -546,17 +567,3 @@ void DShotRMT::printCpuInfo(Stream &output) const
output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz());
output.printf("APB Freq = %lu Hz\n", getApbFrequency());
}
// --- HELPERS ---
void printDShotResult(dshot_result_t &result, Stream &output)
{
output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg);
// Print telemetry data if available
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
{
output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm);
}
output.println();
}

View File

@ -20,29 +20,7 @@ static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
static constexpr auto DSHOT_THROTTLE_MIN = 48;
static constexpr auto DSHOT_THROTTLE_MAX = 2047;
static constexpr auto DSHOT_BITS_PER_FRAME = 16;
static constexpr auto DSHOT_PAUSE_US = 30; // Additional frame pause time
static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111;
static constexpr auto DSHOT_CRC_MASK = 0b0000000000001111;
static constexpr auto DSHOT_RX_TIMEOUT_MS = 2; // Never reached, just a timeeout
static constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14;
static constexpr auto MAGNETS_PER_POLE_PAIR = 2;
static constexpr auto MIN_POLE_PAIRS = 1;
static constexpr auto NO_DSHOT_ERPM = 0;
static constexpr auto NO_DSHOT_RPM = 0;
// RMT Configuration Constants
constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution
constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
constexpr auto RMT_BUFFER_SYMBOLS = 64;
constexpr auto RMT_QUEUE_DEPTH = 1;
// Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us.
// The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes.
constexpr uint32_t DSHOT_PULSE_MIN = 3000;
constexpr uint32_t DSHOT_PULSE_MAX = 60000;
// DShot Modes
typedef enum
@ -62,16 +40,22 @@ typedef struct
uint16_t checksum : 4;
} dshot_packet_t;
// DShot Timing Configuration
// DShot Timings
typedef struct
{
double bit_length_us;
double t1h_lenght_us;
} dshot_timing_us_t;
// RMT Ticks Configuration
typedef struct
{
uint32_t frame_length_us;
uint16_t ticks_per_bit;
uint16_t ticks_one_high;
uint16_t ticks_one_low;
uint16_t ticks_zero_high;
uint16_t ticks_zero_low;
} dshot_timing_t;
uint16_t t1h_ticks;
uint16_t t1l_ticks;
uint16_t t0h_ticks;
uint16_t t0l_ticks;
} rmt_ticks_t;
// Unified DShot result structure
typedef struct
@ -149,11 +133,12 @@ private:
dshot_mode_t _mode;
bool _is_bidirectional;
uint32_t _frame_timer_us;
const dshot_timing_t &_timing_config;
rmt_ticks_t _rmt_ticks;
const dshot_timing_us_t &_dshot_timing;
uint16_t _last_throttle;
// --- TIMING & PACKET VARIABLES ---
uint64_t _last_transmission_time;
uint64_t _last_transmission_time_us;
uint16_t _parsed_packet;
dshot_packet_t _packet;
uint8_t _bitPositions[DSHOT_BITS_PER_FRAME];
@ -193,13 +178,12 @@ private:
// -- CALLBACKS ---
rmt_rx_event_callbacks_t _rx_event_callbacks;
volatile rmt_symbol_word_t _rx_symbols_direct[GCR_BITS_PER_FRAME];
volatile uint16_t _last_erpm_atomic;
volatile bool _telemetry_ready_flag;
static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
// --- DSHOT DEFAULTS ---
static constexpr auto const DSHOT_TELEMETRY_INVALID = (0xffff);
static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111;
// --- CONSTANTS & ERROR MESSAGES ---
static constexpr bool DSHOT_OK = 0;
@ -226,4 +210,26 @@ private:
static constexpr char const *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!";
static constexpr char const *TIMING_CORRECTION = "Timing correction!";
// Configuration Constants
static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000;
static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111;
static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111;
static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution
static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond, based on the RMT resolution in MHz
static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2; // Never reached
static constexpr auto const DSHOT_PADDING_US = 3;
static constexpr auto const RMT_BUFFER_SYMBOLS = 64;
static constexpr auto const RMT_QUEUE_DEPTH = 1;
static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC)
static constexpr auto const POLE_PAIRS_MIN = 1;
static constexpr auto const MAGNETS_PER_POLE_PAIR = 2;
static constexpr auto const NO_DSHOT_TELEMETRY = 0;
// Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us.
// The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes.
static constexpr auto const DSHOT_PULSE_MIN = 3000;
static constexpr auto const DSHOT_PULSE_MAX = 60000;
};