Added all changes to DShot driver, needed to work with ESC Flasher module.

This commit is contained in:
MattKow-Firefly 2025-08-29 13:16:17 -04:00
parent a7e93d1145
commit 8eb753068a
5 changed files with 1280 additions and 253 deletions

View File

@ -129,4 +129,5 @@
"yaml.schemas": {
"${workspaceFolder}/validation/module_schema.yaml": "${workspaceFolder}/src/modules/*/module.yaml"
},
"C_Cpp.errorSquiggles": "disabled",
}

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,11 @@
#include "DShotTelemetry.h"
#include <uORB/topics/esc_flasher_request.h>
#include <uORB/topics/esc_flasher_request_ack.h>
#include <uORB/topics/esc_flasher_status.h>
#include <uORB/topics/esc_flasher_versions.h>
using namespace time_literals;
#if !defined(DIRECT_PWM_OUTPUT_CHANNELS)
@ -76,6 +81,8 @@ public:
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
void retrieve_and_print_esc_info_thread_safe(const int motor_index);
/**
* Send a dshot command to one or all motors
* This is expected to be called from another thread.
@ -93,6 +100,9 @@ public:
bool updateOutputs(uint16_t outputs[MAX_ACTUATORS],
unsigned num_outputs, unsigned num_control_groups_updated) override;
bool updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
unsigned num_outputs, unsigned num_control_groups_updated);// override;
private:
/** Disallow copy construction and move assignment. */
@ -121,7 +131,7 @@ private:
void enable_dshot_outputs(const bool enabled);
void init_telemetry(const char *device, bool swap_rxtx);
void init_telemetry(const char *device);
int handle_new_telemetry_data(const int telemetry_index, const DShotTelemetry::EscData &data, bool ignore_rpm);
@ -129,6 +139,8 @@ private:
int handle_new_bdshot_erpm(void);
int request_esc_info();
void Run() override;
void update_params();
@ -137,7 +149,13 @@ private:
void handle_vehicle_commands();
uint16_t convert_output_to_3d_scaling(uint16_t output);
void handle_esc_flasher_requests();
void run_get_esc_info();
int retrieve_and_print_esc_info_non_blocking(const int motor_index);
int retrieve_and_print_esc_info_check_result(uint8_t* fw_ver_major, uint8_t* fw_ver_minor);
MixingOutput _mixing_output{PARAM_PREFIX, DIRECT_PWM_OUTPUT_CHANNELS, *this, MixingOutput::SchedulingPolicy::Auto, false, false};
uint32_t _reversible_outputs{};
@ -147,31 +165,78 @@ private:
uORB::PublicationMultiData<esc_status_s> esc_status_pub{ORB_ID(esc_status)};
static char _telemetry_device[20];
static bool _telemetry_swap_rxtx;
static px4::atomic_bool _request_telemetry_init;
px4::atomic<Command *> _new_command{nullptr};
px4::atomic<DShotTelemetry::OutputBuffer *> _request_esc_info{nullptr};
bool _outputs_initialized{false};
bool _outputs_on{false};
bool _waiting_for_esc_info{false};
bool _bidirectional_dshot_enabled{false};
const char *esc_type_unknown = "Unknown";
const char *esc_type_am32 = "AM32";
const char *esc_type_blheli32 = "BLHeli32";
const char *esc_type_am32_old = "AM32_Old";
const char *esc_type_bluejay = "BlueJay";
const char* esc_types_strings[5] = {esc_type_unknown, esc_type_am32, esc_type_blheli32, esc_type_am32_old, esc_type_bluejay};
enum class ESCType {
Unknown = 0,
AM32 = 1,
BLHELI32 = 2,
AM32_Old = 3,
BlueJay = 4
};
ESCType _esc_type{ESCType::Unknown};
ESCType _esc_type_temp{ESCType::Unknown};
esc_flasher_request_s _esc_flasher_request{0};
esc_flasher_request_ack_s _esc_flasher_response{0};
uint32_t esc_flasher_esc_info_state{0};
uint32_t esc_flasher_esc_info_motor_index;
uint32_t esc_flasher_flashing_state{0};
DShotTelemetry::OutputBuffer esc_flasher_output_buffer{};
static constexpr unsigned _num_outputs{DIRECT_PWM_OUTPUT_CHANNELS};
uint32_t _output_mask{0};
int _num_motors{0};
uint32_t _broken_esc{0};
bool _broken_esc_flag{0};
// run_get_esc_info variables
struct EscInfoSave {
ESCType type;
uint8_t fw_major;
uint8_t fw_minor;
};
EscInfoSave _esc_info_save[esc_status_s::CONNECTED_ESC_MAX];
uint64_t get_esc_info_time{0};
uint32_t get_esc_info_state{0};
uint32_t get_esc_info_motor_index{0};
uint32_t get_esc_info_start{0};
uint32_t get_esc_info_boot{0};
uint32_t get_esc_info_tries{0};
//uint32_t get_esc_info_debug{0};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")};
perf_counter_t _bdshot_rpm_perf{perf_alloc(PC_COUNT, MODULE_NAME": bdshot rpm")};
perf_counter_t _dshot_telem_perf{perf_alloc(PC_COUNT, MODULE_NAME": dshot telem")};
Command _current_command{};
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)};
uORB::Publication<vehicle_command_ack_s> _command_ack_pub{ORB_ID(vehicle_command_ack)};
uORB::Subscription _esc_flasher_request_sub{ORB_ID(esc_flasher_request)};
uORB::Subscription _esc_flasher_status_sub{ORB_ID(esc_flasher_status)};
uORB::Publication<esc_flasher_request_ack_s> _esc_flasher_request_ack_pub{ORB_ID(esc_flasher_request_ack)};
uint16_t _esc_status_counter{0};
uORB::Publication<esc_flasher_versions_s> _esc_flasher_versions_pub{ORB_ID(esc_flasher_versions)};
DEFINE_PARAMETERS(
(ParamFloat<px4::params::DSHOT_MIN>) _param_dshot_min,
@ -179,6 +244,7 @@ private:
(ParamInt<px4::params::DSHOT_3D_DEAD_H>) _param_dshot_3d_dead_h,
(ParamInt<px4::params::DSHOT_3D_DEAD_L>) _param_dshot_3d_dead_l,
(ParamInt<px4::params::MOT_POLE_COUNT>) _param_mot_pole_count,
(ParamBool<px4::params::DSHOT_BIDIR_EN>) _param_bidirectional_enable
(ParamBool<px4::params::DSHOT_BIDIR_EN>) _param_bidirectional_enable,
(ParamInt<px4::params::ESC_TYPE>) _param_esc_type
)
};

