Compare commits

...

2 Commits

Author SHA1 Message Date
franchioping 86d6823132 working hack 2026-04-19 15:47:03 +01:00
franchioping 4f731edaea fix non dma on esp32-s3. hacky way shouldnt be used. TODO - fix underlying issue with lack of termination of signal 2026-04-13 15:03:58 +01:00
4 changed files with 837 additions and 619 deletions

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,8 @@
#pragma once #pragma once
#include <atomic>
#include <Arduino.h> #include <Arduino.h>
#include <atomic>
#include <driver/gpio.h> #include <driver/gpio.h>
#include <driver/rmt_rx.h> #include <driver/rmt_rx.h>
@ -27,115 +27,147 @@ static constexpr uint8_t DSHOTRMT_PATCH_VERSION = 5;
static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0;
// DShotRMT class for generating DShot signals and receiving telemetry. // DShotRMT class for generating DShot signals and receiving telemetry.
class DShotRMT class DShotRMT {
{
public: public:
// Constructs a new DShotRMT object using a GPIO pin. // Constructs a new DShotRMT object using a GPIO pin.
DShotRMT(gpio_num_t gpio, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); DShotRMT(gpio_num_t gpio, dshot_mode_t mode = DSHOT300,
bool is_bidirectional = false,
uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
// Constructs a new DShotRMT object using an Arduino-style integer pin number. // Constructs a new DShotRMT object using an Arduino-style integer pin number.
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional = false,
uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
// Destructor // Destructor
~DShotRMT(); ~DShotRMT();
// Initialize DShotRMT // Initialize DShotRMT
dshot_result_t begin(); dshot_result_t begin();
// Sends a raw throttle value to the ESC. // Sends a raw throttle value to the ESC.
dshot_result_t sendThrottle(uint16_t throttle); dshot_result_t sendThrottle(uint16_t throttle);
// Sends a throttle value as a percentage to the ESC. // Sends a throttle value as a percentage to the ESC.
dshot_result_t sendThrottlePercent(float percent); dshot_result_t sendThrottlePercent(float percent);
// Sends a DShot command to the ESC by accepting an integer value. // Sends a DShot command to the ESC by accepting an integer value.
dshot_result_t sendCommand(uint16_t command_value); dshot_result_t sendCommand(uint16_t command_value);
// Sends a DShot command to the ESC. // Sends a DShot command to the ESC.
dshot_result_t sendCommand(dshotCommands_e command); dshot_result_t sendCommand(dshotCommands_e command);
// Sends a DShot command to the ESC with a specified repeat count and delay. // Sends a DShot command to the ESC with a specified repeat count and delay.
dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count, uint16_t delay_us); dshot_result_t sendCommand(dshotCommands_e command, uint16_t repeat_count,
uint16_t delay_us);
/** /**
* @brief Sends a custom DShot command to the ESC. Advanced feature, use with caution. * @brief Sends a custom DShot command to the ESC. Advanced feature, use with
* @param command_value The raw command value (0-47). * caution.
* @param repeat_count The number of times to send the command. * @param command_value The raw command value (0-47).
* @param delay_us The delay in microseconds between repetitions. * @param repeat_count The number of times to send the command.
* @return dshot_result_t The result of the operation. * @param delay_us The delay in microseconds between repetitions.
*/ * @return dshot_result_t The result of the operation.
dshot_result_t sendCustomCommand(uint16_t command_value, uint16_t repeat_count, uint16_t delay_us); */
dshot_result_t sendCustomCommand(uint16_t command_value,
uint16_t repeat_count, uint16_t delay_us);
// Retrieves telemetry data from the ESC. // Retrieves telemetry data from the ESC.
dshot_result_t getTelemetry(); dshot_result_t getTelemetry();
// Sets the motor spin direction. // Sets the motor spin direction.
dshot_result_t setMotorSpinDirection(bool reversed); dshot_result_t setMotorSpinDirection(bool reversed);
// Sends a command to the ESC to save its current settings. // Sends a command to the ESC to save its current settings.
dshot_result_t saveESCSettings(); dshot_result_t saveESCSettings();
// Getters for DShot info // Getters for DShot info
dshot_mode_t getMode() const { return _mode; } dshot_mode_t getMode() const { return _mode; }
bool isBidirectional() const { return _is_bidirectional; } bool isBidirectional() const { return _is_bidirectional; }
uint16_t getThrottleValue() const { return _last_throttle; } uint16_t getThrottleValue() const { return _last_throttle; }
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; } uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
rmt_channel_handle_t _rmt_tx_channel = nullptr; // RMT transmit channel handle
// Static Callback Function for RMT RX Events
void _cleanupRmtResources();
private: private:
dshot_result_t _sendRawDshotFrame(uint16_t value); uint32_t *_tx_payload;
static bool IRAM_ATTR _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); 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,
void *user_data);
// DShot Configuration Parameters // DShot Configuration Parameters
gpio_num_t _gpio; // GPIO pin used for DShot communication gpio_num_t _gpio; // GPIO pin used for DShot communication
dshot_mode_t _mode; // DShot mode (e.g., DSHOT300, DSHOT600) dshot_mode_t _mode; // DShot mode (e.g., DSHOT300, DSHOT600)
bool _is_bidirectional; // True if bidirectional DShot is enabled bool _is_bidirectional; // True if bidirectional DShot is enabled
uint16_t _motor_magnet_count; // Number of magnets in the motor for RPM calculation uint16_t
dshot_timing_us_t _dshot_timing; // DShot timing parameters in microseconds _motor_magnet_count; // Number of magnets in the motor for RPM calculation
dshot_timing_us_t _dshot_timing; // DShot timing parameters in microseconds
// RMT Hardware Handles and Configuration // RMT Hardware Handles and Configuration
rmt_channel_handle_t _rmt_tx_channel = nullptr; // RMT transmit channel handle rmt_channel_handle_t _rmt_rx_channel = nullptr; // RMT receive channel handle
rmt_channel_handle_t _rmt_rx_channel = nullptr; // RMT receive channel handle rmt_encoder_handle_t _dshot_encoder = nullptr; // DShot RMT encoder handle
rmt_encoder_handle_t _dshot_encoder = nullptr; // DShot RMT encoder handle rmt_ticks_t _rmt_ticks; // Pre-calculated RMT timing ticks
rmt_ticks_t _rmt_ticks; // Pre-calculated RMT timing ticks uint16_t _pulse_level = 1; // Output level for a pulse (typically high)
uint16_t _pulse_level = 1; // Output level for a pulse (typically high) uint16_t _idle_level = 0; // Output level for idle (typically low)
uint16_t _idle_level = 0; // Output level for idle (typically low)
// DShot Frame Timing and State Variables // DShot Frame Timing and State Variables
uint64_t _last_transmission_time_us = 0; // Timestamp of the last DShot frame transmission uint64_t _last_transmission_time_us =
uint64_t _frame_timer_us = 0; // Minimum time required between DShot frames 0; // Timestamp of the last DShot frame transmission
float _percent_to_throttle_ratio = 0.0f; // Pre-calculated ratio for throttle percentage conversion uint64_t _frame_timer_us = 0; // Minimum time required between DShot frames
uint16_t _last_throttle = 0; // Last transmitted throttle value float _percent_to_throttle_ratio =
dshot_packet_t _packet; // Current DShot packet being processed 0.0f; // Pre-calculated ratio for throttle percentage conversion
uint16_t _encoded_frame_value = 0; // Last encoded 16-bit DShot frame value uint16_t _last_throttle = 0; // Last transmitted throttle value
dshot_packet_t _packet; // Current DShot packet being processed
uint16_t _encoded_frame_value = 0; // Last encoded 16-bit DShot frame value
// Telemetry Related Variables // Telemetry Related Variables
std::atomic<uint16_t> _last_erpm_atomic = 0; // Atomically stored last received eRPM value std::atomic<uint16_t> _last_erpm_atomic =
std::atomic<bool> _telemetry_ready_flag_atomic = false; // Atomically stored flag indicating new telemetry data 0; // Atomically stored last received eRPM value
std::atomic<dshot_telemetry_data_t> _last_telemetry_data_atomic = {}; // Atomically stored last received full telemetry data std::atomic<bool> _telemetry_ready_flag_atomic =
std::atomic<bool> _full_telemetry_ready_flag_atomic = false; // Atomically stored flag indicating new full telemetry data false; // Atomically stored flag indicating new telemetry data
rmt_rx_event_callbacks_t _rx_event_callbacks = { std::atomic<dshot_telemetry_data_t> _last_telemetry_data_atomic =
// RMT receive event callbacks {}; // Atomically stored last received full telemetry data
.on_recv_done = _on_rx_done, std::atomic<bool> _full_telemetry_ready_flag_atomic =
}; false; // Atomically stored flag indicating new full telemetry data
rmt_rx_event_callbacks_t _rx_event_callbacks = {
// RMT receive event callbacks
.on_recv_done = _on_rx_done,
};
// Private Helper Functions for DShot Protocol Logic // Private Helper Functions for DShot Protocol Logic
bool _isValidCommand(dshotCommands_e command) const; // Checks if a given DShot command is valid bool _isValidCommand(dshotCommands_e command)
dshot_packet_t _buildDShotPacket(const uint16_t &value) const; // Builds a DShot packet from a value (throttle or command) const; // Checks if a given DShot command is valid
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; // Combines packet data into a 16-bit DShot frame value dshot_packet_t _buildDShotPacket(const uint16_t &value)
uint16_t _calculateCRC(const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame const; // Builds a DShot packet from a value (throttle or command)
uint8_t _calculateTelemetryCRC(const uint8_t *data, size_t len) const; // Calculates the 8-bit CRC for telemetry data uint16_t _buildDShotFrameValue(const dshot_packet_t &packet)
void _extractTelemetryData(const uint8_t *raw_telemetry_bytes, dshot_telemetry_data_t &telemetry_data) const; // Extracts telemetry data from raw bytes const; // Combines packet data into a 16-bit DShot frame value
void _preCalculateRMTTicks(); // Pre-calculates RMT timing ticks for the selected DShot mode uint16_t _calculateCRC(
dshot_result_t _sendPacket(const dshot_packet_t &packet); // Sends a DShot frame via RMT TX channel const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame
uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols) const; // Decodes a received RMT symbol array into an eRPM value uint8_t _calculateTelemetryCRC(const uint8_t *data, size_t len)
void IRAM_ATTR _processFullTelemetryFrame(const rmt_symbol_word_t *symbols, size_t num_symbols); // Processes a full telemetry frame const; // Calculates the 8-bit CRC for telemetry data
bool IRAM_ATTR _isFrameIntervalElapsed() const; // Checks if enough time has passed since the last frame transmission void _extractTelemetryData(const uint8_t *raw_telemetry_bytes,
void _recordFrameTransmissionTime(); // Records the current time as the last frame transmission time dshot_telemetry_data_t &telemetry_data)
const; // Extracts telemetry data from raw bytes
void _preCalculateRMTTicks(); // Pre-calculates RMT timing ticks for the
// selected DShot mode
dshot_result_t _sendPacket(
const dshot_packet_t &packet); // Sends a DShot frame via RMT TX channel
uint16_t IRAM_ATTR _decodeDShotFrame(const rmt_symbol_word_t *symbols)
const; // Decodes a received RMT symbol array into an eRPM value
void IRAM_ATTR _processFullTelemetryFrame(
const rmt_symbol_word_t *symbols,
size_t num_symbols); // Processes a full telemetry frame
bool IRAM_ATTR
_isFrameIntervalElapsed() const; // Checks if enough time has passed since the
// last frame transmission
void _recordFrameTransmissionTime(); // Records the current time as the last
// frame transmission time
dshot_result_t _sendRepeatedCommand(uint16_t value, uint16_t repeat_count, uint16_t delay_us); dshot_result_t _sendRepeatedCommand(uint16_t value, uint16_t repeat_count,
uint16_t delay_us);
// Static Callback Function for RMT RX Events
void _cleanupRmtResources();
}; };
#include "dshot_utils.h" // Include for helper functions #include "dshot_utils.h" // Include for helper functions

View File

@ -7,89 +7,232 @@
*/ */
#include "dshot_init.h" #include "dshot_init.h"
#include "esp_log.h"
// Function to initialize the RMT TX channel // 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,
rmt_tx_channel_config_t tx_channel_config = { bool is_bidirectional) {
.gpio_num = gpio, rmt_tx_channel_config_t tx_channel_config = {
.clk_src = DSHOT_CLOCK_SRC_DEFAULT, .gpio_num = gpio,
.resolution_hz = DSHOT_RMT_RESOLUTION, .clk_src = DSHOT_CLOCK_SRC_DEFAULT,
.mem_block_symbols = RMT_TX_BUFFER_SYMBOLS, .resolution_hz = DSHOT_RMT_RESOLUTION,
.trans_queue_depth = RMT_QUEUE_DEPTH, .mem_block_symbols = 48,
.flags = { .trans_queue_depth = RMT_QUEUE_DEPTH,
.invert_out = static_cast<uint32_t>(is_bidirectional ? 1 : 0), .flags = {
.init_level = 0}}; .invert_out = static_cast<uint32_t>(is_bidirectional ? 1 : 0),
.with_dma = 0,
.init_level = 0,
}};
rmt_transmit_config_t rmt_tx_config = {}; // Initialize all members to zero if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) {
rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation return {false, DSHOT_TX_INIT_FAILED};
}
if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) if (rmt_enable(*out_channel) != DSHOT_OK) {
{ return {false, DSHOT_TX_INIT_FAILED};
return {false, DSHOT_TX_INIT_FAILED}; }
}
if (rmt_enable(*out_channel) != DSHOT_OK) return {true, DSHOT_TX_INIT_SUCCESS};
{
return {false, DSHOT_TX_INIT_FAILED};
}
return {true, DSHOT_TX_INIT_SUCCESS};
} }
// Function to initialize the RMT RX channel // 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_channel_config_t rx_channel_config = { rmt_rx_event_callbacks_t *rx_event_callbacks,
.gpio_num = gpio, void *user_data) {
.clk_src = DSHOT_CLOCK_SRC_DEFAULT, rmt_rx_channel_config_t rx_channel_config = {
.resolution_hz = DSHOT_RMT_RESOLUTION, .gpio_num = gpio,
.mem_block_symbols = RMT_RX_BUFFER_SYMBOLS, .clk_src = DSHOT_CLOCK_SRC_DEFAULT,
}; .resolution_hz = DSHOT_RMT_RESOLUTION,
.mem_block_symbols = RMT_RX_BUFFER_SYMBOLS,
};
if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) {
{ return {false, DSHOT_RX_INIT_FAILED};
return {false, DSHOT_RX_INIT_FAILED}; }
}
if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK) if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks,
{ user_data) != DSHOT_OK) {
return {false, DSHOT_CALLBACK_REGISTERING_FAILED}; return {false, DSHOT_CALLBACK_REGISTERING_FAILED};
} }
if (rmt_enable(*out_channel) != DSHOT_OK) if (rmt_enable(*out_channel) != DSHOT_OK) {
{ return {false, DSHOT_RX_INIT_FAILED};
return {false, DSHOT_RX_INIT_FAILED}; }
}
return {true, DSHOT_RX_INIT_SUCCESS}; return {true, DSHOT_RX_INIT_SUCCESS};
} }
// Function to initialize the DShot RMT encoder // 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,
rmt_bytes_encoder_config_t encoder_config = { uint16_t pulse_level, uint16_t idle_level) {
.bit0 = { rmt_bytes_encoder_config_t encoder_config = {
.duration0 = rmt_ticks.t0h_ticks, .bit0 =
.level0 = pulse_level, {
.duration1 = rmt_ticks.t0l_ticks, .duration0 = rmt_ticks.t0h_ticks,
.level1 = idle_level, .level0 = pulse_level,
}, .duration1 = rmt_ticks.t0l_ticks,
.bit1 = { .level1 = idle_level,
.duration0 = rmt_ticks.t1h_ticks, },
.level0 = pulse_level, .bit1 =
.duration1 = rmt_ticks.t1l_ticks, {
.level1 = idle_level, .duration0 = rmt_ticks.t1h_ticks,
}, .level0 = pulse_level,
.duration1 = rmt_ticks.t1l_ticks,
.level1 = idle_level,
},
.flags = { .flags = {
.msb_first = 1 // DShot is MSB first .msb_first = 1 // DShot is MSB first
}}; }};
if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK) if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK) {
{ return {false, DSHOT_ENCODER_INIT_FAILED};
return {false, DSHOT_ENCODER_INIT_FAILED}; }
}
return {true, DSHOT_ENCODER_INIT_SUCCESS}; 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(&copy_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};
} }

View File

@ -1,6 +1,7 @@
/** /**
* @file dshot_init.h * @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 * @author Wastl Kraus
* @date 2025-10-04 * @date 2025-10-04
* @license MIT * @license MIT
@ -14,10 +15,22 @@
#include "dshot_definitions.h" #include "dshot_definitions.h"
// Function to initialize the RMT TX channel // 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 // 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 // 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);