Bootloader进行中断向量表重映射

Bootloader进行中断向量表重映射

1. 什么是Bootloader

Bootloader(引导加载程序)是嵌入式系统中运行在 APP 程序之前的一段小程序,主要作用是引导和管理 APP 应用程序的启动,同时提供固件升级、程序校验等辅助功能。

1.1 中断向量表重映射更灵活

除 Cortex-M0 内核外,Cortex-M 系列其他内核(M3/M4/M7/M23/M33 等)自带 VTOR(Vector Table Offset Register),可直接通过寄存器修改向量表地址,无需像 M0 那样依赖硬件映射或向量转发。

1.2 支持更多特权模式与安全特性

  • M3/M4 支持特权(Privileged)和用户(User)模式,Bootloader 可运行在特权模式,通过设置 MSP(主栈指针)和 PSP(进程栈指针)隔离用户程序的栈空间。
  • M23/M33 等带 TrustZone-M 的内核,Bootloader 可作为安全固件运行在安全域,用户程序运行在非安全域,通过安全监控器(Secure Monitor)实现域间通信,防止用户程序篡改 Bootloader。

1.3 启动流程优化

启动流程优化部分内核支持启动延迟配置(如等待外部设备就绪)、内存保护单元(MPU)初始化(提前配置内存读写权限,防止用户程序越界访问 Bootloader 区域)

1.4 FLASH 分区

如果要使用用户自己编写的Bootloader,则需要对 Flash 进行分区管理。假如 Flash 大小为 64KB,可以划分为以下三个分区:

分区名称 起始地址 结束地址 说明
Bootloader 0x0800 0000 0x0800 3FFF 引导加载程序
Bootloader Config 0x0800 4000 0x0800 4FFF 引导加载程序配置区
User Application 0x0800 5000 0x0800 B000 APP 应用程序

如果要实现版本回退,可以再加一个APP区域。需要根据 FLASH 大小合理分配各区域大小。

2. 中断向量表重映射

直接通过寄存器修改向量表地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void jump_to_app(uint32_t app_addr)
{
__disable_irq();

uint32_t app_sp = *((volatile uint32_t *)(app_addr + 0));

if (app_sp < 0x20000000 || app_sp > 0x2000FFFF)
{
NVIC_SystemReset();
}

uint32_t app_reset = *((volatile uint32_t *)(app_addr + 4));
SCB->VTOR = app_addr;
__set_MSP(app_sp);

__DSB();
__ISB();
void (*AppEntry)(void) = (void (*)(void))(app_reset);
AppEntry();
while (1)
__NOP();
}

2.2 向量表偏移宏定义

在 APP 的system_xxx.c中,修改向量表偏移宏 VECT_TAB_OFFSET,确保与 APP 起始地址一致

1
2
// 例如APP起始地址为0x08005000,Flash基地址为0x08000000,则偏移量为0x5000
#define VECT_TAB_OFFSET 0x00005000 // 必须等于 (APP起始地址 - Flash基地址)

3. APP 程序的编译配置

APP 程序的链接脚本必须指定起始地址为 USER_APP_START,且其向量表中的 ISR 地址需基于该起始地址偏移。可以在 Keil 的 Options for Target -> Target -> Read/Only Memory Areas 中的 IROM 定义 APP 的开始地址和分区大小。

IRAM 需要保留中断向量表的大小,防止运行时破坏中断向量表。
假如中断向量表大小为192字节,则 IRAM 开始地址可以设置为 C0

4. 中断相关的额外处理

Bootloader 可能初始化过部分外设(如 UART)并使能了中断,若跳转前未关闭,可能导致 APP 启动后立即进入 Bootloader 的中断服务程序(ISR),引发错误。因此需在 jump_to_app 前增加关闭外设中断的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void peripheral_deinit(void)
{
SysTick->VAL = 0;
SysTick->LOAD = 0;
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

SCB->ICSR &= ~SCB_ICSR_PENDSVSET_Msk;
HAL_RCC_DeInit();
HAL_DeInit();

HAL_UART_MspDeInit(&UartHandle);
NVIC_DisableIRQ(USART1_IRQn);
}

Cortex-M0 内核请看 Cortex-M0 通过Bootloader实现中断向量表重映射