feat(SpiOta): 添加SPI链路状态机避免帧丢失

- 引入SpiLinkState类管理SPI通信节奏控制状态机
- 实现链路状态控制,确保ESP32完成读取前不发起新的按铃请求
- 配置EXTI6中断监控PB6(SPIready)上升沿信号
- 在requestEsp32Read中检查canRingNow()状态防止帧冲突
- 添加onSpiReadyExti中断处理函数更新链路状态
This commit is contained in:
cbb
2026-07-09 02:46:56 +08:00
parent 59c2bf5527
commit dfc12611ed
4 changed files with 59 additions and 2 deletions
+27
View File
@@ -22,6 +22,24 @@ typedef enum {
SPI_OTA_RSP_ERROR = 0x82
} spi_ota_frame_type_t;
/**
* @brief SPI 链路节奏控制状态机(与 DCU_APP AppSpiCommand::SpiLinkState 对齐)。
*
* BOOT 主动按铃(requestEsp32Read)后,必须等待 ESP32 读取完成
* (SPIready/PB6 上升沿,由 EXTI6 捕获)后才允许发起下一帧,
* 避免下降沿被 ESP32 ISR 在非 IDLE 态丢弃。
*/
class SpiLinkState {
public:
bool canRingNow() const { return m_esp32Ready; } ///< 是否允许发起新按铃
void onRingStart() { m_esp32Ready = false; } ///< 发起按铃时消费完成 flag
void onEsp32Complete() { m_esp32Ready = true; } ///< ESP32 releaseReady 上升沿触发(EXTI6 ISR 调用)
void onRingTimeout() { m_esp32Ready = true; } ///< 按铃超时取消时允许重试
void reset() { m_esp32Ready = true; } ///< 初始化(首帧允许)
private:
volatile bool m_esp32Ready = true;
};
class SpiOta {
public:
static constexpr u16 kRxBufSize = 256U;
@@ -39,6 +57,13 @@ public:
static void configureExti();
static void onSpiInterrupt();
static void onCsExtiInterrupt();
/** @brief EXTI6(PB6 SPIready) 上升沿 ISR 入口,通知 SpiLinkState ESP32 已完成读取。
* @note 必须为 public,供 extern "C" EXTI5_9_IRQHandler 调用。 */
static void onSpiReadyExti();
/** @brief 配置 EXTI6 上升沿中断(SPIready/PB6)。 */
static void configureSpiReadyExti();
static void feedRxBytes(const u8 *data, u16 len);
static bool hasTxFrame();
static bool hasActiveSession();
@@ -92,6 +117,8 @@ private:
static void queueTxFrame(const u8 *frame, u16 len);
static uint32_t enterCritical();
static void exitCritical(uint32_t primask);
static SpiLinkState s_linkState; ///< 主动按铃节奏控制状态机
};
/* OTA.cpp 通过此变量更新 SPI 响应帧的 CREDIT 字段 */