...use dshot_packet structure

This commit is contained in:
Wastl Kraus 2025-07-23 00:12:08 +02:00
parent 2a40e2ba12
commit 06ee4d00a0
2 changed files with 41 additions and 54 deletions

View File

@ -41,6 +41,8 @@ void DShotRMT::begin()
_receive_config.signal_range_min_ns = 300; _receive_config.signal_range_min_ns = 300;
_receive_config.signal_range_max_ns = 5000; _receive_config.signal_range_max_ns = 5000;
_dshot_packet.telemetric_request = _isBidirectional;
} }
// Configure TX RMT Channel // Configure TX RMT Channel
@ -82,17 +84,15 @@ void DShotRMT::begin()
// Encodes and transmits a valid DShot throttle value (48 - 2047) // Encodes and transmits a valid DShot throttle value (48 - 2047)
void DShotRMT::setThrottle(uint16_t throttle) void DShotRMT::setThrottle(uint16_t throttle)
{ {
// Clamp input range and mask to 11 bits // Clamp input range for packet
throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0x7FF; _dshot_packet.throttle_value = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX) & 0b0000011111111111;
_lastThrottle = throttle; // Calculate CRC for every throttle value
calculateCRC(&_dshot_packet);
// Convert throttle value to DShot packet format
_tx_packet = assambleDShotPaket(_lastThrottle);
// Encode RMT symbols // Encode RMT symbols
size_t count = 0; size_t count = 0;
encodeDShotTX(_tx_packet, _tx_symbols, count); encodeDShotTX(&_dshot_packet, _tx_symbols, count);
// Transmit the packet // Transmit the packet
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, count * sizeof(rmt_symbol_word_t), &_transmit_config) != 0) if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, _tx_symbols, count * sizeof(rmt_symbol_word_t), &_transmit_config) != 0)
@ -111,13 +111,17 @@ uint32_t DShotRMT::getERPM()
if (_isBidirectional) if (_isBidirectional)
{ {
if (_rmt_rx_channel == nullptr) if (_rmt_rx_channel == nullptr)
{
Serial.println("No bidirectional DShot support."); Serial.println("No bidirectional DShot support.");
return _last_erpm; return _last_erpm;
}
// Try to receive a new frame // Try to receive a new frame
if (!rmt_receive(_rmt_rx_channel, _rx_symbols, sizeof(_rx_symbols), &_receive_config)) if (!rmt_receive(_rmt_rx_channel, _rx_symbols, sizeof(_rx_symbols), &_receive_config))
{
Serial.println("No valid DShot frame received"); Serial.println("No valid DShot frame received");
return _last_erpm; return _last_erpm;
}
_last_erpm = decodeDShotRX(_rx_symbols, DSHOT_BITS_PER_FRAME); _last_erpm = decodeDShotRX(_rx_symbols, DSHOT_BITS_PER_FRAME);
return _last_erpm; return _last_erpm;
@ -138,45 +142,35 @@ uint32_t DShotRMT::getMotorRPM(uint8_t magnet_count)
} }
// Calculates CRC for DShot packet // Calculates CRC for DShot packet
uint16_t DShotRMT::calculateCRC(uint16_t dshot_packet) void DShotRMT::calculateCRC(dshot_packet_t *dshot_packet)
{ {
uint16_t packet = (dshot_packet << 1) | (_isBidirectional ? 1 : 0); uint16_t packet = (dshot_packet->throttle_value << 1) | (dshot_packet->telemetric_request);
// Reset CRC container // Reset CRC container
_packet_crc = DSHOT_NULL_PACKET; dshot_packet->checksum = DSHOT_NULL_PACKET;
// CRC calculation for DShot (4 bits) // CRC calculation for DShot (4 bits)
_packet_crc = ((packet ^ (packet >> 4) ^ (packet >> 8)) & 0xF); dshot_packet->checksum = ((packet ^ (packet >> 4) ^ (packet >> 8)) & 0b0000000000001111);
// CRC is inverted for bidirectional DShot // CRC is inverted for bidirectional DShot
if (_isBidirectional) if (dshot_packet->telemetric_request)
_packet_crc = (~_packet_crc) & 0xF; dshot_packet->checksum = (~dshot_packet->checksum) & 0b0000000000001111;
return _packet_crc;
} }
// Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC) // Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC)
uint16_t DShotRMT::assambleDShotPaket(uint16_t value) uint16_t DShotRMT::parseDShotPacket(const dshot_packet_t *dshot_packet) const
{ {
uint16_t throttle = value & 0x7FF; uint16_t parsed_packet = ((dshot_packet->throttle_value << 1) | (dshot_packet->telemetric_request)) & 0b0000111111111111;
return ((parsed_packet << 4) | (dshot_packet->checksum)) & 0b1111111111111111;
// Reset packet container
_tx_packet = DSHOT_NULL_PACKET;
// Assemble raw DShot packet and add checksum
_packet_crc = calculateCRC(throttle);
_tx_packet = (throttle << 1) | (_isBidirectional ? 1 : 0);
_tx_packet = (_tx_packet << 4) | _packet_crc;
return _tx_packet;
} }
// Converts a 16-bit packet into a valid DShot frame for RMT // Converts a 16-bit packet into a valid DShot frame for RMT
void DShotRMT::encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols, size_t &count) void DShotRMT::encodeDShotTX(dshot_packet_t *dshot_packet, rmt_symbol_word_t *symbols, size_t &count)
{ {
count = 0; count = 0;
uint16_t frame_bits = parseDShotPacket(dshot_packet);
uint32_t ticks_per_bit = 0; uint32_t ticks_per_bit = 0;
uint32_t ticks_zero_high = 0; uint32_t ticks_zero_high = 0;
uint32_t ticks_one_high = 0; uint32_t ticks_one_high = 0;
@ -205,20 +199,16 @@ void DShotRMT::encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols,
ticks_one_high = 6; ticks_one_high = 6;
break; break;
case DSHOT_OFF: case DSHOT_OFF:
default: return;
ticks_per_bit = 0;
ticks_zero_high = 0;
ticks_one_high = 0;
break;
} }
uint32_t ticks_zero_low = ticks_per_bit - ticks_zero_high; uint32_t ticks_zero_low = ticks_per_bit - ticks_zero_high;
uint32_t ticks_one_low = ticks_per_bit - ticks_one_high; uint32_t ticks_one_low = ticks_per_bit - ticks_one_high;
// Fill the 16 DShot bits array with selected timings // Convert the parsed dshot frame to rmt_tx data
for (int i = 15; i >= 0; i--) for (int i = 15; i >= 0; i--)
{ {
bool bit = (dshot_packet >> i) & 0x1; bool bit = (frame_bits >> i) & 0x1;
if (_isBidirectional) if (_isBidirectional)
{ {
symbols[count].level0 = 0; symbols[count].level0 = 0;
@ -251,12 +241,12 @@ uint16_t DShotRMT::decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t coun
// Extract CRC and payload // Extract CRC and payload
uint16_t payload = received_frame >> 4; uint16_t payload = received_frame >> 4;
uint8_t crc_received = received_frame & 0xF; uint8_t crc_received = received_frame & 0b0000000000001111;
// Calculate CRC for received frame // Calculate CRC for received frame
uint8_t crc_calculated = (payload ^ (payload >> 4) ^ (payload >> 8)) & 0xF; uint8_t crc_calculated = (payload ^ (payload >> 4) ^ (payload >> 8)) & 0b0000000000001111;
if (_isBidirectional) if (_isBidirectional)
crc_calculated = (~crc_calculated) & 0xF; crc_calculated = (~crc_calculated) & 0b0000000000001111;
// Check CRC // Check CRC
if (crc_received != crc_calculated) if (crc_received != crc_calculated)
@ -265,8 +255,6 @@ uint16_t DShotRMT::decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t coun
return _last_erpm; return _last_erpm;
} }
// Remove telemetry bit, keep raw value // Remove telemetry bit
uint16_t raw = payload >> 1; return _last_erpm = payload >> 1;
return _last_erpm = raw;
} }

View File

@ -32,15 +32,15 @@ static constexpr size_t TX_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
static constexpr size_t RX_BUFFER_SIZE = 32; // Padding for RX decoding static constexpr size_t RX_BUFFER_SIZE = 32; // Padding for RX decoding
// DShot Packet structure // DShot Packet structure
typedef struct typedef struct dshot_packet_s
{ {
uint16_t throttle_value : 11; uint16_t throttle_value : 11;
bool telemetric_request : 1; bool telemetric_request : 1;
uint16_t checksum : 4; uint8_t checksum : 4;
} dshot_packet_t; } dshot_packet_t;
// --- DShot Mode Selection --- // --- DShot Mode Selection ---
typedef enum typedef enum dshot_mode_s
{ {
DSHOT_OFF, DSHOT_OFF,
DSHOT150, DSHOT150,
@ -72,19 +72,20 @@ public:
uint8_t getPauseDuration() const { return _pauseDuration; } uint8_t getPauseDuration() const { return _pauseDuration; }
void setPauseDuration(uint8_t pauseDuration) { _pauseDuration = pauseDuration; } void setPauseDuration(uint8_t pauseDuration) { _pauseDuration = pauseDuration; }
private: protected:
// Calculates the checksum for a DShot packet // Calculates the checksum for a DShot packet
uint16_t calculateCRC(uint16_t dshot_packet); void calculateCRC(dshot_packet_t *dshot_packet);
// Assembles DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC) // Parses the DShot packet (11 bit throttle + 1 bit telemetry request + 4 bit CRC)
uint16_t assambleDShotPaket(uint16_t value); uint16_t parseDShotPacket(const dshot_packet_t *dshot_packet) const;
// Converts a 16-bit DShot packet into RMT symbols // Converts a 16-bit DShot packet into RMT symbols
void encodeDShotTX(uint16_t dshot_packet, rmt_symbol_word_t *symbols, size_t &count); void encodeDShotTX(dshot_packet_t *dshot_packet, rmt_symbol_word_t *symbols, size_t &count);
// Decodes the ESC answer // Decodes the ESC answer
uint16_t decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t count); uint16_t decodeDShotRX(const rmt_symbol_word_t *symbols, uint32_t count);
private:
// --- Configuration Parameters --- // --- Configuration Parameters ---
gpio_num_t _gpio; gpio_num_t _gpio;
dshot_mode_t _mode; dshot_mode_t _mode;
@ -92,10 +93,8 @@ private:
uint8_t _pauseDuration; uint8_t _pauseDuration;
// --- DShot Packets Container --- // --- DShot Packets Container ---
uint16_t _lastThrottle = DSHOT_NULL_PACKET;
uint16_t _rx_packet = DSHOT_NULL_PACKET; uint16_t _rx_packet = DSHOT_NULL_PACKET;
uint16_t _tx_packet = DSHOT_NULL_PACKET; dshot_packet_t _dshot_packet = {};
uint8_t _packet_crc = 0;
// --- RMT Channel Handles --- // --- RMT Channel Handles ---
rmt_channel_handle_t _rmt_rx_channel = nullptr; rmt_channel_handle_t _rmt_rx_channel = nullptr;