2021-07-04 02:01:26 +01:00
|
|
|
|
//
|
2021-07-04 20:34:26 +01:00
|
|
|
|
// Name: DShotRMT.cpp
|
2021-07-04 02:01:26 +01:00
|
|
|
|
// Created: 20.03.2021 00:49:15
|
|
|
|
|
|
// Author: derdoktor667
|
|
|
|
|
|
//
|
|
|
|
|
|
|
2021-06-30 04:39:00 +01:00
|
|
|
|
#include "DShotRMT.h"
|
2021-06-29 19:05:20 +01:00
|
|
|
|
|
2021-07-04 13:01:10 +01:00
|
|
|
|
DShotRMT* DShotFirst = nullptr;
|
|
|
|
|
|
DShotRMT* DShotNext = nullptr;
|
|
|
|
|
|
|
2021-06-29 19:05:20 +01:00
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
|
|
// ...create clean 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);
|
|
|
|
|
|
|
|
|
|
|
|
// ...create clean packet
|
|
|
|
|
|
encode_dshot_to_rmt(DSHOT_NULL_PACKET);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DShotRMT::~DShotRMT() {
|
|
|
|
|
|
rmt_driver_uninstall(dshot_config.rmt_channel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-04 20:34:26 +01:00
|
|
|
|
DShotRMT::DShotRMT(DShotRMT const&) {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
// ...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];
|
2021-07-04 13:01:10 +01:00
|
|
|
|
dshot_config.bidirectional = is_bidirectional;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
|
|
|
|
|
|
switch (dshot_config.mode) {
|
|
|
|
|
|
case DSHOT150:
|
2021-06-30 02:06:02 +01:00
|
|
|
|
dshot_config.ticks_per_bit = 64; // ...Bit Period Time 6.67 <20>s
|
|
|
|
|
|
dshot_config.ticks_zero_high = 24; // ...zero time 2.50 <20>s
|
|
|
|
|
|
dshot_config.ticks_one_high = 48; // ...one time 5.00 <20>s
|
2021-06-29 19:05:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DSHOT300:
|
2021-06-30 02:06:02 +01:00
|
|
|
|
dshot_config.ticks_per_bit = 32; // ...Bit Period Time 3.33 <20>s
|
|
|
|
|
|
dshot_config.ticks_zero_high = 12; // ...zero time 1.25 <20>s
|
|
|
|
|
|
dshot_config.ticks_one_high = 24; // ...one time 2.50 <20>s
|
2021-06-29 19:05:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DSHOT600:
|
2021-06-30 02:06:02 +01:00
|
|
|
|
dshot_config.ticks_per_bit = 16; // ...Bit Period Time 1.67 <20>s
|
|
|
|
|
|
dshot_config.ticks_zero_high = 6; // ...zero time 0.625 <20>s
|
|
|
|
|
|
dshot_config.ticks_one_high = 12; // ...one time 1.25 <20>s
|
2021-06-29 19:05:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DSHOT1200:
|
2021-06-30 02:06:02 +01:00
|
|
|
|
dshot_config.ticks_per_bit = 8; // ...Bit Period Time 0.83 <20>s
|
|
|
|
|
|
dshot_config.ticks_zero_high = 3; // ...zero time 0.313 <20>s
|
|
|
|
|
|
dshot_config.ticks_one_high = 6; // ...one time 0.625 <20>s
|
2021-06-29 19:05:20 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2021-07-04 02:01:26 +01:00
|
|
|
|
// ...because having a default is "good style"
|
2021-06-29 19:05:20 +01:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
rmt_dshot_config.rmt_mode = RMT_MODE_TX;
|
|
|
|
|
|
rmt_dshot_config.channel = dshot_config.rmt_channel;
|
|
|
|
|
|
rmt_dshot_config.gpio_num = dshot_config.gpio_num;
|
|
|
|
|
|
rmt_dshot_config.mem_block_num = dshot_config.mem_block_num;
|
|
|
|
|
|
rmt_dshot_config.clk_div = dshot_config.clk_div;
|
|
|
|
|
|
|
|
|
|
|
|
rmt_dshot_config.tx_config.loop_en = false;
|
|
|
|
|
|
rmt_dshot_config.tx_config.carrier_en = false;
|
|
|
|
|
|
rmt_dshot_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
|
|
|
|
|
rmt_dshot_config.tx_config.idle_output_en = true;
|
2021-07-04 02:01:26 +01:00
|
|
|
|
|
2021-06-29 19:05:20 +01:00
|
|
|
|
// ...setup selected dshot mode
|
|
|
|
|
|
rmt_config(&rmt_dshot_config);
|
|
|
|
|
|
|
|
|
|
|
|
// ...essential step, return the result
|
|
|
|
|
|
auto init_failed = rmt_driver_install(rmt_dshot_config.channel, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
// ...because esp_err_t returns more than true or false
|
|
|
|
|
|
if (init_failed != 0) {
|
|
|
|
|
|
return true;
|
2021-07-04 02:01:26 +01:00
|
|
|
|
} else {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-04 13:01:10 +01:00
|
|
|
|
if (dshot_config.bidirectional) {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
|
|
|
|
|
|
// ...implement bidirectional mode
|
|
|
|
|
|
|
2021-07-04 02:01:26 +01:00
|
|
|
|
} else {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-04 13:01:10 +01:00
|
|
|
|
// ...get all setup data about current mode
|
2021-06-29 19:05:20 +01:00
|
|
|
|
dshot_config_t* DShotRMT::get_dshot_info() {
|
|
|
|
|
|
return &dshot_config;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-04 13:01:10 +01:00
|
|
|
|
// ...get the ABP_Clock Divider for further calculations
|
|
|
|
|
|
uint8_t* DShotRMT::get_dshot_clock_div() {
|
|
|
|
|
|
return &dshot_config.clk_div;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rmt_item32_t* DShotRMT::encode_dshot_to_rmt(uint16_t parsed_packet) {
|
2021-07-04 02:01:26 +01:00
|
|
|
|
for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1) {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
if (parsed_packet & 0b1000000000000000) {
|
|
|
|
|
|
// set one
|
2021-07-04 20:34:26 +01:00
|
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_one_high;
|
|
|
|
|
|
dshot_tx_rmt_item[i].level0 = 1;
|
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_one_low;
|
|
|
|
|
|
dshot_tx_rmt_item[i].level1 = 0;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
// set zero
|
2021-07-04 20:34:26 +01:00
|
|
|
|
dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high;
|
|
|
|
|
|
dshot_tx_rmt_item[i].level0 = 1;
|
|
|
|
|
|
dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_low;
|
|
|
|
|
|
dshot_tx_rmt_item[i].level1 = 0;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ...end marker added to each frame
|
2021-07-04 20:34:26 +01:00
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration0 = DSHOT_PAUSE_BIDIRECTIONAL;
|
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 0;
|
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = 0;
|
|
|
|
|
|
dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 0;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
|
2021-07-04 20:34:26 +01:00
|
|
|
|
return dshot_tx_rmt_item;
|
2021-06-29 19:05:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ...just returns the checksum
|
|
|
|
|
|
// DOES NOT APPEND CHECKSUM!!!
|
|
|
|
|
|
uint16_t DShotRMT::calc_dshot_chksum(const dshot_packet_t& dshot_packet) {
|
|
|
|
|
|
uint16_t packet = DSHOT_NULL_PACKET;
|
|
|
|
|
|
uint16_t chksum = DSHOT_NULL_PACKET;
|
|
|
|
|
|
|
2021-07-04 13:01:10 +01:00
|
|
|
|
if (dshot_config.bidirectional) {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
|
|
|
|
|
|
// ...implement bidirectional mode
|
|
|
|
|
|
|
2021-07-04 02:01:26 +01:00
|
|
|
|
} else {
|
2021-06-29 19:05:20 +01:00
|
|
|
|
packet = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
|
chksum ^= packet; // xor data by nibbles
|
|
|
|
|
|
packet >>= 4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
chksum &= 0b0000000000001111;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
prepared_to_encode = (dshot_packet.throttle_value << 1) | dshot_packet.telemetric_request;
|
|
|
|
|
|
prepared_to_encode = (prepared_to_encode << 4) | chksum;
|
|
|
|
|
|
|
|
|
|
|
|
return prepared_to_encode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ...finally output using ESP32 RMT
|
|
|
|
|
|
void DShotRMT::output_rmt_data(const dshot_packet_t& dshot_packet) {
|
|
|
|
|
|
encode_dshot_to_rmt(prepare_rmt_data(dshot_packet));
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2021-07-04 20:34:26 +01:00
|
|
|
|
rmt_write_items(rmt_dshot_config.channel, dshot_tx_rmt_item, DSHOT_PACKET_LENGTH, false);
|
2021-06-29 19:05:20 +01:00
|
|
|
|
}
|