...hotfix: memory consumption

...update callback
...performance optimizations
... fix memory consumption
...update timers
...clean up
...update GCR logic
...some more memory testing
...optimize encoder logic
This commit is contained in:
Wastl Kraus 2025-09-03 11:16:14 +02:00 committed by GitHub
parent ff673e475e
commit d9d8dce251
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 81 additions and 84 deletions

View File

@ -7,7 +7,6 @@
*/
#include "DShotRMT.h"
#include <driver/rmt_tx.h>
// 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}
@ -19,19 +18,24 @@ static constexpr dshot_timing_t DSHOT_TIMINGS[] = {
{16, 8, 6, 2, 3, 5} // DSHOT1200
};
// Primary constructor with GPIO number
// Constructor with GPIO number
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
: _gpio(gpio),
_mode(mode),
_is_bidirectional(is_bidirectional),
_frame_timer_us(0),
_timing_config(DSHOT_TIMINGS[mode]),
_rmt_tx_channel(nullptr),
_rmt_rx_channel(nullptr),
_dshot_encoder(nullptr),
_last_transmission_time(0),
_last_erpm(0),
_parsed_packet(0),
_packet{0},
_last_transmission_time(0),
_rmt_tx_channel(nullptr),
_rmt_rx_channel(nullptr),
_dshot_encoder(nullptr),
_tx_channel_config{},
_rx_channel_config{},
_transmit_config{},
_receive_config{},
_rx_queue(nullptr)
{
// Calculate frame timing including switch/pause time
@ -84,14 +88,14 @@ DShotRMT::~DShotRMT()
// Initialize DShotRMT
uint16_t DShotRMT::begin()
{
// Initialize TX channel
// Init TX channel
if (!_initTXChannel())
{
_dshot_log(TX_INIT_FAILED);
return DSHOT_ERROR;
}
// Initialize RX channel only if bidirectional mode is enabled
// Init RX channel
if (_is_bidirectional)
{
if (!_initRXChannel())
@ -101,7 +105,7 @@ uint16_t DShotRMT::begin()
}
}
// Initialize DShot encoder
// Init DShot encoder
if (_initDShotEncoder() != DSHOT_OK)
{
_dshot_log(ENCODER_INIT_FAILED);
@ -111,7 +115,7 @@ uint16_t DShotRMT::begin()
return DSHOT_OK;
}
// Initialize RMT TX channel
// Init RMT TX channel
bool DShotRMT::_initTXChannel()
{
// Configure TX channel
@ -121,7 +125,7 @@ bool DShotRMT::_initTXChannel()
_tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
_tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH;
// Configure transmission
// Config RMT TX
_transmit_config.loop_count = 0; // No automatic loops - real-time calculation
_transmit_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag
@ -135,23 +139,23 @@ bool DShotRMT::_initTXChannel()
return (rmt_enable(_rmt_tx_channel) == DSHOT_OK);
}
// Initialize RMT RX channel
// Init RMT RX channel
bool DShotRMT::_initRXChannel()
{
// Create a queue to receive data from the RX callback
// Create a queue for RX callback data
_rx_queue = xQueueCreate(RMT_QUEUE_DEPTH, sizeof(rmt_rx_done_event_data_t));
if (_rx_queue == nullptr)
{
return DSHOT_ERROR;
}
// Configure RX channel parameters
// Config RMT RX
_rx_channel_config.gpio_num = _gpio;
_rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
_rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
_rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
// Configure reception parameters
// Config RMT RX parameters
_receive_config.signal_range_min_ns = DSHOT_PULSE_MIN;
_receive_config.signal_range_max_ns = DSHOT_PULSE_MAX;
@ -162,9 +166,10 @@ bool DShotRMT::_initRXChannel()
return DSHOT_ERROR;
}
// Register callback for reception
_rx_event_cbs.on_recv_done = _rmt_rx_done_callback;
if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_cbs, _rx_queue) != DSHOT_OK)
// Register RX callback
_rx_event_callbacks.on_recv_done = _rmt_rx_done_callback;
if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, _rx_queue) != DSHOT_OK)
{
_dshot_log(RX_INIT_FAILED);
return DSHOT_ERROR;
@ -174,14 +179,14 @@ bool DShotRMT::_initRXChannel()
}
// Callback for RMT RX
bool IRAM_ATTR DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rx_chan, const rmt_rx_done_event_data_t *edata, void *user_data)
bool IRAM_ATTR DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
// Get the queue handle
// Init RX buffer
QueueHandle_t rx_queue = (QueueHandle_t)user_data;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Send the event data to the queue
xQueueSendFromISR(rx_queue, edata, &xHigherPriorityTaskWoken);
// Copy callback data into RX buffer
xQueueGenericSendFromISR(rx_queue, edata, &xHigherPriorityTaskWoken, queueSEND_TO_BACK);
return (xHigherPriorityTaskWoken == pdTRUE);
}
@ -340,20 +345,26 @@ uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
// Enable RMT RX before RMT TX
if (_is_bidirectional)
{
rmt_receive(_rmt_rx_channel, _rx_symbols, sizeof(_rx_symbols), &_receive_config);
// Performance reasons
rmt_symbol_word_t rx_symbols[DSHOT_BITS_PER_FRAME];
rmt_receive(_rmt_rx_channel, rx_symbols, sizeof(rx_symbols), &_receive_config);
// Disable RMT RX for sending
rmt_disable(_rmt_rx_channel);
}
// Local for performance
rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME];
// Encode DShot packet into RMT symbols
_encodeDShotFrame(packet, _tx_symbols);
_encodeDShotFrame(packet, tx_symbols);
// Calculate transmission data size
size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
// Perform RMT transmission
uint16_t result = rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, tx_size_bytes, &_transmit_config);
uint16_t result = rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_transmit_config);
if (result != DSHOT_OK)
{
@ -377,31 +388,19 @@ uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
// Encode DShot packet into RMT symbol format (placed in IRAM for performance)
bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols)
{
// Parse packet to 16-bit format
_parsed_packet = _parseDShotPacket(packet);
// Convert each bit to RMT symbol
const uint16_t level0 = _is_bidirectional ? 0 : 1;
const uint16_t level1 = _is_bidirectional ? 1 : 0;
for (int i = 0; i < DSHOT_BITS_PER_FRAME; i++)
{
// Extract bit from packet
// Decode MSB
bool bit = (_parsed_packet >> (DSHOT_BITS_PER_FRAME - 1 - i)) & 0b0000000000000001;
if (_is_bidirectional)
{
// Bidirectional DShot uses inverted levels - Idle HIGH
symbols[i].level0 = 0;
symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high;
symbols[i].level1 = 1;
symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low;
}
else
{
// Standard DShot levels - Idle LOW
symbols[i].level0 = 1;
symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high;
symbols[i].level1 = 0;
symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low;
}
symbols[i].level0 = level0;
symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high;
symbols[i].level1 = level1;
symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low;
}
return DSHOT_OK;
@ -410,30 +409,33 @@ bool IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_sym
// Decode received RMT symbols
uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
{
// DShot answer is GCR encoded?
// DShot answer is GCR encoded.
// GCR decoding: bit_N = gcr_bit_N ^ gcr_bit_(N-1)
uint32_t raw_gcr_data = 0;
// Reconstruct the raw GCR frame from RMT symbols
for (size_t i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
// Based on DShot bidirectional protocol, idle state is high,
// so the first duration is a low pulse.
// Bit 1: long low pulse, short high pulse
// Bit 0: short low pulse, long high pulse
for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i)
{
// Based on DShot bidirectional protocol, idle state is high, so first duration is low pulse.
// Bit 1: long low pulse, short high pulse
// Bit 0: short low pulse, long high pulse
// Check which duration is longer to determine if it's a '1' bit
bool bit_is_one = symbols[i].duration0 > symbols[i].duration1;
raw_gcr_data = (raw_gcr_data << 1) | bit_is_one;
}
// Extract the 10-bit data from the GCR frame
uint16_t gcr_data = (raw_gcr_data >> 5) & 0b0000001111111111; // Mask for 10 bits
// GCR decoding over the "throttle" bits
// GCR encoding rule is: bit_n = gcr_bit_n ^ gcr_bit_(n-1)
uint16_t gcr_data = (raw_gcr_data >> 5) & 0b000000001111111111;
uint16_t received_data = gcr_data ^ (gcr_data >> 1);
// Extract CRC from gcr answer
uint16_t received_crc = raw_gcr_data & 0b0000000000001111;
// Extract CRC from gcr answer (4 bits)
uint16_t received_crc = raw_gcr_data & 0b0000000000001111; // Mask for 4 bits
// Calculate expected CRC using the new, centralized function
uint16_t data_for_crc = (received_data << 1) | 1; // Telemetry request bit is always 1 for bidirectional
// Telemetry request bit is always 1 for bidirectional
uint16_t data_for_crc = (received_data << 1) | 1;
uint16_t calculated_crc = _calculateCRC(data_for_crc);
// Validate CRC
@ -450,10 +452,10 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
// Check if enough time has passed for next transmission
bool IRAM_ATTR DShotRMT::_timer_signal()
{
uint32_t current_time = micros();
uint64_t current_time = esp_timer_get_time();
// Handle potential overflow
uint32_t elapsed = current_time - _last_transmission_time;
uint64_t elapsed = current_time - _last_transmission_time;
return elapsed >= _frame_timer_us;
}
@ -461,7 +463,7 @@ bool IRAM_ATTR DShotRMT::_timer_signal()
// Reset transmission timer to current time
bool DShotRMT::_timer_reset()
{
_last_transmission_time = micros();
_last_transmission_time = esp_timer_get_time();
return DSHOT_OK;
}
@ -473,10 +475,10 @@ void DShotRMT::printDshotInfo(Stream &output) const
// Current DShot mode
output.printf("Current Mode: DSHOT%d\n",
_mode == DSHOT150 ? 150 : _mode == DSHOT300 ? 300
: _mode == DSHOT600 ? 600
: _mode == DSHOT1200 ? 1200
: 0);
_mode == DSHOT150 ? 150 :
_mode == DSHOT300 ? 300 :
_mode == DSHOT600 ? 600 :
_mode == DSHOT1200 ? 1200 : 0);
output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO");

View File

@ -13,8 +13,6 @@
#include <driver/gpio.h>
#include <driver/rmt_tx.h>
#include <driver/rmt_rx.h>
#include <iostream>
#include <string_view>
// DShot Protocol Constants
constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
@ -29,6 +27,7 @@ constexpr auto DSHOT_RX_TIMEOUT_MS = 2;
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 GCR_BITS_PER_FRAME = 20;
constexpr auto RMT_BUFFER_SYMBOLS = 64;
constexpr auto RMT_QUEUE_DEPTH = 1;
@ -70,7 +69,7 @@ typedef struct
class DShotRMT
{
public:
// Primary constructor with GPIO enum
// Constructor with GPIO enum
explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16,
dshot_mode_t mode = DSHOT300,
bool is_bidirectional = false);
@ -121,7 +120,7 @@ private:
const dshot_timing_t &_timing_config;
// --- TIMING & STATE VARIABLES ---
uint32_t _last_transmission_time;
uint64_t _last_transmission_time;
uint16_t _last_erpm;
uint16_t _parsed_packet;
dshot_packet_t _packet;
@ -137,10 +136,6 @@ private:
rmt_transmit_config_t _transmit_config;
rmt_receive_config_t _receive_config;
// --- RMT DATA BUFFERS ---
rmt_symbol_word_t _tx_symbols[RMT_BUFFER_SYMBOLS];
rmt_symbol_word_t _rx_symbols[RMT_BUFFER_SYMBOLS];
// --- INITS ---
bool _initTXChannel();
bool _initRXChannel();
@ -162,24 +157,24 @@ private:
// -- CALLBACKS ---
QueueHandle_t _rx_queue;
rmt_rx_event_callbacks_t _rx_event_cbs;
static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rx_chan, const rmt_rx_done_event_data_t *edata, void *user_data);
rmt_rx_event_callbacks_t _rx_event_callbacks;
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);
// --- ERROR HANDLING & LOGGING ---
void _dshot_log(std::string_view msg) { std::cerr << msg << '\n'; }
void _dshot_log(const char *msg, Stream &output = Serial) { output.println(msg); }
// --- CONSTANTS & ERROR MESSAGES ---
static constexpr uint16_t DSHOT_OK = 0;
static constexpr uint16_t DSHOT_ERROR = 1;
static constexpr std::string_view NEW_LINE = " ";
static constexpr std::string_view TX_INIT_FAILED = "Failed to initialize TX channel!";
static constexpr std::string_view RX_INIT_FAILED = "Failed to initialize RX channel!";
static constexpr std::string_view ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!";
static constexpr std::string_view CRC_CHECK_FAILED = "RX CRC Check failed!";
static constexpr std::string_view THROTTLE_NOT_IN_RANGE = "Throttle value not in range (48 - 2047)!";
static constexpr std::string_view COMMAND_NOT_VALID = "Not a valid DShot Command (0 - 47)!";
static constexpr std::string_view BIDIR_NOT_ENABLED = "Bidirectional DShot support not enabled!";
static constexpr std::string_view RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!";
static constexpr std::string_view PACKET_BUILD_ERROR = "Value too big for DShot Packet!";
static constexpr char *NEW_LINE = " ";
static constexpr char *TX_INIT_FAILED = "Failed to initialize TX channel!";
static constexpr char *RX_INIT_FAILED = "Failed to initialize RX channel!";
static constexpr char *ENCODER_INIT_FAILED = "Failed to initialize DShot encoder!";
static constexpr char *CRC_CHECK_FAILED = "RX CRC Check failed!";
static constexpr char *THROTTLE_NOT_IN_RANGE = "Throttle value not in range (48 - 2047)!";
static constexpr char *COMMAND_NOT_VALID = "Not a valid DShot Command (0 - 47)!";
static constexpr char *BIDIR_NOT_ENABLED = "Bidirectional DShot support not enabled!";
static constexpr char *RX_RMT_RECEIVER_ERROR = "RX RMT receiver error!";
static constexpr char *PACKET_BUILD_ERROR = "Value too big for DShot Packet!";
};