From 62eab6fcdf9926a72c8302d32d1ab41312b43ac2 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Fri, 17 Jul 2026 02:06:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(SpiOta):=20=E5=BC=95=E5=85=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8C=96=E5=B8=B8=E9=87=8F=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?SPI=20OTA=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将硬编码的超时时间和缓冲区大小替换为配置文件中的常量 - 重构响应缓存对齐方式,使用SpiOta::kTxBufSize进行长度对齐 - 重新调整初始化顺序:先预装DMA再开启CS中断,避免首笔CS竞争问题 - 优化CS释放逻辑注释,明确说明ESP32数据准备状态的处理机制 - 在应用跳转前增加80ms延时,避免与主机HELLO时钟重叠影响SPI初始化 - 在配置头文件中统一定义SPI相关参数,提高代码可维护性 - 更新SPI握手信号语义说明,明确v2协议的数据就绪状态机制 --- GD32E50X_BOOT/device/SpiOta.cpp | 17 ++++++++--------- GD32E50X_BOOT/device/SpiOta.h | 7 ++++--- GD32E50X_BOOT/sys/OTA.cpp | 4 +++- GD32E50X_BOOT/sys/OTA.h | 4 ++-- GD32E50X_BOOT/sys/config.h | 17 +++++++++++++---- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/GD32E50X_BOOT/device/SpiOta.cpp b/GD32E50X_BOOT/device/SpiOta.cpp index 1fbe3f1..c8a7b5b 100644 --- a/GD32E50X_BOOT/device/SpiOta.cpp +++ b/GD32E50X_BOOT/device/SpiOta.cpp @@ -26,7 +26,7 @@ */ /* 主动按铃超时时间(毫秒) */ -static constexpr u32 kRingTimeoutMs = 500U; +static constexpr u32 kRingTimeoutMs = CONFIG_SPI_RING_TIMEOUT_MS; #define SPI_TX_DMA_PERIPH DMA0 #define SPI_TX_DMA_CH DMA_CH2 #define SPI_RX_DMA_PERIPH DMA0 @@ -68,9 +68,9 @@ static volatile u8 s_expectedRxSeq = 0U; static volatile bool s_v2Active = false; /* 响应缓存:用于重复帧重发(dist 128~255 分支)。 - * Boot 的 TX buffer 最多 128 字节,缓存长度对齐 kTxBufSize。 */ + * 长度对齐 SpiOta::kTxBufSize(128);payload 上限 = kTxBufSize - V2_OVERHEAD = 108。 */ static u8 s_lastReqSeq = 0xFFU; -static u8 s_lastRespBuf[128] = {}; /* kTxBufSize=128 */ +static u8 s_lastRespBuf[SpiOta::kTxBufSize] = {}; static u16 s_lastRespLen = 0U; static bool s_hasCachedResp = false; @@ -96,7 +96,7 @@ static volatile u8 s_pendingAckSeq = 0U; * ESP32 下一笔 CS 到达时 DMA 尚未 re-arm → 中后期(约几十~上百包后)卡死。 * ISR 只入队;主循环 SpiOta::poll() 调 handleLinkMessage 再回 ACK。 */ -static constexpr u8 kOtaDataSlots = 4U; +static constexpr u8 kOtaDataSlots = CONFIG_OTA_ISR_DATA_SLOTS; static u8 s_otaDataSlots[kOtaDataSlots][64]; static volatile u8 s_otaDataWi = 0U; static volatile u8 s_otaDataRi = 0U; @@ -766,10 +766,10 @@ void SpiOta::init() { configureGpio(); configureDma(); configureSpi0(); - configureExti(); s_linkState.reset(); - /* 首笔 CS 到来前预装 DMA,避免 EXTI 晚于首 SCK */ + /* 必须先预装 DMA,再开 CS EXTI(与 APP 一致,避免首笔 CS 赶在 arm 前) */ armSpiDmasForNextCs(); + configureExti(); } /* ============================================================ @@ -812,9 +812,8 @@ void SpiOta::onCsRelease() { /* ★ 关键:本 CS 已时钟输出预装的 TX 帧(延迟一拍响应)。 * armSpiDmasForNextCs() 装载后并不清除 s_txPending,若此处不标记交付, - * pending 会永远为 true → updateDataReady() 把 PB6 永久拉低 → - * ESP32 waitSpiReady(等 HIGH) 每包空等 SPI_READY_TIMEOUT_MS, - * 1116 包 OTA 表现为"卡在 1/1116 十几分钟"。 + * pending 会永远为 true → updateDataReady() 把 PB6 永久拉低, + * ESP32 侧 DATA_READY 常低(收路径依赖 waitSpiDataReady/盲 poll)。 * 先清交付态;随后 isrProcessFrame/submitResponse 可再置 pending。 * 重复帧缓存在 s_lastRespBuf,不依赖 s_txPending 保活。 * diff --git a/GD32E50X_BOOT/device/SpiOta.h b/GD32E50X_BOOT/device/SpiOta.h index 9fd2916..22265ff 100644 --- a/GD32E50X_BOOT/device/SpiOta.h +++ b/GD32E50X_BOOT/device/SpiOta.h @@ -1,7 +1,8 @@ #ifndef __SPI_OTA_H__ #define __SPI_OTA_H__ -#include "OTA.h" +#include "OTA.h" /* 间接 config.h */ +#include "config.h" /** * @file SpiOta.h @@ -43,8 +44,8 @@ private: class SpiOta { public: - static constexpr u16 kRxBufSize = 256U; - static constexpr u16 kTxBufSize = 128U; + 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(); diff --git a/GD32E50X_BOOT/sys/OTA.cpp b/GD32E50X_BOOT/sys/OTA.cpp index 9ae04f8..ccfd72e 100644 --- a/GD32E50X_BOOT/sys/OTA.cpp +++ b/GD32E50X_BOOT/sys/OTA.cpp @@ -362,7 +362,9 @@ void OTAHandler::process() { if (pending_app_jump) { pending_app_jump = false; - delay_1ms(20); + /* 给 ESP32 FINISH 后安静窗口留时间:本侧再稍等再 jump, + * 避免 jump 瞬间与主机 HELLO 时钟重叠打坏 APP SPI 初始化。 */ + delay_1ms(80); jumpToApplication(); return; } diff --git a/GD32E50X_BOOT/sys/OTA.h b/GD32E50X_BOOT/sys/OTA.h index 14d2792..7346ebc 100644 --- a/GD32E50X_BOOT/sys/OTA.h +++ b/GD32E50X_BOOT/sys/OTA.h @@ -172,8 +172,8 @@ class OTAHandler { bool pending_app_jump = false; /**< 是否存在待处理的应用跳转请求。 */ ota_send_fn_t transport_send = nullptr; /**< 当前底层回包发送函数。 */ - /* Flash 写入环形缓冲(ISR 写入,主循环 drain 写 Flash) */ - static constexpr u8 kFlashBufSlots = 8U; + /* Flash 写入环形缓冲(ISR 写入,主循环 drain 写 Flash);槽位数见 config.h */ + static constexpr u8 kFlashBufSlots = CONFIG_OTA_FLASH_BUF_SLOTS; struct FlashSlot { u32 offset; u8 data[OTA_PACKET_SIZE]; diff --git a/GD32E50X_BOOT/sys/config.h b/GD32E50X_BOOT/sys/config.h index 4238e43..862a6a4 100644 --- a/GD32E50X_BOOT/sys/config.h +++ b/GD32E50X_BOOT/sys/config.h @@ -79,10 +79,8 @@ inline constexpr u32 CANFD_OTA_WAIT_ID = 0x00000103U; inline constexpr u32 CANFD_OTA_CANCEL_ID = 0x00000000U; /** - * SPI Ready 握手输出引脚(PB6 复用为 SPI Ready)。 - * 与 DCU_APP sys/config.h 保持一致,适配 ESP32 waitSpiReady 硬件握手: - * - CS 下降沿拉低 PB6,表示从机忙碌 - * - 响应帧就绪后拉高 PB6,通知主机可发起下一笔事务 + * SPI DATA_READY 输出引脚(PB6 → ESP32 GPIO10,开漏)。 + * v2:低 = 有待读数据,高 = 队列空;帧同步靠 SEQ/ACK/CREDIT,非「下一笔许可」。 */ inline constexpr u32 CONFIG_SPI_READY_PORT = GPIOB; inline constexpr u32 CONFIG_SPI_READY_PIN = GPIO_PIN_6; @@ -123,6 +121,17 @@ inline constexpr uint8_t K_master[16] = { inline constexpr u32 OTA_TIMEOUT = 50000U; inline constexpr u32 APP_DELAY_BOOT_TIME = 10000U; +/** @brief SPI 主动按铃超时(ms):ESP32 未在窗口内来读则放弃本次按铃。 */ +inline constexpr u32 CONFIG_SPI_RING_TIMEOUT_MS = 500U; +/** @brief BOOT SPI TX DMA 满缓冲(字节)。payload 上限 = 本值 − V2_OVERHEAD(20) = 108。 */ +inline constexpr u16 CONFIG_SPI_TX_BUF_SIZE = 128U; +/** @brief BOOT SPI RX 缓冲(主机可按 APP 预算打 256 时钟)。 */ +inline constexpr u16 CONFIG_SPI_RX_BUF_SIZE = 256U; +/** @brief ISR→主循环 OTA DATA 队列槽位数(每槽 64B 明文包)。 */ +inline constexpr u8 CONFIG_OTA_ISR_DATA_SLOTS = 4U; +/** @brief flash 写入缓冲槽位数(绑定 g_localCredit / kFlashBufSlots)。 */ +inline constexpr u8 CONFIG_OTA_FLASH_BUF_SLOTS = 8U; + /** 常用尺寸常量。 */ static const u8 SIZE_U32 = sizeof(uint32_t); static const u8 SIZE_FLOAT = sizeof(float);