2025-06-11 08:29:59 +01:00
|
|
|
/**
|
|
|
|
|
* @file DShotRMT.cpp
|
2025-06-12 23:45:48 +01:00
|
|
|
* @brief DShot signal generation using ESP32 RMT with continuous repeat and pause between frames, including BiDirectional support
|
2025-06-11 08:29:59 +01:00
|
|
|
* @author Wastl Kraus
|
|
|
|
|
* @date 2025-06-11
|
|
|
|
|
* @license MIT
|
|
|
|
|
*/
|
2021-07-04 02:01:26 +01:00
|
|
|
|
2023-03-26 15:19:54 +01:00
|
|
|
#include <DShotRMT.h>
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
//
|
2025-06-11 08:29:59 +01:00
|
|
|
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool isBidirectional)
|
|
|
|
|
: _gpio(gpio), _mode(mode), _isBidirectional(isBidirectional) {}
|
|
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
//
|
2025-06-11 08:29:59 +01:00
|
|
|
void DShotRMT::begin()
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2025-06-12 23:45:48 +01:00
|
|
|
if (_isBidirectional)
|
|
|
|
|
{
|
|
|
|
|
rmt_rx_channel_config_t rmt_rx_channel_config = {
|
|
|
|
|
.gpio_num = _gpio,
|
|
|
|
|
.clk_src = RMT_CLK_SRC_DEFAULT,
|
|
|
|
|
.resolution_hz = DEFAULT_RES_HZ,
|
|
|
|
|
.mem_block_symbols = 64,
|
|
|
|
|
.flags = {
|
|
|
|
|
.invert_in = false,
|
|
|
|
|
.with_dma = false}};
|
|
|
|
|
|
|
|
|
|
rmt_new_rx_channel(&rmt_rx_channel_config, &_rmt_rx_channel);
|
|
|
|
|
rmt_enable(_rmt_rx_channel);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-12 11:24:27 +01:00
|
|
|
rmt_tx_channel_config_t rmt_tx_channel_config = {
|
2025-06-11 08:29:59 +01:00
|
|
|
.gpio_num = _gpio,
|
|
|
|
|
.clk_src = RMT_CLK_SRC_DEFAULT,
|
|
|
|
|
.resolution_hz = DEFAULT_RES_HZ,
|
|
|
|
|
.mem_block_symbols = 64,
|
|
|
|
|
.trans_queue_depth = 1,
|
|
|
|
|
.flags = {
|
2025-06-12 23:45:48 +01:00
|
|
|
// invert Signal if BiDirectional DShot Mode
|
2025-06-11 08:29:59 +01:00
|
|
|
.invert_out = _isBidirectional,
|
|
|
|
|
.with_dma = false}};
|
|
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
rmt_new_tx_channel(&rmt_tx_channel_config, &_rmt_tx_channel);
|
|
|
|
|
rmt_enable(_rmt_tx_channel);
|
2025-06-11 08:29:59 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Create new encoder
|
|
|
|
|
if (!_dshot_encoder)
|
2025-06-11 08:29:59 +01:00
|
|
|
{
|
|
|
|
|
rmt_copy_encoder_config_t enc_cfg = {};
|
2025-06-12 23:45:48 +01:00
|
|
|
rmt_new_copy_encoder(&enc_cfg, &_dshot_encoder);
|
2025-06-11 08:29:59 +01:00
|
|
|
}
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
_transmit_config.loop_count = -1;
|
|
|
|
|
_transmit_config.flags.eot_level = _isBidirectional;
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
//
|
|
|
|
|
void DShotRMT::setThrottle(uint16_t throttle)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2025-06-12 23:45:48 +01:00
|
|
|
// Fake 10 Bit transformation to be sure
|
|
|
|
|
throttle = throttle & 0b0000011111111111;
|
2025-06-12 11:24:27 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Has Throttle really changed?
|
2025-06-12 11:24:27 +01:00
|
|
|
if (throttle == _lastThrottle)
|
2025-06-11 08:29:59 +01:00
|
|
|
return;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2025-06-11 08:29:59 +01:00
|
|
|
_lastThrottle = throttle;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Prepare Throttle paket
|
|
|
|
|
dshot_packet = (throttle << 1) | (_isBidirectional ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
// CRC Calculation
|
|
|
|
|
uint16_t crc = 0;
|
|
|
|
|
|
|
|
|
|
if (_isBidirectional)
|
|
|
|
|
{
|
|
|
|
|
// Calculate checksum in inverted/BiDirectional Mode
|
|
|
|
|
crc = (~(dshot_packet ^ (dshot_packet >> 4) ^ (dshot_packet >> 8))) & 0x0F;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
crc = (dshot_packet ^ (dshot_packet >> 4) ^ (dshot_packet >> 8)) & 0x0F;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// attach CRC to DShot Paket
|
|
|
|
|
dshot_packet = (dshot_packet << 4) | crc;
|
2024-02-11 09:44:56 +00:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Encode DShot Paket
|
2025-06-11 08:29:59 +01:00
|
|
|
rmt_symbol_word_t symbols[32] = {};
|
|
|
|
|
size_t count = 0;
|
2024-02-11 09:44:56 +00:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
buildFrameSymbols(dshot_packet, symbols, count);
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Reset RMT Signnal loop before sending new value
|
|
|
|
|
rmt_disable(_rmt_tx_channel);
|
|
|
|
|
rmt_enable(_rmt_tx_channel);
|
2025-06-11 08:29:59 +01:00
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
// Finally transmit the complete DShot Paket
|
|
|
|
|
rmt_transmit(_rmt_tx_channel, _dshot_encoder, symbols, count * sizeof(rmt_symbol_word_t), &_transmit_config);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-12 23:45:48 +01:00
|
|
|
//
|
2025-06-11 08:29:59 +01:00
|
|
|
void DShotRMT::buildFrameSymbols(uint16_t dshot_packet, rmt_symbol_word_t *symbols, size_t &count)
|
2023-03-27 18:47:23 +01:00
|
|
|
{
|
2025-06-11 08:29:59 +01:00
|
|
|
uint32_t ticks_per_bit = 0;
|
|
|
|
|
uint32_t ticks_zero_high = 0;
|
|
|
|
|
uint32_t ticks_one_high = 0;
|
2023-03-27 18:47:23 +01:00
|
|
|
|
2025-06-11 08:29:59 +01:00
|
|
|
switch (_mode)
|
2023-03-27 18:47:23 +01:00
|
|
|
{
|
|
|
|
|
case DSHOT150:
|
2025-06-12 23:45:48 +01:00
|
|
|
ticks_per_bit = 64;
|
|
|
|
|
ticks_zero_high = 24;
|
|
|
|
|
ticks_one_high = 48;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
case DSHOT300:
|
2025-06-12 23:45:48 +01:00
|
|
|
ticks_per_bit = 32;
|
2025-06-11 08:29:59 +01:00
|
|
|
ticks_zero_high = 12;
|
2025-06-12 23:45:48 +01:00
|
|
|
ticks_one_high = 24;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
case DSHOT600:
|
2025-06-12 23:45:48 +01:00
|
|
|
ticks_per_bit = 16;
|
2025-06-11 08:29:59 +01:00
|
|
|
ticks_zero_high = 6;
|
2025-06-12 23:45:48 +01:00
|
|
|
ticks_one_high = 12;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-11 08:29:59 +01:00
|
|
|
uint32_t ticks_zero_low = ticks_per_bit - ticks_zero_high;
|
|
|
|
|
uint32_t ticks_one_low = ticks_per_bit - ticks_one_high;
|
2023-04-15 06:45:14 +01:00
|
|
|
|
2025-06-11 08:29:59 +01:00
|
|
|
for (int i = 15; i >= 0; i--)
|
2023-04-15 06:45:14 +01:00
|
|
|
{
|
2025-06-11 08:29:59 +01:00
|
|
|
bool bit = (dshot_packet >> i) & 0x01;
|
|
|
|
|
symbols[count].level0 = 1;
|
|
|
|
|
symbols[count].duration0 = bit ? ticks_one_high : ticks_zero_high;
|
|
|
|
|
symbols[count].level1 = 0;
|
|
|
|
|
symbols[count].duration1 = bit ? ticks_one_low : ticks_zero_low;
|
|
|
|
|
count++;
|
2023-04-15 06:45:14 +01:00
|
|
|
}
|
|
|
|
|
|
2025-06-11 08:29:59 +01:00
|
|
|
symbols[count].level0 = 0;
|
|
|
|
|
symbols[count].duration0 = ticks_per_bit * PAUSE_BITS;
|
|
|
|
|
symbols[count].level1 = 0;
|
|
|
|
|
symbols[count].duration1 = 0;
|
|
|
|
|
count++;
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|