RMT timing rewritten
This commit is contained in:
parent
034bd59b64
commit
8d07812548
|
|
@ -8,14 +8,28 @@
|
||||||
|
|
||||||
#include "DShotRMT.h"
|
#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
|
// 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}
|
// Format: {frame_length_ticks, ticks_per_bit, t1h_ticks, t1l_ticks, t0h_ticks, t0l_ticks}
|
||||||
static constexpr dshot_timing_t DSHOT_TIMINGS[] = {
|
static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = {
|
||||||
{0, 0, 0, 0, 0, 0}, // DSHOT_OFF
|
{0.00, 0.00},
|
||||||
{128, 64, 48, 16, 24, 40}, // DSHOT150
|
{6.67, 5.00},
|
||||||
{64, 32, 24, 8, 12, 20}, // DSHOT300
|
{3.33, 2.50},
|
||||||
{32, 16, 12, 4, 6, 10}, // DSHOT600
|
{1.67, 1.25},
|
||||||
{16, 8, 6, 2, 3, 5} // DSHOT1200
|
{0.83, 0.67}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructor with GPIO number
|
// 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),
|
_last_erpm_atomic(0),
|
||||||
_telemetry_ready_flag(false),
|
_telemetry_ready_flag(false),
|
||||||
_frame_timer_us(0),
|
_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_throttle(DSHOT_CMD_MOTOR_STOP),
|
||||||
_last_transmission_time(0),
|
_last_transmission_time_us(0),
|
||||||
_parsed_packet(0),
|
_parsed_packet(0),
|
||||||
_packet{0},
|
_packet{0},
|
||||||
_bitPositions{0},
|
_bitPositions{0},
|
||||||
|
|
@ -42,8 +57,15 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
||||||
_transmit_config{},
|
_transmit_config{},
|
||||||
_receive_config{}
|
_receive_config{}
|
||||||
{
|
{
|
||||||
// Calculate frame timing including switch/pause time
|
// Convert DShot timings (us) to RMT ticks
|
||||||
_frame_timer_us = _timing_config.frame_length_us + DSHOT_PAUSE_US;
|
_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)
|
// Double frame time for bidirectional mode (includes response time)
|
||||||
if (_is_bidirectional)
|
if (_is_bidirectional)
|
||||||
|
|
@ -54,7 +76,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
||||||
|
|
||||||
// Constructor using pin number
|
// Constructor using pin number
|
||||||
DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional)
|
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
|
// Delegates to primary constructor with type cast
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +174,6 @@ dshot_result_t DShotRMT::_initTXChannel()
|
||||||
// Init RMT RX channel
|
// Init RMT RX channel
|
||||||
dshot_result_t DShotRMT::_initRXChannel()
|
dshot_result_t DShotRMT::_initRXChannel()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Direct RMT symbol processing - Performance optimized
|
// Direct RMT symbol processing - Performance optimized
|
||||||
_rx_event_callbacks.on_recv_done = _rmt_rx_done_callback;
|
_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)
|
dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
||||||
{
|
{
|
||||||
// Result container with unified structure
|
// 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
|
// Check if bidirectional mode is enabled
|
||||||
if (!_is_bidirectional)
|
if (!_is_bidirectional)
|
||||||
|
|
@ -277,7 +298,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
||||||
//
|
//
|
||||||
if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1)
|
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);
|
uint32_t motor_rpm = (erpm / pole_pairs);
|
||||||
|
|
||||||
result.success = true;
|
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;
|
bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001;
|
||||||
symbols[i].level0 = _level0;
|
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].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;
|
return DSHOT_OK;
|
||||||
|
|
@ -487,7 +508,7 @@ bool DShotRMT::_timer_signal()
|
||||||
uint64_t current_time = esp_timer_get_time();
|
uint64_t current_time = esp_timer_get_time();
|
||||||
|
|
||||||
// Handle potential overflow
|
// 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;
|
return elapsed >= _frame_timer_us;
|
||||||
}
|
}
|
||||||
|
|
@ -495,7 +516,7 @@ bool DShotRMT::_timer_signal()
|
||||||
// Reset transmission timer to current time
|
// Reset transmission timer to current time
|
||||||
bool DShotRMT::_timer_reset()
|
bool DShotRMT::_timer_reset()
|
||||||
{
|
{
|
||||||
_last_transmission_time = esp_timer_get_time();
|
_last_transmission_time_us = esp_timer_get_time();
|
||||||
|
|
||||||
return DSHOT_OK;
|
return DSHOT_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -546,17 +567,3 @@ void DShotRMT::printCpuInfo(Stream &output) const
|
||||||
output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz());
|
output.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz());
|
||||||
output.printf("APB Freq = %lu Hz\n", getApbFrequency());
|
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();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -20,29 +20,7 @@ static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
|
||||||
static constexpr auto DSHOT_THROTTLE_MIN = 48;
|
static constexpr auto DSHOT_THROTTLE_MIN = 48;
|
||||||
static constexpr auto DSHOT_THROTTLE_MAX = 2047;
|
static constexpr auto DSHOT_THROTTLE_MAX = 2047;
|
||||||
static constexpr auto DSHOT_BITS_PER_FRAME = 16;
|
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 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
|
// DShot Modes
|
||||||
typedef enum
|
typedef enum
|
||||||
|
|
@ -62,16 +40,22 @@ typedef struct
|
||||||
uint16_t checksum : 4;
|
uint16_t checksum : 4;
|
||||||
} dshot_packet_t;
|
} 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
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t frame_length_us;
|
|
||||||
uint16_t ticks_per_bit;
|
uint16_t ticks_per_bit;
|
||||||
uint16_t ticks_one_high;
|
uint16_t t1h_ticks;
|
||||||
uint16_t ticks_one_low;
|
uint16_t t1l_ticks;
|
||||||
uint16_t ticks_zero_high;
|
uint16_t t0h_ticks;
|
||||||
uint16_t ticks_zero_low;
|
uint16_t t0l_ticks;
|
||||||
} dshot_timing_t;
|
} rmt_ticks_t;
|
||||||
|
|
||||||
// Unified DShot result structure
|
// Unified DShot result structure
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|
@ -149,11 +133,12 @@ private:
|
||||||
dshot_mode_t _mode;
|
dshot_mode_t _mode;
|
||||||
bool _is_bidirectional;
|
bool _is_bidirectional;
|
||||||
uint32_t _frame_timer_us;
|
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;
|
uint16_t _last_throttle;
|
||||||
|
|
||||||
// --- TIMING & PACKET VARIABLES ---
|
// --- TIMING & PACKET VARIABLES ---
|
||||||
uint64_t _last_transmission_time;
|
uint64_t _last_transmission_time_us;
|
||||||
uint16_t _parsed_packet;
|
uint16_t _parsed_packet;
|
||||||
dshot_packet_t _packet;
|
dshot_packet_t _packet;
|
||||||
uint8_t _bitPositions[DSHOT_BITS_PER_FRAME];
|
uint8_t _bitPositions[DSHOT_BITS_PER_FRAME];
|
||||||
|
|
@ -193,13 +178,12 @@ private:
|
||||||
|
|
||||||
// -- CALLBACKS ---
|
// -- CALLBACKS ---
|
||||||
rmt_rx_event_callbacks_t _rx_event_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 uint16_t _last_erpm_atomic;
|
||||||
volatile bool _telemetry_ready_flag;
|
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);
|
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 ---
|
// --- DSHOT DEFAULTS ---
|
||||||
static constexpr auto const DSHOT_TELEMETRY_INVALID = (0xffff);
|
static constexpr auto const DSHOT_TELEMETRY_INVALID = 0b1111111111111111;
|
||||||
|
|
||||||
// --- CONSTANTS & ERROR MESSAGES ---
|
// --- CONSTANTS & ERROR MESSAGES ---
|
||||||
static constexpr bool DSHOT_OK = 0;
|
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 *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
|
||||||
static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!";
|
static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!";
|
||||||
static constexpr char const *TIMING_CORRECTION = "Timing correction!";
|
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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue