From b39a78e3fcf30d76b41848a8963cfb132a03fe75 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Sat, 30 May 2026 20:03:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(bootloader):=20OTA=E5=8F=AF=E7=94=A8?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E4=B8=BB=E5=BA=94=E7=94=A8=E5=89=8D=E7=9A=84=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=8F=8A=E5=A4=96=E8=AE=BE=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 把关闭中断操作提前到写 Flash 标记前,避免中断嵌套导致栈溢出 - 完整清空 NVIC 中断使能和挂起标志 - 关闭 DMA 通道及相关外设(CAN0、SPI0、EXTI15)并打印调试信息 - 关闭 SysTick 定时器,避免影响主应用运行 - 取消了不必要的时钟切换,简化收尾逻辑,避免引入额外变量 - 通过裸汇编函数安全切换 MSP,并跳转至主应用入口 - 增加十六进制堆栈大小定义调整,将堆栈大小由0x200增大至0x400 --- .../Device/GD32E508VE/regions_GD32E508VE.h | 4 +- GD32E50X_BOOT/sys/OTA.cpp | 115 +++++++++++++----- 2 files changed, 86 insertions(+), 33 deletions(-) diff --git a/GD32E50X_BOOT/RTE/Device/GD32E508VE/regions_GD32E508VE.h b/GD32E50X_BOOT/RTE/Device/GD32E508VE/regions_GD32E508VE.h index f39f80f..19d2948 100644 --- a/GD32E50X_BOOT/RTE/Device/GD32E508VE/regions_GD32E508VE.h +++ b/GD32E50X_BOOT/RTE/Device/GD32E508VE/regions_GD32E508VE.h @@ -1,4 +1,4 @@ -#ifndef REGIONS_GD32E508VE_H +#ifndef REGIONS_GD32E508VE_H #define REGIONS_GD32E508VE_H @@ -93,7 +93,7 @@ // Stack / Heap Configuration // Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> // Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> -#define __STACK_SIZE 0x00000200 +#define __STACK_SIZE 0x00000400 #define __HEAP_SIZE 0x00000C00 // diff --git a/GD32E50X_BOOT/sys/OTA.cpp b/GD32E50X_BOOT/sys/OTA.cpp index 5a87134..5136c7b 100644 --- a/GD32E50X_BOOT/sys/OTA.cpp +++ b/GD32E50X_BOOT/sys/OTA.cpp @@ -17,6 +17,24 @@ extern CAN_transmiter CAN0_transmiter; OTAHandler ota; +namespace { +__attribute__((naked, noreturn)) void branchToApplication(uint32_t msp_value, uint32_t reset_handler) { + __asm volatile( + "movs r2, #0 \n" + "msr control, r2 \n" + "msr primask, r2 \n" + "msr basepri, r2 \n" + "msr faultmask, r2 \n" + "msr psp, r2 \n" + "msr psplim, r2 \n" + "msr msplim, r2 \n" + "msr msp, r0 \n" + "dsb \n" + "isb \n" + "bx r1 \n"); +} +} // namespace + /** * OTA.cpp 模块说明 * ---------------- @@ -391,7 +409,6 @@ bool validateApplication() { */ void OTAHandler::jumpToApplication() { if (!validateApplication()) { - /** 向量表不合法时,拒绝跳转,避免飞到未知地址。 */ #if DEBUG_PRINT printf("[BOOT] jump rejected: invalid app vector @0x%08lX\r\n", (unsigned long)APPLATEST_START_ADDR); #endif @@ -402,52 +419,88 @@ void OTAHandler::jumpToApplication() { printf("[BOOT] jumping to app @0x%08lX\r\n", (unsigned long)APPLATEST_START_ADDR); #endif - /** 把当前主应用重新标记为"有效且允许启动"。 */ - OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL); - OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL); - - /** 关闭并清空所有 NVIC 中断。 */ + /* + * [关键修复] 先关中断再做 Flash 操作。 + * 原来 OTA_SET 在 NVIC disable 之前执行。 + * Flash 编程期间 CPU stall,若此时 SPI0/CAN0 中断触发, + * ISR 嵌套调用会撑爆栈 -> HardFault -> 死循环。 + */ + __disable_irq(); for (int i = 0; i < 8; ++i) { NVIC->ICER[i] = 0xFFFFFFFFU; NVIC->ICPR[i] = 0xFFFFFFFFU; } + __DSB(); + __ISB(); - /** 关闭 Bootloader 运行过程中启用过的外设。 */ - can_deinit(CAN0); - spi_i2s_deinit(SPI0); - exti_interrupt_disable(EXTI_15); - exti_interrupt_flag_clear(EXTI_15); +#if DEBUG_PRINT + printf("[DBG] NVIC disabled\r\n"); +#endif + + /* 把当前主应用重新标记为"有效且允许启动"。 */ + OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL); + OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL); + +#if DEBUG_PRINT + printf("[DBG] OTA_SET done\r\n"); +#endif + + /* 关闭 Bootloader 运行过程中启用过的外设。先关 DMA 再关外设。 */ DMA_CHCTL(DMA0, DMA_CH1) &= ~DMA_CHXCTL_CHEN; +#if DEBUG_PRINT + printf("[DBG] DMA_CH1 disabled\r\n"); +#endif DMA_CHCTL(DMA0, DMA_CH2) &= ~DMA_CHXCTL_CHEN; - rcu_system_clock_source_config(RCU_CKSYSSRC_IRC8M); +#if DEBUG_PRINT + printf("[DBG] DMA_CH2 disabled\r\n"); +#endif + can_deinit(CAN0); +#if DEBUG_PRINT + printf("[DBG] CAN0 deinit\r\n"); +#endif + spi_i2s_deinit(SPI0); +#if DEBUG_PRINT + printf("[DBG] SPI0 deinit\r\n"); +#endif + exti_interrupt_disable(EXTI_15); +#if DEBUG_PRINT + printf("[DBG] EXTI15 disabled\r\n"); +#endif - rcu_periph_clock_disable(RCU_GPIOA); - rcu_periph_clock_disable(RCU_GPIOB); - rcu_periph_clock_disable(RCU_GPIOC); - rcu_periph_clock_disable(RCU_USART0); - rcu_periph_clock_disable(RCU_CAN0); - rcu_periph_clock_disable(RCU_SPI0); - rcu_periph_clock_disable(RCU_AF); - rcu_periph_clock_disable(RCU_TIMER0); - rcu_periph_clock_disable(RCU_DMA0); +#if DEBUG_PRINT + printf("[DBG] peripherals deinit done\r\n"); +#endif - /** 把中断向量表切到主应用起始地址。 */ + /* + * SysTick 不属于 NVIC 外设中断,跳转前单独关闭即可。 + * APP 的 SystemInit() 会完整重置时钟树,因此这里不再切 IRC8M, + * 也不再关 GPIO/USART/RCU 时钟,避免 Boot 收尾逻辑引入额外变量。 + */ + SysTick->CTRL = 0U; + SysTick->LOAD = 0U; + SysTick->VAL = 0U; + +#if DEBUG_PRINT + printf("[DBG] SysTick disabled\r\n"); +#endif + + /* 把中断向量表切到主应用起始地址。 */ SCB->VTOR = APPLATEST_START_ADDR; - /** 读取主应用向量表中的 MSP 和 Reset_Handler。 */ + /* 读取主应用向量表中的 MSP 和 Reset_Handler。 */ uint32_t msp_value = *(__IO uint32_t *)APPLATEST_START_ADDR; uint32_t reset_handler = *(__IO uint32_t *)(APPLATEST_START_ADDR + 4U); - typedef void (*AppEntryFunc)(void); - AppEntryFunc app_entry = (AppEntryFunc)reset_handler; +#if DEBUG_PRINT + printf("[DBG] app MSP=0x%08lX Reset=0x%08lX\r\n", + (unsigned long)msp_value, + (unsigned long)reset_handler); + printf("[DBG] calling app entry\r\n"); +#endif - /** 切栈后直接跳转执行主应用。 */ - __set_MSP(msp_value); - __set_CONTROL(0); - __DSB(); - __ISB(); + /* 仍在 Boot 栈上做最后一次清理,真正切栈和跳转交给裸汇编 trampoline。 */ rcu_all_reset_flag_clear(); - app_entry(); + branchToApplication(msp_value, reset_handler); } /**