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
+27 -2
View File
@@ -114,6 +114,7 @@ esp_err_t Gd32FlashStore::ensureErasedForWrite(uint32_t offset, size_t len)
addr += chunk;
m_erasedUpTo = addr;
(void)esp_task_wdt_reset();
taskYIELD(); /* 长时间兜底擦时让 WiFi/LWIP 有机会跑 */
}
return ESP_OK;
}
@@ -121,6 +122,13 @@ esp_err_t Gd32FlashStore::ensureErasedForWrite(uint32_t offset, size_t len)
/** @brief 向 gd32_ota 分区写入数据块,自动处理非 4 字节对齐的读写。 */
esp_err_t Gd32FlashStore::flashWriteChunk(uint32_t offset, const uint8_t *data, size_t len)
{
if (len == 0U) {
return ESP_OK;
}
if (data == nullptr) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *part = getPartition();
if (!part) return ESP_ERR_NOT_FOUND;
@@ -136,7 +144,7 @@ esp_err_t Gd32FlashStore::flashWriteChunk(uint32_t offset, const uint8_t *data,
return ESP_ERR_INVALID_SIZE;
}
// 正常路径:prepare 已整分区预擦;此处仅兜底(水位未覆盖时才擦)
// 按需擦除目标扇区(与收流交织;prepare 不再整区预擦)
esp_err_t erase_err = ensureErasedForWrite(aligned_offset, aligned_len);
if (erase_err != ESP_OK) {
return erase_err;
@@ -192,8 +200,22 @@ esp_err_t Gd32FlashStore::flashWriteChunk(uint32_t offset, const uint8_t *data,
/** @brief 从 gd32_ota 分区读取数据块。 */
esp_err_t Gd32FlashStore::flashReadChunk(uint32_t offset, uint8_t *data, size_t len)
{
if (len == 0U) {
return ESP_OK;
}
if (data == nullptr) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *part = getPartition();
if (!part) return ESP_ERR_NOT_FOUND;
/* 防坏 meta / 错误 offset 读越界 */
if (offset >= part->size || len > (size_t)(part->size - offset)) {
DEBUG_ERR("OTA", "flashReadChunk越界: offset=%" PRIu32 " len=%zu part=%" PRIu32,
offset, len, part->size);
return ESP_ERR_INVALID_SIZE;
}
return esp_partition_read(part, offset, data, len);
}
@@ -206,7 +228,7 @@ esp_err_t Gd32FlashStore::erasePartition(uint32_t size)
DEBUG_ERR("OTA", "erasePartition: size=%" PRIu32 " > partition=%" PRIu32, size, part->size);
return ESP_ERR_INVALID_SIZE;
}
// 按扇区擦除并喂狗,避免大块 erase 长时间占住调用任务
// 按扇区擦除并喂狗/yield,避免大块 erase 长时间占住调用任务饿死 WiFi
uint32_t done = 0;
while (done < size) {
uint32_t chunk = kFlashSectorSize;
@@ -215,6 +237,8 @@ esp_err_t Gd32FlashStore::erasePartition(uint32_t size)
}
esp_err_t err = esp_partition_erase_range(part, done, chunk);
if (err != ESP_OK) {
DEBUG_ERR("OTA", "erasePartition: erase @%" PRIu32 " len=%" PRIu32 " failed: %s",
done, chunk, esp_err_to_name(err));
return err;
}
done += chunk;
@@ -222,6 +246,7 @@ esp_err_t Gd32FlashStore::erasePartition(uint32_t size)
m_erasedUpTo = done;
}
(void)esp_task_wdt_reset();
taskYIELD();
}
return ESP_OK;
}
+3 -3
View File
@@ -72,7 +72,7 @@ public:
/**
* @brief 新一轮 GD32 固件写入会话开始时调用:复位擦除水位。
* prepareGd32Storage 随后会 erasePartition(整分区)。
* 后续 flashWriteChunk 经 ensureErasedForWrite 按需擦扇区。
*/
void beginWriteSession();
@@ -152,8 +152,8 @@ private:
const esp_partition_t *m_partition = nullptr; ///< gd32_ota 分区指针(懒初始化)
// ── 擦除水位:prepare 整分区预擦后 m_erasedUpTo==part->size;
// flashWriteChunk 内 ensureErased 为安全兜底(水位已覆盖则立即返回)。
// ── 擦除水位:beginWriteSession 后从 0 起;
// flashWriteChunk → ensureErasedForWrite 按需推进(不再依赖 prepare 整区预擦)。
static constexpr uint32_t kFlashSectorSize = 4096U;
uint32_t m_erasedUpTo = 0;
esp_err_t ensureErasedForWrite(uint32_t offset, size_t len);