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:
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user