experiments

This commit is contained in:
Wastl Kraus 2023-03-24 13:44:49 +01:00
parent de2358e3c8
commit d7e8386129
3 changed files with 224 additions and 226 deletions

View File

@ -17,7 +17,7 @@ The checksum is calculated over the throttle value and the telemetry bit, so the
crc = (value ^ (value >> 4) ^ (value >> 8)) & 0x0F; crc = (value ^ (value >> 4) ^ (value >> 8)) & 0x0F;
### Bidirectional DSHOT ### Bidirectional DSHOT
Bidirictional DSHOT is also known as inverted DSHOT, because the signal level is inverted, so 1 is low and a 0 is high. This is done in order to let the ESC know, that we are operating in bidirectional mode and that it should be sending back eRPM telemetry packages. Bidirectional DSHOT is also known as inverted DSHOT, because the signal level is inverted, so 1 is low and a 0 is high. This is done in order to let the ESC know, that we are operating in bidirectional mode and that it should be sending back eRPM telemetry packages.
#### Calculating the Bidirectional CRC #### Calculating the Bidirectional CRC
The calculation of the checksum is basically the same, just before the last step the values are inverted: The calculation of the checksum is basically the same, just before the last step the values are inverted:

View File

@ -8,250 +8,250 @@
DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel) DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel)
{ {
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 clean packet // ...create empty packet
encode_dshot_to_rmt(DSHOT_NULL_PACKET); encode_dshot_to_rmt(DSHOT_NULL_PACKET);
} }
DShotRMT::DShotRMT(uint8_t pin, uint8_t channel) DShotRMT::DShotRMT(uint8_t pin, uint8_t channel)
{ {
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 clean packet // ...create empty packet
encode_dshot_to_rmt(DSHOT_NULL_PACKET); encode_dshot_to_rmt(DSHOT_NULL_PACKET);
} }
DShotRMT::~DShotRMT() DShotRMT::~DShotRMT()
{ {
rmt_driver_uninstall(dshot_config.rmt_channel); rmt_driver_uninstall(dshot_config.rmt_channel);
} }
DShotRMT::DShotRMT(DShotRMT const&) DShotRMT::DShotRMT(DShotRMT const &)
{ {
// ...write me // ...write me
} }
bool DShotRMT::begin(dshot_mode_t dshot_mode, bool is_bidirectional) bool DShotRMT::begin(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.bidirectional = is_bidirectional;
switch (dshot_config.mode) switch (dshot_config.mode)
{ {
case DSHOT150: case DSHOT150:
dshot_config.ticks_per_bit = 64; // ...Bit Period Time 6.67 us dshot_config.ticks_per_bit = 64; // ...Bit Period Time 6.67 us
dshot_config.ticks_zero_high = 24; // ...zero time 2.50 us dshot_config.ticks_zero_high = 24; // ...zero time 2.50 us
dshot_config.ticks_one_high = 48; // ...one time 5.00 us dshot_config.ticks_one_high = 48; // ...one time 5.00 us
break; break;
case DSHOT300: case DSHOT300:
dshot_config.ticks_per_bit = 32; // ...Bit Period Time 3.33 us dshot_config.ticks_per_bit = 32; // ...Bit Period Time 3.33 us
dshot_config.ticks_zero_high = 12; // ...zero time 1.25 us dshot_config.ticks_zero_high = 12; // ...zero time 1.25 us
dshot_config.ticks_one_high = 24; // ...one time 2.50 us dshot_config.ticks_one_high = 24; // ...one time 2.50 us
break; break;
case DSHOT600: case DSHOT600:
dshot_config.ticks_per_bit = 16; // ...Bit Period Time 1.67 us dshot_config.ticks_per_bit = 16; // ...Bit Period Time 1.67 us
dshot_config.ticks_zero_high = 6; // ...zero time 0.625 us dshot_config.ticks_zero_high = 6; // ...zero time 0.625 us
dshot_config.ticks_one_high = 12; // ...one time 1.25 us dshot_config.ticks_one_high = 12; // ...one time 1.25 us
break; break;
case DSHOT1200: case DSHOT1200:
dshot_config.ticks_per_bit = 8; // ...Bit Period Time 0.83 us dshot_config.ticks_per_bit = 8; // ...Bit Period Time 0.83 us
dshot_config.ticks_zero_high = 3; // ...zero time 0.313 us dshot_config.ticks_zero_high = 3; // ...zero time 0.313 us
dshot_config.ticks_one_high = 6; // ...one time 0.625 us dshot_config.ticks_one_high = 6; // ...one time 0.625 us
break; break;
// ...because having a default is "good style" // ...because having a default is "good style"
default: default:
dshot_config.ticks_per_bit = 0; // ...Bit Period Time endless dshot_config.ticks_per_bit = 0; // ...Bit Period Time endless
dshot_config.ticks_zero_high = 0; // ...no bits, no time dshot_config.ticks_zero_high = 0; // ...no bits, no time
dshot_config.ticks_one_high = 0; // ......no bits, no time dshot_config.ticks_one_high = 0; // ......no bits, no time
break; break;
} }
// ...calc low signal timing // ...calc low signal timing
dshot_config.ticks_zero_low = (dshot_config.ticks_per_bit - dshot_config.ticks_zero_high); 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); dshot_config.ticks_one_low = (dshot_config.ticks_per_bit - dshot_config.ticks_one_high);
dshot_tx_rmt_config.rmt_mode = RMT_MODE_TX; dshot_tx_rmt_config.rmt_mode = RMT_MODE_TX;
dshot_tx_rmt_config.channel = dshot_config.rmt_channel; dshot_tx_rmt_config.channel = dshot_config.rmt_channel;
dshot_tx_rmt_config.gpio_num = dshot_config.gpio_num; 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.mem_block_num = dshot_config.mem_block_num;
dshot_tx_rmt_config.clk_div = dshot_config.clk_div; 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.loop_en = false;
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.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;
} }
else else
{ {
dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
} }
// ...setup selected dshot mode // ...setup selected dshot mode
rmt_config(&dshot_tx_rmt_config); rmt_config(&dshot_tx_rmt_config);
// ...essential step, return the result // ...essential step, return the result
return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0); return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0);
} }
// ...the config part is done, now the calculating and sending part // ...the config part is done, now the calculating and sending part
void DShotRMT::send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request) void DShotRMT::send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request)
{ {
dshot_packet_t dshot_rmt_packet = {}; dshot_packet_t dshot_rmt_packet = {};
if (throttle_value < DSHOT_THROTTLE_MIN) if (throttle_value < DSHOT_THROTTLE_MIN)
{ {
throttle_value = DSHOT_THROTTLE_MIN; throttle_value = DSHOT_THROTTLE_MIN;
} }
if (throttle_value > DSHOT_THROTTLE_MAX) if (throttle_value > DSHOT_THROTTLE_MAX)
{ {
throttle_value = DSHOT_THROTTLE_MAX; throttle_value = DSHOT_THROTTLE_MAX;
} }
// ...packets are the same for bidirectional mode // ...packets are the same for bidirectional mode
dshot_rmt_packet.throttle_value = throttle_value; dshot_rmt_packet.throttle_value = throttle_value;
dshot_rmt_packet.telemetric_request = telemetric_request; dshot_rmt_packet.telemetric_request = telemetric_request;
dshot_rmt_packet.checksum = this->calc_dshot_chksum(dshot_rmt_packet); dshot_rmt_packet.checksum = this->calc_dshot_chksum(dshot_rmt_packet);
output_rmt_data(dshot_rmt_packet); output_rmt_data(dshot_rmt_packet);
} }
// ...get all setup data about current mode // ...get all setup data about current mode
dshot_config_t* DShotRMT::get_dshot_info() dshot_config_t *DShotRMT::get_dshot_info()
{ {
return &dshot_config; return &dshot_config;
} }
// ...get the ABP_Clock Divider for further calculations // ...get the ABP_Clock Divider for further calculations
uint8_t* DShotRMT::get_dshot_clock_div() uint8_t *DShotRMT::get_dshot_clock_div()
{ {
return &dshot_config.clk_div; return &dshot_config.clk_div;
} }
rmt_item32_t* DShotRMT::encode_dshot_to_rmt(uint16_t parsed_packet) rmt_item32_t *DShotRMT::encode_dshot_to_rmt(uint16_t parsed_packet)
{ {
// ...is bidirecional mode activated // ...is bidirecional mode activated
if (dshot_config.bidirectional) if (dshot_config.bidirectional)
{ {
// ..."invert" the signal duration // ..."invert" the signal duration
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1) for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{ {
if (parsed_packet & 0b1000000000000000) if (parsed_packet & 0b1000000000000000)
{ {
// set one // set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_low; dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_low;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_high; dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_high;
} }
else else
{ {
// set zero // set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_low; 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].duration1 = dshot_config.ticks_zero_high;
} }
dshot_tx_rmt_item[i].level0 = 0; dshot_tx_rmt_item[i].level0 = 0;
dshot_tx_rmt_item[i].level1 = 1; dshot_tx_rmt_item[i].level1 = 1;
} }
} }
// ..."normal" DShot mode / "bidirectional" mode OFF // ..."normal" DShot mode / "bidirectional" mode OFF
else else
{ {
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1) for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1)
{ {
if (parsed_packet & 0b1000000000000000) if (parsed_packet & 0b1000000000000000)
{ {
// set one // set one
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high; dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high;
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low; dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low;
} }
else else
{ {
// set zero // set zero
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high; 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].duration1 = dshot_config.ticks_zero_low;
} }
dshot_tx_rmt_item[i].level0 = 1; dshot_tx_rmt_item[i].level0 = 1;
dshot_tx_rmt_item[i].level1 = 0; dshot_tx_rmt_item[i].level1 = 0;
} }
} }
if (dshot_config.bidirectional) if (dshot_config.bidirectional)
{ {
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 1; dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 1;
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 0; dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 0;
} }
else else
{ {
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 0; dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 0;
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 1; dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 1;
} }
// ...end marker added to each frame // ...end marker added to each frame
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = (DSHOT_PAUSE / 2); dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = (DSHOT_PAUSE / 2);
return dshot_tx_rmt_item; return dshot_tx_rmt_item;
} }
// ...just returns the checksum // ...just returns the checksum
// DOES NOT APPEND CHECKSUM!!! // DOES NOT APPEND CHECKSUM!!!
uint16_t DShotRMT::calc_dshot_chksum(const dshot_packet_t& dshot_packet) uint16_t DShotRMT::calc_dshot_chksum(const dshot_packet_t &dshot_packet)
{ {
// ...start with two emprty "container" // ...start with two emprty "container"
uint16_t packet = DSHOT_NULL_PACKET; uint16_t packet = DSHOT_NULL_PACKET;
uint16_t chksum = DSHOT_NULL_PACKET; uint16_t chksum = DSHOT_NULL_PACKET;
// ...same initial 12bit data for bidirectional or "normal" mode // ...same initial 12bit data for bidirectional or "normal" mode
packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request; packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
if (dshot_config.bidirectional) if (dshot_config.bidirectional)
{ {
// ...calc the checksum "inverted" / bidirectional mode // ...calc the checksum "inverted" / bidirectional mode
chksum = (~(packet ^ (packet >> 4) ^ (packet >> 8))) & 0x0F; chksum = (~(packet ^ (packet >> 4) ^ (packet >> 8))) & 0x0F;
} }
else else
{ {
// ...calc the checksum "normal" mode // ...calc the checksum "normal" mode
chksum = (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F; chksum = (packet ^ (packet >> 4) ^ (packet >> 8)) & 0x0F;
} }
return chksum; return chksum;
} }
uint16_t DShotRMT::prepare_rmt_data(const dshot_packet_t& dshot_packet) uint16_t DShotRMT::prepare_rmt_data(const dshot_packet_t &dshot_packet)
{ {
uint16_t prepared_to_encode = DSHOT_NULL_PACKET; uint16_t prepared_to_encode = DSHOT_NULL_PACKET;
uint16_t chksum = calc_dshot_chksum(dshot_packet); uint16_t chksum = calc_dshot_chksum(dshot_packet);
// ..."construct" the packet // ..."construct" the packet
prepared_to_encode = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request; prepared_to_encode = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
prepared_to_encode = (prepared_to_encode << 4) | chksum; prepared_to_encode = (prepared_to_encode << 4) | chksum;
return prepared_to_encode; return prepared_to_encode;
} }
// ...finally output using ESP32 RMT // ...finally output using ESP32 RMT
void DShotRMT::output_rmt_data(const dshot_packet_t& dshot_packet) void DShotRMT::output_rmt_data(const dshot_packet_t &dshot_packet)
{ {
encode_dshot_to_rmt(prepare_rmt_data(dshot_packet)); encode_dshot_to_rmt(prepare_rmt_data(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,8 +4,6 @@
// Author: derdoktor667 // Author: derdoktor667
// //
#pragma once
#ifndef _DSHOTRMT_h #ifndef _DSHOTRMT_h
#define _DSHOTRMT_h #define _DSHOTRMT_h
@ -37,87 +35,87 @@ constexpr auto RMT_CYCLES_PER_ESP_CYCLE = (F_CPU / RMT_CYCLES_PER_SEC);
typedef enum dshot_mode_e typedef enum dshot_mode_e
{ {
DSHOT_OFF, DSHOT_OFF,
DSHOT150, DSHOT150,
DSHOT300, DSHOT300,
DSHOT600, DSHOT600,
DSHOT1200 DSHOT1200
} dshot_mode_t; } dshot_mode_t;
// ...to get human readable DShot Mode // ...to get human readable DShot Mode
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"};
typedef String dshot_name_t; 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;
// ...set bitcount for DShot packet // ...set bitcount for DShot packet
typedef struct dshot_packet_s typedef struct dshot_packet_s
{ {
uint16_t throttle_value : 11; uint16_t throttle_value : 11;
telemetric_request_t telemetric_request : 1; telemetric_request_t telemetric_request : 1;
uint16_t checksum : 4; uint16_t checksum : 4;
} dshot_packet_t; } dshot_packet_t;
// ...set bitcount for eRPM packet // ...set bitcount for eRPM packet
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;
// ...all settings for the dshot mode // ...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; dshot_name_t name_str;
bool bidirectional; bool 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;
uint8_t mem_block_num; uint8_t mem_block_num;
uint16_t ticks_per_bit; uint16_t ticks_per_bit;
uint8_t clk_div; uint8_t clk_div;
uint16_t ticks_zero_high; uint16_t ticks_zero_high;
uint16_t ticks_zero_low; uint16_t ticks_zero_low;
uint16_t ticks_one_high; uint16_t ticks_one_high;
uint16_t ticks_one_low; uint16_t ticks_one_low;
} dshot_config_t; } dshot_config_t;
class DShotRMT class DShotRMT
{ {
public: public:
DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel); DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel);
DShotRMT(uint8_t pin, uint8_t channel); DShotRMT(uint8_t pin, uint8_t channel);
~DShotRMT(); ~DShotRMT();
DShotRMT(DShotRMT const&); DShotRMT(DShotRMT const &);
// ...safety first ...no parameters, no DShot // ...safety first ...no parameters, no DShot
bool begin(dshot_mode_t dshot_mode = DSHOT_OFF, bool is_bidirectional = false); bool begin(dshot_mode_t dshot_mode = DSHOT_OFF, bool is_bidirectional = false);
void send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request = NO_TELEMETRIC); void send_dshot_value(uint16_t throttle_value, telemetric_request_t telemetric_request = NO_TELEMETRIC);
dshot_config_t* get_dshot_info(); dshot_config_t *get_dshot_info();
uint8_t* get_dshot_clock_div(); uint8_t *get_dshot_clock_div();
private: private:
rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH]; rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH];
rmt_config_t dshot_tx_rmt_config; rmt_config_t dshot_tx_rmt_config;
dshot_config_t dshot_config; dshot_config_t dshot_config;
rmt_item32_t* encode_dshot_to_rmt(uint16_t parsed_packet); rmt_item32_t *encode_dshot_to_rmt(uint16_t parsed_packet);
uint16_t calc_dshot_chksum(const dshot_packet_t& dshot_packet); uint16_t calc_dshot_chksum(const dshot_packet_t &dshot_packet);
uint16_t prepare_rmt_data(const dshot_packet_t& dshot_packet); uint16_t prepare_rmt_data(const dshot_packet_t &dshot_packet);
void output_rmt_data(const dshot_packet_t& dshot_packet); void output_rmt_data(const dshot_packet_t &dshot_packet);
}; };
#endif #endif