This commit is contained in:
Jacob Dahl
2026-02-17 13:42:51 -09:00
parent 4d58f00bf8
commit ceba801573
20 changed files with 1677 additions and 4350 deletions
File diff suppressed because it is too large Load Diff
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include <px4_platform_common/defines.h>
#define INCLUDE_AM32_FIRMWARE
#define AM32_FIRMWARE_ADDR (0x08001000)
#define AM32_FIRMWARE_TAG_ADDR (0x08007BE0)
__EXPORT extern const uint8_t am32_fw_version_major;
__EXPORT extern const uint8_t am32_fw_version_minor;
__EXPORT extern const uint8_t am32_firmware[20752];
__EXPORT extern const uint8_t am32_firmware_tag[16];
+5 -5
View File
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2020 PX4 Development Team. All rights reserved.
# Copyright (c) 2024 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -34,9 +34,9 @@
px4_add_module(
MODULE modules__esc_flasher
MAIN esc_flasher
COMPILE_FLAGS
SRCS
EscFlasher.cpp
EscFlasher.hpp
AM32Firmware.c
)
am32_protocol.cpp
DEPENDS
px4_work_queue
)
File diff suppressed because it is too large Load Diff
+113 -182
View File
@@ -1,47 +1,78 @@
/****************************************************************************
*
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file EscFlasher.hpp
*
* AM32 ESC firmware update module.
*
* Triggered by MAV_CMD_ESC_FIRMWARE_UPDATE via vehicle_command.
* Reads firmware from SD card, stops DShot, takes over motor GPIOs,
* and flashes each ESC via bit-banged AM32 bootloader protocol.
* Requires reboot to restore normal DShot operation.
*/
#pragma once
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/cli.h>
#include <px4_platform_common/posix.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <lib/perf/perf_counter.h>
#include <drivers/drv_hrt.h>
#include <parameters/param.h>
#include <px4_platform_common/posix.h>
#include <drivers/drv_hrt.h>
#include <systemlib/mavlink_log.h>
#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/topics/parameter_update.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/led_control.h>
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/tune_control.h>
#include <uORB/topics/esc_firmware_update_status.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_command_ack.h>
#include <uORB/topics/vehicle_status.h>
// Firefly drones have 4 ESCs
#define ESC_COUNT (4)
#include "am32_protocol.hpp"
class ESC_Flasher : public ModuleBase<ESC_Flasher>, public ModuleParams
class EscFlasher : public ModuleBase<EscFlasher>, public ModuleParams
{
public:
EscFlasher();
~EscFlasher() override;
ESC_Flasher();
~ESC_Flasher() override;
ESC_Flasher(const ESC_Flasher &) = delete;
ESC_Flasher operator=(const ESC_Flasher &) = delete;
EscFlasher(const EscFlasher &) = delete;
EscFlasher &operator=(const EscFlasher &) = delete;
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/** @see ModuleBase */
static ESC_Flasher *instantiate(int argc, char *argv[]);
static EscFlasher *instantiate(int argc, char *argv[]);
/** @see ModuleBase::run() */
void run() override;
@@ -56,174 +87,74 @@ public:
int print_status() override;
private:
static constexpr uint8_t MAX_ESCS = 8;
static constexpr const char *FIRMWARE_PATH = "/fs/microsd/am32/firmware.bin";
static constexpr uint32_t DSHOT_STOP_TIMEOUT_US = 2000000; // 2 s
static constexpr uint32_t GPIO_SETTLE_TIME_US = 600000; // 600 ms
static constexpr uint32_t IDLE_INTERVAL_US = 1000000; // 1 s
void RunLoop();
void subscribe_orb_messages(void);
void unsubscribe_orb_messages(void);
void update_params(void);
uint8_t get_current_arming_state(void);
void set_esc_type(bool print);
void load_firmware_source(void);
int bitbang_send_packet(uint32_t gpio, uint8_t* packet, uint16_t length, uint8_t* response, uint8_t response_length);
uint16_t make_crc(uint8_t* buffer, uint16_t length);
void play_tune(int tune);
void stop_tune(void);
void set_physical_led_outputs(uint8_t color);
void send_vehicle_command_leds(void);
perf_counter_t _loop_perf; /**< loop performance counter */
uint32_t run_delay{10000};
const uint32_t run_delay_min = 5000; //wait 0.005 sec between running the work loop (200 Hz)
const uint32_t run_delay_max = 1000000; //wait 1 sec between running the work loop (1 Hz)
volatile bool wake{false};
uint32_t delay_loops;
hrt_abstime time_now;
hrt_abstime time_1s;
hrt_abstime time_250ms;
hrt_abstime time_flash_start;
hrt_abstime time_delayed_start;
uint32_t run_calls;
bool print_flag; // Turn this on to enable continuous status printing
bool flashing_in_progress{false};
bool flashing_in_progress_last{false};
uint8_t vehicle_armed; // Indicates if the vehicle is armed
uint8_t last_color{0};
uint8_t _am32_fw_ver_major;
uint8_t _am32_fw_ver_minor;
enum class AM32_FIRMWARE_SOURCE {
MEM_ARRAY,
SD_CARD,
STREAM_HEAP
enum class State : uint8_t {
Idle,
StoppingDShot,
InitGPIOs,
ConnectESC,
ReadInfo,
LoadFirmware,
Flashing,
RunApp,
Complete,
Failed
};
AM32_FIRMWARE_SOURCE _am32_fw_source{AM32_FIRMWARE_SOURCE::MEM_ARRAY};
enum class ESCType {
Unknown = 0,
AM32 = 1,
BLHELI32 = 2,
AM32_Old = 3,
BlueJay = 4
};
ESCType _esc_type{ESCType::Unknown};
// --- state machine helpers ---
void handle_vehicle_command();
void ack_vehicle_command(const vehicle_command_s &cmd, uint8_t result);
void begin_update(uint8_t esc_index);
void advance_to_next_esc();
void transition(State s);
void fail(uint8_t error_code);
void cleanup();
void publish_status();
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";
// --- state handlers (called from run loop) ---
void state_stopping_dshot();
void state_init_gpios();
void state_connect_esc();
void state_read_info();
void state_load_firmware();
void state_flash_chunk();
void state_run_app();
void state_complete();
void state_failed();
const char* esc_types_strings[5] = {esc_type_unknown, esc_type_am32, esc_type_blheli32, esc_type_am32_old, esc_type_bluejay};
// --- current state ---
State _state{State::Idle};
hrt_abstime _state_entered{0};
hrt_abstime _flash_started{0};
enum class ESCUpdate {
Disabled,
Update,
ForceUpdate,
Recover,
Recover2,
Cancel
};
ESCUpdate _esc_update{ESCUpdate::Disabled};
uint32_t esc_update_state{0};
uint32_t prev_esc_update_state{0};
uint8_t esc_update_motors[ESC_COUNT];
uint32_t esc_update_motor_index;
uint32_t flashing_result;
// --- ESC tracking ---
uint8_t _target_esc{255}; // from vehicle_command param1
uint8_t _current_esc{0}; // index into _gpio_pins[]
uint8_t _esc_count{0};
uint32_t _gpio_pins[MAX_ESCS]{};
uint8_t _device_info[am32::DEVICE_INFO_SIZE]{};
// Struct to track flashing progress for a single ESC
typedef struct {
uint8_t current_motor;
uint32_t current_gpio;
uint32_t fw_length;
uint32_t fw_bytes_sent;
uint32_t fw_bytes_left;
uint16_t base_address;
uint16_t next_address;
uint8_t fw_part; // We have 2 parts to write for AM32 firmware, am32_firmware and am32_firmware_tag
uint16_t crc;
uint32_t tx_length;
uint8_t tx_buffer[258];
} ESC_FLASHING_STATE;
ESC_FLASHING_STATE esc_flashing_state;
// --- firmware file ---
int _fw_fd{-1};
uint32_t _fw_size{0};
uint32_t _fw_written{0};
const uint16_t max_chunk_length = 256;
// --- published status ---
esc_firmware_update_status_s _status{};
// Variables to save responses from DShot
typedef struct {
uint8_t major;
uint8_t minor;
} ESC_FW_VERSIONS;
ESC_FW_VERSIONS esc_versions[ESC_COUNT];
// --- publications ---
uORB::Publication<esc_firmware_update_status_s> _status_pub{ORB_ID(esc_firmware_update_status)};
uORB::Publication<vehicle_command_ack_s> _cmd_ack_pub{ORB_ID(vehicle_command_ack)};
uint32_t esc_gpios[ESC_COUNT];
uint32_t gpio_high;
hrt_abstime time_gpio_set;
// --- subscriptions ---
uORB::Subscription _vehicle_cmd_sub{ORB_ID(vehicle_command)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
// AM32 Bootloader commands
uint8_t response_data[16];
const uint8_t am32_boot_init[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0x0D, 'B', 'L', 'H', 'e', 'l', 'i', 0xF4, 0x7D};
const uint8_t am32_boot_init_resp_len = 9;
uint8_t am32_set_addr[6] = {0xFF, 0, 0x10, 0x00, 0, 0};
const uint8_t am32_set_addr_resp_len = 1;
uint8_t am32_set_buff_size[6] = {0xFE, 0, 1, 0, 0, 0};
const uint8_t am32_set_buff_size_resp_len = 0;
const uint8_t am32_send_buff_resp_len = 1;
uint8_t am32_write[4] = {0x01, 0, 0, 0};
const uint8_t am32_write_resp_len = 1;
const uint8_t am32_run_app[4] = {0x00, 0, 0, 0};
const uint8_t am32_run_app_resp_len = 0;
// These variables are useful for starting a flash from command-line
volatile uint32_t start_flash;
volatile uint16_t motor_flags;
volatile uint32_t cancel_flash;
// Request/Ack variables
uint32_t msg_id;
uint32_t esc_info_timeout;
// Tune control
uint32_t esc_flasher_tune{0}; // Play some different tunes during flashing
uint32_t esc_flasher_stop_tune{0}; // Stop tune 1 second after starting
// Publication variables
uORB::Publication<esc_flasher_request_s> _esc_flasher_request_pub{ORB_ID(esc_flasher_request)};
uORB::Publication<esc_flasher_status_s> _esc_flasher_status_pub{ORB_ID(esc_flasher_status)};
uORB::Publication<tune_control_s> _tune_control_pub{ORB_ID(tune_control)};
uORB::PublicationData<led_control_s> _led_control_pub{ORB_ID(led_control)};
uORB::Publication<vehicle_command_s> _vehicle_command_pub{ORB_ID(vehicle_command)};
// Subscription variables
uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)};
uORB::Subscription _esc_flasher_request_ack_sub{ORB_ID(esc_flasher_request_ack)};
uORB::Subscription _sensor_gps_sub {ORB_ID(sensor_gps)};
uORB::Subscription _vehicle_status_sub {ORB_ID(vehicle_status)};
// MAVLink
// --- mavlink log ---
orb_advert_t _mavlink_log_pub{nullptr};
sensor_gps_s _sensor_gps {}; /**< gps sensor */
vehicle_status_s _vehicle_status {}; /**< vehicle status */
DEFINE_PARAMETERS(
(ParamInt<px4::params::ESC_TYPE>) _param_esc_type,
(ParamInt<px4::params::ESC_UPDATE>) _param_esc_update
)
};
+304
View File
@@ -0,0 +1,304 @@
/****************************************************************************
*
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file am32_protocol.cpp
*
* AM32 ESC bootloader protocol — bit-banged UART implementation.
*/
#include "am32_protocol.hpp"
#include <string.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/micro_hal.h>
#include <drivers/drv_hrt.h>
namespace am32 {
// Bootloader handshake packet (8 zero bytes + 0x0D + "BLHeli" + CRC)
static const uint8_t boot_init[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0D, 'B', 'L', 'H', 'e', 'l', 'i', 0xF4, 0x7D
};
uint16_t crc16(const uint8_t *buf, uint16_t len)
{
uint16_t crc = 0;
for (uint16_t i = 0; i < len; i++) {
uint8_t xb = buf[i];
for (uint8_t j = 0; j < 8; j++) {
if (((xb & 0x01) ^ (crc & 0x0001)) != 0) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
xb >>= 1;
}
}
return crc;
}
int send_packet(uint32_t gpio, const uint8_t *tx_data, uint16_t tx_len,
uint8_t *rx_data, uint8_t rx_len)
{
// Build GPIO configurations from the port+pin value
uint32_t gpio_out = (gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK))
| GPIO_OUTPUT | GPIO_OUTPUT_SET | GPIO_PULLUP;
uint32_t gpio_in = (gpio & (GPIO_PORT_MASK | GPIO_PIN_MASK))
| GPIO_INPUT | GPIO_PULLUP;
// Start as output HIGH
px4_arch_configgpio(gpio_out);
// --- critical section: interrupts off for timing accuracy ---
irqstate_t irq_state = px4_enter_critical_section();
// Transmit bytes (19200 8N1, LSB-first)
for (uint16_t k = 0; k < tx_len; k++) {
uint8_t byte = tx_data[k];
// Start bit (LOW)
px4_arch_gpiowrite(gpio_out, 0);
hrt_abstime t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US) {}
// 8 data bits, LSB first
for (uint8_t i = 0; i < 8; i++) {
px4_arch_gpiowrite(gpio_out, (byte >> i) & 1);
t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US) {}
}
// Stop bit (HIGH)
px4_arch_gpiowrite(gpio_out, 1);
t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US) {}
}
// Switch to input for receiving
px4_arch_configgpio(gpio_in);
// Receive response bytes
uint8_t read_count = 0;
if (rx_len > 0 && rx_data != nullptr) {
for (uint8_t i = 0; i < rx_len; i++) {
// Wait for start bit (line goes LOW)
hrt_abstime t = hrt_absolute_time();
while (px4_arch_gpioread(gpio_in)) {
if (hrt_absolute_time() - t > RX_TIMEOUT_US) {
// Timeout — restore output HIGH and bail
px4_arch_configgpio(gpio_out);
px4_leave_critical_section(irq_state);
return ERR_TIMEOUT;
}
}
// Half-bit delay to centre sampling window
t = hrt_absolute_time();
while (hrt_absolute_time() - t < HALF_BIT_TIME_US) {}
// Read 8 data bits
uint8_t byte = 0;
for (uint8_t bit = 0; bit < 8; bit++) {
t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US) {}
if (px4_arch_gpioread(gpio_in)) {
byte |= (1 << bit);
}
}
rx_data[read_count++] = byte;
// Wait through stop bit
t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US) {}
}
}
// Restore pin to output HIGH
px4_arch_configgpio(gpio_out);
// Inter-packet gap (8 bit-times)
hrt_abstime t = hrt_absolute_time();
while (hrt_absolute_time() - t < BIT_TIME_US * 8) {}
px4_leave_critical_section(irq_state);
return read_count;
}
int handshake(uint32_t gpio, uint8_t device_info[DEVICE_INFO_SIZE])
{
int ret = send_packet(gpio, boot_init, sizeof(boot_init),
device_info, DEVICE_INFO_SIZE);
if (ret == DEVICE_INFO_SIZE) {
return OK;
}
return (ret < 0) ? ret : ERR_NACK;
}
int set_address(uint32_t gpio, uint16_t address)
{
uint8_t cmd[6];
cmd[0] = CMD_SET_ADDRESS;
cmd[1] = 0x00;
cmd[2] = (address >> 8) & 0xFF; // high byte
cmd[3] = address & 0xFF; // low byte
uint16_t crc = crc16(cmd, 4);
cmd[4] = crc & 0xFF;
cmd[5] = (crc >> 8) & 0xFF;
uint8_t response;
int ret = send_packet(gpio, cmd, sizeof(cmd), &response, 1);
if (ret == 1 && response == ACK) {
return OK;
}
return (ret < 0) ? ret : ERR_NACK;
}
int write_chunk(uint32_t gpio, const uint8_t *data, uint16_t len)
{
if (len == 0 || len > MAX_CHUNK_SIZE || data == nullptr) {
return ERR_INVALID;
}
// 1) CMD_SET_BUFFER — tell bootloader how many bytes follow
uint8_t size_cmd[6];
size_cmd[0] = CMD_SET_BUFFER;
size_cmd[1] = 0x00;
if (len == MAX_CHUNK_SIZE) {
size_cmd[2] = 1; // 256 encoded as (1, 0)
size_cmd[3] = 0;
} else {
size_cmd[2] = 0;
size_cmd[3] = (uint8_t)len;
}
uint16_t crc = crc16(size_cmd, 4);
size_cmd[4] = crc & 0xFF;
size_cmd[5] = (crc >> 8) & 0xFF;
// SET_BUFFER has no response
send_packet(gpio, size_cmd, sizeof(size_cmd), nullptr, 0);
// 2) Send data + CRC — bootloader ACKs after receiving
uint8_t buf[MAX_CHUNK_SIZE + 2];
memcpy(buf, data, len);
crc = crc16(data, len);
buf[len] = crc & 0xFF;
buf[len + 1] = (crc >> 8) & 0xFF;
uint8_t response;
int ret = send_packet(gpio, buf, len + 2, &response, 1);
if (ret != 1 || response != ACK) {
return (ret < 0) ? ret : ERR_NACK;
}
// 3) CMD_PROG_FLASH — commit buffer to flash
uint8_t prog_cmd[4];
prog_cmd[0] = CMD_PROG_FLASH;
prog_cmd[1] = 0x00;
crc = crc16(prog_cmd, 2);
prog_cmd[2] = crc & 0xFF;
prog_cmd[3] = (crc >> 8) & 0xFF;
ret = send_packet(gpio, prog_cmd, sizeof(prog_cmd), &response, 1);
if (ret == 1 && response == ACK) {
return OK;
}
return (ret < 0) ? ret : ERR_NACK;
}
int read_flash(uint32_t gpio, uint8_t *buf, uint16_t len)
{
if (len == 0 || buf == nullptr) {
return ERR_INVALID;
}
// Address must already be set via set_address()
uint8_t cmd[4];
cmd[0] = CMD_READ_FLASH;
cmd[1] = (uint8_t)len;
uint16_t crc = crc16(cmd, 2);
cmd[2] = crc & 0xFF;
cmd[3] = (crc >> 8) & 0xFF;
int ret = send_packet(gpio, cmd, sizeof(cmd), buf, (uint8_t)len);
if (ret == (int)len) {
return OK;
}
return (ret < 0) ? ret : ERR_NACK;
}
int run_app(uint32_t gpio)
{
static const uint8_t cmd[] = {0x00, 0x00, 0x00, 0x00};
// No response expected
send_packet(gpio, cmd, sizeof(cmd), nullptr, 0);
return OK;
}
} // namespace am32
+156
View File
@@ -0,0 +1,156 @@
/****************************************************************************
*
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file am32_protocol.hpp
*
* AM32 ESC bootloader protocol — bit-banged UART at 19200 baud.
*
* Extracted from Matt's proof-of-concept and cleaned up into a
* function-based API for reuse by the esc_flasher module.
*/
#pragma once
#include <stdint.h>
namespace am32 {
// AM32 bootloader command bytes
static constexpr uint8_t CMD_RUN = 0x00;
static constexpr uint8_t CMD_PROG_FLASH = 0x01;
static constexpr uint8_t CMD_READ_FLASH = 0x03;
static constexpr uint8_t CMD_SET_BUFFER = 0xFE;
static constexpr uint8_t CMD_SET_ADDRESS = 0xFF;
// Bootloader ACK byte
static constexpr uint8_t ACK = 0x30;
// AM32 firmware memory layout (STM32 flash addresses)
static constexpr uint32_t FIRMWARE_ADDR = 0x08001000;
static constexpr uint32_t FIRMWARE_TAG_ADDR = 0x08007BE0;
static constexpr uint16_t FIRMWARE_TAG_SIZE = 16;
// Protocol constants
static constexpr uint16_t MAX_CHUNK_SIZE = 256;
static constexpr uint32_t BIT_TIME_US = 52; // ~19200 baud
static constexpr uint32_t HALF_BIT_TIME_US = 26;
static constexpr uint32_t RX_TIMEOUT_US = 100000; // 100 ms
// Device info from handshake response
static constexpr uint8_t DEVICE_INFO_SIZE = 9;
// Return codes
static constexpr int OK = 0;
static constexpr int ERR_TIMEOUT = -1;
static constexpr int ERR_NACK = -2;
static constexpr int ERR_CRC = -3;
static constexpr int ERR_INVALID = -4;
/**
* CRC-16 with polynomial 0xA001 (AM32 bootloader).
*/
uint16_t crc16(const uint8_t *buf, uint16_t len);
/**
* Bit-banged UART send/receive on a GPIO pin.
*
* Enters a critical section (interrupts disabled) for the
* duration of the transfer to maintain 19200-baud timing.
*
* @param gpio GPIO configuration value (port + pin bits)
* @param tx_data Bytes to transmit
* @param tx_len Number of bytes to transmit
* @param rx_data Buffer for response (may be nullptr if rx_len == 0)
* @param rx_len Expected number of response bytes
* @return Number of bytes received, or negative error code
*/
int send_packet(uint32_t gpio, const uint8_t *tx_data, uint16_t tx_len,
uint8_t *rx_data, uint8_t rx_len);
/**
* Perform the bootloader handshake.
*
* Sends the AM32 boot-init sequence and reads back 9 bytes
* of device information.
*
* @param gpio Target ESC GPIO
* @param device_info Output buffer for 9-byte device info
* @return OK or negative error code
*/
int handshake(uint32_t gpio, uint8_t device_info[DEVICE_INFO_SIZE]);
/**
* Set the flash write/read address.
*
* @param gpio Target ESC GPIO
* @param address 16-bit flash address
* @return OK or negative error code
*/
int set_address(uint32_t gpio, uint16_t address);
/**
* Write a chunk to flash.
*
* Sends CMD_SET_BUFFER + size, then data + CRC, then
* CMD_PROG_FLASH and waits for ACK. Caller must call
* set_address() first.
*
* @param gpio Target ESC GPIO
* @param data Data to write (max MAX_CHUNK_SIZE bytes)
* @param len Byte count
* @return OK or negative error code
*/
int write_chunk(uint32_t gpio, const uint8_t *data, uint16_t len);
/**
* Read flash memory.
*
* Caller must call set_address() first.
*
* @param gpio Target ESC GPIO
* @param buf Output buffer
* @param len Number of bytes to read
* @return OK or negative error code
*/
int read_flash(uint32_t gpio, uint8_t *buf, uint16_t len);
/**
* Send the RUN_APP command so the ESC boots its application.
*
* @param gpio Target ESC GPIO
* @return OK (always; no response expected)
*/
int run_app(uint32_t gpio);
} // namespace am32
-73
View File
@@ -1,73 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2012-2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file esc_params.c
*
* Parameters defined for ESCs.
*
*/
/**
* ESC type
*
* @reboot_required true
*
* @group ESC Flasher
* @value 0 - Default
* @value 1 - AM32
* @value 2 - BLHeli32
* @value 3 - AM32_Old
* @value 4 - BlueJay
*
* @min 0
* @max 4
*/
PARAM_DEFINE_INT32(ESC_TYPE, 0);
/**
* ESC update
*
* @reboot_required false
*
* @group ESC Flasher
* @value 0 - Disabled
* @value 1 - Update if version is outdated
* @value 2 - Force update all ESCs
* @value 3 - Start recovery process
* @value 4 - Continue recovery process
*
* @min 0
* @max 4
*/
PARAM_DEFINE_INT32(ESC_UPDATE, 0);