Files
GD32E50X_BOOT/GD32E50X_BOOT/device/SpiOta.h
T
cbb f9ae1be120 feat(ota): 完善OTA会话管理机制并优化AES实现
完善OTA会话状态管理,增加clearOtaSessionQueues函数统一处理会话清理,
确保CANCEL/超时/错误情况下完全复位状态、缓冲区和信用额度。

修复bootloader在stay_in_ota模式下的死循环问题,添加活动状态监控,
当OTA会话结束后自动跳转回APP,避免设备永久停留在bootloader。

统一AES模块的数据类型定义,从u8改为标准uint8_t,提升代码兼容性。
更新AES实现中的变量声明,使用一致的uint8_t类型避免潜在类型转换问题。
2026-07-30 11:57:51 +08:00

135 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __SPI_OTA_H__
#define __SPI_OTA_H__
#include "OTA.h" /* 间接 config.h */
#include "config.h"
/**
* @file SpiOta.h
* @brief SPI OTA 从机协议头文件(ISR 直接响应版)。
*
* CS 释放后,EXTI ISR 直接完成帧校验+处理+响应准备,
* 响应帧在 ISR 中写入 s_txBuf,下次 CS 拉低时 DMA 自动发出。
* 无需 TBE 中断,DMA 硬件逐字节推到 MISO,消除字节错位问题。
*/
typedef enum {
SPI_OTA_CMD_REQ = 0x01,
SPI_OTA_CMD_DATA = 0x02,
SPI_OTA_CMD_CANCEL = 0x03,
SPI_OTA_CMD_BOOT = 0xB0,
SPI_OTA_RSP_ACK = 0x80,
SPI_OTA_RSP_WAIT = 0x81,
SPI_OTA_RSP_ERROR = 0x82
} spi_ota_frame_type_t;
/**
* @brief SPI 链路节奏控制状态机(与 DCU_APP AppSpiCommand::SpiLinkState 对齐)。
*
* v2 DATA_READY: ESP32 不再操作 PB6,EXTI6 上升沿信号源消失。
* canRingNow 总是返回 true,靠 requestEsp32Read 内部 s_ringPending
* 检查防止重复按铃;onEsp32Complete 改由 onCsRelease 的 ring pending
* 分支补充调用(ESP32 CS 完成即确认读走)。
*/
class SpiLinkState {
public:
bool canRingNow() const { return true; } ///< v2: 总是允许(s_ringPending 已防重复)
void onRingStart() { m_esp32Ready = false; } ///< 发起按铃时消费完成 flag
void onEsp32Complete() { m_esp32Ready = true; } ///< ESP32 CS 完成即确认读走(onCsRelease 调用)
void onRingTimeout() { m_esp32Ready = true; } ///< 按铃超时取消时允许重试
void reset() { m_esp32Ready = true; } ///< 初始化(首帧允许)
private:
volatile bool m_esp32Ready = true;
};
class SpiOta {
public:
static constexpr u16 kRxBufSize = CONFIG_SPI_RX_BUF_SIZE; /* 主机可打满 256 */
static constexpr u16 kTxBufSize = CONFIG_SPI_TX_BUF_SIZE; /* BOOT 响应满缓冲 128 */
static constexpr u16 kIsrLogBufSize = 128U;
static void init();
static void poll();
static void setSender(OTAHandler *handler);
static void onCsAssert();
static void onCsRelease();
static void configureGpio();
static void configureSpi0();
static void configureDma();
static void configureExti();
static void onCsExtiInterrupt();
static bool hasTxFrame();
/** @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 CREDIT 从 0 恢复为 >0 时发送 CREDIT_UPDATE 事件并拉低 DATA_READY。 */
static void notifyCreditRestore();
/** @brief ISR DATA 队列中待处理包数(供 OTA.cpp 计算 CREDIT 用)。 */
static u8 pendingDataCount();
/**
* @brief OTA 会话完整复位(CANCEL / resetToIdle / 进 BOOT 共用)。
* 清空 ISR DATA 队列、会话标志,并恢复 g_localCredit。
*/
static void clearOtaSessionQueues();
/** @brief 主循环入队 TX 后装载 DMA + 更新 DATA_READY(擦除中途刷 WAIT 用)。 */
static void servicePendingTx();
/** @brief 翻转 SPI Ready 握手引脚(PB6)电平。
* @param[in] ready true=就绪(HIGH), false=忙(LOW)。
* @note GPIO BSRR/BRR 写入原子,无需临界区;可由 ISR 与主循环共同调用。
* 与 DCU_APP AppSpiCommand::setSpiReady 行为一致,适配 ESP32 硬件握手。 */
static void setSpiReady(bool ready);
static void isrProcessFrame();
static void isrLogFrame(const char *tag, const u8 *data, u16 len);
static OTAHandler *s_otaHandler;
static volatile bool s_csActive;
static volatile u16 s_rxStreamLen;
static volatile u16 s_txStreamPos;
static u8 s_rxStream[kRxBufSize];
static u8 s_txBuf[kTxBufSize];
static volatile u16 s_txLen;
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 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,
spi_ota_frame_type_t *type,
const u8 **payload,
u16 *payload_len);
static ota_link_msg_type_t mapMsgType(spi_ota_frame_type_t type);
static void submitResponse(const u8 *frame, u16 len);
static uint32_t enterCritical();
static void exitCritical(uint32_t primask);
static SpiLinkState s_linkState; ///< 主动按铃节奏控制状态机
};
/* OTA.cpp 通过此变量更新 SPI 响应帧的 CREDIT 字段 */
extern volatile u8 g_localCredit;
#endif