2021-07-04 02:01:26 +01:00
|
|
|
//
|
2023-03-10 16:45:18 +00:00
|
|
|
// Name: DShotRMT.cpp
|
2021-07-04 02:01:26 +01:00
|
|
|
// Created: 20.03.2021 00:49:15
|
|
|
|
|
// Author: derdoktor667
|
|
|
|
|
//
|
|
|
|
|
|
2023-03-26 15:19:54 +01:00
|
|
|
#include <DShotRMT.h>
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Constructor that takes gpio and rmtChannel as arguments
|
2023-03-27 18:47:23 +01:00
|
|
|
DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Initialize the dshot_config structure with the arguments passed to the constructor
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_config.gpio_num = gpio;
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.pin_num = static_cast<uint8_t>(gpio);
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_config.rmt_channel = rmtChannel;
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.mem_block_num = static_cast<uint8_t>(RMT_CHANNEL_MAX - static_cast<uint8_t>(rmtChannel));
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Create an empty packet using the DSHOT_NULL_PACKET and the buildTxRmtItem function
|
2023-03-27 18:47:23 +01:00
|
|
|
buildTxRmtItem(DSHOT_NULL_PACKET);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Constructor that takes pin and channel as arguments
|
2023-03-27 18:47:23 +01:00
|
|
|
DShotRMT::DShotRMT(uint8_t pin, uint8_t channel)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Initialize the dshot_config structure with the arguments passed to the constructor
|
|
|
|
|
dshot_config.gpio_num = static_cast<gpio_num_t>(pin);
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_config.pin_num = pin;
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.rmt_channel = static_cast<rmt_channel_t>(channel);
|
|
|
|
|
dshot_config.mem_block_num = RMT_CHANNEL_MAX - channel;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Create an empty packet using the DSHOT_NULL_PACKET and the buildTxRmtItem function
|
2023-03-27 18:47:23 +01:00
|
|
|
buildTxRmtItem(DSHOT_NULL_PACKET);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-25 15:08:58 +00:00
|
|
|
DShotRMT::~DShotRMT()
|
|
|
|
|
{
|
2023-03-27 18:47:23 +01:00
|
|
|
// Uninstall the RMT driver
|
2023-03-24 12:44:49 +00:00
|
|
|
rmt_driver_uninstall(dshot_config.rmt_channel);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-24 12:44:49 +00:00
|
|
|
DShotRMT::DShotRMT(DShotRMT const &)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-03-24 12:44:49 +00:00
|
|
|
// ...write me
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-27 18:47:23 +01:00
|
|
|
bool DShotRMT::begin(dshot_mode_t dshot_mode, bool is_bidirectional)
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set DShot configuration parameters based on input parameters
|
2023-03-27 18:47:23 +01:00
|
|
|
dshot_config.mode = dshot_mode;
|
|
|
|
|
dshot_config.clk_div = DSHOT_CLK_DIVIDER;
|
|
|
|
|
dshot_config.name_str = dshot_mode_name[dshot_mode];
|
|
|
|
|
dshot_config.is_bidirectional = is_bidirectional;
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set timing parameters based on selected DShot mode
|
2023-03-27 18:47:23 +01:00
|
|
|
switch (dshot_config.mode)
|
|
|
|
|
{
|
|
|
|
|
case DSHOT150:
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.ticks_per_bit = 64;
|
|
|
|
|
dshot_config.ticks_zero_high = 24;
|
|
|
|
|
dshot_config.ticks_one_high = 48;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DSHOT300:
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.ticks_per_bit = 32;
|
|
|
|
|
dshot_config.ticks_zero_high = 12;
|
|
|
|
|
dshot_config.ticks_one_high = 24;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DSHOT600:
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.ticks_per_bit = 16;
|
|
|
|
|
dshot_config.ticks_zero_high = 6;
|
|
|
|
|
dshot_config.ticks_one_high = 12;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DSHOT1200:
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.ticks_per_bit = 8;
|
|
|
|
|
dshot_config.ticks_zero_high = 3;
|
|
|
|
|
dshot_config.ticks_one_high = 6;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Default case to handle invalid input
|
2023-03-27 18:47:23 +01:00
|
|
|
default:
|
2023-04-15 06:45:14 +01:00
|
|
|
dshot_config.ticks_per_bit = 0;
|
|
|
|
|
dshot_config.ticks_zero_high = 0;
|
|
|
|
|
dshot_config.ticks_one_high = 0;
|
2023-03-27 18:47:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Calculate low signal timing
|
2023-03-27 18:47:23 +01:00
|
|
|
dshot_config.ticks_zero_low = (dshot_config.ticks_per_bit - dshot_config.ticks_zero_high);
|
|
|
|
|
dshot_config.ticks_one_low = (dshot_config.ticks_per_bit - dshot_config.ticks_one_high);
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set up RMT configuration for DShot transmission
|
2023-03-27 18:47:23 +01:00
|
|
|
dshot_tx_rmt_config.rmt_mode = RMT_MODE_TX;
|
|
|
|
|
dshot_tx_rmt_config.channel = dshot_config.rmt_channel;
|
|
|
|
|
dshot_tx_rmt_config.gpio_num = dshot_config.gpio_num;
|
|
|
|
|
dshot_tx_rmt_config.mem_block_num = dshot_config.mem_block_num;
|
|
|
|
|
dshot_tx_rmt_config.clk_div = dshot_config.clk_div;
|
|
|
|
|
dshot_tx_rmt_config.tx_config.loop_en = false;
|
|
|
|
|
dshot_tx_rmt_config.tx_config.carrier_en = false;
|
|
|
|
|
dshot_tx_rmt_config.tx_config.idle_output_en = true;
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set idle level for RMT transmission based on input parameter
|
2023-03-27 18:47:23 +01:00
|
|
|
if (dshot_config.is_bidirectional)
|
|
|
|
|
{
|
|
|
|
|
dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set up selected DShot mode
|
2023-03-27 18:47:23 +01:00
|
|
|
rmt_config(&dshot_tx_rmt_config);
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Install RMT driver and return result
|
2023-03-27 18:47:23 +01:00
|
|
|
return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Define a function to send a DShot command over an RMT interface to control a brushless motor's speed.
|
2023-04-13 20:10:51 +01:00
|
|
|
void DShotRMT::sendThrottleValue(uint16_t throttle_value)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_packet_t dshot_rmt_packet = {};
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Check if the throttle value is less than the minimum allowed value for the DShot protocol.
|
2023-03-24 12:44:49 +00:00
|
|
|
if (throttle_value < DSHOT_THROTTLE_MIN)
|
|
|
|
|
{
|
|
|
|
|
throttle_value = DSHOT_THROTTLE_MIN;
|
|
|
|
|
}
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Check if the throttle value is greater than the maximum allowed value for the DShot protocol.
|
2023-03-24 12:44:49 +00:00
|
|
|
if (throttle_value > DSHOT_THROTTLE_MAX)
|
|
|
|
|
{
|
|
|
|
|
throttle_value = DSHOT_THROTTLE_MAX;
|
|
|
|
|
}
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_rmt_packet.throttle_value = throttle_value;
|
2023-04-15 06:45:14 +01:00
|
|
|
|
|
|
|
|
// Telemetric using additional pin on the ESC is not supported.
|
2023-04-13 20:10:51 +01:00
|
|
|
dshot_rmt_packet.telemetric_request = NO_TELEMETRIC;
|
2023-04-15 06:45:14 +01:00
|
|
|
|
|
|
|
|
// Calculate the checksum for the DShot packet using the calculateCRC function.
|
2023-04-13 20:10:51 +01:00
|
|
|
dshot_rmt_packet.checksum = calculateCRC(dshot_rmt_packet);
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Send the DShot packet over the RMT interface to control the motor's speed.
|
2023-03-27 18:47:23 +01:00
|
|
|
sendRmtPaket(dshot_rmt_packet);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// This method builds the RMT data transmission sequence for the DShot protocol
|
2023-03-27 18:47:23 +01:00
|
|
|
rmt_item32_t *DShotRMT::buildTxRmtItem(uint16_t parsed_packet)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Check if DShot is set to bidirectional mode
|
2023-03-27 18:47:23 +01:00
|
|
|
if (dshot_config.is_bidirectional)
|
2023-03-24 12:44:49 +00:00
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// If bidirectional, invert the high/low bits
|
2023-03-24 12:44:49 +00:00
|
|
|
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
|
|
|
|
|
{
|
|
|
|
|
if (parsed_packet & 0b1000000000000000)
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set RMT item for a logic high signal
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_low;
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_high;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set RMT item for a logic low signal
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_low;
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_high;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set level of RMT item
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].level0 = 0;
|
|
|
|
|
dshot_tx_rmt_item[i].level1 = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// If not bidirectional, set the RMT items as usual
|
2023-03-24 12:44:49 +00:00
|
|
|
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
|
|
|
|
|
{
|
|
|
|
|
if (parsed_packet & 0b1000000000000000)
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set RMT item for a logic high signal
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high;
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set RMT item for a logic low signal
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high;
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_low;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set level of RMT item
|
2023-03-24 12:44:49 +00:00
|
|
|
dshot_tx_rmt_item[i].level0 = 1;
|
|
|
|
|
dshot_tx_rmt_item[i].level1 = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Set end marker for each frame
|
2023-03-27 18:47:23 +01:00
|
|
|
if (dshot_config.is_bidirectional)
|
2023-03-24 12:44:49 +00:00
|
|
|
{
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 1;
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 0;
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Add packet seperator aka DShot Pause.
|
2023-03-27 18:47:23 +01:00
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = DSHOT_PAUSE;
|
2023-03-24 12:44:49 +00:00
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Return the rmt_item
|
2023-03-24 12:44:49 +00:00
|
|
|
return dshot_tx_rmt_item;
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-04-15 06:45:14 +01:00
|
|
|
// Calculates a CRC value for a DShot digital control signal packet
|
2023-03-27 18:47:23 +01:00
|
|
|
uint16_t DShotRMT::calculateCRC(const dshot_packet_t &dshot_packet)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-04-15 06:45:14 +01:00
|
|
|
uint16_t crc;
|
|
|
|
|
|
|
|
|
|
// Combine the throttle value and telemetric request flag into a 16-bit packet value
|
2023-03-27 18:47:23 +01:00
|
|
|
const uint16_t packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
|
2023-04-15 06:45:14 +01:00
|
|
|
|
|
|
|
|
// Calculate the CRC value using different bitwise operations depending on the DShot configuration
|
|
|
|
|
if (dshot_config.is_bidirectional)
|
|
|
|
|
{
|
|
|
|
|
// Bidirectional configuration: perform a bitwise negation of the result of XORing the packet with its right-shifted values by 4 and 8 bits,
|
|
|
|
|
// and then bitwise AND the result with 0x0F
|
|
|
|
|
const uint16_t intermediate_result = packet ^ (packet >> 4) ^ (packet >> 8);
|
|
|
|
|
crc = (~intermediate_result) & 0x0F;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Unidirectional configuration: XOR the packet with its right-shifted values by 4 and 8 bits,
|
|
|
|
|
// and then bitwise AND the result with 0x0F
|
|
|
|
|
crc = (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the calculated CRC value as a 16-bit unsigned integer
|
2023-03-27 18:47:23 +01:00
|
|
|
return crc;
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-27 18:47:23 +01:00
|
|
|
uint16_t DShotRMT::parseRmtPaket(const dshot_packet_t &dshot_packet)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-03-27 18:47:23 +01:00
|
|
|
uint16_t parsedRmtPaket = DSHOT_NULL_PACKET;
|
|
|
|
|
uint16_t crc = calculateCRC(dshot_packet);
|
2023-03-26 15:19:54 +01:00
|
|
|
|
2023-03-27 18:47:23 +01:00
|
|
|
// Complete the paket
|
|
|
|
|
parsedRmtPaket = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
|
|
|
|
|
parsedRmtPaket = (parsedRmtPaket << 4) | crc;
|
2023-03-26 15:19:54 +01:00
|
|
|
|
2023-03-27 18:47:23 +01:00
|
|
|
return parsedRmtPaket;
|
2023-03-26 15:19:54 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-27 18:47:23 +01:00
|
|
|
// Output using ESP32 RMT
|
|
|
|
|
void DShotRMT::sendRmtPaket(const dshot_packet_t &dshot_packet)
|
2022-11-25 15:08:58 +00:00
|
|
|
{
|
2023-03-27 18:47:23 +01:00
|
|
|
buildTxRmtItem(parseRmtPaket(dshot_packet));
|
2021-06-29 19:05:20 +01:00
|
|
|
|
2023-03-24 12:44:49 +00:00
|
|
|
rmt_write_items(dshot_tx_rmt_config.channel, dshot_tx_rmt_item, DSHOT_PACKET_LENGTH, false);
|
2021-06-29 19:05:20 +01:00
|
|
|
}
|