diff --git a/DShotRMT.cpp b/DShotRMT.cpp index 819e414..3828579 100644 --- a/DShotRMT.cpp +++ b/DShotRMT.cpp @@ -6,27 +6,29 @@ #include +// Constructor that takes gpio and rmtChannel as arguments DShotRMT::DShotRMT(gpio_num_t gpio, rmt_channel_t rmtChannel) { - // Initialize the configuration structure + // Initialize the dshot_config structure with the arguments passed to the constructor dshot_config.gpio_num = gpio; - dshot_config.pin_num = uint8_t(gpio); + dshot_config.pin_num = static_cast(gpio); dshot_config.rmt_channel = rmtChannel; - dshot_config.mem_block_num = uint8_t(RMT_CHANNEL_MAX - uint8_t(rmtChannel)); + dshot_config.mem_block_num = static_cast(RMT_CHANNEL_MAX - static_cast(rmtChannel)); - // ...create empty packet + // Create an empty packet using the DSHOT_NULL_PACKET and the buildTxRmtItem function buildTxRmtItem(DSHOT_NULL_PACKET); } +// Constructor that takes pin and channel as arguments DShotRMT::DShotRMT(uint8_t pin, uint8_t channel) { - // Initialize the configuration structure - dshot_config.gpio_num = gpio_num_t(pin); + // Initialize the dshot_config structure with the arguments passed to the constructor + dshot_config.gpio_num = static_cast(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.rmt_channel = static_cast(channel); + dshot_config.mem_block_num = RMT_CHANNEL_MAX - channel; - // ...create empty packet + // Create an empty packet using the DSHOT_NULL_PACKET and the buildTxRmtItem function buildTxRmtItem(DSHOT_NULL_PACKET); } @@ -43,59 +45,62 @@ DShotRMT::DShotRMT(DShotRMT const &) bool DShotRMT::begin(dshot_mode_t dshot_mode, bool is_bidirectional) { + // Set DShot configuration parameters based on input parameters 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; + // Set timing parameters based on selected DShot mode 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 + dshot_config.ticks_per_bit = 64; + dshot_config.ticks_zero_high = 24; + dshot_config.ticks_one_high = 48; 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 + dshot_config.ticks_per_bit = 32; + dshot_config.ticks_zero_high = 12; + dshot_config.ticks_one_high = 24; 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 + dshot_config.ticks_per_bit = 16; + dshot_config.ticks_zero_high = 6; + dshot_config.ticks_one_high = 12; 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 + dshot_config.ticks_per_bit = 8; + dshot_config.ticks_zero_high = 3; + dshot_config.ticks_one_high = 6; break; - // ...because having a default is "good style" + // Default case to handle invalid input 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 + dshot_config.ticks_per_bit = 0; + dshot_config.ticks_zero_high = 0; + dshot_config.ticks_one_high = 0; break; } - // ...calc low signal timing + // Calculate 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); + // Set up RMT configuration for DShot transmission 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; + // Set idle level for RMT transmission based on input parameter if (dshot_config.is_bidirectional) { dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH; @@ -105,87 +110,94 @@ bool DShotRMT::begin(dshot_mode_t dshot_mode, bool is_bidirectional) dshot_tx_rmt_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; } - // ...setup selected dshot mode + // Set up selected DShot mode rmt_config(&dshot_tx_rmt_config); - // ...essential step, return the result + // Install RMT driver and return 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::sendThrottleValue(uint16_t throttle_value, telemetric_request_t telemetric_request) +// Define a function to send a DShot command over an RMT interface to control a brushless motor's speed. void DShotRMT::sendThrottleValue(uint16_t throttle_value) { dshot_packet_t dshot_rmt_packet = {}; + // Check if the throttle value is less than the minimum allowed value for the DShot protocol. if (throttle_value < DSHOT_THROTTLE_MIN) { throttle_value = DSHOT_THROTTLE_MIN; } + // Check if the throttle value is greater than the maximum allowed value for the DShot protocol. 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; + + // Telemetric using additional pin on the ESC is not supported. dshot_rmt_packet.telemetric_request = NO_TELEMETRIC; + + // Calculate the checksum for the DShot packet using the calculateCRC function. dshot_rmt_packet.checksum = calculateCRC(dshot_rmt_packet); + // Send the DShot packet over the RMT interface to control the motor's speed. sendRmtPaket(dshot_rmt_packet); } +// This method builds the RMT data transmission sequence for the DShot protocol rmt_item32_t *DShotRMT::buildTxRmtItem(uint16_t parsed_packet) { - // ...for bidirectional mode + // Check if DShot is set to bidirectional mode if (dshot_config.is_bidirectional) { - // ..."invert" the high / low bits + // If bidirectional, invert the high/low bits for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1) { if (parsed_packet & 0b1000000000000000) { - // set one + // Set RMT item for a logic high signal 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 + // Set RMT item for a logic low signal dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_low; dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_high; } + // Set level of RMT item dshot_tx_rmt_item[i].level0 = 0; dshot_tx_rmt_item[i].level1 = 1; } } - - // ..."normal" DShot mode / "bidirectional" mode OFF else { + // If not bidirectional, set the RMT items as usual for (int i = 0; i < DSHOT_PAUSE_BIT; i++, parsed_packet <<= 1) { if (parsed_packet & 0b1000000000000000) { - // set one + // Set RMT item for a logic high signal 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 + // Set RMT item for a logic low signal dshot_tx_rmt_item[i].duration0 = dshot_config.ticks_zero_high; dshot_tx_rmt_item[i].duration1 = dshot_config.ticks_zero_low; } + // Set level of RMT item dshot_tx_rmt_item[i].level0 = 1; dshot_tx_rmt_item[i].level1 = 0; } } + // Set end marker for each frame if (dshot_config.is_bidirectional) { dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level0 = 1; @@ -197,39 +209,37 @@ rmt_item32_t *DShotRMT::buildTxRmtItem(uint16_t parsed_packet) dshot_tx_rmt_item[DSHOT_PAUSE_BIT].level1 = 1; } - // ...end marker added to each frame + // Add packet seperator aka DShot Pause. dshot_tx_rmt_item[DSHOT_PAUSE_BIT].duration1 = DSHOT_PAUSE; + // Return the rmt_item 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 +// Calculates a CRC value for a DShot digital control signal packet uint16_t DShotRMT::calculateCRC(const dshot_packet_t &dshot_packet) { + uint16_t crc; + + // Combine the throttle value and telemetric request flag into a 16-bit packet value 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; + + // 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 return crc; } diff --git a/DShotRMT.h b/DShotRMT.h index a238e0d..075e6dc 100644 --- a/DShotRMT.h +++ b/DShotRMT.h @@ -86,31 +86,32 @@ typedef struct dshot_config_s } dshot_config_t; // The official DShot Commands -typedef enum dshot_cmd_e { - DSHOT_CMD_MOTOR_STOP = 0, // Currently not implemented - STOP Motors - DSHOT_CMD_BEEP1, // Wait at least length of beep (380ms) before next command - DSHOT_CMD_BEEP2, // Wait at least length of beep (380ms) before next command - DSHOT_CMD_BEEP3, // Wait at least length of beep (400ms) before next command - DSHOT_CMD_BEEP4, // Wait at least length of beep (400ms) before next command - DSHOT_CMD_BEEP5, // Wait at least length of beep (400ms) before next command - DSHOT_CMD_ESC_INFO, // Currently not implemented - DSHOT_CMD_SPIN_DIRECTION_1, // Need 6x, no wait required - DSHOT_CMD_SPIN_DIRECTION_2, // Need 6x, no wait required - DSHOT_CMD_3D_MODE_OFF, // Need 6x, no wait required - DSHOT_CMD_3D_MODE_ON, // Need 6x, no wait required - DSHOT_CMD_SETTINGS_REQUEST, // Currently not implemented - DSHOT_CMD_SAVE_SETTINGS, // Need 6x, wait at least 12ms before next command - DSHOT_CMD_SPIN_DIRECTION_NORMAL, // Need 6x, no wait required - DSHOT_CMD_SPIN_DIRECTION_REVERSED, // Need 6x, no wait required - DSHOT_CMD_LED0_ON, // Currently not implemented - DSHOT_CMD_LED1_ON, // Currently not implemented - DSHOT_CMD_LED2_ON, // Currently not implemented - DSHOT_CMD_LED3_ON, // Currently not implemented - DSHOT_CMD_LED0_OFF, // Currently not implemented - DSHOT_CMD_LED1_OFF, // Currently not implemented - DSHOT_CMD_LED2_OFF, // Currently not implemented - DSHOT_CMD_LED3_OFF, // Currently not implemented - DSHOT_CMD_MAX = 47 +typedef enum dshot_cmd_e +{ + DSHOT_CMD_MOTOR_STOP = 0, // Currently not implemented - STOP Motors + DSHOT_CMD_BEEP1, // Wait at least length of beep (380ms) before next command + DSHOT_CMD_BEEP2, // Wait at least length of beep (380ms) before next command + DSHOT_CMD_BEEP3, // Wait at least length of beep (400ms) before next command + DSHOT_CMD_BEEP4, // Wait at least length of beep (400ms) before next command + DSHOT_CMD_BEEP5, // Wait at least length of beep (400ms) before next command + DSHOT_CMD_ESC_INFO, // Currently not implemented + DSHOT_CMD_SPIN_DIRECTION_1, // Need 6x, no wait required + DSHOT_CMD_SPIN_DIRECTION_2, // Need 6x, no wait required + DSHOT_CMD_3D_MODE_OFF, // Need 6x, no wait required + DSHOT_CMD_3D_MODE_ON, // Need 6x, no wait required + DSHOT_CMD_SETTINGS_REQUEST, // Currently not implemented + DSHOT_CMD_SAVE_SETTINGS, // Need 6x, wait at least 12ms before next command + DSHOT_CMD_SPIN_DIRECTION_NORMAL, // Need 6x, no wait required + DSHOT_CMD_SPIN_DIRECTION_REVERSED, // Need 6x, no wait required + DSHOT_CMD_LED0_ON, // Currently not implemented + DSHOT_CMD_LED1_ON, // Currently not implemented + DSHOT_CMD_LED2_ON, // Currently not implemented + DSHOT_CMD_LED3_ON, // Currently not implemented + DSHOT_CMD_LED0_OFF, // Currently not implemented + DSHOT_CMD_LED1_OFF, // Currently not implemented + DSHOT_CMD_LED2_OFF, // Currently not implemented + DSHOT_CMD_LED3_OFF, // Currently not implemented + DSHOT_CMD_MAX = 47 } dshot_cmd_t; // The main DShotRMT class