Use official byte-Encoder
This commit is contained in:
parent
0028db7dca
commit
d899ba1471
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml)
|
||||
|
||||
A C++ library for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT encoder API** (`rmt_tx.h` / `rmt_rx.h`). It provides a simple, efficient, and hardware-timed way to control brushless motors in both Arduino and ESP-IDF projects. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch.
|
||||
A C++ library for generating DShot signals on ESP32 microcontrollers using the **modern ESP-IDF 5 RMT encoder API** (`rmt_tx.h` / `rmt_rx.h`). It leverages the standard `rmt_bytes_encoder` to ensure an efficient, hardware-timed, and maintainable implementation. **Note:** A byte-swapping fix has been implemented to address endianness differences when using `rmt_bytes_encoder` on ESP32. The library provides a simple way to control brushless motors in both Arduino and ESP-IDF projects. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch.
|
||||
|
||||
## 🚀 Core Features
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, ui
|
|||
_last_command_timestamp(0),
|
||||
_encoded_frame_value(0),
|
||||
_packet{0},
|
||||
_level0(1), // DShot standard: signal is idle-low, so pulses start by going HIGH
|
||||
_level1(0), // DShot standard: signal returns to LOW after the high pulse
|
||||
_pulse_level(1), // DShot standard: signal is idle-low, so pulses start by going HIGH
|
||||
_idle_level(0), // DShot standard: signal returns to LOW after the high pulse
|
||||
_rmt_tx_channel(nullptr),
|
||||
_rmt_rx_channel(nullptr),
|
||||
_dshot_encoder(nullptr),
|
||||
|
|
@ -355,9 +355,25 @@ dshot_result_t DShotRMT::_initRXChannel()
|
|||
|
||||
dshot_result_t DShotRMT::_initDShotEncoder()
|
||||
{
|
||||
rmt_copy_encoder_config_t encoder_config = {};
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
|
||||
if (rmt_new_bytes_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
|
||||
{
|
||||
return {false, dshot_msg_code_t::DSHOT_ERROR_ENCODER_INIT_FAILED};
|
||||
}
|
||||
|
|
@ -424,50 +440,30 @@ void DShotRMT::_preCalculateRMTTicks()
|
|||
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
||||
{
|
||||
// Ensure enough time has passed since the last transmission
|
||||
if (!_timer_signal())
|
||||
if (!_isFrameIntervalElapsed())
|
||||
{
|
||||
return {true, dshot_msg_code_t::DSHOT_ERROR_NONE};
|
||||
}
|
||||
|
||||
rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME];
|
||||
dshot_result_t result = _encodeDShotFrame(packet, tx_symbols);
|
||||
_encoded_frame_value = _buildDShotFrameValue(packet);
|
||||
|
||||
if (!result.success)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
// Byte-swap the 16-bit value for correct transmission order (ESP32 is little-endian, DShot is MSB first)
|
||||
uint16_t swapped_value = __builtin_bswap16(_encoded_frame_value);
|
||||
|
||||
size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
||||
// The DShot frame is 16 bits, which is 2 bytes
|
||||
size_t tx_size_bytes = sizeof(swapped_value);
|
||||
|
||||
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK)
|
||||
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, &swapped_value, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK)
|
||||
{
|
||||
return {false, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_FAILED};
|
||||
}
|
||||
|
||||
_timer_reset(); // Reset the timer for the next frame
|
||||
_recordFrameTransmissionTime(); // Reset the timer for the next frame
|
||||
|
||||
return {true, dshot_msg_code_t::DSHOT_ERROR_TRANSMISSION_SUCCESS};
|
||||
}
|
||||
|
||||
// This function needs to be fast, as it generates the RMT symbols just before sending
|
||||
dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols)
|
||||
{
|
||||
_encoded_frame_value = _buildDShotFrameValue(packet);
|
||||
|
||||
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
|
||||
{
|
||||
int bit_position = DSHOT_BITS_PER_FRAME - 1 - i;
|
||||
bool bit = (_encoded_frame_value >> bit_position) & 1;
|
||||
|
||||
// A '1' bit has a longer high-time, a '0' bit has a shorter high-time
|
||||
symbols[i].level0 = _level0; // Go HIGH
|
||||
symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks;
|
||||
symbols[i].level1 = _level1; // Go LOW
|
||||
symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks;
|
||||
}
|
||||
|
||||
return {true, dshot_msg_code_t::DSHOT_ERROR_ENCODING_SUCCESS};
|
||||
}
|
||||
|
||||
// Placed in IRAM for high performance, as it's called from an ISR context
|
||||
uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
|
||||
|
|
@ -510,7 +506,7 @@ uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
|
|||
}
|
||||
|
||||
// Timing Control Functions
|
||||
bool IRAM_ATTR DShotRMT::_timer_signal()
|
||||
bool IRAM_ATTR DShotRMT::_isFrameIntervalElapsed()
|
||||
{
|
||||
// Check if the minimum interval between frames has passed
|
||||
uint64_t current_time = esp_timer_get_time();
|
||||
|
|
@ -518,11 +514,10 @@ bool IRAM_ATTR DShotRMT::_timer_signal()
|
|||
return elapsed >= _frame_timer_us;
|
||||
}
|
||||
|
||||
bool DShotRMT::_timer_reset()
|
||||
void DShotRMT::_recordFrameTransmissionTime()
|
||||
{
|
||||
// Record the time of the current transmission
|
||||
_last_transmission_time_us = esp_timer_get_time();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Static Callback Functions
|
||||
|
|
|
|||
|
|
@ -213,8 +213,8 @@ private:
|
|||
uint64_t _last_command_timestamp;
|
||||
uint16_t _encoded_frame_value;
|
||||
dshot_packet_t _packet;
|
||||
uint16_t _level0; // DShot protocol: Signal is idle-low, so pulses start by going HIGH.
|
||||
uint16_t _level1; // DShot protocol: Signal returns to LOW after the high pulse.
|
||||
uint16_t _pulse_level; // DShot protocol: Signal is idle-low, so pulses start by going HIGH.
|
||||
uint16_t _idle_level; // DShot protocol: Signal returns to LOW after the high pulse.
|
||||
|
||||
// RMT Hardware Handles
|
||||
rmt_channel_handle_t _rmt_tx_channel;
|
||||
|
|
@ -245,12 +245,11 @@ private:
|
|||
|
||||
// Private Frame Processing Functions
|
||||
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
||||
dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
|
||||
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
|
||||
|
||||
// Private Timing Control Functions
|
||||
bool _timer_signal();
|
||||
bool _timer_reset();
|
||||
bool _isFrameIntervalElapsed();
|
||||
void _recordFrameTransmissionTime();
|
||||
|
||||
// Static Callback Functions
|
||||
static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
||||
|
|
|
|||
Loading…
Reference in New Issue