diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index a69631d..bd42cee 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -39,7 +39,7 @@ DShotRMT::~DShotRMT() { _cleanupRmtResources(); } // Initialize DShotRMT dshot_result_t DShotRMT::begin() { - _tx_payload = (uint16_t *)heap_caps_malloc(sizeof(uint16_t), + _tx_payload = (uint32_t *)heap_caps_malloc(sizeof(uint32_t), MALLOC_CAP_8BIT | MALLOC_CAP_DMA); dshot_result_t result = init_rmt_tx_channel(_gpio, &_rmt_tx_channel, _is_bidirectional); @@ -58,8 +58,8 @@ dshot_result_t DShotRMT::begin() { } } - result = init_dshot_encoder(&_dshot_encoder, _rmt_ticks, _pulse_level, - _idle_level); + result = init_dshot_encoder_tail(&_dshot_encoder, _rmt_ticks, _pulse_level, + _idle_level); if (!result.success) { _cleanupRmtResources(); // Clean up any allocated resources on failure @@ -408,7 +408,7 @@ dshot_result_t DShotRMT::_sendPacket(const dshot_packet_t &packet) { &tx_config) != DSHOT_OK) { return {false, DSHOT_TRANSMISSION_FAILED}; } - delayMicroseconds(50); + delayMicroseconds(60); rmt_disable(_rmt_tx_channel); rmt_enable(_rmt_tx_channel); diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 0a9ec75..2e2cad4 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -92,7 +92,7 @@ public: void _cleanupRmtResources(); private: - uint16_t *_tx_payload; + uint32_t *_tx_payload; dshot_result_t _sendRawDshotFrame(uint16_t value); static bool IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, diff --git a/src/dshot_init.cpp b/src/dshot_init.cpp index bf05cf7..8133202 100644 --- a/src/dshot_init.cpp +++ b/src/dshot_init.cpp @@ -7,6 +7,7 @@ */ #include "dshot_init.h" +#include "esp_log.h" // Function to initialize the RMT TX channel dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, @@ -18,9 +19,11 @@ dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, .resolution_hz = DSHOT_RMT_RESOLUTION, .mem_block_symbols = 48, .trans_queue_depth = RMT_QUEUE_DEPTH, - .flags = {.invert_out = static_cast(is_bidirectional ? 1 : 0), - .init_level = 0, - .with_dma = 0}}; + .flags = { + .invert_out = static_cast(is_bidirectional ? 1 : 0), + .with_dma = 0, + .init_level = 0, + }}; if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) { return {false, DSHOT_TX_INIT_FAILED}; @@ -91,3 +94,145 @@ dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, return {true, DSHOT_ENCODER_INIT_SUCCESS}; } + +typedef struct { + rmt_encoder_t base; + rmt_encoder_t *bytes_encoder; // Handles the 16-bit DShot frame + rmt_encoder_t *copy_encoder; // Handles the ending symbols + rmt_symbol_word_t tail_symbol; + uint32_t state; +} rmt_dshot_plus_tail_encoder_t; + +static size_t rmt_encode_dshot_with_tail(rmt_encoder_t *encoder, + rmt_channel_handle_t channel, + const void *primary_data, + size_t data_size, + rmt_encode_state_t *ret_state) { + rmt_dshot_plus_tail_encoder_t *dshot_encoder = + __containerof(encoder, rmt_dshot_plus_tail_encoder_t, base); + rmt_encode_state_t session_state = RMT_ENCODING_RESET; + uint32_t state = RMT_ENCODING_RESET; + size_t encoded_symbols = 0; + + uint16_t *dshot_frame = (uint16_t *)primary_data; + rmt_encoder_handle_t bytes_encoder = dshot_encoder->bytes_encoder; + rmt_encoder_handle_t copy_encoder = dshot_encoder->copy_encoder; + + switch (dshot_encoder->state) { + case 0: // Phase 1: Encode the 16-bit DShot Frame + encoded_symbols += bytes_encoder->encode( + bytes_encoder, channel, dshot_frame, sizeof(uint16_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + dshot_encoder->state = 1; + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; + } + // fall-through + case 1: // Phase 2: Encode the 50us Low Tail + encoded_symbols += + copy_encoder->encode(copy_encoder, channel, &dshot_encoder->tail_symbol, + sizeof(rmt_symbol_word_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + dshot_encoder->state = 0; // Reset for next transmission + state |= RMT_ENCODING_COMPLETE; + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; + } + } + +out: + *ret_state = (rmt_encode_state_t)state; + return encoded_symbols; +} + +static esp_err_t rmt_del_dshot_with_tail_encoder(rmt_encoder_t *encoder) { + rmt_dshot_plus_tail_encoder_t *dshot_encoder = + __containerof(encoder, rmt_dshot_plus_tail_encoder_t, base); + rmt_del_encoder(dshot_encoder->bytes_encoder); + rmt_del_encoder(dshot_encoder->copy_encoder); + free(dshot_encoder); + return ESP_OK; +} + +static esp_err_t rmt_reset_dshot_with_tail_encoder(rmt_encoder_t *encoder) { + rmt_dshot_plus_tail_encoder_t *dshot_encoder = + __containerof(encoder, rmt_dshot_plus_tail_encoder_t, base); + + dshot_encoder->state = 0; + + rmt_encoder_reset(dshot_encoder->bytes_encoder); + rmt_encoder_reset(dshot_encoder->copy_encoder); + + return ESP_OK; +} + +dshot_result_t init_dshot_encoder_tail(rmt_encoder_handle_t *out_encoder, + const rmt_ticks_t &rmt_ticks, + uint16_t pulse_level, + uint16_t idle_level) { + + auto *dshot_encoder = (rmt_dshot_plus_tail_encoder_t *)calloc( + 1, sizeof(rmt_dshot_plus_tail_encoder_t)); + if (!dshot_encoder) + return {false, DSHOT_ENCODER_INIT_FAILED}; + + // 1. Initialize Bytes Encoder (DShot Logic) + rmt_bytes_encoder_config_t bytes_config = { + .bit0 = {.duration0 = rmt_ticks.t0h_ticks, + .level0 = pulse_level, + .duration1 = rmt_ticks.t0l_ticks, + .level1 = idle_level}, + .bit1 = {.duration0 = rmt_ticks.t1h_ticks, + .level0 = pulse_level, + .duration1 = rmt_ticks.t1l_ticks, + .level1 = idle_level}, + .flags = {.msb_first = 1}}; + if (rmt_new_bytes_encoder(&bytes_config, &dshot_encoder->bytes_encoder) != + ESP_OK) { + free(dshot_encoder); + return {false, DSHOT_ENCODER_INIT_FAILED}; + } + + // 2. Initialize Copy Encoder (Tail Logic) + rmt_copy_encoder_config_t copy_config = {}; + if (rmt_new_copy_encoder(©_config, &dshot_encoder->copy_encoder) != + ESP_OK) { + rmt_del_encoder(dshot_encoder->bytes_encoder); + free(dshot_encoder); + return {false, DSHOT_ENCODER_INIT_FAILED}; + } + + // 3. Setup the 50us Tail Symbol + // Assuming 80MHz resolution if not provided, or calculate based on rmt_ticks + // Since we don't have resolution_hz, we can estimate from rmt_ticks.t0h_ticks + // + t0l_ticks DShot600 total bit time is ~1.67us. + uint32_t ticks_per_us = + (rmt_ticks.t0h_ticks + rmt_ticks.t0l_ticks) * 600000 / 1000000; + if (ticks_per_us == 0) + ticks_per_us = 80; // Fallback for 80MHz + // + + uint32_t ticks_50us = ticks_per_us * 50; + // Assigning members directly to avoid union/struct initialization ambiguity + dshot_encoder->tail_symbol.duration0 = + (uint16_t)(ticks_50us & 0x7FFF); // Mask to 15 bits + dshot_encoder->tail_symbol.level0 = (uint16_t)idle_level; + dshot_encoder->tail_symbol.duration1 = 0; + dshot_encoder->tail_symbol.level1 = (uint16_t)idle_level; + + // 4. Link Interface + dshot_encoder->base.encode = rmt_encode_dshot_with_tail; + dshot_encoder->base.reset = rmt_reset_dshot_with_tail_encoder; + // dshot_encoder->base.del = rmt_del_dshot_with_tail_encoder; + + *out_encoder = &dshot_encoder->base; + ESP_LOGI("DSHOT", "Encoder initialized at %p, encode fn at %p", dshot_encoder, + dshot_encoder->base.encode); + ESP_LOGI("DSHOT", "Internal Bytes Encoder: %p", dshot_encoder->bytes_encoder); + ESP_LOGI("DSHOT", "Internal Copy Encoder: %p", dshot_encoder->copy_encoder); + return {true, DSHOT_ENCODER_INIT_SUCCESS}; +} diff --git a/src/dshot_init.h b/src/dshot_init.h index 7003f5e..bb2f09e 100644 --- a/src/dshot_init.h +++ b/src/dshot_init.h @@ -1,6 +1,7 @@ /** * @file dshot_init.h - * @brief RMT configuration and initialization function declarations for DShotRMT library + * @brief RMT configuration and initialization function declarations for + * DShotRMT library * @author Wastl Kraus * @date 2025-10-04 * @license MIT @@ -14,10 +15,22 @@ #include "dshot_definitions.h" // Function to initialize the RMT TX channel -dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, bool is_bidirectional); +dshot_result_t init_rmt_tx_channel(gpio_num_t gpio, + rmt_channel_handle_t *out_channel, + bool is_bidirectional); // Function to initialize the RMT RX channel -dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, rmt_channel_handle_t *out_channel, rmt_rx_event_callbacks_t *rx_event_callbacks, void *user_data); +dshot_result_t init_rmt_rx_channel(gpio_num_t gpio, + rmt_channel_handle_t *out_channel, + rmt_rx_event_callbacks_t *rx_event_callbacks, + void *user_data); // Function to initialize the DShot RMT encoder -dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, const rmt_ticks_t &rmt_ticks, uint16_t pulse_level, uint16_t idle_level); +dshot_result_t init_dshot_encoder(rmt_encoder_handle_t *out_encoder, + const rmt_ticks_t &rmt_ticks, + uint16_t pulse_level, uint16_t idle_level); + +dshot_result_t init_dshot_encoder_tail(rmt_encoder_handle_t *out_encoder, + const rmt_ticks_t &rmt_ticks, + uint16_t pulse_level, + uint16_t idle_level);