View File

@ -46,38 +46,53 @@ using namespace time_literals;
DShotTelemetry::~DShotTelemetry()
{
_uart.close();
deinit();
}
int DShotTelemetry::init(const char *port, bool swap_rxtx)
int DShotTelemetry::init(const char *uart_device)
{
if (!_uart.setPort(port)) {
PX4_ERR("Error configuring port %s", port);
return PX4_ERROR;
deinit();
_uart_fd = ::open(uart_device, O_RDONLY | O_NOCTTY);
if (_uart_fd < 0) {
PX4_ERR("failed to open serial port: %s err: %d", uart_device, errno);
return -errno;
}
if (!_uart.setBaudrate(DSHOT_TELEMETRY_UART_BAUDRATE)) {
PX4_ERR("Error setting baudrate on %s", port);
return PX4_ERROR;
_num_timeouts = 0;
_num_successful_responses = 0;
_current_motor_index_request = -1;
return setBaudrate(DSHOT_TELEMETRY_UART_BAUDRATE);
}
void DShotTelemetry::deinit()
{
if (_uart_fd >= 0) {
close(_uart_fd);
_uart_fd = -1;
}
}
int DShotTelemetry::redirectOutput(OutputBuffer &buffer)
{
if (expectingData()) {
// Error: cannot override while we already expect data
return -EBUSY;
}
if (swap_rxtx) {
if (!_uart.setSwapRxTxMode()) {
PX4_ERR("Error swapping TX/RX");
return PX4_ERROR;
}
}
if (! _uart.open()) {
PX4_ERR("Error opening %s", port);
return PX4_ERROR;
}
return PX4_OK;
_current_motor_index_request = buffer.motor_index;
_current_request_start = hrt_absolute_time();
_redirect_output = &buffer;
_redirect_output->buf_pos = 0;
return 0;
}
int DShotTelemetry::update(int num_motors)
{
if (_uart_fd < 0) {
return -1;
}
if (_current_motor_index_request == -1) {
// nothing in progress, start a request
_current_motor_index_request = 0;
@ -87,9 +102,10 @@ int DShotTelemetry::update(int num_motors)
}
// read from the uart. This must be non-blocking, so check first if there is data available
int bytes_available = _uart.bytesAvailable();
int bytes_available = 0;
int ret = ioctl(_uart_fd, FIONREAD, (unsigned long)&bytes_available);
if (bytes_available <= 0) {
if (ret != 0 || bytes_available <= 0) {
// no data available. Check for a timeout
const hrt_abstime now = hrt_absolute_time();
@ -111,12 +127,13 @@ int DShotTelemetry::update(int num_motors)
return -1;
}
uint8_t buf[ESC_FRAME_SIZE];
int bytes = _uart.read(buf, sizeof(buf));
const int buf_length = ESC_FRAME_SIZE;
uint8_t buf[buf_length];
int ret = -1;
int num_read = read(_uart_fd, buf, buf_length);
ret = -1;
for (int i = 0; i < bytes && ret == -1; ++i) {
for (int i = 0; i < num_read && ret == -1; ++i) {
if (_redirect_output) {
_redirect_output->buffer[_redirect_output->buf_pos++] = buf[i];
@ -166,9 +183,6 @@ bool DShotTelemetry::decodeByte(uint8_t byte, bool &successful_decoding)
_latest_data.erpm);
++_num_successful_responses;
successful_decoding = true;
} else {
++_num_checksum_errors;
}
return true;
@ -181,30 +195,32 @@ void DShotTelemetry::printStatus() const
{
PX4_INFO("Number of successful ESC frames: %i", _num_successful_responses);
PX4_INFO("Number of timeouts: %i", _num_timeouts);
PX4_INFO("Number of CRC errors: %i", _num_checksum_errors);
}
uint8_t DShotTelemetry::updateCrc8(uint8_t crc, uint8_t crc_seed)
{
uint8_t crc_u = crc ^ crc_seed;
for (int i = 0; i < 8; ++i) {
crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1);
}
return crc_u;
}
uint8_t DShotTelemetry::crc8(const uint8_t *buf, uint8_t len)
{
auto update_crc8 = [](uint8_t crc, uint8_t crc_seed) {
uint8_t crc_u = crc ^ crc_seed;
for (int i = 0; i < 8; ++i) {
crc_u = (crc_u & 0x80) ? 0x7 ^ (crc_u << 1) : (crc_u << 1);
}
return crc_u;
};
uint8_t crc = 0;
for (int i = 0; i < len; ++i) {
crc = update_crc8(buf[i], crc);
crc = updateCrc8(buf[i], crc);
}
return crc;
}
void DShotTelemetry::requestNextMotor(int num_motors)
{
_current_motor_index_request = (_current_motor_index_request + 1) % num_motors;
@ -237,6 +253,7 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer)
BLHELI32,
KissV1,
KissV2,
AM32
};
ESCVersionInfo version;
int packet_length;
@ -250,8 +267,21 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer)
packet_length = esc_info_size_kiss_v2;
} else {
version = ESCVersionInfo::KissV1;
packet_length = esc_info_size_kiss_v1;
if (buffer.buf_pos > esc_info_size_kiss_v1) {
version = ESCVersionInfo::AM32;
packet_length = esc_info_size_am32;
}
else {
version = ESCVersionInfo::KissV1;
packet_length = esc_info_size_kiss_v1;
}
}
// Check for AM32 but with bad data
if (version != ESCVersionInfo::AM32 && buffer.buf_pos == esc_info_size_am32) {
version = ESCVersionInfo::AM32;
packet_length = esc_info_size_am32;
PX4_WARN("Packet length mismatch (%i != %i), reverting to AM32 decoding", buffer.buf_pos, packet_length);
}
if (buffer.buf_pos != packet_length) {
@ -264,116 +294,337 @@ void DShotTelemetry::decodeAndPrintEscInfoPacket(const OutputBuffer &buffer)
return;
}
uint8_t esc_firmware_version = 0;
uint8_t esc_firmware_subversion = 0;
uint8_t esc_type = 0;
switch (version) {
case ESCVersionInfo::KissV1:
esc_firmware_version = data[12];
esc_firmware_subversion = (data[13] & 0x1f) + 97;
esc_type = (data[13] & 0xe0) >> 5;
break;
case ESCVersionInfo::KissV2:
case ESCVersionInfo::BLHELI32:
esc_firmware_version = data[13];
esc_firmware_subversion = data[14];
esc_type = data[15];
break;
}
const char *esc_type_str = "";
switch (version) {
case ESCVersionInfo::KissV1:
case ESCVersionInfo::KissV2:
switch (esc_type) {
case 1: esc_type_str = "KISS8A";
break;
case 2: esc_type_str = "KISS16A";
break;
case 3: esc_type_str = "KISS24A";
break;
case 5: esc_type_str = "KISS Ultralite";
break;
default: esc_type_str = "KISS (unknown)";
break;
if (version == ESCVersionInfo::AM32) {
// AM32 2.18+ ESC_INFO packet sends bytes 0-47 of its EEPROM struct
if (buffer.buf_pos < packet_length) {
PX4_ERR("Not enough data received");
return;
}
break;
case ESCVersionInfo::BLHELI32: {
char *esc_type_mutable = (char *)(data + 31);
esc_type_mutable[32] = 0;
esc_type_str = esc_type_mutable;
}
break;
}
PX4_INFO("ESC Type: %s", esc_type_str);
PX4_INFO("MCU Serial Number: %02x%02x%02x-%02x%02x%02x-%02x%02x%02x-%02x%02x%02x",
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
data[9], data[10], data[11]);
switch (version) {
case ESCVersionInfo::KissV1:
case ESCVersionInfo::KissV2:
PX4_INFO("Firmware version: %d.%d%c", esc_firmware_version / 100, esc_firmware_version % 100,
(char)esc_firmware_subversion);
break;
case ESCVersionInfo::BLHELI32:
PX4_INFO("Firmware version: %d.%d", esc_firmware_version, esc_firmware_subversion);
break;
}
if (version == ESCVersionInfo::KissV2 || version == ESCVersionInfo::BLHELI32) {
PX4_INFO("Rotation Direction: %s", data[16] ? "reversed" : "normal");
PX4_INFO("3D Mode: %s", data[17] ? "on" : "off");
}
if (version == ESCVersionInfo::BLHELI32) {
uint8_t setting = data[18];
switch (setting) {
case 0:
PX4_INFO("Low voltage Limit: off");
break;
case 255:
PX4_INFO("Low voltage Limit: unsupported");
break;
default:
PX4_INFO("Low voltage Limit: %d.%01d V", setting / 10, setting % 10);
break;
uint8_t eeprom_version = data[1];
if (eeprom_version != 2) {
PX4_ERR("Unsupported EEPROM version from ESC");
return;
}
setting = data[19];
AM32_EEprom_st* esc_info = (AM32_EEprom_st*)buffer.buffer;
switch (setting) {
case 0:
PX4_INFO("Current Limit: off");
char firmware_name[sizeof(esc_info->firmware_name) + 1];
memcpy(firmware_name, esc_info->firmware_name, sizeof(firmware_name));
firmware_name[sizeof(esc_info->firmware_name)] = 0;
PX4_INFO("Received ESC_INFO packet from motor %d:", buffer.motor_index + 1);
PX4_INFO(" ESC firmware version: %d.%d - ESC firmware name: %s", esc_info->version.major, esc_info->version.minor, firmware_name);
if (esc_info->stuck_rotor_protection) PX4_INFO(" Stuck rotor protection is ENABLED");
else PX4_INFO(" Stuck rotor protection is DISABLED");
if (esc_info->telemetry_on_interval) PX4_INFO(" Auto-Telemetry interval is ENABLED");
else PX4_INFO(" Auto-Telemetry interval is DISABLED");
if (esc_info->bi_direction) PX4_INFO(" Bi-Directional DShot is ENABLED");
else PX4_INFO(" Bi-Directional DShot is DISABLED");
}
else {
uint8_t esc_firmware_version = 0;
uint8_t esc_firmware_subversion = 0;
uint8_t esc_type = 0;
switch (version) {
case ESCVersionInfo::KissV1:
esc_firmware_version = data[12];
esc_firmware_subversion = (data[13] & 0x1f) + 97;
esc_type = (data[13] & 0xe0) >> 5;
break;
case 255:
PX4_INFO("Current Limit: unsupported");
break;
default:
PX4_INFO("Current Limit: %d A", setting);
case ESCVersionInfo::KissV2:
case ESCVersionInfo::BLHELI32:
esc_firmware_version = data[13];
esc_firmware_subversion = data[14];
esc_type = data[15];
break;
case ESCVersionInfo::AM32:
// AM32 is handled above, should not get to this case
return;
}
for (int i = 0; i < 4; ++i) {
setting = data[i + 20];
PX4_INFO("LED %d: %s", i, setting ? (setting == 255 ? "unsupported" : "on") : "off");
const char *esc_type_str = "";
switch (version) {
case ESCVersionInfo::KissV1:
case ESCVersionInfo::KissV2:
switch (esc_type) {
case 1: esc_type_str = "KISS8A";
break;
case 2: esc_type_str = "KISS16A";
break;
case 3: esc_type_str = "KISS24A";
break;
case 5: esc_type_str = "KISS Ultralite";
break;
default: esc_type_str = "KISS (unknown)";
break;
}
break;
case ESCVersionInfo::BLHELI32: {
char *esc_type_mutable = (char *)(data + 31);
esc_type_mutable[32] = 0;
esc_type_str = esc_type_mutable;
}
break;
case ESCVersionInfo::AM32:
// AM32 is handled above, should not get to this case
return;
}
PX4_INFO("ESC Type: %s", esc_type_str);
PX4_INFO("MCU Serial Number: %02x%02x%02x-%02x%02x%02x-%02x%02x%02x-%02x%02x%02x",
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
data[9], data[10], data[11]);
switch (version) {
case ESCVersionInfo::KissV1:
case ESCVersionInfo::KissV2:
PX4_INFO("Firmware version: %d.%d%c", esc_firmware_version / 100, esc_firmware_version % 100,
(char)esc_firmware_subversion);
break;
case ESCVersionInfo::BLHELI32:
PX4_INFO("Firmware version: %d.%d", esc_firmware_version, esc_firmware_subversion);
break;
case ESCVersionInfo::AM32:
// AM32 is handled above, should not get to this case
return;
}
if (version == ESCVersionInfo::KissV2 || version == ESCVersionInfo::BLHELI32) {
PX4_INFO("Rotation Direction: %s", data[16] ? "reversed" : "normal");
PX4_INFO("3D Mode: %s", data[17] ? "on" : "off");
}
if (version == ESCVersionInfo::BLHELI32) {
uint8_t setting = data[18];
switch (setting) {
case 0:
PX4_INFO("Low voltage Limit: off");
break;
case 255:
PX4_INFO("Low voltage Limit: unsupported");
break;
default:
PX4_INFO("Low voltage Limit: %d.%01d V", setting / 10, setting % 10);
break;
}
setting = data[19];
switch (setting) {
case 0:
PX4_INFO("Current Limit: off");
break;
case 255:
PX4_INFO("Current Limit: unsupported");
break;
default:
PX4_INFO("Current Limit: %d A", setting);
break;
}
for (int i = 0; i < 4; ++i) {
setting = data[i + 20];
PX4_INFO("LED %d: %s", i, setting ? (setting == 255 ? "unsupported" : "on") : "off");
}
}
}
}
int DShotTelemetry::decodeEscInfoPacketFwVersion(const OutputBuffer &buffer, uint8_t* fw_version_major, uint8_t* fw_version_minor)
{
// Return 0 for AM32 success
// Return -1 for not enough data (unknown)
// Return -2 for BLHeli success
static constexpr int version_position = 12;
const uint8_t *data = buffer.buffer;
if (buffer.buf_pos < version_position) {
PX4_ERR("Not enough data received");
return -1;
}
enum class ESCVersionInfo {
BLHELI32,
KissV1,
KissV2,
AM32
};
ESCVersionInfo version;
int packet_length;
if (data[version_position] == 254) {
version = ESCVersionInfo::BLHELI32;
packet_length = esc_info_size_blheli32;
} else if (data[version_position] == 255) {
version = ESCVersionInfo::KissV2;
packet_length = esc_info_size_kiss_v2;
} else {
if (buffer.buf_pos > esc_info_size_kiss_v1) {
version = ESCVersionInfo::AM32;
packet_length = esc_info_size_am32;
}
else {
version = ESCVersionInfo::KissV1;
packet_length = esc_info_size_kiss_v1;
}
}
// Check for AM32 but with bad data
if (version != ESCVersionInfo::AM32 && buffer.buf_pos == esc_info_size_am32) {
version = ESCVersionInfo::AM32;
packet_length = esc_info_size_am32;
PX4_WARN("Packet length mismatch (%i != %i), reverting to AM32 decoding", buffer.buf_pos, packet_length);
}
if (buffer.buf_pos != packet_length) {
PX4_ERR("Packet length mismatch (%i != %i)", buffer.buf_pos, packet_length);
return -1;
}
if (DShotTelemetry::crc8(data, packet_length - 1) != data[packet_length - 1]) {
PX4_ERR("Checksum mismatch");
return -1;
}
if (version == ESCVersionInfo::AM32) {
// AM32 2.18+ ESC_INFO packet sends bytes 0-47 of its EEPROM struct
if (buffer.buf_pos < packet_length) {
PX4_ERR("Not enough data received");
return -1;
}
uint8_t eeprom_version = data[1];
if (eeprom_version != 2) {
PX4_ERR("Unsupported EEPROM version from ESC");
return -1;
}
AM32_EEprom_st* esc_info = (AM32_EEprom_st*)buffer.buffer;
*fw_version_major = esc_info->version.major;
*fw_version_minor = esc_info->version.minor;
return 0;
}
else {
//PX4_ERR("Unsupported ESC type, this function only supports AM32");
switch (version) {
case ESCVersionInfo::KissV1:
*fw_version_major = data[12];
*fw_version_minor = (data[13] & 0x1f) + 97;
break;
case ESCVersionInfo::KissV2:
case ESCVersionInfo::BLHELI32:
*fw_version_major = data[13];
*fw_version_minor = data[14];
break;
case ESCVersionInfo::AM32:
// AM32 is handled above, should not get to this case
return -1;
}
return -2;
}
}
int DShotTelemetry::setBaudrate(unsigned baud)
{
int speed;
switch (baud) {
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
default:
return -EINVAL;
}
struct termios uart_config;
int termios_state;
/* fill the struct for the new configuration */
tcgetattr(_uart_fd, &uart_config);
//
// Input flags - Turn off input processing
//
// convert break to null byte, no CR to NL translation,
// no NL to CR translation, don't mark parity errors or breaks
// no input parity check, don't strip high bit off,
// no XON/XOFF software flow control
//
uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL |
INLCR | PARMRK | INPCK | ISTRIP | IXON);
//
// Output flags - Turn off output processing
//
// no CR to NL translation, no NL to CR-NL translation,
// no NL to CR translation, no column 0 CR suppression,
// no Ctrl-D suppression, no fill characters, no case mapping,
// no local output processing
//
// config.c_oflag &= ~(OCRNL | ONLCR | ONLRET |
// ONOCR | ONOEOT| OFILL | OLCUC | OPOST);
uart_config.c_oflag = 0;
//
// No line processing
//
// echo off, echo newline off, canonical mode off,
// extended input processing off, signal chars off
//
uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
/* no parity, one stop bit, disable flow control */
uart_config.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS);
/* set baud rate */
if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) {
return -errno;
}
if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
return -errno;
}
if ((termios_state = tcsetattr(_uart_fd, TCSANOW, &uart_config)) < 0) {
return -errno;
}
return 0;
}

View File

@ -33,9 +33,55 @@
#pragma once
#include <px4_platform_common/Serial.hpp>
#include <drivers/drv_hrt.h>
typedef struct {
uint8_t reserved_0; //0
uint8_t eeprom_version; //1
uint8_t reserved_1; //2
struct {
uint8_t major; //3
uint8_t minor; //4
} version;
char firmware_name[12]; //5-16
uint8_t dir_reversed; // 17
uint8_t bi_direction; // 18
uint8_t use_sine_start; // 19
uint8_t comp_pwm; // 20
uint8_t variable_pwm; // 21
uint8_t stuck_rotor_protection; // 22
uint8_t advance_level; // 23
uint8_t pwm_frequency; // 24
uint8_t startup_power; // 25
uint8_t motor_kv; // 26
uint8_t motor_poles; // 27
uint8_t brake_on_stop; // 28
uint8_t stall_protection; // 29
uint8_t beep_volume; // 30
uint8_t telemetry_on_interval; // 31
struct {
uint8_t low_threshold; // 32
uint8_t high_threshold; // 33
uint8_t neutral; // 34
uint8_t dead_band; // 35
} servo;
uint8_t low_voltage_cut_off; // 36
uint8_t low_cell_volt_cutoff; // 37
uint8_t rc_car_reverse; // 38
uint8_t use_hall_sensors; // 39
uint8_t sine_mode_changeover_thottle_level; // 40
uint8_t drag_brake_strength; // 41
uint8_t driving_brake_strength; // 42
struct {
uint8_t temperature; // 43
uint8_t current; // 44
} limits;
uint8_t sine_mode_power; // 45
uint8_t input_type; // 46
uint8_t auto_advance; // 47
uint8_t crc; // 48
} AM32_EEprom_st;
class DShotTelemetry
{
public:
@ -51,6 +97,7 @@ public:
static constexpr int esc_info_size_blheli32 = 64;
static constexpr int esc_info_size_kiss_v1 = 15;
static constexpr int esc_info_size_kiss_v2 = 21;
static constexpr int esc_info_size_am32 = 49;
static constexpr int max_esc_info_size = esc_info_size_blheli32;
struct OutputBuffer {
@ -61,7 +108,9 @@ public:
~DShotTelemetry();
int init(const char *uart_device, bool swap_rxtx);
int init(const char *uart_device);
void deinit();
/**
* Read telemetry from the UART (non-blocking) and handle timeouts.
@ -70,6 +119,16 @@ public:
*/
int update(int num_motors);
/**
* Redirect everything that is read into a different buffer.
* Future calls to @update will write to that instead of an internal buffer, until @update returns
* a value different from -1. No decoding is done.
* The caller must ensure the buffer exists until that point.
* @param buffer
* @return 0 on success <0 on error
*/
int redirectOutput(OutputBuffer &buffer);
bool redirectActive() const { return _redirect_output != nullptr; }
/**
@ -89,9 +148,18 @@ public:
static void decodeAndPrintEscInfoPacket(const OutputBuffer &buffer);
static int decodeEscInfoPacketFwVersion(const OutputBuffer &buffer, uint8_t* fw_version_major, uint8_t* fw_version_minor);
private:
static constexpr int ESC_FRAME_SIZE = 10;
/**
* set the Baudrate
* @param baud
* @return 0 on success, <0 on error
*/
int setBaudrate(unsigned baud);
void requestNextMotor(int num_motors);
/**
@ -102,10 +170,10 @@ private:
*/
bool decodeByte(uint8_t byte, bool &successful_decoding);
static inline uint8_t updateCrc8(uint8_t crc, uint8_t crc_seed);
static uint8_t crc8(const uint8_t *buf, uint8_t len);
device::Serial _uart {};
int _uart_fd{-1};
uint8_t _frame_buffer[ESC_FRAME_SIZE];
int _frame_position{0};
@ -119,5 +187,4 @@ private:
// statistics
int _num_timeouts{0};
int _num_successful_responses{0};
int _num_checksum_errors{0};
};