mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-16 12:43:34 +08:00
claude first attempt at converting https://github.com/PX4/PX4-Autopilot/pull/25503
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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];
|
||||
@@ -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
|
||||
)
|
||||
|
||||
+551
-1412
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
)
|
||||
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user