fix non dma on esp32-s3. hacky way shouldnt be used. TODO - fix underlying issue with lack of termination of signal
This commit is contained in:
parent
262feec75c
commit
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);
|
uint16_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
|
||||||
|
|
|
||||||
|
|
@ -9,87 +9,85 @@
|
||||||
#include "dshot_init.h"
|
#include "dshot_init.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 = {.invert_out = static_cast<uint32_t>(is_bidirectional ? 1 : 0),
|
||||||
.init_level = 0}};
|
.init_level = 0,
|
||||||
|
.with_dma = 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};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue