Compare commits
2 Commits
262feec75c
...
86d6823132
| Author | SHA1 | Date |
|---|---|---|
|
|
86d6823132 | |
|
|
4f731edaea |
950
src/DShotRMT.cpp
950
src/DShotRMT.cpp
File diff suppressed because it is too large
Load Diff
208
src/DShotRMT.h
208
src/DShotRMT.h
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <Arduino.h>
|
||||
#include <atomic>
|
||||
|
||||
#include <driver/gpio.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;
|
||||
|
||||
// DShotRMT class for generating DShot signals and receiving telemetry.
|
||||
class DShotRMT
|
||||
{
|
||||
class DShotRMT {
|
||||
public:
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
// Destructor
|
||||
~DShotRMT();
|
||||
// Destructor
|
||||
~DShotRMT();
|
||||
|
||||
// Initialize DShotRMT
|
||||
dshot_result_t begin();
|
||||
// Initialize DShotRMT
|
||||
dshot_result_t begin();
|
||||
|
||||
// Sends a raw throttle value to the ESC.
|
||||
dshot_result_t sendThrottle(uint16_t throttle);
|
||||
// Sends a raw throttle value to the ESC.
|
||||
dshot_result_t sendThrottle(uint16_t throttle);
|
||||
|
||||
// Sends a throttle value as a percentage to the ESC.
|
||||
dshot_result_t sendThrottlePercent(float percent);
|
||||
// Sends a throttle value as a percentage to the ESC.
|
||||
dshot_result_t sendThrottlePercent(float percent);
|
||||
|
||||
// Sends a DShot command to the ESC by accepting an integer value.
|
||||
dshot_result_t sendCommand(uint16_t command_value);
|
||||
// Sends a DShot command to the ESC by accepting an integer value.
|
||||
dshot_result_t sendCommand(uint16_t command_value);
|
||||
|
||||
// Sends a DShot command to the ESC.
|
||||
dshot_result_t sendCommand(dshotCommands_e command);
|
||||
// Sends a DShot command to the ESC.
|
||||
dshot_result_t sendCommand(dshotCommands_e command);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
/**
|
||||
* @brief Sends a custom DShot command to the ESC. Advanced feature, use with caution.
|
||||
* @param command_value The raw command value (0-47).
|
||||
* @param repeat_count The number of times to send the command.
|
||||
* @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);
|
||||
/**
|
||||
* @brief Sends a custom DShot command to the ESC. Advanced feature, use with
|
||||
* caution.
|
||||
* @param command_value The raw command value (0-47).
|
||||
* @param repeat_count The number of times to send the command.
|
||||
* @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);
|
||||
|
||||
// Retrieves telemetry data from the ESC.
|
||||
dshot_result_t getTelemetry();
|
||||
// Retrieves telemetry data from the ESC.
|
||||
dshot_result_t getTelemetry();
|
||||
|
||||
// Sets the motor spin direction.
|
||||
dshot_result_t setMotorSpinDirection(bool reversed);
|
||||
// Sets the motor spin direction.
|
||||
dshot_result_t setMotorSpinDirection(bool reversed);
|
||||
|
||||
// Sends a command to the ESC to save its current settings.
|
||||
dshot_result_t saveESCSettings();
|
||||
// Sends a command to the ESC to save its current settings.
|
||||
dshot_result_t saveESCSettings();
|
||||
|
||||
// Getters for DShot info
|
||||
dshot_mode_t getMode() const { return _mode; }
|
||||
bool isBidirectional() const { return _is_bidirectional; }
|
||||
uint16_t getThrottleValue() const { return _last_throttle; }
|
||||
uint16_t getEncodedFrameValue() const { return _encoded_frame_value; }
|
||||
// Getters for DShot info
|
||||
dshot_mode_t getMode() const { return _mode; }
|
||||
bool isBidirectional() const { return _is_bidirectional; }
|
||||
uint16_t getThrottleValue() const { return _last_throttle; }
|
||||
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:
|
||||
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);
|
||||
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,
|
||||
void *user_data);
|
||||
|
||||
// DShot Configuration Parameters
|
||||
gpio_num_t _gpio; // GPIO pin used for DShot communication
|
||||
dshot_mode_t _mode; // DShot mode (e.g., DSHOT300, DSHOT600)
|
||||
bool _is_bidirectional; // True if bidirectional DShot is enabled
|
||||
uint16_t _motor_magnet_count; // Number of magnets in the motor for RPM calculation
|
||||
dshot_timing_us_t _dshot_timing; // DShot timing parameters in microseconds
|
||||
// DShot Configuration Parameters
|
||||
gpio_num_t _gpio; // GPIO pin used for DShot communication
|
||||
dshot_mode_t _mode; // DShot mode (e.g., DSHOT300, DSHOT600)
|
||||
bool _is_bidirectional; // True if bidirectional DShot is enabled
|
||||
uint16_t
|
||||
_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_channel_handle_t _rmt_tx_channel = nullptr; // RMT transmit 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_ticks_t _rmt_ticks; // Pre-calculated RMT timing ticks
|
||||
uint16_t _pulse_level = 1; // Output level for a pulse (typically high)
|
||||
uint16_t _idle_level = 0; // Output level for idle (typically low)
|
||||
// RMT Hardware Handles and Configuration
|
||||
rmt_channel_handle_t _rmt_rx_channel = nullptr; // RMT receive channel handle
|
||||
rmt_encoder_handle_t _dshot_encoder = nullptr; // DShot RMT encoder handle
|
||||
rmt_ticks_t _rmt_ticks; // Pre-calculated RMT timing ticks
|
||||
uint16_t _pulse_level = 1; // Output level for a pulse (typically high)
|
||||
uint16_t _idle_level = 0; // Output level for idle (typically low)
|
||||
|
||||
// DShot Frame Timing and State Variables
|
||||
uint64_t _last_transmission_time_us = 0; // Timestamp of the last DShot frame transmission
|
||||
uint64_t _frame_timer_us = 0; // Minimum time required between DShot frames
|
||||
float _percent_to_throttle_ratio = 0.0f; // Pre-calculated ratio for throttle percentage conversion
|
||||
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
|
||||
// DShot Frame Timing and State Variables
|
||||
uint64_t _last_transmission_time_us =
|
||||
0; // Timestamp of the last DShot frame transmission
|
||||
uint64_t _frame_timer_us = 0; // Minimum time required between DShot frames
|
||||
float _percent_to_throttle_ratio =
|
||||
0.0f; // Pre-calculated ratio for throttle percentage conversion
|
||||
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
|
||||
std::atomic<uint16_t> _last_erpm_atomic = 0; // Atomically stored last received eRPM value
|
||||
std::atomic<bool> _telemetry_ready_flag_atomic = false; // Atomically stored flag indicating new telemetry data
|
||||
std::atomic<dshot_telemetry_data_t> _last_telemetry_data_atomic = {}; // Atomically stored last received full telemetry data
|
||||
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,
|
||||
};
|
||||
// Telemetry Related Variables
|
||||
std::atomic<uint16_t> _last_erpm_atomic =
|
||||
0; // Atomically stored last received eRPM value
|
||||
std::atomic<bool> _telemetry_ready_flag_atomic =
|
||||
false; // Atomically stored flag indicating new telemetry data
|
||||
std::atomic<dshot_telemetry_data_t> _last_telemetry_data_atomic =
|
||||
{}; // Atomically stored last received full telemetry data
|
||||
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
|
||||
bool _isValidCommand(dshotCommands_e command) const; // Checks if a given DShot command is valid
|
||||
dshot_packet_t _buildDShotPacket(const uint16_t &value) const; // Builds a DShot packet from a value (throttle or command)
|
||||
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet) const; // Combines packet data into a 16-bit DShot frame value
|
||||
uint16_t _calculateCRC(const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame
|
||||
uint8_t _calculateTelemetryCRC(const uint8_t *data, size_t len) const; // Calculates the 8-bit CRC for telemetry data
|
||||
void _extractTelemetryData(const uint8_t *raw_telemetry_bytes, 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
|
||||
// Private Helper Functions for DShot Protocol Logic
|
||||
bool _isValidCommand(dshotCommands_e command)
|
||||
const; // Checks if a given DShot command is valid
|
||||
dshot_packet_t _buildDShotPacket(const uint16_t &value)
|
||||
const; // Builds a DShot packet from a value (throttle or command)
|
||||
uint16_t _buildDShotFrameValue(const dshot_packet_t &packet)
|
||||
const; // Combines packet data into a 16-bit DShot frame value
|
||||
uint16_t _calculateCRC(
|
||||
const uint16_t &data) const; // Calculates the 4-bit CRC for a DShot frame
|
||||
uint8_t _calculateTelemetryCRC(const uint8_t *data, size_t len)
|
||||
const; // Calculates the 8-bit CRC for telemetry data
|
||||
void _extractTelemetryData(const uint8_t *raw_telemetry_bytes,
|
||||
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);
|
||||
|
||||
// Static Callback Function for RMT RX Events
|
||||
void _cleanupRmtResources();
|
||||
dshot_result_t _sendRepeatedCommand(uint16_t value, uint16_t repeat_count,
|
||||
uint16_t delay_us);
|
||||
};
|
||||
|
||||
#include "dshot_utils.h" // Include for helper functions
|
||||
|
|
|
|||
|
|
@ -7,89 +7,232 @@
|
|||
*/
|
||||
|
||||
#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, rmt_channel_handle_t *out_channel, bool is_bidirectional)
|
||||
{
|
||||
rmt_tx_channel_config_t tx_channel_config = {
|
||||
.gpio_num = gpio,
|
||||
.clk_src = DSHOT_CLOCK_SRC_DEFAULT,
|
||||
.resolution_hz = DSHOT_RMT_RESOLUTION,
|
||||
.mem_block_symbols = RMT_TX_BUFFER_SYMBOLS,
|
||||
.trans_queue_depth = RMT_QUEUE_DEPTH,
|
||||
.flags = {
|
||||
.invert_out = static_cast<uint32_t>(is_bidirectional ? 1 : 0),
|
||||
.init_level = 0}};
|
||||
dshot_result_t init_rmt_tx_channel(gpio_num_t gpio,
|
||||
rmt_channel_handle_t *out_channel,
|
||||
bool is_bidirectional) {
|
||||
rmt_tx_channel_config_t tx_channel_config = {
|
||||
.gpio_num = gpio,
|
||||
.clk_src = DSHOT_CLOCK_SRC_DEFAULT,
|
||||
.resolution_hz = DSHOT_RMT_RESOLUTION,
|
||||
.mem_block_symbols = 48,
|
||||
.trans_queue_depth = RMT_QUEUE_DEPTH,
|
||||
.flags = {
|
||||
.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
|
||||
rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation
|
||||
if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK) {
|
||||
return {false, DSHOT_TX_INIT_FAILED};
|
||||
}
|
||||
|
||||
if (rmt_new_tx_channel(&tx_channel_config, out_channel) != DSHOT_OK)
|
||||
{
|
||||
return {false, DSHOT_TX_INIT_FAILED};
|
||||
}
|
||||
if (rmt_enable(*out_channel) != DSHOT_OK) {
|
||||
return {false, DSHOT_TX_INIT_FAILED};
|
||||
}
|
||||
|
||||
if (rmt_enable(*out_channel) != DSHOT_OK)
|
||||
{
|
||||
return {false, DSHOT_TX_INIT_FAILED};
|
||||
}
|
||||
|
||||
return {true, DSHOT_TX_INIT_SUCCESS};
|
||||
return {true, DSHOT_TX_INIT_SUCCESS};
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
rmt_rx_channel_config_t rx_channel_config = {
|
||||
.gpio_num = gpio,
|
||||
.clk_src = DSHOT_CLOCK_SRC_DEFAULT,
|
||||
.resolution_hz = DSHOT_RMT_RESOLUTION,
|
||||
.mem_block_symbols = RMT_RX_BUFFER_SYMBOLS,
|
||||
};
|
||||
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) {
|
||||
rmt_rx_channel_config_t rx_channel_config = {
|
||||
.gpio_num = gpio,
|
||||
.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)
|
||||
{
|
||||
return {false, DSHOT_RX_INIT_FAILED};
|
||||
}
|
||||
if (rmt_new_rx_channel(&rx_channel_config, out_channel) != DSHOT_OK) {
|
||||
return {false, DSHOT_RX_INIT_FAILED};
|
||||
}
|
||||
|
||||
if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks, user_data) != DSHOT_OK)
|
||||
{
|
||||
return {false, DSHOT_CALLBACK_REGISTERING_FAILED};
|
||||
}
|
||||
if (rmt_rx_register_event_callbacks(*out_channel, rx_event_callbacks,
|
||||
user_data) != DSHOT_OK) {
|
||||
return {false, DSHOT_CALLBACK_REGISTERING_FAILED};
|
||||
}
|
||||
|
||||
if (rmt_enable(*out_channel) != DSHOT_OK)
|
||||
{
|
||||
return {false, DSHOT_RX_INIT_FAILED};
|
||||
}
|
||||
if (rmt_enable(*out_channel) != DSHOT_OK) {
|
||||
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
|
||||
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)
|
||||
{
|
||||
rmt_bytes_encoder_config_t encoder_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,
|
||||
},
|
||||
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) {
|
||||
rmt_bytes_encoder_config_t encoder_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 // DShot is MSB first
|
||||
}};
|
||||
.flags = {
|
||||
.msb_first = 1 // DShot is MSB first
|
||||
}};
|
||||
|
||||
if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK)
|
||||
{
|
||||
return {false, DSHOT_ENCODER_INIT_FAILED};
|
||||
}
|
||||
if (rmt_new_bytes_encoder(&encoder_config, out_encoder) != DSHOT_OK) {
|
||||
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(©_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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue