...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:
parent
ff673e475e
commit
d9d8dce251
128
DShotRMT.cpp
128
DShotRMT.cpp
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
37
DShotRMT.h
37
DShotRMT.h
|
|
@ -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!";
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue