Updated Method Names

Roll back to use "begin" method again for better selecting the beginning of Signal transmission.

Renamed the methods to fit it´s functunallty.

Updated the CRC Generation, either
This commit is contained in:
Wastl Kraus 2023-03-27 19:47:23 +02:00
parent 3366d2f775
commit 640604c146
4 changed files with 208 additions and 212 deletions

View File

@ -18,13 +18,16 @@ void setup()
{ {
// ...always start the onboard usb support // ...always start the onboard usb support
USB_Serial.begin(USB_SERIAL_BAUD); USB_Serial.begin(USB_SERIAL_BAUD);
// ...start the dshot generation
motor01.begin(DSHOT300);
} }
void loop() void loop()
{ {
read_SerialThrottle(); readSerialThrottle();
motor01.send_dshot_value(throttle_value); motor01.sendThrottleValue(throttle_value);
// ...print to console // ...print to console
USB_Serial.println(throttle_value); USB_Serial.println(throttle_value);
@ -32,7 +35,7 @@ void loop()
// //
// //
uint16_t read_SerialThrottle() uint16_t readSerialThrottle()
{ {
if (USB_Serial.available() > 0) if (USB_Serial.available() > 0)
{ {

View File

@ -1,5 +1,5 @@
name=DShotRMT name=DShotRMT
version=0.2.3 version=0.2.4
author=derdoktor667 author=derdoktor667
maintainer=derdoktor667 maintainer=derdoktor667
sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S. sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.

View File

@ -6,36 +6,33 @@
#include <DShotRMT.h> #include <DShotRMT.h>
DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel, dshot_mode_t dshot_mode, bool is_bidirectional) DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel)
{ {
// Initialize the configuration structure
dshot_config.gpio_num = gpio; dshot_config.gpio_num = gpio;
dshot_config.pin_num = uint8_t(gpio); dshot_config.pin_num = uint8_t(gpio);
dshot_config.rmt_channel = rmtChannel; dshot_config.rmt_channel = rmtChannel;
dshot_config.mem_block_num = uint8_t(RMT_CHANNEL_MAX - uint8_t(rmtChannel)); dshot_config.mem_block_num = uint8_t(RMT_CHANNEL_MAX - uint8_t(rmtChannel));
// ...create empty packet // ...create empty packet
encode_dshot_to_rmt(DSHOT_NULL_PACKET); buildTxRmtItem(DSHOT_NULL_PACKET);
// install the RMT driver with dshot values
install_dshot_driver(dshot_mode, is_bidirectional);
} }
DShotRMT::DShotRMT(uint8_t pin, uint8_t channel, dshot_mode_t dshot_mode, bool is_bidirectional) DShotRMT::DShotRMT(uint8_t pin, uint8_t channel)
{ {
// Initialize the configuration structure
dshot_config.gpio_num = gpio_num_t(pin); dshot_config.gpio_num = gpio_num_t(pin);
dshot_config.pin_num = pin; dshot_config.pin_num = pin;
dshot_config.rmt_channel = rmt_channel_t(channel); dshot_config.rmt_channel = rmt_channel_t(channel);
dshot_config.mem_block_num = (RMT_CHANNEL_MAX - channel); dshot_config.mem_block_num = (RMT_CHANNEL_MAX - channel);
// ...create empty packet // ...create empty packet
encode_dshot_to_rmt(DSHOT_NULL_PACKET); buildTxRmtItem(DSHOT_NULL_PACKET);
// install the RMT driver with dshot values
install_dshot_driver(dshot_mode, is_bidirectional);
} }
DShotRMT::~DShotRMT() DShotRMT::~DShotRMT()
{ {
// Uninstall the RMT driver
rmt_driver_uninstall(dshot_config.rmt_channel); rmt_driver_uninstall(dshot_config.rmt_channel);
} }
@ -44,150 +41,12 @@ DShotRMT::DShotRMT(DShotRMT const &)
// ...write me // ...write me
} }
// ...the config part is done, now the calculating and sending part bool DShotRMT::begin(dshot_mode_t dshot_mode, bool is_bidirectional)
void DShotRMT::send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request)
{
dshot_packet_t dshot_rmt_packet = {};
if (throttle_value < DSHOT_THROTTLE_MIN)
{
throttle_value = DSHOT_THROTTLE_MIN;
}
if (throttle_value > DSHOT_THROTTLE_MAX)
{
throttle_value = DSHOT_THROTTLE_MAX;
}
// ...packets are the same for bidirectional mode
dshot_rmt_packet.throttle_value = throttle_value;
dshot_rmt_packet.telemetric_request = telemetric_request;
dshot_rmt_packet.checksum = this->calc_dshot_chksum(dshot_rmt_packet);
output_rmt_data(dshot_rmt_packet);
}
// ...get all setup data about current mode
dshot_config_t *DShotRMT::get_dshot_info()
{
return &dshot_config;
}
// ...get the ABP_Clock Divider for further calculations
uint8_t *DShotRMT::get_dshot_clock_div()
{
return &dshot_config.clk_div;
}
rmt_item32_t *DShotRMT::encode_dshot_to_rmt(uint16_t parsed_packet)
{
// ...is bidirecional mode activated
if (dshot_config.bidirectional)
{
// ..."invert" the signal duration
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{
if (parsed_packet & 0b1000000000000000)
{
// set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_low;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_high;
}
else
{
// set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_low;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_high;
}
dshot_tx_rmt_item[i].level0 = 0;
dshot_tx_rmt_item[i].level1 = 1;
}
}
// ..."normal" DShot mode / "bidirectional" mode OFF
else
{
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{
if (parsed_packet & 0b1000000000000000)
{
// set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low;
}
else
{
// set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_low;
}
dshot_tx_rmt_item[i].level0 = 1;
dshot_tx_rmt_item[i].level1 = 0;
}
}
if (dshot_config.bidirectional)
{
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;
}
// ...end marker added to each frame
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = (DSHOT_PAUSE / 2);
return dshot_tx_rmt_item;
}
// ...just returns the checksum
// DOES NOT APPEND CHECKSUM!!!
uint16_t DShotRMT::calc_dshot_chksum(const dshot_packet_t &dshot_packet)
{
// ...start with two emprty "container"
uint16_t packet = DSHOT_NULL_PACKET;
uint16_t chksum = DSHOT_NULL_PACKET;
// ...same initial 12bit data for bidirectional or "normal" mode
packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
if (dshot_config.bidirectional)
{
// ...calc the checksum "inverted" / bidirectional mode
chksum = (~(packet ^ (packet >> 4) ^ (packet >> 8))) & 0x0F;
}
else
{
// ...calc the checksum "normal" mode
chksum = (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F;
}
return chksum;
}
uint16_t DShotRMT::prepare_rmt_data(const dshot_packet_t &dshot_packet)
{
uint16_t prepared_to_encode = DSHOT_NULL_PACKET;
uint16_t chksum = calc_dshot_chksum(dshot_packet);
// ..."construct" the packet
prepared_to_encode = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
prepared_to_encode = (prepared_to_encode << 4) | chksum;
return prepared_to_encode;
}
void DShotRMT::install_dshot_driver(dshot_mode_t dshot_mode, bool is_bidirectional)
{ {
dshot_config.mode = dshot_mode; dshot_config.mode = dshot_mode;
dshot_config.clk_div = DSHOT_CLK_DIVIDER; dshot_config.clk_div = DSHOT_CLK_DIVIDER;
dshot_config.name_str = dshot_mode_name[dshot_mode]; dshot_config.name_str = dshot_mode_name[dshot_mode];
dshot_config.bidirectional = is_bidirectional; dshot_config.is_bidirectional = is_bidirectional;
switch (dshot_config.mode) switch (dshot_config.mode)
{ {
@ -237,7 +96,7 @@ void DShotRMT::install_dshot_driver(dshot_mode_t dshot_mode, bool is_bidirection
dshot_tx_rmt_config.tx_config.carrier_en = false; dshot_tx_rmt_config.tx_config.carrier_en = false;
dshot_tx_rmt_config.tx_config.idle_output_en = true; dshot_tx_rmt_config.tx_config.idle_output_en = true;
if (dshot_config.bidirectional) if (dshot_config.is_bidirectional)
{ {
dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH; dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
} }
@ -249,15 +108,145 @@ void DShotRMT::install_dshot_driver(dshot_mode_t dshot_mode, bool is_bidirection
// ...setup selected dshot mode // ...setup selected dshot mode
rmt_config(&dshot_tx_rmt_config); rmt_config(&dshot_tx_rmt_config);
// ...essential step, install rmt driver // ...essential step, return the result
rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0); return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0);
} }
// ...finally output using ESP32 RMT // ...the config part is done, now the calculating and sending part
void DShotRMT::output_rmt_data(const dshot_packet_t &dshot_packet) void DShotRMT::sendThrottleValue(uint16_t throttle_value, telemetric_request_t telemetric_request)
{ {
encode_dshot_to_rmt(prepare_rmt_data(dshot_packet)); dshot_packet_t dshot_rmt_packet = {};
if (throttle_value < DSHOT_THROTTLE_MIN)
{
throttle_value = DSHOT_THROTTLE_MIN;
}
if (throttle_value > DSHOT_THROTTLE_MAX)
{
throttle_value = DSHOT_THROTTLE_MAX;
}
// ...packets are the same for bidirectional mode
dshot_rmt_packet.throttle_value = throttle_value;
dshot_rmt_packet.telemetric_request = telemetric_request;
dshot_rmt_packet.checksum = this->calculateCRC(dshot_rmt_packet);
sendRmtPaket(dshot_rmt_packet);
}
rmt_item32_t *DShotRMT::buildTxRmtItem(uint16_t parsed_packet)
{
// ...for bidirectional mode
if (dshot_config.is_bidirectional)
{
// ..."invert" the high / low bits
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{
if (parsed_packet & 0b1000000000000000)
{
// set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_low;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_high;
}
else
{
// set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_low;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_high;
}
dshot_tx_rmt_item[i].level0 = 0;
dshot_tx_rmt_item[i].level1 = 1;
}
}
// ..."normal" DShot mode / "bidirectional" mode OFF
else
{
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{
if (parsed_packet & 0b1000000000000000)
{
// set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low;
}
else
{
// set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_low;
}
dshot_tx_rmt_item[i].level0 = 1;
dshot_tx_rmt_item[i].level1 = 0;
}
}
if (dshot_config.is_bidirectional)
{
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;
}
// ...end marker added to each frame
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = DSHOT_PAUSE;
return dshot_tx_rmt_item;
}
// Legacy CRC calculation
// uint16_t DShotRMT::calculateCRC(const dshot_packet_t &dshot_packet)
// {
// uint16_t packet = DSHOT_NULL_PACKET;
// uint16_t crc = DSHOT_NULL_PACKET;
// // Same initial 11 bits for both bidirectional and normal mode
// packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
// if (dshot_config.is_bidirectional)
// {
// // Calculate checksum in inverted/bidirectional mode
// crc = (~(packet ^ (packet >> 4) ^ (packet >> 8))) & 0x0F;
// }
// else
// {
// // Calculate checksum in normal mode
// crc = (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F;
// }
// return crc;
// }
// New way of calculating CRC
uint16_t DShotRMT::calculateCRC(const dshot_packet_t &dshot_packet)
{
const uint16_t packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
const uint16_t crc = dshot_config.is_bidirectional
? (~(packet ^ (packet >> 4) ^ (packet >> 8))) & 0x0F
: (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F;
return crc;
}
uint16_t DShotRMT::parseRmtPaket(const dshot_packet_t &dshot_packet)
{
uint16_t parsedRmtPaket = DSHOT_NULL_PACKET;
uint16_t crc = calculateCRC(dshot_packet);
// Complete the paket
parsedRmtPaket = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
parsedRmtPaket = (parsedRmtPaket << 4) | crc;
return parsedRmtPaket;
}
// Output using ESP32 RMT
void DShotRMT::sendRmtPaket(const dshot_packet_t &dshot_packet)
{
buildTxRmtItem(parseRmtPaket(dshot_packet));
//
rmt_write_items(dshot_tx_rmt_config.channel, dshot_tx_rmt_item, DSHOT_PACKET_LENGTH, false); rmt_write_items(dshot_tx_rmt_config.channel, dshot_tx_rmt_item, DSHOT_PACKET_LENGTH, false);
} }

View File

@ -4,35 +4,30 @@
// Author: derdoktor667 // Author: derdoktor667
// //
#pragma once #ifndef _DSHOTRMT_h
#define _DSHOTRMT_h
#include <Arduino.h> #include <Arduino.h>
// Use the RMT Module library for generating the DShot signal // The RMT (Remote Control) module library is used for generating the DShot signal.
#include <driver/rmt.h> #include <driver/rmt.h>
// Define library version // Defines the library version
constexpr auto DSHOT_LIB_VERSION = "0.2.3"; constexpr auto DSHOT_LIB_VERSION = "0.2.4";
// Define constants for generating the DShot signal // Constants related to the DShot protocol
constexpr auto DSHOT_CLK_DIVIDER = 8; // Slow down RMT clock to 0.1 microseconds / 100 nanoseconds per cycle constexpr auto DSHOT_CLK_DIVIDER = 8; // Slow down RMT clock to 0.1 microseconds / 100 nanoseconds per cycle
constexpr auto DSHOT_PACKET_LENGTH = 17; // Last pack is the pause constexpr auto DSHOT_PACKET_LENGTH = 17; // Last pack is the pause
// Define constants for DShot throttle range and null packet
constexpr auto DSHOT_THROTTLE_MIN = 48; constexpr auto DSHOT_THROTTLE_MIN = 48;
constexpr auto DSHOT_THROTTLE_MAX = 2047; constexpr auto DSHOT_THROTTLE_MAX = 2047;
constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000;
constexpr auto DSHOT_PAUSE = 21; // 21-bit is recommended
// Define constants for DShot pause
constexpr auto DSHOT_PAUSE = 21; // 21bit is recommended, but to be sure
constexpr auto DSHOT_PAUSE_BIT = 16; constexpr auto DSHOT_PAUSE_BIT = 16;
// Define constants for RMT timing
constexpr auto F_CPU_RMT = APB_CLK_FREQ; constexpr auto F_CPU_RMT = APB_CLK_FREQ;
constexpr auto RMT_CYCLES_PER_SEC = (F_CPU_RMT / DSHOT_CLK_DIVIDER); constexpr auto RMT_CYCLES_PER_SEC = (F_CPU_RMT / DSHOT_CLK_DIVIDER);
constexpr auto RMT_CYCLES_PER_ESP_CYCLE = (F_CPU / RMT_CYCLES_PER_SEC); constexpr auto RMT_CYCLES_PER_ESP_CYCLE = (F_CPU / RMT_CYCLES_PER_SEC);
// Define enumeration for DShot mode // Enumeration for the DShot mode
typedef enum dshot_mode_e typedef enum dshot_mode_e
{ {
DSHOT_OFF, DSHOT_OFF,
@ -42,24 +37,22 @@ typedef enum dshot_mode_e
DSHOT1200 DSHOT1200
} dshot_mode_t; } dshot_mode_t;
// Define human-readable names for DShot modes // Array of human-readable DShot mode names
static const char *const dshot_mode_name[] = { static const char *const dshot_mode_name[] = {
"DSHOT_OFF", "DSHOT_OFF",
"DSHOT150", "DSHOT150",
"DSHOT300", "DSHOT300",
"DSHOT600", "DSHOT600",
"DSHOT1200" "DSHOT1200"};
};
// Define type aliases for DShot name and telemetry request // Enumeration for telemetric request
typedef String dshot_name_t;
typedef enum telemetric_request_e typedef enum telemetric_request_e
{ {
NO_TELEMETRIC, NO_TELEMETRIC,
ENABLE_TELEMETRIC, ENABLE_TELEMETRIC,
} telemetric_request_t; } telemetric_request_t;
// Define structure for DShot packet // Structure for DShot packets
typedef struct dshot_packet_s typedef struct dshot_packet_s
{ {
uint16_t throttle_value : 11; uint16_t throttle_value : 11;
@ -67,19 +60,19 @@ typedef struct dshot_packet_s
uint16_t checksum : 4; uint16_t checksum : 4;
} dshot_packet_t; } dshot_packet_t;
// Define structure for eRPM packet // Structure for eRPM packets
typedef struct eRPM_packet_s typedef struct eRPM_packet_s
{ {
uint16_t eRPM_data : 12; uint16_t eRPM_data : 12;
uint8_t checksum : 4; uint8_t checksum : 4;
} eRPM_packet_t; } eRPM_packet_t;
// Define structure for DShot configuration // Structure for all settings for the DShot mode
typedef struct dshot_config_s typedef struct dshot_config_s
{ {
dshot_mode_t mode; dshot_mode_t mode;
dshot_name_t name_str; String name_str;
bool bidirectional; bool is_bidirectional;
gpio_num_t gpio_num; gpio_num_t gpio_num;
uint8_t pin_num; uint8_t pin_num;
rmt_channel_t rmt_channel; rmt_channel_t rmt_channel;
@ -92,30 +85,41 @@ typedef struct dshot_config_s
uint16_t ticks_one_low; uint16_t ticks_one_low;
} dshot_config_t; } dshot_config_t;
// Define class for DShot generation using RMT // The main DShotRMT class
class DShotRMT class DShotRMT
{ {
public: public:
DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel, dshot_mode_t dshot_mode = DSHOT300, bool is_bidirectional = false); // Constructor for the DShotRMT class
DShotRMT(uint8_t pin, uint8_t channel, dshot_mode_t dshot_mode = DSHOT300, bool is_bidirectional = false); DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel);
DShotRMT(uint8_t pin, uint8_t channel);
// Destructor for the DShotRMT class
~DShotRMT(); ~DShotRMT();
// Copy constructor for the DShotRMT class
DShotRMT(DShotRMT const &); DShotRMT(DShotRMT const &);
// ...safety first ...no parameters, no DShot // The begin() function initializes the DShotRMT class with
void send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request = NO_TELEMETRIC); // a given DShot mode (DSHOT_OFF, DSHOT150, DSHOT300, DSHOT600, DSHOT1200)
// and a bidirectional flag. It returns a boolean value
// indicating whether or not the initialization was successful.
bool begin(dshot_mode_t dshot_mode = DSHOT_OFF, bool is_bidirectional = false);
dshot_config_t *get_dshot_info(); // The sendThrottleValue() function sends a DShot packet with a given
uint8_t *get_dshot_clock_div(); // throttle value (between 0 and 2047) and an optional telemetry
// request flag.
void sendThrottleValue(uint16_t throttle_value, telemetric_request_t telemetric_request = NO_TELEMETRIC);
private: private:
rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH]; rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH]; // An array of RMT items used to send a DShot packet.
rmt_config_t dshot_tx_rmt_config; rmt_config_t dshot_tx_rmt_config; // The RMT configuration used for sending DShot packets.
dshot_config_t dshot_config; dshot_config_t dshot_config; // The configuration for the DShot mode.
rmt_item32_t *encode_dshot_to_rmt(uint16_t parsed_packet); rmt_item32_t *buildTxRmtItem(uint16_t parsed_packet); // Constructs an RMT item from a parsed DShot packet.
uint16_t calc_dshot_chksum(const dshot_packet_t &dshot_packet); uint16_t calculateCRC(const dshot_packet_t &dshot_packet); // Calculates the CRC checksum for a DShot packet.
uint16_t prepare_rmt_data(const dshot_packet_t &dshot_packet); uint16_t parseRmtPaket(const dshot_packet_t &dshot_packet); // Parses an RMT packet to obtain a DShot packet.
void install_dshot_driver(dshot_mode_t dshot_mode, bool is_bidirectional); void sendRmtPaket(const dshot_packet_t &dshot_packet); // Sends a DShot packet via RMT.
void output_rmt_data(const dshot_packet_t &dshot_packet);
}; };
#endif