From 2f59227788603e1101326b62a4028395be9f14b3 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Wed, 30 Jul 2025 00:40:09 +0200 Subject: [PATCH] ...simplify --- DShotRMT.cpp | 82 +++++++++++++++++----------------- DShotRMT.h | 48 ++++++++++---------- examples/dshot300/dshot300.ino | 4 +- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 673f28c..1acc3ec 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -58,7 +58,7 @@ void DShotRMT::begin() .gpio_num = _gpio, .clk_src = DSHOT_CLOCK_SRC_DEFAULT, .resolution_hz = DSHOT_RMT_RESOLUTION, - .mem_block_symbols = 64, + .mem_block_symbols = DSHOT_SYMBOLS_SIZE, }; if (rmt_new_rx_channel(&_rmt_rx_channel_config, &_rmt_rx_channel) != 0) @@ -66,6 +66,7 @@ void DShotRMT::begin() Serial.println("Failed to create RX channel"); return; } + if (rmt_enable(_rmt_rx_channel) != 0) { Serial.println("Failed to enable RX channel"); @@ -74,8 +75,6 @@ void DShotRMT::begin() _receive_config.signal_range_min_ns = 300; _receive_config.signal_range_max_ns = 5000; - - _dshot_packet.telemetric_request = _isBidirectional; } // Configure TX RMT Channel @@ -83,9 +82,8 @@ void DShotRMT::begin() .gpio_num = _gpio, .clk_src = DSHOT_CLOCK_SRC_DEFAULT, .resolution_hz = DSHOT_RMT_RESOLUTION, - .mem_block_symbols = 64, - .trans_queue_depth = 10, - }; + .mem_block_symbols = DSHOT_SYMBOLS_SIZE, + .trans_queue_depth = TX_BUFFER_SIZE}; // Transmission configuration _transmit_config.loop_count = 0; @@ -96,6 +94,7 @@ void DShotRMT::begin() Serial.println("Failed to create TX channel"); return; } + if (rmt_enable(_rmt_tx_channel) != 0) { Serial.println("Failed to enable TX channel"); @@ -120,21 +119,22 @@ void DShotRMT::setThrottle(uint16_t throttle) // Simple timer static unsigned long last_time = NULL; + // + dshot_packet_t packet = {}; + + packet.throttle_value = (constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0b0000011111111111); + packet.telemetric_request = _isBidirectional; + packet.checksum = calculateCRC(packet); + + // DShot transcoding + rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME] = {}; + encodeDShotTX(packet, tx_symbols); + // Ensure frame lenght for compatibility if (micros() - last_time >= _frameLength) { - // Clamp input range for throttle value - _dshot_packet.throttle_value = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0b0000011111111111; - - // Calculate CRC for every throttle value - calculateCRC(&_dshot_packet); - - // Encode RMT symbols - size_t count = NULL; - encodeDShotTX(&_dshot_packet, _tx_symbols, count); - // Transmit the packet - if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, count * sizeof(rmt_symbol_word_t), &_transmit_config) != 0) + if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, DSHOT_SYMBOLS_SIZE, &_transmit_config) != 0) { Serial.println("Failed to transmit DShot packet"); return; @@ -157,7 +157,7 @@ uint32_t DShotRMT::getERPM() } // Try to receive a new frame - if (!rmt_receive(_rmt_rx_channel, _rx_symbols, sizeof(_rx_symbols), &_receive_config)) + if (!rmt_receive(_rmt_rx_channel, _rx_symbols, DSHOT_SYMBOLS_SIZE, &_receive_config)) { Serial.println("No valid DShot frame received"); return _last_erpm; @@ -182,38 +182,33 @@ uint32_t DShotRMT::getMotorRPM(uint8_t magnet_count) } // Calculates CRC for DShot packet -void DShotRMT::calculateCRC(dshot_packet_t *dshot_packet) +uint16_t DShotRMT::calculateCRC(dshot_packet_t dshot_packet) { - _parsed_dshot_tx_packet = (dshot_packet->throttle_value << 1) | (dshot_packet->telemetric_request); - - // Reset CRC container - dshot_packet->checksum = DSHOT_NULL_PACKET; + uint16_t crc = (dshot_packet.throttle_value << 1) | (dshot_packet.telemetric_request); // CRC calculation for DShot (4 bits) - dshot_packet->checksum = ((_parsed_dshot_tx_packet ^ (_parsed_dshot_tx_packet >> 4) ^ (_parsed_dshot_tx_packet >> 8)) & 0b0000000000001111); + dshot_packet.checksum = ((crc ^ (crc >> 4) ^ (crc >> 8)) & 0b0000000000001111); // CRC is inverted for bidirectional DShot - if (dshot_packet->telemetric_request) - dshot_packet->checksum = (~dshot_packet->checksum) & 0b0000000000001111; + if (dshot_packet.telemetric_request) + dshot_packet.checksum = (~dshot_packet.checksum) & 0b0000000000001111; + + return dshot_packet.checksum; } // Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC) -uint16_t DShotRMT::parseDShotPacket(const dshot_packet_t *dshot_packet) +uint16_t DShotRMT::parseDShotPacket(const dshot_packet_t dshot_packet) { - _parsed_dshot_tx_packet = ((dshot_packet->throttle_value << 1) | (dshot_packet->telemetric_request)) & 0b0000111111111111; - return ((_parsed_dshot_tx_packet << 4) | (dshot_packet->checksum)) & 0b1111111111111111; + uint16_t raw = (((dshot_packet.throttle_value << 1) | (dshot_packet.telemetric_request)) & 0b0000111111111111); + return (((raw << 4) | (dshot_packet.checksum)) & 0b1111111111111111); } // Converts a 16-bit packet into a valid DShot frame for RMT -void DShotRMT::encodeDShotTX(dshot_packet_t *dshot_packet, rmt_symbol_word_t *symbols, size_t &count) +void DShotRMT::encodeDShotTX(dshot_packet_t dshot_packet, rmt_symbol_word_t *symbols) { - count = 0; - - uint16_t frame_bits = parseDShotPacket(dshot_packet); - - uint32_t ticks_per_bit = 0; - uint32_t ticks_zero_high = 0; - uint32_t ticks_one_high = 0; + uint16_t ticks_per_bit = 0; + uint16_t ticks_zero_high = 0; + uint16_t ticks_one_high = 0; // Select timing based on DShot mode switch (_mode) @@ -242,13 +237,18 @@ void DShotRMT::encodeDShotTX(dshot_packet_t *dshot_packet, rmt_symbol_word_t *sy return; } - uint32_t ticks_zero_low = ticks_per_bit - ticks_zero_high; - uint32_t ticks_one_low = ticks_per_bit - ticks_one_high; + uint16_t ticks_zero_low = ticks_per_bit - ticks_zero_high; + uint16_t ticks_one_low = ticks_per_bit - ticks_one_high; + + uint16_t frame_bits = parseDShotPacket(dshot_packet); + + // Always start with the "first" bit + size_t count = NULL; // Convert the parsed dshot frame to rmt_tx data - for (int i = 15; i >= 0; i--) + for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; i--) { - bool bit = (frame_bits >> i) & 0x1; + bool bit = (frame_bits >> i) & 0b0000000000000001; if (_isBidirectional) { symbols[count].level0 = 0; diff --git a/DShotRMT.h b/DShotRMT.h index c763e02..aff4be9 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -14,30 +14,20 @@ #include #include +static constexpr bool DSHOT_OK = 0; +static constexpr bool DSHOT_FAILED = 1; + // --- DShot Protocol Constants --- static constexpr uint16_t DSHOT_THROTTLE_FAILSAVE = 0; static constexpr uint16_t DSHOT_THROTTLE_MIN = 48; static constexpr uint16_t DSHOT_THROTTLE_MAX = 2047; -static constexpr uint8_t DSHOT_BITS_PER_FRAME = 16; -static constexpr uint8_t DSHOT_SWITCH_TIME = 21; - -static constexpr uint16_t DSHOT_NULL_PACKET = 0x0000; -static constexpr uint16_t DSHOT_FULL_PACKET = 0xFFFF; -static constexpr uint16_t NO_ERPM_SIGNAL = 0; - -// RMT configuration parameters -static constexpr rmt_clock_source_t DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; -static constexpr uint32_t DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz Clock - -static constexpr size_t TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; -static constexpr size_t RX_BUFFER_SIZE = 32; // Padding for RX decoding // DShot Packet structure typedef struct dshot_packet_s { uint16_t throttle_value : 11; bool telemetric_request : 1; - uint8_t checksum : 4; + uint16_t checksum : 4; } dshot_packet_t; // --- DShot Mode Selection --- @@ -47,7 +37,8 @@ typedef enum dshot_mode_s DSHOT150, DSHOT300, DSHOT600, - DSHOT1200 + DSHOT1200, + COUNTER } dshot_mode_t; // --- DShotRMT Class --- @@ -71,22 +62,36 @@ public: gpio_num_t getGPIO() const { return _gpio; } dshot_mode_t getDShotMode() const { return _mode; } uint16_t getFrameLenght() const { return _frameLength; } - uint16_t getDShotPacket() const { return _parsed_dshot_tx_packet; } private: + static constexpr uint8_t DSHOT_BITS_PER_FRAME = 16; + static constexpr uint8_t DSHOT_SWITCH_TIME = 21; + + static constexpr uint16_t DSHOT_NULL_PACKET = 0b000000000000000; + static constexpr uint16_t DSHOT_FULL_PACKET = 0b111111111111111; + static constexpr uint16_t NO_ERPM_SIGNAL = 0; + + // RMT configuration parameters + static constexpr rmt_clock_source_t DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; + static constexpr uint32_t DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz Clock + + static constexpr size_t TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; + static constexpr size_t RX_BUFFER_SIZE = 32; + static constexpr size_t DSHOT_SYMBOLS_SIZE = 64; + // Calculates the checksum for a DShot packet - void calculateCRC(dshot_packet_t *dshot_packet); + uint16_t calculateCRC(dshot_packet_t dshot_packet); // Parses the DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC) - uint16_t parseDShotPacket(const dshot_packet_t *dshot_packet); + uint16_t parseDShotPacket(const dshot_packet_t dshot_packet); // Converts a 16-bit DShot packet into RMT symbols - void encodeDShotTX(dshot_packet_t *dshot_packet, rmt_symbol_word_t *symbols, size_t &count); + void encodeDShotTX(dshot_packet_t dshot_packet, rmt_symbol_word_t *symbols); // Decodes the ESC answer uint16_t decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t count); -// --- Configuration Parameters --- + // --- Configuration Parameters --- gpio_num_t _gpio = GPIO_NUM_NC; dshot_mode_t _mode = DSHOT_OFF; bool _isBidirectional = false; @@ -94,8 +99,6 @@ private: // --- DShot Packets Container --- uint16_t _rx_packet = DSHOT_NULL_PACKET; - uint16_t _parsed_dshot_tx_packet = DSHOT_NULL_PACKET; - dshot_packet_t _dshot_packet = {}; // --- RMT Channel Handles --- rmt_channel_handle_t _rmt_rx_channel = nullptr; @@ -112,7 +115,6 @@ private: // --- RMT Symbol Buffers --- rmt_symbol_word_t _rx_symbols[RX_BUFFER_SIZE] = {}; - rmt_symbol_word_t _tx_symbols[TX_BUFFER_SIZE] = {}; // Stores the last valid eRPM received from the ESC uint16_t _last_erpm = NULL; diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 368ca62..79b5d2a 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -73,12 +73,10 @@ uint16_t readSerialThrottle() String input = USB_SERIAL.readStringUntil('\n'); int throttle_input = input.toInt(); - // Clamp the value to the DShot range - throttle_input = constrain(throttle_input, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); - if (throttle_input < DSHOT_THROTTLE_MIN || throttle_input > DSHOT_THROTTLE_MAX) { USB_SERIAL.println("Invalid input. Please enter a value between 48 and 2047."); + return last_throttle; } else {