fix(bootloader): OTA可用版本,修复跳转主应用前的中断关闭及外设清理顺序
- 把关闭中断操作提前到写 Flash 标记前,避免中断嵌套导致栈溢出 - 完整清空 NVIC 中断使能和挂起标志 - 关闭 DMA 通道及相关外设(CAN0、SPI0、EXTI15)并打印调试信息 - 关闭 SysTick 定时器,避免影响主应用运行 - 取消了不必要的时钟切换,简化收尾逻辑,避免引入额外变量 - 通过裸汇编函数安全切换 MSP,并跳转至主应用入口 - 增加十六进制堆栈大小定义调整,将堆栈大小由0x200增大至0x400
This commit is contained in:
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user