diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 5aba4a2..d7c1970 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -9,7 +9,8 @@ #include "DShotRMT.h" // --- DShot Timings --- -const dshot_timing_t DSHOT_TIMINGS[] = { +// frame_length_us, ticks_per_bit, ticks_one_high, ticks_zero_high, ticks_zero_low, ticks_one_low +constexpr dshot_timing_t DSHOT_TIMINGS[] = { {0, 0, 0, 0, 0, 0}, // DSHOT_OFF {128, 64, 48, 24, 40, 16}, // DSHOT150 {64, 32, 24, 12, 20, 8}, // DSHOT300 @@ -18,15 +19,16 @@ const dshot_timing_t DSHOT_TIMINGS[] = { }; // -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) - , _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), + _last_transmission_time(0) { // Calculate frame time including switch time _frame_time_us = _timing_config.frame_length_us + DSHOT_SWITCH_TIME; @@ -38,21 +40,24 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) } } -// +// Init DShotRMT bool DShotRMT::begin() { + // Init TX Channel if (!_initTXChannel()) { Serial.println("Failed to initialize TX channel"); return DSHOT_ERROR; } + // Init RX Channel if (!_initRXChannel() && _is_bidirectional) { Serial.println("Failed to initialize RX channel"); return DSHOT_ERROR; } + // Init DShot Decoder if (!_initDShotEncoder()) { Serial.println("Failed to initialize encoder"); @@ -65,7 +70,6 @@ bool DShotRMT::begin() // bool DShotRMT::setThrottle(uint16_t throttle) { - // DShot Frame Container dshot_packet_t packet = {}; @@ -78,6 +82,26 @@ bool DShotRMT::setThrottle(uint16_t throttle) { return DSHOT_ERROR; } + + return DSHOT_OK; +} + +// +bool DShotRMT::sendDShotCommand(uint16_t command) +{ + // DShot Frame Container + dshot_packet_t packet = {}; + + // Create DShot packet + packet.throttle_value = constrain(command, DSHOT_CMD_MOTOR_STOP, DSHOT_CMD_MAX); + packet.telemetric_request = _is_bidirectional; + packet.checksum = _calculateCRC(packet); + + if (!_sendDShotFrame(packet)) + { + return DSHOT_ERROR; + } + return DSHOT_OK; } // @@ -164,28 +188,30 @@ bool DShotRMT::_initDShotEncoder() return rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) == 0; } -// -bool DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) +// Use RMT to transmit a prepared DShot packet and returns it +uint16_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) { - // - if (!_timer_signal()) - { - return DSHOT_ERROR; - } - // Encodes packet directly into RMT Buffer rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME]; _encodeDShotFrame(packet, tx_symbols); + // + if (!_timer_signal()) + { + return DSHOT_NULL_PACKET; + } + // Trigger RMT Transmit if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, DSHOT_SYMBOLS_SIZE, &_transmit_config) != 0) { Serial.println("Failed to transmit DShot packet"); - return DSHOT_ERROR; + return DSHOT_NULL_PACKET; } + // Time Stamp _timer_reset(); - return DSHOT_OK; + + return _assembleDShotFrame(packet); } // @@ -212,33 +238,32 @@ uint16_t DShotRMT::_assembleDShotFrame(const dshot_packet_t &packet) } // -void DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) +bool DShotRMT::_encodeDShotFrame(const dshot_packet_t& packet, rmt_symbol_word_t* symbols) { - { - // Encoding to "raw" DShot Packet - uint16_t frame_bits = _assembleDShotFrame(packet); + // Encoding to "raw" DShot Packet + uint16_t frame_bits = _assembleDShotFrame(packet); - // Convert the parsed dshot frame to rmt_tx data - for (int i = 0; i < DSHOT_BITS_PER_FRAME; i++) + // Convert the parsed dshot frame to rmt_tx data + for (int i = 0; i < DSHOT_BITS_PER_FRAME; i++) + { + // Encode RMT symbols bitwise (MSB first) - tricky + bool bit = (frame_bits >> (DSHOT_BITS_PER_FRAME - 1 - i)) & 0b0000000000000001; + if (_is_bidirectional) { - // Encode RMT symbols bitwise (MSB first) - tricky - bool bit = (frame_bits >> (DSHOT_BITS_PER_FRAME - 1 - i)) & 0b0000000000000001; - if (_is_bidirectional) - { - 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 - { - 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 = 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 + { + 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; } } + return DSHOT_OK; } // @@ -282,7 +307,9 @@ bool DShotRMT::_timer_signal() } // -void DShotRMT::_timer_reset() +bool DShotRMT::_timer_reset() { + // Timestamp update _last_transmission_time = micros(); + return DSHOT_OK; } diff --git a/DShotRMT.h b/DShotRMT.h index 1cdfa5e..4a79d1a 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -13,6 +13,7 @@ #include #include #include +#include static constexpr bool DSHOT_OK = 0; static constexpr bool DSHOT_ERROR = 1; @@ -23,7 +24,7 @@ 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 uint8_t DSHOT_SWITCH_TIME = 300; // 30us static constexpr uint16_t DSHOT_NULL_PACKET = 0b0000000000000000; // --- RMT Config Constants --- @@ -65,6 +66,7 @@ typedef struct // --- DShot Timing Config --- extern const dshot_timing_t DSHOT_TIMINGS[]; +// class DShotRMT { public: @@ -122,12 +124,12 @@ private: bool _initRXChannel(); bool _initDShotEncoder(); + uint16_t _sendDShotFrame(const dshot_packet_t &packet); uint16_t _calculateCRC(const dshot_packet_t &packet); uint16_t _assembleDShotFrame(const dshot_packet_t &packet); - void _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); + bool _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols, size_t symbol_count); - bool _sendDShotFrame(const dshot_packet_t &packet); bool _timer_signal(); - void _timer_reset(); + bool _timer_reset(); }; diff --git a/examples/dshot300/dshot300.ino b/examples/dshot300/dshot300.ino index 1753da0..82c816f 100644 --- a/examples/dshot300/dshot300.ino +++ b/examples/dshot300/dshot300.ino @@ -10,18 +10,18 @@ #include // USB serial port settings -constexpr auto &USB_SERIAL = Serial0; -constexpr uint32_t USB_SERIAL_BAUD = 115200; +static constexpr HardwareSerial &USB_SERIAL = Serial0; +static constexpr uint32_t USB_SERIAL_BAUD = 115200; // Motor configuration -constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_17; -constexpr dshot_mode_t DSHOT_MODE = DSHOT300; +static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_17; +static constexpr dshot_mode_t DSHOT_MODE = DSHOT300; // BiDirectional DShot Support (default: false) -constexpr bool IS_BIDIRECTIONAL = false; +static constexpr bool IS_BIDIRECTIONAL = false; // Motor magnet count for RPM calculation -constexpr uint8_t MOTOR01_MAGNET_COUNT = 14; +static constexpr uint8_t MOTOR01_MAGNET_COUNT = 14; // Setup Motor Pin, DShot Mode and optional BiDirectional Support DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL); diff --git a/hw_defaults.h b/hw_defaults.h new file mode 100644 index 0000000..02457bb --- /dev/null +++ b/hw_defaults.h @@ -0,0 +1,36 @@ +/** + * @file hw_defaults.h + * @brief Some Shortcuts + * @author Wastl Kraus + * @date 2025-08-02 + * @license MIT + */ + +#ifdef ESP32 +// USB-Serial (UART0): GPIO1/3 in use by default + +// Serial1 +constexpr auto PIN_UART1_TX = GPIO_NUM_13; +constexpr auto PIN_UART1_RX = GPIO_NUM_14; + +// Serial2 +constexpr auto PIN_UART2_TX = GPIO_NUM_16; +constexpr auto PIN_UART2_RX = GPIO_NUM_17; + +// SPI (VSPI) +constexpr auto PIN_SPI_SCLK = GPIO_NUM_18; +constexpr auto PIN_SPI_MISO = GPIO_NUM_19; +constexpr auto PIN_SPI_MOSI = GPIO_NUM_23; +constexpr auto PIN_SPI_SS = GPIO_NUM_5; + +// I2C (Wire) +constexpr auto PIN_I2C_SDA = GPIO_NUM_21; +constexpr auto PIN_I2C_SCL = GPIO_NUM_22; + +// RMT (5 Channels) +constexpr auto PIN_RMT_CH0 = GPIO_NUM_25; +constexpr auto PIN_RMT_CH1 = GPIO_NUM_26; +constexpr auto PIN_RMT_CH2 = GPIO_NUM_27; +constexpr auto PIN_RMT_CH3 = GPIO_NUM_32; +constexpr auto PIN_RMT_CH4 = GPIO_NUM_33; +#endif // ESP32 diff --git a/img/DShotVSIbus/Detail_01.png b/img/DShotVSIbus/Detail_01.png new file mode 100644 index 0000000..3cb4e6b Binary files /dev/null and b/img/DShotVSIbus/Detail_01.png differ diff --git a/img/DShotVSIbus/Detail_02.png b/img/DShotVSIbus/Detail_02.png new file mode 100644 index 0000000..7185c60 Binary files /dev/null and b/img/DShotVSIbus/Detail_02.png differ diff --git a/img/DShotVSIbus/Full_01.png b/img/DShotVSIbus/Full_01.png new file mode 100644 index 0000000..463bd6c Binary files /dev/null and b/img/DShotVSIbus/Full_01.png differ diff --git a/img/DShotVSIbus/Overview.png b/img/DShotVSIbus/Overview.png new file mode 100644 index 0000000..37ad4f6 Binary files /dev/null and b/img/DShotVSIbus/Overview.png differ