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
|
#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
|
||||||
|
|
|
||||||
|
|
@ -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(©_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
|
* @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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue