完善OTA会话状态管理,增加clearOtaSessionQueues函数统一处理会话清理, 确保CANCEL/超时/错误情况下完全复位状态、缓冲区和信用额度。 修复bootloader在stay_in_ota模式下的死循环问题,添加活动状态监控, 当OTA会话结束后自动跳转回APP,避免设备永久停留在bootloader。 统一AES模块的数据类型定义,从u8改为标准uint8_t,提升代码兼容性。 更新AES实现中的变量声明,使用一致的uint8_t类型避免潜在类型转换问题。
135 lines
5.3 KiB
C++
135 lines
5.3 KiB
C++
#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
|