fix(SpiOta): 修复ISR响应缓存逻辑,只缓存ACK/ERROR拒绝WAIT

此前isrProcessFrame()无条件将当前s_txBuf缓存到s_lastRespBuf,
导致OTA数据包在ISR入队时立即返回的WAIT被写入重发缓存,覆盖
了sender()内只缓存ACK的语义。当ESP32超时重发时,DUP-SEQ分支
回放的仍是过期WAIT,造成50ms等待→重发链被拉长的问题。

现在按v2头TYPE字节过滤,只将ACK/ERROR放入重发缓存,WAIT由
sender()途径主循环写ACK时再统一更新缓存。

feat(main): 添加ESP32_EN引脚初始化和调试信息输出

添加PC9引脚初始化代码,确保BOOT阶段ESP32能正常启动。
同时增加调试打印信息,便于检查ESP32_EN状态和D3V3电源域
就绪情况。

perf(boot): 优化主循环节拍控制策略

回滚之前"有工作时不休眠"的尝试,恢复为固定1ms节拍。
因为条件化延迟会让ESP32在armSpiDmasForNextCs()的
spi_dma_disable/enable窗口中抓取CS,读到错帧导致通信异常。

fix(OTA): 恢复20us超时步进以保证SPI通信稳定性

回滚之前"本轮干活就不delay"的尝试,恢复为统一20us步进。
因为实测发现条件化延迟会让ESP32在DMA开关窗口中抓取CS,
导致READ错帧和saw_wait=0问题。

chore(config): 更新固件版本号至v1.1.0.5

feat(UART): 将调试串口波特率提升至3Mbps

