diff --git a/GD32E50X_BOOT/device/SpiOta.cpp b/GD32E50X_BOOT/device/SpiOta.cpp index 94ddb26..839b553 100644 --- a/GD32E50X_BOOT/device/SpiOta.cpp +++ b/GD32E50X_BOOT/device/SpiOta.cpp @@ -1,4 +1,4 @@ -#include "SpiOta.h" +#include "SpiOta.h" #include "core_cm33.h" #include "gd32e50x_gpio.h" #include "gd32e50x_exti.h" @@ -237,6 +237,26 @@ void SpiOta::isrProcessFrame() { return; } + /* 仅转发 BOOT 实际支持的 OTA 协议帧(REQ/DATA/CANCEL)给 OTA 状态机。 + * 其它帧类型(如 0xC0~0xC3 CAN bridge 命令,由 DCU_APP 注册处理器处理, + * BOOT 无此功能)直接返回 ERROR,避免 mapMsgType 的 default 分支把 + * 未知类型误当成 CANCEL,导致 ESP32 待机时 1Hz CAN 反馈轮询(0xC1) + * 不断重置 OTA 状态机,干扰正常 OTA 握手。 */ + if (frame_type != SPI_OTA_CMD_REQ && + frame_type != SPI_OTA_CMD_DATA && + frame_type != SPI_OTA_CMD_CANCEL) { + u8 err = 0xEEU; + u8 err_frame[kTxBufSize] = {}; + u16 built = 0U; + if (buildFrame(SPI_OTA_RSP_ERROR, &err, 1U, err_frame, &built)) { + queueTxFrame(err_frame, built); + } + isrLogFrameEx("[UNK-TYPE]", s_rxStream, (rx_len < 8U) ? rx_len : 8U); + s_rxStreamLen = 0U; + s_isrResponded = true; + return; + } + if (s_otaHandler != nullptr) { s_otaSessionActive = true; s_otaHandler->setTransportSender(sender); @@ -286,8 +306,21 @@ void SpiOta::configureGpio() { gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE); gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5); gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4); - gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7); + gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_7); gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_15); + /* PB6 复用为 SPI Ready 握手输出,默认 HIGH(就绪),允许主机立刻发起首帧。 + * 与 DCU_APP app_spi_command.cpp configureGpio 行为一致, + * 适配 ESP32 GPIO10 输入监测 PB6 电平的硬件握手时序。 */ + gpio_init(CONFIG_SPI_READY_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, CONFIG_SPI_READY_PIN); + gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); +} + +void SpiOta::setSpiReady(bool ready) { + if (ready) { + gpio_bit_set(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); + } else { + gpio_bit_reset(CONFIG_SPI_READY_PORT, CONFIG_SPI_READY_PIN); + } } void SpiOta::configureSpi0() { @@ -386,6 +419,11 @@ void SpiOta::init() { void SpiOta::onCsAssert() { if (s_csActive) { return; } s_csActive = true; + /* CS 下降沿立即拉低 PB6:表示从机即将忙碌, + * 防止主机在 onCsRelease 准备好响应前抢跑发起下一笔 CS。 + * (若此帧为空轮询/残包,onCsRelease 会恢复 HIGH) + * 与 DCU_APP AppSpiCommand::onCsAssert 行为一致。 */ + setSpiReady(false); s_rxStreamLen = 0U; s_txStreamPos = 0U; s_isrResponded = false; @@ -442,6 +480,12 @@ void SpiOta::onCsRelease() { if (s_rxStreamLen > 0U && !s_isrResponded) { isrProcessFrame(); } + + /* BOOT 在 ISR 内同步完成帧处理,响应帧已就绪(或为空轮询/残包无需响应), + * 拉高 PB6 通知主机可发起下一笔事务。 + * 与 DCU_APP processCommand() 末尾 setSpiReady(true) 行为一致; + * 空轮询事务同样恢复 HIGH,避免 PB6 卡 LOW 导致 ESP32 waitSpiReady 超时。 */ + setSpiReady(true); } /* ============================================================ diff --git a/GD32E50X_BOOT/device/SpiOta.h b/GD32E50X_BOOT/device/SpiOta.h index f65e905..fff793f 100644 --- a/GD32E50X_BOOT/device/SpiOta.h +++ b/GD32E50X_BOOT/device/SpiOta.h @@ -46,6 +46,12 @@ public: static bool hasActiveSession(); static u16 copyTxFrame(u8 *out, u16 max_len); + /** @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 tryProcessCompleteFrame(); static void isrLogFrame(const char *tag, const u8 *data, u16 len); diff --git a/GD32E50X_BOOT/sys/config.h b/GD32E50X_BOOT/sys/config.h index f89d4a1..22f1745 100644 --- a/GD32E50X_BOOT/sys/config.h +++ b/GD32E50X_BOOT/sys/config.h @@ -1,7 +1,8 @@ -#ifndef __CONFIG_H__ +#ifndef __CONFIG_H__ #define __CONFIG_H__ #include +#include "gd32e50x.h" /** 项目内部统一使用的简写类型。 */ typedef uint8_t u8; @@ -77,6 +78,15 @@ inline constexpr u32 CANFD_OTA_ACK_ID = 0x00000102U; 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,通知主机可发起下一笔事务 + */ +inline constexpr u32 CONFIG_SPI_READY_PORT = GPIOB; +inline constexpr u32 CONFIG_SPI_READY_PIN = GPIO_PIN_6; + /** GD32E50x 芯片 UID 三段地址。 */ inline constexpr u32 DEVICE_SERIAL_ADDR_LSB = 0x1FFFF7E8U; inline constexpr u32 DEVICE_SERIAL_ADDR_MID = 0x1FFFF7ECU;