feat(protocol): 更新SPI Link协议至v2版本并优化CAN通信

- 将SPI OTA帧格式升级为SPI Link v2,采用新的帧格式:
  | SOF0 0xA5 | SOF1 0x5A | VER 0x02 | FLAGS | SESSION 4B LE |
  TX_SEQ | ACK_SEQ | CREDIT | TYPE | LEN 2B LE | RSV 2B | PAYLOAD |
  CRC32 4B LE |
- 新增V2_OVERHEAD=20字节开销,APP最大payload为236字节,BOOT为108字节
- 修改帧类型定义:ENTER_BOOT从0xB5合并至0xB0,新增CAN业务类型0xC0~C3
- 更新CAN协议文档,支持SPI Link v2-only模式,调整最大业务payload计算
- 扩展0xC1帧功能,支持双向通信和1Hz主动推送ID快照
- 添加0xC3完整ID快照帧类型,包含seq/rx_count等完整字段
- 更新协议文档中的错误码定义和反馈payload格式

refactor(main): 重构主程序入口并添加CAN反馈任务

- 在main.cpp中添加CAN反馈任务,更新启动流程注释
- 创建createCriticalTask辅助函数,统一关键任务创建逻辑
- 调整SPI初始化顺序,确保CAN反馈任务依赖正确
- 优化错误处理机制,关键任务失败时立即重启
- 将GD32版本探测移至ota_relay任务处理
- 更新版本号至0.3.48,调整编译时间和Git哈希

config: 更新SPI和BLE配置参数

- 添加SPI主机相关配置参数,包括CS GPIO、READY GPIO等引脚定义
- 配置SPI缓冲区大小:APP为256字节,BOOT为128字节
- 更新BLE滑动窗口参数,ACK窗口设为32,数据队列深度为72
- 调整SPI工作队列大小至5120字节,增加任务堆栈监控说明
This commit is contained in:
小龙虾
2026-07-16 23:00:15 +08:00
parent 51c89b3065
commit 5db3f66b8e
57 changed files with 4728 additions and 2098 deletions
+18 -14
View File
@@ -30,7 +30,7 @@ typedef struct {
can_channel_t channel; ///< CAN 通道 (0~2)
uint32_t ext_id; ///< 扩展帧 ID (29bit, Big-Endian 存储)
uint8_t data[CAN_MAX_DLC]; ///< CAN 数据负载 (最大 64 字节)
uint8_t dlc; ///< 数据长度 (1~64)
uint8_t dlc; ///< 数据长度 (0~64)
} can_frame_t;
/**
@@ -38,8 +38,8 @@ typedef struct {
* 内部通过 SpiMaster 的公共 SPI 接口完成传输。
* @param channel CAN 通道编号(0~2)。
* @param ext_id 扩展帧 ID(29 位)。
* @param data CAN 数据负载。
* @param dlc 数据长度。
* @param data CAN 数据负载;dlc==0 时可为 nullptr。
* @param dlc 数据长度(0~64);0 表示空数据帧,仍发送 ID。
* @param recv_buf 接收数据缓冲区(可为 nullptr)。
* @param recv_max 接收缓冲区最大长度。
* @param timeout_ms 超时时间(毫秒)。
@@ -61,11 +61,14 @@ int canFdSend(can_channel_t channel, uint32_t ext_id,
/**
* @brief 单个不重复 ID 的最新帧快照条目。
*
* 0xC1 线协议条目:id(4LE)+dlc(1)+flags(1)+rsvd(2)+data(dlc)。
* seq / rx_count 未上线,解析后恒为 0(保留字段便于日后扩协议)。
*/
typedef struct {
uint32_t id;
uint32_t seq;
uint32_t rx_count;
uint32_t id; ///< CAN ID
uint32_t seq; ///< 线协议未携带,解析后恒 0
uint32_t rx_count; ///< 线协议未携带,解析后恒 0
uint8_t dlc;
uint8_t fd_flag;
uint8_t brs;
@@ -77,8 +80,6 @@ typedef struct {
// CAN 接收过滤模式 & 1Hz 反馈快照
// 与 GD32 DCU_APP SPI 协议帧 0xC1/0xC2 配套
// ============================================================
#define SPI_CAN_FEEDBACK_LAST_DATA_BYTES 93 ///< 反馈快照中最近一帧数据字节数(受 SPI 128B 应答帧约束(kTxBufSize=128 - overhead=5 - 前缀30 = 93))
typedef enum {
CAN_RX_MODE_ALL_PASS = 0, ///< 默认:禁用 ID 过滤,三路全部接收
CAN_RX_MODE_FILTER_ID = 1, ///< 按 CONFIG_CAN_RX_FILTER_ID/MASK 做标准帧过滤
@@ -115,24 +116,27 @@ typedef struct {
* @brief 解析 GD32 主动推送的一页 CAN 反馈 payload,累积到结果结构中。
*
* GD32 APP 通过 requestEsp32Read(0xC1) 主动推送每页反馈,ESP32 收到后
* 调用本函数解析。page=0 时重置 out 并填充固定头 + 首批条目;
* page=1 时在已有条目后追加。每页解析后置 out->valid=1。
* 调用本函数解析:
* - page=0:清空 entries、重置 entryIdx,填固定头 + 首批条目;
* - page=1:校验 channel 与 page0 一致后追加;
* - page>1 或声明条目却截断无法解析 → 失败。
* 成功时置 out->valid=1。
*
* @param payload GD32 推送帧的 payload([channel, page, total, count, mode, seq, ...])。
* @param len payload 长度。
* @param out 累积结果(跨 page 0/1 累积)。
* @param entryIdx 当前已填充条目数(page 0 时被重置为 0,调用方按通道维护)。
* @return 0 成功,<0 失败(payload 过短或通道非法)。
* @return 0 成功,<0 失败(payload 过短、页号/通道非法或条目截断)。
*/
int canFdParseFeedbackPage(const uint8_t *payload, size_t len,
can_feedback_result_t *out, uint8_t *entryIdx);
/**
* @brief 切换 GD32 三路 CAN 的接收过滤模式。
* @param mode 目标模式(0=全收,1=按 ID 过滤)。
* @brief 切换 GD32 三路 CAN 的接收过滤模式(SPI 0xC2)。
* @param mode 目标模式(0=全收,1=按 ID 过滤,2=CAN0 桥接到 1/2)。
* @param actual 可选输出,GD32 实际生效模式。
* @param timeout_ms SPI 事务超时。
* @return 0 成功,<0 失败。
* @return 0 成功,<0 失败(失败时打 WARN 日志)。
*/
int canFdSetRxMode(can_rx_mode_t mode,
can_rx_mode_t *actual,