提升Bootloader系统的调试串口通信速率,从11520提升至
3000000,加快日志输出和调试效率。
This commit is contained in:
cbb
2026-09-01 02:48:04 +08:00
parent 1ec8aa3b5b
commit 45690f6134
5 changed files with 45 additions and 10 deletions
+13 -1
View File
@@ -503,13 +503,25 @@ void SpiOta::isrProcessFrame() {
}
}
/* ---- 6. 分发后缓存响应到 s_lastRespBuf ---- */
/* ---- 6. 分发后缓存响应到 s_lastRespBuf:只缓存 ACK/ERROR,拒收 WAIT ----
*
* 此前这里无条件 memcpy 当前 s_txBuf 进 s_lastRespBuf。OTA 数据包
* 在 ISR 入队时会立刻回 WAIT(见上),WAIT 会被写进重发缓存,
* 覆盖 sender() 内“只缓存 ACK”的语义:之后 ESP32 一旦 50ms 超时重发,
* DUP-SEQ 分支回放的还是过期 WAIT,又是 50ms 等待 → 重发链被拉长,
* 只剩主循环终于把 ACK 写进 s_txBuf 并“凑巧”再经过这里时才解脱。
*
* 按 v2 头 TYPE 字节(V2_OFF_TYPE=11)过滤:只把 ACK/ERROR 放进重发缓存,
* WAIT 由 sender() 途经主循环写 ACK 时再统一更新缓存。 */
if (s_txPending && s_txLen > 0U && s_txLen <= sizeof(s_lastRespBuf)) {
const u8 cached_type = (s_txLen > V2_OFF_TYPE) ? s_txBuf[V2_OFF_TYPE] : 0xFFU;
if (cached_type == SPI_OTA_RSP_ACK || cached_type == SPI_OTA_RSP_ERROR) {
memcpy(s_lastRespBuf, s_txBuf, s_txLen);
s_lastRespLen = s_txLen;
s_lastReqSeq = hdr.tx_seq;
s_hasCachedResp = true;
}
}
s_isrResponded = true;
}
+20 -1
View File
@@ -1,4 +1,4 @@
#include "main.h"
#include "main.h"
#include "device/Can.h"
#include "device/SpiOta.h"
#include "sys/config.h"
@@ -42,6 +42,11 @@ void BootloaderSystem::initHardware() {
(unsigned long)APPLATEST_START_ADDR,
(unsigned long)APP_MAX_SIZE);
printf("[BOOT] SystemCoreClock=%lu Hz\r\n", (unsigned long)SystemCoreClock);
printf("[BOOT] ESP32_EN PC9=%d (1=released, 0=HOLDING ESP32 IN RESET)\r\n",
(int)gpio_input_bit_get(GPIOC, GPIO_PIN_9));
/* D3V3 正常时 HIGH;若此处读回 0 说明 ESP32 上拉电源域未就绪 */
printf("[BOOT] D3V3 check: PC9 readback=%d\r\n",
(int)gpio_input_bit_get(GPIOC, GPIO_PIN_9));
#endif
rcu_periph_clock_enable(RCU_CRC); /* OTA CRC 校验需要 CRC 外设时钟 */
CAN0_transmiter.init();
@@ -292,6 +297,12 @@ void BootloaderSystem::run() {
/* jumpToApplication 失败(CRC/向量不合法)则留在 BOOT */
was_active = false;
}
/* 呼吸节拍:固定 1ms 节流,避免满速空转反复 re-arm SPI DMA。
* 之前做过"有工作时不睡"的尝试(配合 P1b),实测会让 ESP32 在
* 主循环 armSpiDmasForNextCs() 的 spi_dma_disable/enable 窗口里
* 抓 CS,读到错帧 → saw_wait=0 → 连 WAIT 都看不到。
* 回滚为固定 1ms:DATA→ACK 延迟由 SpiOta::poll 内的
* 消化队列+回 ACK 承担,不在这条 1ms 上。 */
delay_1ms(1);
}
} else {
@@ -374,6 +385,14 @@ int main(void) {
* 复位后 GPIO 高阻,靠外部上拉保持 HIGH,不再制造 LOW→HIGH 上升沿。 */
rcu_periph_clock_enable(RCU_GPIOB);
/* ESP32 EN 释放:BOOT 阶段也必须把 PC9 拉高。
* 原理图 p3:ESP_CHIP_EN = R39 上拉 D3V3,U11(WLESS_EN=PC9)/U10(PROGRAM_EN) 肖特基钳低。
* 复位后 PC9 为浮空输入,WLESS_EN 被 C41 拉低 → ESP32 一直被按住复位。
* APP 在 initial.cpp 中会再次置高;BOOT 等 OTA 时若不主动拉高,ESP32 永远起不来。 */
rcu_periph_clock_enable(RCU_GPIOC);
gpio_bit_set(GPIOC, GPIO_PIN_9);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
sys.initHardware();
sys.run();
+5 -1
View File
@@ -410,7 +410,11 @@ void OTAHandler::process() {
} else {
/**
* 非空闲态时,按固定节奏推进超时计数。
* 这里用 100us 粗略步进,不追求绝对精确,只做会话保活超时保护。
* 这里用 20us 粗略步进,不追求绝对精确,只做会话保活超时保护。
* ★ 回滚说明:曾尝试过"本轮干活就不 delay",但实测配合主循环取掉
* 1ms 节拍后会导致 SpiOta 被反复满速调用,armSpiDmasForNextCs()
* 的 spi_dma_disable/enable 窗口被 ESP32 抓到,READ 到错帧、
* saw_wait=0。恢复为统一 20us 步进,让回 ACK 节奏稳定。
*/
delay_1us(20);
ota_time++;
+1 -1
View File
@@ -16,7 +16,7 @@ typedef uint32_t u32;
#define BOOT_FW_VERSION_MAJOR 1U
#define BOOT_FW_VERSION_MINOR 1U
#define BOOT_FW_VERSION_PATCH 0U
#define BOOT_FW_VERSION_BUILD 3U /* v1.1.0.3: stay_in_ota 跳回 APP 前加 CHECK/AVAL 标志门控,修复中断 OTA 后 verifyAppCrc FLASH_EMPTY 误判跳入半成品固件导致 GD32 永久失联 */
#define BOOT_FW_VERSION_BUILD 5U /* v1.1.0.5: isrProcessFrame 分发尾部缓存只收 ACK/ERROR,滤掉 WAIT——之前 WAIT 会污染 s_lastRespBuf,ESP32 超时重发时 DUP-SEQ 回放的仍是过期 WAIT 而非最终 ACK,重发链被拉长。曾同时试验过 stay_in_ota 去掉固定 1ms delay + process() 20us 步进条件化(v1.1.0.5 中间态),实测 ESP32 会在 armSpiDmasForNextCs() 的 dma disable/enable 窗口里抓 CS 读到错帧、saw_wait=0,已回滚为固定节拍。 */
/**
* Flash 布局说明(GD32E508VE,512KB)
+2 -2
View File
@@ -28,9 +28,9 @@ u8 BootDebugUart::init()
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
/** 配置串口为 115200 8N1(其余参数使用默认值)。 */
/** 配置串口为 3000000 8N1(其余参数使用默认值)。 */
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_baudrate_set(USART0, 3000000U);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_enable(USART0);