From 443d032d56ea9f581630bb71cfe17c734ab43747 Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Thu, 28 Aug 2025 13:22:42 +0200 Subject: [PATCH] ...sync branch * ...squashed ...some simple info display ...fixing possible overflow ...reenable "telemetric bit" choice * Update DShotRMT.h * ...update buffer size ...increase RX Buffer and calculate RMT Sybmbol size * ...update Workflow badges * Update .gitignore --- .gitignore | 1 + DShotRMT.cpp | 103 ++++++++++++++++++++------------- DShotRMT.h | 11 ++-- examples/dshot300/dshot300.ino | 52 ++++++++++------- 4 files changed, 102 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 7da924c..2704b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # Caching ESP32 Builds buildCache +build examples/dshot300/debug.cfg examples/dshot300/esp32.svd examples/dshot300/debug_custom.json diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 73dca3f..d1f24db 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -7,6 +7,7 @@ */ #include "DShotRMT.h" +#include // --- DShot Timings --- // frame_length_us, ticks_per_bit, ticks_one_high, ticks_one_low, ticks_zero_high, ticks_zero_low @@ -19,18 +20,17 @@ constexpr dshot_timing_t DSHOT_TIMINGS[] = { }; // --- DShot Config Constructor --- -DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional): - _gpio(gpio), - _mode(mode), - _is_bidirectional(is_bidirectional), - _timing_config(DSHOT_TIMINGS[mode]), - _rmt_tx_channel(nullptr), - _rmt_rx_channel(nullptr), - _dshot_encoder(nullptr), - _last_erpm(0), - _current_packet(0), - _packet{0}, - _last_transmission_time(0) +DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) : _gpio(gpio), + _mode(mode), + _is_bidirectional(is_bidirectional), + _timing_config(DSHOT_TIMINGS[mode]), + _rmt_tx_channel(nullptr), + _rmt_rx_channel(nullptr), + _dshot_encoder(nullptr), + _last_erpm(0), + _current_packet(0), + _packet{0}, + _last_transmission_time(0) { // Calculates frame time and adds switch/pause time _frame_timer_us = _timing_config.frame_length_us + DSHOT_SWITCH_TIME; @@ -44,12 +44,10 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional): } // Easy Constructor -DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional): - DShotRMT( - (gpio_num_t)pin_nr, - mode, - is_bidirectional - ) +DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional) : DShotRMT( + (gpio_num_t)pin_nr, + mode, + is_bidirectional) { // ...just to accept pin numbers and GPIO_NUMs } @@ -91,14 +89,19 @@ bool DShotRMT::setThrottle(uint16_t throttle) // Sends a valid throttle value bool DShotRMT::sendThrottle(uint16_t throttle) { - // Make sure throttle value is valid by force + // Validate throttle value + if (throttle < DSHOT_THROTTLE_MIN || throttle > DSHOT_THROTTLE_MAX) + { + Serial.println(DSHOT_MSG_05); + return DSHOT_ERROR; + } + + // Constrain throttle value auto value = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX); - // Converts throttle value to dshot packet RMT symbols + // Build and send packet _packet = _buildDShotPacket(value); - - // Actually send the RMT symbols - return (_sendDShotFrame(_packet)); + return _sendDShotFrame(_packet); } // Deprecated, use "sendCommand()"" instead @@ -117,8 +120,7 @@ bool DShotRMT::sendCommand(uint16_t command) } _packet = _buildDShotPacket(command); - - return (_sendDShotFrame(_packet)); + return _sendDShotFrame(_packet); } // @@ -220,20 +222,28 @@ bool DShotRMT::_initDShotEncoder() // Uses RMT to transmit a prepared DShot packet and returns it bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) { - // Excluding calculation from timing is more timing stable - _encodeDShotFrame(packet, _tx_symbols); - - // Checking timer signal - if (_timer_signal()) + // Check if we can send (timing check) + if (!_timer_signal()) { - // Triggers RMT Transmit - rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, DSHOT_SYMBOLS_SIZE, &_transmit_config); - - // Time Stamp - return _timer_reset(); + return DSHOT_ERROR; } - return DSHOT_ERROR; + // Encode the frame + _encodeDShotFrame(packet, _tx_symbols); + + // Attempt to transmit + size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t); + bool 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; } // Calculates checksum for given package @@ -266,7 +276,7 @@ dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) // Creates DShot packet packet.throttle_value = value; - packet.telemetric_request = 1; // needed to get the motor spinning + packet.telemetric_request = _is_bidirectional ? 1 : 0; packet.checksum = _calculateCRC(packet); // @@ -336,11 +346,26 @@ uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) return data >> 1; } +// +void DShotRMT::printTimingDiagnostics() const +{ + uint32_t current_time = micros(); + Serial.println("\n=== DShot Timing Diagnostics ==="); + Serial.printf("Current mode: DSHOT%d\n", _mode == DSHOT150 ? 150 : _mode == DSHOT300 ? 300 + : _mode == DSHOT600 ? 600 + : _mode == DSHOT1200); + Serial.printf("Protocol Frame length: %u µs\n", _timing_config.frame_length_us); + Serial.printf("Frame to Frame: %u µs\n", _frame_timer_us); + Serial.printf("Bidirectional: %s\n", _is_bidirectional ? "Yes" : "No"); +} + // Timer triggered bool DShotRMT::_timer_signal() { - // trying new tricks - return __builtin_expect((micros() - _last_transmission_time >= _frame_timer_us), 1); + // fixing possible overflow + uint32_t current_time = micros(); + uint32_t elapsed = current_time - _last_transmission_time; + return elapsed >= _frame_timer_us; } // Updates timestamp diff --git a/DShotRMT.h b/DShotRMT.h index 6e364c0..3bea80a 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -27,7 +27,7 @@ static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; static constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; static constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz static constexpr auto TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; -static constexpr auto RX_BUFFER_SIZE = 64; // debug +static constexpr auto RX_BUFFER_SIZE = 128; static constexpr auto DSHOT_SYMBOLS_SIZE = 64; // --- DShot Mode Select --- @@ -51,7 +51,7 @@ typedef struct dshot_packet_s // --- DShot Timing Config --- typedef struct dshot_timing_s { - uint16_t frame_length_us; + uint32_t frame_length_us; uint16_t ticks_per_bit; uint16_t ticks_one_high; uint16_t ticks_one_low; @@ -98,12 +98,15 @@ public: // bool is_bidirectional() const { return _is_bidirectional; } + // --- Performance monitoring functions --- + void printTimingDiagnostics() const; + private: // --- Config --- gpio_num_t _gpio; dshot_mode_t _mode; bool _is_bidirectional; - uint16_t _frame_timer_us; + uint32_t _frame_timer_us; // --- DShot Timings --- const dshot_timing_t &_timing_config; @@ -114,7 +117,7 @@ private: rmt_encoder_handle_t _dshot_encoder; // --- RMT Config --- - rmt_symbol_word_t _tx_symbols[DSHOT_BITS_PER_FRAME]; + rmt_symbol_word_t _tx_symbols[DSHOT_SYMBOLS_SIZE]; rmt_symbol_word_t _rx_symbols[RX_BUFFER_SIZE]; rmt_tx_channel_config_t _tx_channel_config; rmt_rx_channel_config_t _rx_channel_config; diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 1556632..fea9183 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -39,6 +39,10 @@ void setup() // Initializes DShot Signal motor01.begin(); + USB_SERIAL.printf("CPU Freq = %lu MHz\n", getCpuFrequencyMhz()); + USB_SERIAL.printf("XTAL Freq = %lu MHz\n", getXtalFrequencyMhz()); + USB_SERIAL.printf("APB Freq = %lu Hz\n", getApbFrequency()); + USB_SERIAL.println("***********************************"); USB_SERIAL.println(" === DShotRMT Demo started. === "); USB_SERIAL.println("Enter a throttle value (48 – 2047):"); @@ -49,9 +53,12 @@ void loop() { // Safety first: start with DSHOT_MIN_THROTTLE static auto throttle = DSHOT_THROTTLE_MIN; - + + // Performance monitoring + static uint32_t last_stats_print = 0; + // Takes "every" throttle value - if (USB_SERIAL.available() > NULL) + if (USB_SERIAL.available() > 0) { throttle = (USB_SERIAL.readStringUntil('\n').toInt()); @@ -66,8 +73,13 @@ void loop() // Prints out RPM if BiDirectional DShot is enabled every 2 seconds // printRPMPeriodically(2000); - // Debug: Prints out "raw" DShot packet every 2 seconds - print_RMT_packet(2000); + // Print performance statistics every 2 seconds + if (millis() - last_stats_print >= 2000) + { + motor01.printTimingDiagnostics(); + print_RMT_packet(); + last_stats_print = millis(); + } } // Prints RPM every X_ms @@ -90,28 +102,24 @@ void printRPMPeriodically(auto timer_ms) } // Prints "raw" packet every ms -void print_RMT_packet(auto timer_ms) +void print_RMT_packet() { - static auto last_print_time = 0; + auto packet = motor01.getDShotPacket(); - if (millis() - last_print_time >= timer_ms) + USB_SERIAL.print("Current Frame: "); + + // Print bit by bit + for (auto i = 15; i >= 0; --i) { - auto packet = motor01.getDShotPacket(); - - // Print bit by bit - for (auto i = 15; i >= 0; --i) + if ((packet >> i) & 1) { - if ((packet >> i) & 1) - { - USB_SERIAL.print("1"); - } - else - { - USB_SERIAL.print("0"); - } + USB_SERIAL.print("1"); + } + else + { + USB_SERIAL.print("0"); } - - USB_SERIAL.println(""); - last_print_time = millis(); } + + USB_SERIAL.println(""); }