From 1bdd2babd27513aff6125980cc669926ce0254c9 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Sun, 31 Aug 2025 00:21:03 +0200 Subject: [PATCH] ...cleaning --- DShotRMT.cpp | 316 +++++++++++++++++++++++++-------------------------- DShotRMT.h | 89 ++++++++------- 2 files changed, 200 insertions(+), 205 deletions(-) diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 087be36..49d2f21 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -9,8 +9,6 @@ #include "DShotRMT.h" #include -// DShot Timing Configurations - // 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} constexpr dshot_timing_t DSHOT_TIMINGS[] = { @@ -21,7 +19,7 @@ constexpr dshot_timing_t DSHOT_TIMINGS[] = { {16, 8, 6, 2, 3, 5} // DSHOT1200 }; -// Constructor with GPIO number +// Primary constructor with GPIO number DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) : _gpio(gpio), _mode(mode), @@ -45,7 +43,7 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) } } -// Simple constructor using pin number +// 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) { @@ -75,7 +73,69 @@ uint16_t DShotRMT::begin() return DSHOT_ERROR; } - // All good, start + return DSHOT_OK; +} + +// Initialize RMT TX channel +bool DShotRMT::_initTXChannel() +{ + // Configure TX channel + _tx_channel_config.gpio_num = _gpio; + _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; + _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; + _tx_channel_config.mem_block_symbols = TX_BUFFER_SIZE; + _tx_channel_config.trans_queue_depth = RMT_BUFFER_SIZE; + + // Configure transmission + _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 + + // Create RMT TX channel + if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) + { + _dshot_log(TX_INIT_FAILED); + return DSHOT_ERROR; + } + + return (rmt_enable(_rmt_tx_channel) == DSHOT_OK); +} + +// Initialize RMT RX channel +bool DShotRMT::_initRXChannel() +{ + // Configure RX channel parameters + _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 = RX_BUFFER_SIZE; + + // Configure reception parameters (TODO: determine optimal ranges) + _receive_config.signal_range_min_ns = 2; + _receive_config.signal_range_max_ns = 64; + + // Create RMT RX channel + if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) + { + _dshot_log(RX_INIT_FAILED); + return DSHOT_ERROR; + } + + return (rmt_enable(_rmt_rx_channel) == DSHOT_OK); +} + +// Initialize DShot encoder +bool DShotRMT::_initDShotEncoder() +{ + // Create copy encoder configuration + rmt_copy_encoder_config_t encoder_config = {}; + + // Create encoder instance + if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) + { + _dshot_log(ENCODER_INIT_FAILED); + return DSHOT_ERROR; + } + return DSHOT_OK; } @@ -90,13 +150,13 @@ bool DShotRMT::sendThrottle(uint16_t throttle) return sendCommand(DSHOT_CMD_MOTOR_STOP); } - // Log only if throttle is out of range and different from last time (tricky little thing) + // Log only if throttle is out of range and different from last time if ((throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) && throttle != last_throttle) { _dshot_log(THROTTLE_NOT_IN_RANGE); } - // Always store the original throttle value (good one) + // Always store the original throttle value last_throttle = throttle; // Constrain throttle for transmission and send @@ -147,136 +207,13 @@ uint16_t DShotRMT::getERPM() return _last_erpm; } -// The actual motor rpm +// Convert eRPM to actual motor RPM uint32_t DShotRMT::getMotorRPM(uint8_t magnet_count) { uint8_t pole_pairs = max(1, magnet_count / 2); return getERPM() / pole_pairs; } -// Initialize RMT TX channel -bool DShotRMT::_initTXChannel() -{ - // Configure TX channel - _tx_channel_config.gpio_num = _gpio; - _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _tx_channel_config.mem_block_symbols = TX_BUFFER_SIZE; - _tx_channel_config.trans_queue_depth = RMT_BUFFER_SIZE; - - // Configure transmission - _transmit_config.loop_count = 0; // No automatic loops - real-time calculation - - // Telemetric Bit used as bidir flag - _transmit_config.flags.eot_level = _is_bidirectional ? 1 : 0; - - // Create RMT TX channel - if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK) - { - _dshot_log(TX_INIT_FAILED); - return DSHOT_ERROR; - } - - return (rmt_enable(_rmt_tx_channel) == DSHOT_OK); -} - -// Initialize RMT RX channel -bool DShotRMT::_initRXChannel() -{ - // Configure RX channel parameters - _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 = RX_BUFFER_SIZE; - - // Configure reception parameters (TODO: determine optimal ranges) - _receive_config.signal_range_min_ns = 2; - _receive_config.signal_range_max_ns = 128; - - // Create RMT RX channel - if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK) - { - _dshot_log(RX_INIT_FAILED); - return DSHOT_ERROR; - } - - // - return (rmt_enable(_rmt_rx_channel) == DSHOT_OK); -} - -// Initialize DShot encoder -bool DShotRMT::_initDShotEncoder() -{ - // Create copy encoder configuration - rmt_copy_encoder_config_t encoder_config = {}; - - // Create encoder instance - if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) - { - _dshot_log(ENCODER_INIT_FAILED); - return DSHOT_ERROR; - } - - return DSHOT_OK; -} - -// Transmit DShot packet via RMT -uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) -{ - // RMT is crazy fast - if (!_timer_signal()) - { - return DSHOT_ERROR; - } - - // Encode DShot packet into RMT symbols - _encodeDShotFrame(packet, _tx_symbols); - - // Calculate transmission data size - size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); - - // Actual RMT transmission - uint16_t result = rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, tx_size_bytes, &_transmit_config); - - if (result != DSHOT_OK) - { - return DSHOT_ERROR; - } - - // Update timestamp - _timer_reset(); - - return DSHOT_OK; -} - -// Calculate CRC -uint16_t DShotRMT::_calculateCRC(const dshot_packet_t &packet) -{ - // - uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; - - // DShot CRC calculation - uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & 0b0000000000001111; - - // Invert CRC for bidirectional DShot mode - if (_is_bidirectional) - { - crc = (~crc) & 0b0000000000001111; - } - - return crc; -} - -// Parse DShot packet into 16-bit format -uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) -{ - // - uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; - - // Add CRC checksum - return (data << 4) | _calculateCRC(packet); -} - // Build a complete DShot packet dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) { @@ -291,10 +228,65 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) return packet; } +// Parse DShot packet into 16-bit format +uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) +{ + uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; + + // Add CRC checksum + return (data << 4) | _calculateCRC(packet); +} + +// Calculate CRC checksum +uint16_t DShotRMT::_calculateCRC(const dshot_packet_t &packet) +{ + uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; + + // DShot CRC calculation + uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & 0b0000000000001111; + + // Invert CRC for bidirectional DShot mode + if (_is_bidirectional) + { + crc = (~crc) & 0b0000000000001111; + } + + return crc; +} + +// Transmit DShot packet via RMT +uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) +{ + // Check timing requirements + if (!_timer_signal()) + { + return DSHOT_ERROR; + } + + // Encode DShot packet into RMT 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); + + if (result != DSHOT_OK) + { + return DSHOT_ERROR; + } + + // Update timestamp + _timer_reset(); + + return DSHOT_OK; +} + // Encode DShot packet into RMT symbol format (placed in IRAM for performance) bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) { - // Parse packet + // Parse packet to 16-bit format _parsed_packet = _parseDShotPacket(packet); // Convert each bit to RMT symbol @@ -337,7 +329,7 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) received_frame = (received_frame << 1) | bit; } - // Extract data from received frame + // Extract data and CRC from received frame uint16_t data = received_frame >> 4; uint16_t received_crc = received_frame & 0b0000000000001111; @@ -359,7 +351,25 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) return data >> 1; } -// Print timing diagnostic information to specified stream (default Serial0) +// Check if enough time has passed for next transmission +bool DShotRMT::_timer_signal() +{ + uint32_t current_time = micros(); + + // Handle potential overflow + uint32_t elapsed = current_time - _last_transmission_time; + + return elapsed >= _frame_timer_us; +} + +// Reset transmission timer to current time +bool DShotRMT::_timer_reset() +{ + _last_transmission_time = micros(); + return DSHOT_OK; +} + +// Print timing diagnostic information to specified stream void DShotRMT::printDshotInfo(Stream &output) const { output.println(NEW_LINE); @@ -367,10 +377,11 @@ 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"); // Timing Info @@ -396,7 +407,7 @@ void DShotRMT::printDshotInfo(Stream &output) const output.printf("Current Value: %u\n", _packet.throttle_value); } -// CPU Info +// Print CPU information void DShotRMT::printCpuInfo(Stream &output) const { output.println(NEW_LINE); @@ -408,15 +419,17 @@ void DShotRMT::printCpuInfo(Stream &output) const output.printf("APB Freq = %lu Hz\n", getApbFrequency()); } +// Print debug values as stream void DShotRMT::printDebugStream(Stream &output) const { - // Debug Values as a list + // Debug Values as CSV format output.print(_mode); output.print(","); output.print(_is_bidirectional); output.print(","); output.print(_packet.throttle_value); output.print(","); + // The packet bitwise for (int i = 15; i >= 0; --i) { @@ -429,25 +442,6 @@ void DShotRMT::printDebugStream(Stream &output) const output.print("0"); } } - output.print("*/"); - output.print("\n"); -} - -// Check if enough time has passed for next transmission -bool DShotRMT::_timer_signal() -{ - // - uint32_t current_time = micros(); - - // Handle potential overflow - uint32_t elapsed = current_time - _last_transmission_time; - - return elapsed >= _frame_timer_us; -} - -// Reset transmission timer to current time -bool DShotRMT::_timer_reset() -{ - _last_transmission_time = micros(); - return DSHOT_OK; + output.print("*/"); + output.print("\n"); } diff --git a/DShotRMT.h b/DShotRMT.h index 791e684..473523c 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -18,20 +18,18 @@ constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; constexpr auto DSHOT_THROTTLE_MIN = 48; constexpr auto DSHOT_THROTTLE_MAX = 2047; - constexpr auto DSHOT_BITS_PER_FRAME = 16; constexpr auto DSHOT_SWITCH_TIME = 30; constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; // 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 DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; - constexpr auto RX_BUFFER_SIZE = 128; constexpr auto TX_BUFFER_SIZE = 64; -// DShot Mode +// DShot Mode Enumeration typedef enum dshot_mode_e { DSHOT_OFF, @@ -44,12 +42,12 @@ typedef enum dshot_mode_e // DShot Packet Structure typedef struct dshot_packet_s { - uint16_t throttle_value : 11; // 11-bit throttle value - uint16_t telemetric_request : 1; // Telemetry request bit - uint16_t checksum : 4; // 4-bit CRC checksum + uint16_t throttle_value : 11; + uint16_t telemetric_request : 1; + uint16_t checksum : 4; } dshot_packet_t; -// DShot Timing Config Structure +// DShot Timing Configuration Structure typedef struct dshot_timing_s { uint32_t frame_length_us; @@ -60,10 +58,10 @@ typedef struct dshot_timing_s uint16_t ticks_zero_low; } dshot_timing_t; -// Timing config for DShot modes +// External timing configurations extern const dshot_timing_t DSHOT_TIMINGS[]; -// DShotRMT Class +// class DShotRMT { public: @@ -72,90 +70,93 @@ public: dshot_mode_t mode = DSHOT300, bool is_bidirectional = false); - // Alternative constructor with pin number + // Constructor with pin number DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional); // Initialize the RMT module and DShot config uint16_t begin(); // Send throttle value (48-2047) - [[deprecated("Use sendThrottle() instead")]] - bool setThrottle(uint16_t throttle) { return sendThrottle(throttle); }; bool sendThrottle(uint16_t throttle); // Send DShot command (0-47) - [[deprecated("Use sendCommand() instead")]] - bool sendDShotCommand(uint16_t command) { return sendCommand(command); }; bool sendCommand(uint16_t command); // Get telemetry data (bidirectional mode only) uint16_t getERPM(); - uint32_t getMotorRPM(uint8_t magnet_count); // Convert eRPM to motor RPM - // Tools - gpio_num_t getGPIO() const { return _gpio; } // Get GPIO pin - uint16_t getDShotPacket() const { return _parsed_packet; } // Get raw packet - bool is_bidirectional() const { return _is_bidirectional; } // Check if bidirectional + // Convert eRPM to motor RPM + uint32_t getMotorRPM(uint8_t magnet_count); - // Print DShot Info + // + gpio_num_t getGPIO() const { return _gpio; } + uint16_t getDShotPacket() const { return _parsed_packet; } + bool is_bidirectional() const { return _is_bidirectional; } + + // --- INFO --- void printDshotInfo(Stream &output = Serial0) const; void printCpuInfo(Stream &output = Serial0) const; - - // Prints debug values stream void printDebugStream(Stream &output = Serial0) const; + // --- DEPRECATED METHODS --- + [[deprecated("Use sendThrottle() instead")]] + bool setThrottle(uint16_t throttle) { return sendThrottle(throttle); } + + [[deprecated("Use sendCommand() instead")]] + bool sendDShotCommand(uint16_t command) { return sendCommand(command); } + private: - // Configuration Variables + // --- CONFIG --- gpio_num_t _gpio; dshot_mode_t _mode; uint16_t _is_bidirectional; uint32_t _frame_timer_us; const dshot_timing_t &_timing_config; - // RMT Config + // --- TIMING & STATE VARIABLES --- + uint32_t _last_transmission_time; + uint16_t _last_erpm; + uint16_t _parsed_packet; + dshot_packet_t _packet; + + // --- RMT HARDWARE HANDLES --- rmt_channel_handle_t _rmt_tx_channel; rmt_channel_handle_t _rmt_rx_channel; rmt_encoder_handle_t _dshot_encoder; - // RMT Config Structures - rmt_symbol_word_t _tx_symbols[TX_BUFFER_SIZE]; - rmt_symbol_word_t _rx_symbols[RX_BUFFER_SIZE]; + // --- RMT CONFIG STRUCTURES --- rmt_tx_channel_config_t _tx_channel_config; rmt_rx_channel_config_t _rx_channel_config; rmt_transmit_config_t _transmit_config; rmt_receive_config_t _receive_config; - // - uint16_t _last_erpm; - uint16_t _parsed_packet; - dshot_packet_t _packet; - uint32_t _last_transmission_time; + // --- RMT DATA BUFFERS --- + rmt_symbol_word_t _tx_symbols[TX_BUFFER_SIZE]; + rmt_symbol_word_t _rx_symbols[RX_BUFFER_SIZE]; - // Helpers + // --- INITS --- bool _initTXChannel(); bool _initRXChannel(); bool _initDShotEncoder(); - // Utilizing RMT - uint16_t _sendDShotFrame(const dshot_packet_t &packet); - - // Packet management - uint16_t _calculateCRC(const dshot_packet_t &packet); + // --- PACKET MANAGEMENT --- dshot_packet_t _buildDShotPacket(const uint16_t value); uint16_t _parseDShotPacket(const dshot_packet_t &packet); + uint16_t _calculateCRC(const dshot_packet_t &packet); - // Frame processing + // --- FRAME PROCESSING --- + uint16_t _sendDShotFrame(const dshot_packet_t &packet); bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); - // Timer Config + // --- TIMING CONTROL --- bool IRAM_ATTR _timer_signal(); bool _timer_reset(); - // DShot Messages - void _dshot_log(char *msg, Stream &output = Serial0) { output.println(msg); }; + // --- ERROR HANDLING & LOGGING --- + void _dshot_log(char *msg, Stream &output = Serial0) { output.println(msg); } - // Error Codes and Messages + // --- CONSTANTS & ERROR MESSAGES --- static constexpr uint16_t DSHOT_OK = 0; static constexpr uint16_t DSHOT_ERROR = 1;