feat(SpiOta): 实现SPI OTA同步协议和主动按铃机制

- 添加SPI OTA同步协议,使用新的帧格式包含信用控制和CRC32校验
- 实现BOOT主动按铃功能,通过SPI Ready线通知ESP32读取数据
- 添加按铃超时保护机制,防止死锁情况发生
- 优化SPI中断处理,统一在CS释放时处理帧避免RX溢出错误
- 修改GPIO配置为开漏输出模式,实现硬件握手信号
- 添加同步帧解析功能,支持帧头偏移扫描和完整性校验
- 实现信用控制机制,当flash缓冲区满时阻止ESP32继续发送数据
- 修复BOOT命令响应逻辑,确保一致性处理裸字节和帧式命令
- 将OTA完成验证移到主循环处理,避免SPI ready线阻塞过久
- 添加详细的调试日志输出便于问题排查

refactor(SpiOta): 重构SPI OTA协议栈

- 使用CRC32替代CRC8进行帧校验,提高数据完整性
- 重命名帧相关常量以匹配新的同步协议格式
- 移除旧的帧处理方法,采用新的同步帧解析流程
- 添加大小端转换辅助函数用于数据编码解码
- 优化内存布局减少RAM使用
- 统一错误处理流程,添加帧校验失败日志记录
This commit is contained in:
cbb
2026-07-08 02:51:57 +08:00
parent 52e59876cf
commit 59c2bf5527
7 changed files with 349 additions and 114 deletions
+18 -4
View File
@@ -24,10 +24,8 @@ typedef enum {
class SpiOta {
public:
static constexpr u8 kSof = 0xA5U;
static constexpr u16 kRxBufSize = 256U;
static constexpr u16 kTxBufSize = 128U;
static constexpr u16 kFrameOverhead = 5U;
static constexpr u16 kIsrLogBufSize = 128U;
static void init();
@@ -46,6 +44,18 @@ public:
static bool hasActiveSession();
static u16 copyTxFrame(u8 *out, u16 max_len);
/** @brief BOOT 主动按铃:构建请求帧并拉低 SPIready,通知 ESP32 来读。
* @param[in] frame_type 请求帧类型。
* @param[in] payload 请求帧负载。
* @param[in] payload_len 负载长度。
* @return true 按铃成功,false 当前已有按铃未处理或帧构建失败。
* @note 与 DCU_APP AppSpiCommand::requestEsp32Read 对齐。
*/
static bool requestEsp32Read(u8 frame_type, const u8 *payload, u16 payload_len);
/** @brief 取消当前按铃请求,释放 SPIready。 */
static void cancelRing();
/** @brief 翻转 SPI Ready 握手引脚(PB6)电平。
* @param[in] ready true=就绪(HIGH), false=忙(LOW)。
* @note GPIO BSRR/BRR 写入原子,无需临界区;可由 ISR 与主循环共同调用。
@@ -53,7 +63,6 @@ public:
static void setSpiReady(bool ready);
static void isrProcessFrame();
static void tryProcessCompleteFrame();
static void isrLogFrame(const char *tag, const u8 *data, u16 len);
static OTAHandler *s_otaHandler;
@@ -66,11 +75,13 @@ public:
static volatile bool s_txPending;
static volatile bool s_isrResponded;
static volatile bool s_otaSessionActive;
static volatile bool s_ringPending; ///< 是否有未读走的主动按铃请求
static volatile u16 s_ringTxLen; ///< 按铃帧长度
static volatile u32 s_ringDeadline; ///< 按铃超时截止时间(ms)
static void sender(ota_link_tx_type_t type, u8 len, const u8 *data);
private:
static u8 crc8(const u8 *data, u16 len);
static bool buildFrame(spi_ota_frame_type_t type, const u8 *payload, u16 payload_len,
u8 *out, u16 *out_len);
static bool tryParseFrame(const u8 *frame, u16 frame_len,
@@ -83,6 +94,9 @@ private:
static void exitCritical(uint32_t primask);
};
/* OTA.cpp 通过此变量更新 SPI 响应帧的 CREDIT 字段 */
extern volatile u8 g_localCredit;
#ifdef __cplusplus
extern "C" {
#endif