fix(bootloader): OTA可用版本,修复跳转主应用前的中断关闭及外设清理顺序

- 把关闭中断操作提前到写 Flash 标记前,避免中断嵌套导致栈溢出
- 完整清空 NVIC 中断使能和挂起标志
- 关闭 DMA 通道及相关外设(CAN0、SPI0、EXTI15)并打印调试信息
- 关闭 SysTick 定时器,避免影响主应用运行
- 取消了不必要的时钟切换,简化收尾逻辑,避免引入额外变量
- 通过裸汇编函数安全切换 MSP,并跳转至主应用入口
- 增加十六进制堆栈大小定义调整,将堆栈大小由0x200增大至0x400
This commit is contained in:
cbb
2026-05-30 20:03:03 +08:00
parent ea7a885de4
commit b39a78e3fc
2 changed files with 86 additions and 33 deletions
@@ -1,4 +1,4 @@
#ifndef REGIONS_GD32E508VE_H
#ifndef REGIONS_GD32E508VE_H
#define REGIONS_GD32E508VE_H
@@ -93,7 +93,7 @@
// <h> Stack / Heap Configuration
// <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
// <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
#define __STACK_SIZE 0x00000200
#define __STACK_SIZE 0x00000400
#define __HEAP_SIZE 0x00000C00
// </h>
+84 -31
View File
@@ -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);
}
/**