...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
This commit is contained in:
Wastl Kraus 2025-08-28 13:22:42 +02:00 committed by GitHub
parent 8f08a789bb
commit 443d032d56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 65 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@
# Caching ESP32 Builds
buildCache
build
examples/dshot300/debug.cfg
examples/dshot300/esp32.svd
examples/dshot300/debug_custom.json

View File

@ -7,6 +7,7 @@
*/
#include "DShotRMT.h"
#include <driver/rmt_tx.h>
// --- 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

View File

@ -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;

View File

@ -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("");
}