From d7e83861295d12fc6a9b3e7709feab5c8714ba6c Mon Sep 17 00:00:00 2001 From: Wastl Kraus Date: Fri, 24 Mar 2023 13:44:49 +0100 Subject: [PATCH] experiments --- README.md | 2 +- src/DShotRMT.cpp | 348 +++++++++++++++++++++++------------------------ src/DShotRMT.h | 100 +++++++------- 3 files changed, 224 insertions(+), 226 deletions(-) diff --git a/README.md b/README.md index 2d27fcc..a4792e1 100644 --- a/README.md +++ b/README.md @@ -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; ### 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 The calculation of the checksum is basically the same, just before the last step the values are inverted: diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index 76ae607..002f5ab 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -8,250 +8,250 @@ DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel) { - dshot_config.gpio_num = gpio; - dshot_config.pin_num = uint8_t(gpio); - dshot_config.rmt_channel = rmtChannel; - dshot_config.mem_block_num = uint8_t(RMT_CHANNEL_MAX - uint8_t(rmtChannel)); + dshot_config.gpio_num = gpio; + dshot_config.pin_num = uint8_t(gpio); + dshot_config.rmt_channel = rmtChannel; + dshot_config.mem_block_num = uint8_t(RMT_CHANNEL_MAX - uint8_t(rmtChannel)); - // ...create clean packet - encode_dshot_to_rmt(DSHOT_NULL_PACKET); + // ...create empty packet + encode_dshot_to_rmt(DSHOT_NULL_PACKET); } DShotRMT::DShotRMT(uint8_t pin, uint8_t channel) { - dshot_config.gpio_num = gpio_num_t(pin); - dshot_config.pin_num = pin; - dshot_config.rmt_channel = rmt_channel_t(channel); - dshot_config.mem_block_num = (RMT_CHANNEL_MAX - channel); + dshot_config.gpio_num = gpio_num_t(pin); + dshot_config.pin_num = pin; + dshot_config.rmt_channel = rmt_channel_t(channel); + dshot_config.mem_block_num = (RMT_CHANNEL_MAX - channel); - // ...create clean packet - encode_dshot_to_rmt(DSHOT_NULL_PACKET); + // ...create empty packet + encode_dshot_to_rmt(DSHOT_NULL_PACKET); } 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) { - dshot_config.mode = dshot_mode; - dshot_config.clk_div = DSHOT_CLK_DIVIDER; - dshot_config.name_str = dshot_mode_name[dshot_mode]; - dshot_config.bidirectional = is_bidirectional; + dshot_config.mode = dshot_mode; + dshot_config.clk_div = DSHOT_CLK_DIVIDER; + dshot_config.name_str = dshot_mode_name[dshot_mode]; + dshot_config.bidirectional = is_bidirectional; - switch (dshot_config.mode) - { - case DSHOT150: - 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_one_high = 48; // ...one time 5.00 us - break; + switch (dshot_config.mode) + { + case DSHOT150: + 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_one_high = 48; // ...one time 5.00 us + break; - case DSHOT300: - 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_one_high = 24; // ...one time 2.50 us - break; + case DSHOT300: + 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_one_high = 24; // ...one time 2.50 us + break; - case DSHOT600: - 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_one_high = 12; // ...one time 1.25 us - break; + case DSHOT600: + 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_one_high = 12; // ...one time 1.25 us + break; - case DSHOT1200: - 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_one_high = 6; // ...one time 0.625 us - break; + case DSHOT1200: + 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_one_high = 6; // ...one time 0.625 us + break; - // ...because having a default is "good style" - default: - dshot_config.ticks_per_bit = 0; // ...Bit Period Time endless - dshot_config.ticks_zero_high = 0; // ...no bits, no time - dshot_config.ticks_one_high = 0; // ......no bits, no time - break; - } + // ...because having a default is "good style" + default: + dshot_config.ticks_per_bit = 0; // ...Bit Period Time endless + dshot_config.ticks_zero_high = 0; // ...no bits, no time + dshot_config.ticks_one_high = 0; // ......no bits, no time + break; + } - // ...calc low signal timing - 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); + // ...calc low signal timing + 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_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.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; + 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; - if (dshot_config.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; - } + if (dshot_config.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; + } - // ...setup selected dshot mode - rmt_config(&dshot_tx_rmt_config); + // ...setup selected dshot mode + rmt_config(&dshot_tx_rmt_config); - // ...essential step, return the result - return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0); + // ...essential step, return the result + return rmt_driver_install(dshot_tx_rmt_config.channel, 0, 0); } // ...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) { - dshot_packet_t dshot_rmt_packet = {}; + dshot_packet_t dshot_rmt_packet = {}; - if (throttle_value < DSHOT_THROTTLE_MIN) - { - throttle_value = DSHOT_THROTTLE_MIN; - } + if (throttle_value < DSHOT_THROTTLE_MIN) + { + throttle_value = DSHOT_THROTTLE_MIN; + } - if (throttle_value > DSHOT_THROTTLE_MAX) - { - throttle_value = DSHOT_THROTTLE_MAX; - } + 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); + // ...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); + output_rmt_data(dshot_rmt_packet); } // ...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 -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 - 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; - } + // ...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; - } - } + 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; - } + // ..."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; - } - } + 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; - } + 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); + // ...end marker added to each frame + 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 // 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" - uint16_t packet = DSHOT_NULL_PACKET; - uint16_t chksum = DSHOT_NULL_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; + // ...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; - } + 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; + 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 chksum = calc_dshot_chksum(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; + // ..."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; + return prepared_to_encode; } // ...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); } diff --git a/src/DShotRMT.h b/src/DShotRMT.h index b1ea067..74bd0ae 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -4,8 +4,6 @@ // Author: derdoktor667 // -#pragma once - #ifndef _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 { - DSHOT_OFF, - DSHOT150, - DSHOT300, - DSHOT600, - DSHOT1200 + DSHOT_OFF, + DSHOT150, + DSHOT300, + DSHOT600, + DSHOT1200 } dshot_mode_t; // ...to get human readable DShot Mode -static const char* const dshot_mode_name[] = { - "DSHOT_OFF", - "DSHOT150", - "DSHOT300", - "DSHOT600", - "DSHOT1200" }; +static const char *const dshot_mode_name[] = { + "DSHOT_OFF", + "DSHOT150", + "DSHOT300", + "DSHOT600", + "DSHOT1200"}; typedef String dshot_name_t; typedef enum telemetric_request_e { - NO_TELEMETRIC, - ENABLE_TELEMETRIC, + NO_TELEMETRIC, + ENABLE_TELEMETRIC, } telemetric_request_t; // ...set bitcount for DShot packet typedef struct dshot_packet_s { - uint16_t throttle_value : 11; - telemetric_request_t telemetric_request : 1; - uint16_t checksum : 4; + uint16_t throttle_value : 11; + telemetric_request_t telemetric_request : 1; + uint16_t checksum : 4; } dshot_packet_t; // ...set bitcount for eRPM packet typedef struct eRPM_packet_s { - uint16_t eRPM_data : 12; - uint8_t checksum : 4; + uint16_t eRPM_data : 12; + uint8_t checksum : 4; } eRPM_packet_t; // ...all settings for the dshot mode typedef struct dshot_config_s { - dshot_mode_t mode; - dshot_name_t name_str; - bool bidirectional; - gpio_num_t gpio_num; - uint8_t pin_num; - rmt_channel_t rmt_channel; - uint8_t mem_block_num; - uint16_t ticks_per_bit; - uint8_t clk_div; - uint16_t ticks_zero_high; - uint16_t ticks_zero_low; - uint16_t ticks_one_high; - uint16_t ticks_one_low; + dshot_mode_t mode; + dshot_name_t name_str; + bool bidirectional; + gpio_num_t gpio_num; + uint8_t pin_num; + rmt_channel_t rmt_channel; + uint8_t mem_block_num; + uint16_t ticks_per_bit; + uint8_t clk_div; + uint16_t ticks_zero_high; + uint16_t ticks_zero_low; + uint16_t ticks_one_high; + uint16_t ticks_one_low; } dshot_config_t; class DShotRMT { - public: - DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel); - DShotRMT(uint8_t pin, uint8_t channel); - ~DShotRMT(); - DShotRMT(DShotRMT const&); +public: + DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel); + DShotRMT(uint8_t pin, uint8_t channel); + ~DShotRMT(); + DShotRMT(DShotRMT const &); - // ...safety first ...no parameters, no DShot - 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); + // ...safety first ...no parameters, no DShot + 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); - dshot_config_t* get_dshot_info(); - uint8_t* get_dshot_clock_div(); + dshot_config_t *get_dshot_info(); + uint8_t *get_dshot_clock_div(); - private: - rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH]; - rmt_config_t dshot_tx_rmt_config; - dshot_config_t dshot_config; +private: + rmt_item32_t dshot_tx_rmt_item[DSHOT_PACKET_LENGTH]; + rmt_config_t dshot_tx_rmt_config; + dshot_config_t dshot_config; - rmt_item32_t* encode_dshot_to_rmt(uint16_t parsed_packet); - uint16_t calc_dshot_chksum(const dshot_packet_t& dshot_packet); - uint16_t prepare_rmt_data(const dshot_packet_t& dshot_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 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