feat(sys): 添加AES加密和电机OTA中继功能模块
添加了AES128加密算法实现和电机OTA中继相关文件,包括: - ./sys/aes128.h 和 ./sys/aes128.cpp - AES128加密算法头文件和实现 - ./sys/motor_ota_relay.h 和 ./sys/motor_ota_relay.cpp - 电机OTA中继功能 - 更新了项目配置文件包含这些新模块
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include "device/adc.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/motor_ota_relay.h"
|
||||
#include "sys/task_runner.h"
|
||||
#include "main.h"
|
||||
#include <cstring>
|
||||
@@ -130,6 +131,11 @@ void adcFeedbackPushStep()
|
||||
s_needPush = false;
|
||||
return;
|
||||
}
|
||||
/* 电机 OTA relay 活跃期间停止 ADC 反馈推送,避免按铃干扰 0xD1 数据流
|
||||
* (详见 canFeedbackPushStep 同款注释)。 */
|
||||
if (MotorOtaRelay::isActive()) {
|
||||
return;
|
||||
}
|
||||
/* 与 CAN 反馈共用 ring:未读走则等下一主循环,不抢占 */
|
||||
if (AppSpiCommandBus.isRingPending()) {
|
||||
return;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "device/canfd.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/motor_ota_relay.h"
|
||||
#include "spi_link_v2.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
@@ -281,6 +282,15 @@ void canFeedbackPushStep()
|
||||
return;
|
||||
}
|
||||
|
||||
/* 电机 OTA relay 活跃期间停止 CAN 反馈主动推送:
|
||||
* OTA 数据流(0xD1)在 SPI 上是紧循环 stop-and-wait,本推送会拉低 PB6 按
|
||||
* 铃、占用 m_txBuf,与 0xD1 的 ACK 响应争用单缓冲;ESP32 的 send/poll
|
||||
* 两步法会把 0xC1 EVENT 帧当作 0xD1 响应误读,导致 feedTask 误判失败。
|
||||
* OTA 结束后 s_roundActive 由 taskHeartbeat 重新激活。 */
|
||||
if (MotorOtaRelay::isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 1Hz 节流:当前轮次未激活则等待 taskHeartbeat() 触发 */
|
||||
if (!s_roundActive) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @file app_spi_cmd_motor_ota.cpp
|
||||
* @brief SPI 命令处理:电机 OTA relay(0xD0 命令 / 0xD1 数据)。
|
||||
*
|
||||
* 0xD0 payload: [cmd(1B)] + 参数
|
||||
* cmd=0 (START): [ch(1B) + fw_size(4B LE)]
|
||||
* cmd=1 (CANCEL): 无参数
|
||||
* cmd=2 (QUERY): 无参数 → 返回 12B 状态
|
||||
*
|
||||
* 0xD1 payload: [offset:u32 LE][固件明文…](总长≤236,数据≤232),
|
||||
* GD32 按绝对 offset 幂等写入环缓后本地加密发 CAN。
|
||||
*/
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/motor_ota_relay.h"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
|
||||
class SpiMotorOtaHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return (frame_type == SpiDefs::kTypeMotorOtaCmd ||
|
||||
frame_type == SpiDefs::kTypeMotorOtaData);
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
if (ctx.frame_type == SpiDefs::kTypeMotorOtaCmd) {
|
||||
handleCmd(bus, ctx);
|
||||
} else {
|
||||
handleData(bus, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void handleCmd(AppSpiCommand &bus, const SpiCommandContext &ctx)
|
||||
{
|
||||
if (ctx.payload_len < 1U) {
|
||||
const uint8_t err = 0xE0U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t cmd = ctx.payload[0];
|
||||
switch (cmd) {
|
||||
case 0U: { /* START */
|
||||
if (ctx.payload_len < 6U) {
|
||||
const uint8_t err = 0xE0U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
return;
|
||||
}
|
||||
const uint8_t ch = ctx.payload[1];
|
||||
const uint32_t fw_size =
|
||||
static_cast<uint32_t>(ctx.payload[2])
|
||||
| (static_cast<uint32_t>(ctx.payload[3]) << 8)
|
||||
| (static_cast<uint32_t>(ctx.payload[4]) << 16)
|
||||
| (static_cast<uint32_t>(ctx.payload[5]) << 24);
|
||||
|
||||
printf("[MOTOR-OTA] SPI 0xD0 START recv ch=%u fw=%lu\r\n",
|
||||
(unsigned)ch, (unsigned long)fw_size);
|
||||
if (MotorOtaRelay::start(ch, fw_size)) {
|
||||
const uint8_t ack = 0x00U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
} else {
|
||||
const uint8_t err = 0xE5U; /* 已在进行中或参数无效 */
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1U: { /* CANCEL */
|
||||
MotorOtaRelay::cancel();
|
||||
const uint8_t ack = 0x00U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
break;
|
||||
}
|
||||
case 2U: { /* QUERY */
|
||||
const MotorOtaRelay::Status st = MotorOtaRelay::getStatus();
|
||||
uint8_t resp[12];
|
||||
resp[0] = static_cast<uint8_t>(st.state);
|
||||
resp[1] = st.error_code;
|
||||
resp[2] = static_cast<uint8_t>(st.packets_acked & 0xFF);
|
||||
resp[3] = static_cast<uint8_t>(st.packets_acked >> 8);
|
||||
resp[4] = static_cast<uint8_t>(st.total_packets & 0xFF);
|
||||
resp[5] = static_cast<uint8_t>(st.total_packets >> 8);
|
||||
resp[6] = static_cast<uint8_t>(st.fw_size & 0xFF);
|
||||
resp[7] = static_cast<uint8_t>((st.fw_size >> 8) & 0xFF);
|
||||
resp[8] = static_cast<uint8_t>((st.fw_size >> 16) & 0xFF);
|
||||
resp[9] = static_cast<uint8_t>((st.fw_size >> 24) & 0xFF);
|
||||
resp[10] = 0; /* reserved */
|
||||
resp[11] = 0;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, resp, 12U);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const uint8_t err = 0xE1U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleData(AppSpiCommand &bus, const SpiCommandContext &ctx)
|
||||
{
|
||||
if (ctx.payload_len == 0U) {
|
||||
const uint8_t ack = 0x00U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MotorOtaRelay::feedData(ctx.payload, ctx.payload_len)) {
|
||||
const uint8_t ack = 0x00U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
} else {
|
||||
/* 缓冲满或状态不对:返回 WAIT,ESP32 稍后重试 */
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspWait, nullptr, 0U);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SpiMotorOtaHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerSpiMotorOtaCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @file app_spi_cmd_motor_state.cpp
|
||||
* @brief 多电机状态 250ms 主动推送 (TYPE 0xC5) 与被动查询处理器。
|
||||
*
|
||||
* 与 0xC1 CAN 反馈 / 0xC4 ADC 反馈同款模型:
|
||||
* - 主循环 pushStep 非阻塞;ring 未读走则跳过。
|
||||
* - 优先级:CAN feedback > Motor state > ADC(runPending 调用顺序决定)。
|
||||
* - 启动节拍:taskMotorStatePush()(250ms 独立任务位,与 1Hz heartbeat 解耦)。
|
||||
* - 总开关:sys/config.h 中 CONFIG_MOTOR_STATE_ENABLE
|
||||
*
|
||||
* payload 格式 (每页):
|
||||
* [0] page 页号 (0/1)
|
||||
* [1] total_pages 总页数 (2)
|
||||
* [2] entry_count 本页有效电机数
|
||||
* [3] active_count 全局在线电机数
|
||||
* [4..] 每个 entry 44B:
|
||||
* [0] node_id, [1] channel, [2] online, [3] dlc,
|
||||
* [4..7] rx_count (LE), [8..11] seq (LE),
|
||||
* [12..43] data[32] (原始 STATUS payload)
|
||||
*/
|
||||
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "device/motor_state.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/motor_ota_relay.h"
|
||||
#include "spi_link_v2.h"
|
||||
#include <cstring>
|
||||
|
||||
#if (CONFIG_MOTOR_STATE_ENABLE != 0)
|
||||
|
||||
/* ---- 被动查询 handler ---- */
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr uint16_t kMotorPayloadMax =
|
||||
static_cast<uint16_t>(AppSpiCommand::kTxBufSize - V2_OVERHEAD);
|
||||
|
||||
class SpiMotorStateHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeMotorState;
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
/* 请求 payload[0] = page (可选, 默认 0) */
|
||||
const uint8_t page = (ctx.payload_len >= 1U) ? ctx.payload[0] : 0U;
|
||||
|
||||
uint8_t payload[kMotorPayloadMax] = {};
|
||||
const uint16_t n = MotorState::buildPayload(page, payload, sizeof(payload));
|
||||
|
||||
if (n == 0U) {
|
||||
uint8_t err = 0xE1U; /* 非法页号 */
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
return;
|
||||
}
|
||||
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, payload, n);
|
||||
}
|
||||
};
|
||||
|
||||
static SpiMotorStateHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
/* ---- 主动推送状态机 ---- */
|
||||
|
||||
static volatile bool s_roundActive = true; /* 首次允许启动,无需等 1s */
|
||||
static uint8_t s_page = 0U;
|
||||
|
||||
/**
|
||||
* @brief 激活新一轮 0xC5 推送。
|
||||
* @note 若上一轮仍在进行中(s_roundActive=true),不重置 s_page,
|
||||
* 避免在 250ms 节拍 < 单轮 P0+P1 排空时间时反复打断、
|
||||
* 导致 P1 永远没机会推(前端只看到 P0 的 5 个节点)。
|
||||
* 新一轮请求会被合并到当前轮次,推完 P1 后自然进入下一轮。
|
||||
* 这与 0xC1 canFeedbackStartRound() 的语义不同:CAN 反馈 1Hz
|
||||
* 远大于排空时间,不会发生自打断;0xC5 在 250ms 节拍下需要这层保护。
|
||||
*/
|
||||
void motorStateStartRound()
|
||||
{
|
||||
s_roundActive = true;
|
||||
/* 仅在轮次已结束时重置页号;进行中保持 s_page 不动 */
|
||||
if (s_page >= kMotorTotalPages) {
|
||||
s_page = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void motorStatePushStep()
|
||||
{
|
||||
if (!s_roundActive) {
|
||||
return;
|
||||
}
|
||||
if (OtaBootRequest::isRebootPending()) {
|
||||
s_roundActive = false;
|
||||
return;
|
||||
}
|
||||
/* 电机 OTA relay 活跃期间停止 motor state 推送,避免按铃干扰 0xD1 数据流
|
||||
* (详见 canFeedbackPushStep 同款注释)。 */
|
||||
if (MotorOtaRelay::isActive()) {
|
||||
return;
|
||||
}
|
||||
if (AppSpiCommandBus.isRingPending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t payload[kMotorPayloadMax] = {};
|
||||
const uint16_t n = MotorState::buildPayload(s_page, payload, sizeof(payload));
|
||||
|
||||
if (n > 0U) {
|
||||
(void)AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeMotorState, payload, n);
|
||||
}
|
||||
|
||||
/* 推进页号;到末页后结束本轮 */
|
||||
s_page++;
|
||||
if (s_page >= kMotorTotalPages) {
|
||||
s_page = 0U;
|
||||
s_roundActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void registerSpiMotorStateCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
|
||||
#else /* CONFIG_MOTOR_STATE_ENABLE == 0 */
|
||||
|
||||
void motorStateStartRound() {}
|
||||
void motorStatePushStep() {}
|
||||
void registerSpiMotorStateCommand(AppSpiCommand & /*bus*/) {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file app_spi_cmd_motor_version.cpp
|
||||
* @brief 多电机版本 0xC6 事件驱动推送(pushStep)。
|
||||
*
|
||||
* 与 0xC1/0xC4/0xC5 同款 ring 按铃模型,但触发方式不同:
|
||||
* - 0xC5 是 250ms 周期轮次;0xC6 是事件驱动——电机回报版本(0x00FF)时
|
||||
* MotorVersion::onCanFrame() 缓存并置 dirty,本 pushStep 在主循环发现
|
||||
* 有待推送(dirty)且 ring 空闲时,打包一帧 0xC6 按铃推给 ESP32。
|
||||
* - 版本是按需查询的低频数据,推送优先级最低(runPending 中排在 0xC1/0xC5/0xC4 之后)。
|
||||
* - 总开关:sys/config.h 中 CONFIG_MOTOR_VERSION_ENABLE
|
||||
*
|
||||
* payload 格式(单帧,Header 2B + 每 entry 29B):
|
||||
* [0] count 本帧节点数
|
||||
* [1] rsvd 保留
|
||||
* [2..] 每个 entry 29B:
|
||||
* [0] node_id, [1..4] seq (LE), [5..28] version[24] (原始版本 payload)
|
||||
*
|
||||
* 仅推送(无被动查询 handler):ESP32 收到后自行缓存,Web 从 ESP32 缓存读取。
|
||||
*/
|
||||
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "device/motor_version.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/motor_ota_relay.h"
|
||||
#include "spi_link_v2.h"
|
||||
#include <cstring>
|
||||
|
||||
#if (CONFIG_MOTOR_VERSION_ENABLE != 0)
|
||||
|
||||
namespace {
|
||||
static constexpr uint16_t kVerPayloadMax =
|
||||
static_cast<uint16_t>(AppSpiCommand::kTxBufSize - V2_OVERHEAD);
|
||||
}
|
||||
|
||||
void motorVersionPushStep()
|
||||
{
|
||||
/* 无待推送 → 直接返回(事件驱动,绝大多数主循环迭代在此短路) */
|
||||
if (!MotorVersion::hasPending()) {
|
||||
return;
|
||||
}
|
||||
if (OtaBootRequest::isRebootPending()) {
|
||||
return;
|
||||
}
|
||||
/* 电机 OTA relay 活跃期间停止版本推送,避免按铃干扰 0xD1 数据流
|
||||
* (详见 canFeedbackPushStep 同款注释)。dirty 保留不丢,OTA 结束后继续推。 */
|
||||
if (MotorOtaRelay::isActive()) {
|
||||
return;
|
||||
}
|
||||
if (AppSpiCommandBus.isRingPending()) {
|
||||
return; /* 上一帧未被 ESP32 读走,下轮再试(dirty 保留不丢) */
|
||||
}
|
||||
|
||||
uint8_t payload[kVerPayloadMax] = {};
|
||||
const uint16_t n = MotorVersion::buildPayload(payload, sizeof(payload));
|
||||
if (n > 0U) {
|
||||
/* ring 空闲时 requestEsp32Read 必成功(单线程主循环,检查与调用之间无并发);
|
||||
* buildPayload 已清掉已打包槽的 dirty。 */
|
||||
(void)AppSpiCommandBus.requestEsp32Read(SpiDefs::kTypeMotorVersion, payload, n);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_MOTOR_VERSION_ENABLE == 0 */
|
||||
|
||||
void motorVersionPushStep() {}
|
||||
|
||||
#endif
|
||||
@@ -386,6 +386,8 @@ extern void registerSpiCanBridgeCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiCanFeedbackCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiIdSnapshotCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiAdcFeedbackCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiMotorStateCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiMotorOtaCommand(AppSpiCommand &bus);
|
||||
AppSpiCommand AppSpiCommandBus;
|
||||
void AppSpiCommand::init()
|
||||
{
|
||||
@@ -407,6 +409,8 @@ void AppSpiCommand::init()
|
||||
registerSpiCanFeedbackCommand(AppSpiCommandBus);
|
||||
registerSpiIdSnapshotCommand(AppSpiCommandBus);
|
||||
registerSpiAdcFeedbackCommand(AppSpiCommandBus); /* CONFIG 关时为空注册 */
|
||||
registerSpiMotorStateCommand(AppSpiCommandBus); /* CONFIG 关时为空注册 */
|
||||
registerSpiMotorOtaCommand(AppSpiCommandBus);
|
||||
|
||||
configureGpio();
|
||||
configureDma();
|
||||
@@ -467,7 +471,14 @@ void AppSpiCommand::armSpiDmasForNextCs()
|
||||
|
||||
void AppSpiCommand::registerHandler(ISpiCommandHandler *handler)
|
||||
{
|
||||
if (!handler || m_commandCount >= kMaxCommands) {
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
if (m_commandCount >= kMaxCommands) {
|
||||
/* 表满静默丢弃会导致命令无声失效(motor_ota 曾因此 0xD0 回 0xEE)。
|
||||
* init 在任务上下文,printf 走 USART0 中断环,启动日志可见。 */
|
||||
printf("[SPI-APP] registerHandler: 注册表满(%u),handler 被丢弃! 请增大 CONFIG_SPI_MAX_COMMANDS\r\n",
|
||||
static_cast<unsigned>(kMaxCommands));
|
||||
return;
|
||||
}
|
||||
m_commands[m_commandCount++] = handler;
|
||||
@@ -811,6 +822,13 @@ void AppSpiCommand::onCsRelease()
|
||||
m_txLen = 0U;
|
||||
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
|
||||
|
||||
/* ★ 关键:在 processCommand 之前同步 g_localCredit。
|
||||
* 此时 m_txLen=0, m_ringPending=false → g_localCredit=1。
|
||||
* 若不做,buildResponse 会读到上一次 pushStatus/requestEsp32Read 留下的
|
||||
* g_localCredit=0 → 响应帧携带 credit=0 → ESP32 m_remoteCredit=0 → 后续发送死锁。
|
||||
* 这是电机 OTA 吐量只有 1 chunk/2s 的根因。 */
|
||||
updateDataReady();
|
||||
|
||||
/* 命令处理在 EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame 模式),
|
||||
* 消除主循环延迟。处理完成后 processCommand() 末尾再 re-arm DMA + 释放 SPIready。 */
|
||||
processCommand();
|
||||
|
||||
@@ -173,6 +173,21 @@ void adcFeedbackPushStep();
|
||||
/** @brief 激活一次 ADC 调试反馈推送(1Hz taskHeartbeat)。 */
|
||||
void adcFeedbackStartRound();
|
||||
|
||||
/**
|
||||
* @brief 多电机状态(0xC5)主动推送一步。
|
||||
* @note CONFIG_MOTOR_STATE_ENABLE=0 时为空操作。
|
||||
*/
|
||||
void motorStatePushStep();
|
||||
|
||||
/** @brief 激活新一轮多电机状态推送(1Hz taskHeartbeat)。 */
|
||||
void motorStateStartRound();
|
||||
|
||||
/**
|
||||
* @brief 多电机版本(0xC6)事件驱动推送步进:有 dirty 版本且 ring 空闲时按铃推一帧。
|
||||
* @note CONFIG_MOTOR_VERSION_ENABLE=0 时为空操作。优先级低于 0xC1/0xC5/0xC4。
|
||||
*/
|
||||
void motorVersionPushStep();
|
||||
|
||||
/** @brief 全局 SPI 命令总线实例。 */
|
||||
extern AppSpiCommand AppSpiCommandBus;
|
||||
|
||||
|
||||
@@ -27,6 +27,21 @@ static constexpr uint8_t kTypeIdSnapshot = 0xC3U; /**< ESP32 -> GD32 拉取不
|
||||
/** @brief GD32 ADC 调试反馈(温度/VDDA/VIN);1Hz 主动 EVENT 或被动查询。
|
||||
* 由 CONFIG_SPI_ADC_FEEDBACK_ENABLE 门控,调试用可随时关。 */
|
||||
static constexpr uint8_t kTypeAdcFeedback = 0xC4U;
|
||||
/** @brief 多电机状态推送;GD32 1Hz 主动 EVENT,将 CAN STATUS 帧缓存表推给 ESP32。
|
||||
* 由 CONFIG_MOTOR_STATE_ENABLE 门控。 */
|
||||
static constexpr uint8_t kTypeMotorState = 0xC5U;
|
||||
/** @brief 多电机版本推送;GD32 收到电机版本应答(0x00FF)后事件驱动 EVENT,
|
||||
* 将缓存的 24B 版本推给 ESP32(按需查询、低频,不进周期推送)。
|
||||
* 由 CONFIG_MOTOR_VERSION_ENABLE 门控。 */
|
||||
static constexpr uint8_t kTypeMotorVersion = 0xC6U;
|
||||
|
||||
/** @brief 电机 OTA relay 命令(ESP32→GD32):start / cancel / query。
|
||||
* payload[0]=cmd(0=start,1=cancel,2=query), start 时 payload[1]=ch, payload[2..5]=fw_size LE */
|
||||
static constexpr uint8_t kTypeMotorOtaCmd = 0xD0U;
|
||||
/** @brief 电机 OTA relay 数据(ESP32→GD32):固件明文块,GD32 本地加密后发 CAN。 */
|
||||
static constexpr uint8_t kTypeMotorOtaData = 0xD1U;
|
||||
/** @brief 电机 OTA relay 状态推送(GD32→ESP32 ring push):12B 进度。 */
|
||||
static constexpr uint8_t kTypeMotorOtaStatus = 0xD2U;
|
||||
|
||||
/* 响应类型 */
|
||||
static constexpr uint8_t kRspAck = 0x80U;
|
||||
|
||||
Reference in New Issue
Block a user