Files
DCU_APP/GD32E508_DCU_APP/sys/motor_ota_relay.h
T

86 lines
3.3 KiB
C++
Raw Normal View History

/**
* @file motor_ota_relay.h
* @brief 电机 OTA relay 状态机 — GD32 本地完成与电机 Bootloader 的全部交互。
*
* 职责:
* - AES-128 密钥协商(K_master 存本地 flash)
* - 固件数据 AES-ECB 加密 + keyed-CRC32
* - CAN 上逐包 stop-and-wait(发 0x101 → 等 0x102 ACK → 按包号继续)
* - 进度/完成/错误通过 SPI ring push 报 ESP32
*
* ESP32 通过 SPI 0xD0(start) / 0xD1(data) / 0xD3(cancel) 喂数据,
* GD32 通过 0xD2 ring push 报进度。
*/
#ifndef SYS_MOTOR_OTA_RELAY_H
#define SYS_MOTOR_OTA_RELAY_H
#include <cstdint>
namespace MotorOtaRelay {
/* 电机 Bootloader CAN ID(固定,非节点相关) */
static constexpr uint32_t kOtaReqId = 0x00000100U; /* Host→Motor 握手 */
static constexpr uint32_t kOtaDataId = 0x00000101U; /* Host→Motor 数据 */
static constexpr uint32_t kOtaAckId = 0x00000102U; /* Motor→Host ACK */
static constexpr uint32_t kOtaWaitId = 0x00000103U; /* Motor→Host IDLE心跳 */
static constexpr uint32_t kOtaCancelId = 0x00000000U; /* Host→Motor 取消 */
static constexpr uint32_t kPacketPayload = 48U; /* 每包有效载荷 */
static constexpr uint32_t kFrameDlc = 64U; /* CAN FD DLC */
static constexpr uint32_t kMaxFwSize = 0xC000U; /* 48 KB */
static constexpr uint32_t kAckTimeoutMs = 5000U; /* 单包 ACK 超时 */
static constexpr uint32_t kHandshakeTimeoutMs = 3000U;
enum class State : uint8_t {
kIdle = 0,
kHandshakeReq, /* 已发 [00 AC],等 ACK */
kHandshakeNonce, /* 已发 nonce,等 36B ACK */
kHandshakeConfirm, /* 已发 confirm,等 [BA C0] */
kErase, /* 已发 size,等擦除完成 ACK */
kTransfer, /* 逐包传输中 */
kFinishOta, /* 收到 0xF0F0F0F0,已发 [0A AC],等 FINISH ACK */
kFinished, /* 完成 */
kError, /* 错误 */
kPreCancel = 9, /* 等待电机 BOOT 心跳(0x103)确认已进入 Bootloader,然后进 kHandshakeReq */
kWaitAppRun = 10 /* 尾包 ACK 后电机已跳转,等新 APP 上电自主发的 [0A AC] 运行确认,收到才进 kFinished */
};
struct Status {
State state;
uint8_t error_code;
uint16_t packets_acked;
uint16_t total_packets;
uint32_t fw_size;
};
/* 初始化(注册 CAN RX 回调等) */
void init();
/* 主循环推进(从 task_runner 周期调用,建议 1~5ms 一次) */
void process();
/* 数据发送推进:由主循环 TaskRunner 每圈调用,按 SysTick 钟表控制每包间隔
* (不再走 ISR 计时 + TASK 标志,消除跨上下文计数器竞争)。 */
void onTxTick();
/* CAN RX 分发入口:匹配 0x102 ACK 帧时消费,返回 true 表示已处理 */
bool onCanFrame(uint8_t channel, uint32_t id, const uint8_t *data, uint8_t len);
/* SPI 命令接口 */
bool start(uint8_t can_channel, uint32_t fw_size);
/**
* @brief 喂入固件明文。payload 布局:[offset:u32 LE][data...]
* offset 为固件绝对字节偏移;重复/重叠区间幂等 ACK,缺口拒绝(→SPI WAIT)。
* 无 offset 的旧纯流会在 SPI 超时重传时把同一 chunk 追加进环缓(BLF 见前 236B 循环)。
*/
bool feedData(const uint8_t *data, uint16_t len);
void cancel();
/* 状态查询 */
Status getStatus();
bool isActive();
} /* namespace MotorOtaRelay */
#endif /* SYS_MOTOR_OTA_RELAY_H */