为什么需要向量表重映射?

STM32的向量表(Vector Table)默认位于Flash起始地址(如0x08000000),存放着复位、中断服务函数(ISR)的入口地址。当Bootloader跳转到App时,App的代码通常位于偏移地址(如0x08010000)。若向量表仍指向Bootloader区域,中断触发时CPU会从错误地址取指,导致程序崩溃。因此,跳转前必须将向量表重映射到App的起始地址。

向量表重映射的两种方式

1. 修改VTOR寄存器(Cortex-M3/M4/M7)

对于Cortex-M3及以上内核(如STM32F1/F2/F3/F4/F7/H7),可通过设置系统控制块中的VTOR(Vector Table Offset Register)寄存器实现重映射。

#define APP_ADDRESS 0x08010000

void jump_to_app(void)
{
    // 设置向量表偏移
    SCB->VTOR = APP_ADDRESS;
    // 后续跳转代码...
}

2. 使用SYSCFG_MEMRMP寄存器(Cortex-M0/M0+)

对于Cortex-M0/M0+内核(如STM32F0/L0),没有VTOR寄存器,需通过SYSCFG配置内存映射,将Flash的起始地址重映射到App区域。

void jump_to_app(void)
{
    // 使能SYSCFG时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    // 重映射Flash到0x08010000
    SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE_0; // 根据实际芯片调整
}

跳转前的关键步骤

1. 关闭全局中断

跳转过程中,任何中断都可能导致不可预知行为。必须确保所有中断关闭,并清除挂起标志。

__disable_irq();
// 关闭所有外设中断(示例:关闭SysTick)
SysTick->CTRL = 0;

2. 设置主栈指针(MSP)

App的起始地址前4字节存放初始栈顶地址,需将其加载到MSP。

typedef void (*pFunction)(void);
uint32_t app_stack = *(volatile uint32_t*)APP_ADDRESS;
__set_MSP(app_stack);

3. 获取App复位向量并跳转

App起始地址后4字节为复位向量,即App的入口函数地址。

pFunction app_entry = (pFunction)(*(volatile uint32_t*)(APP_ADDRESS + 4));
app_entry();

4. 完整跳转函数示例

#define APP_ADDRESS 0x08010000

void jump_to_app(void)
{
    // 1. 关闭全局中断
    __disable_irq();
    // 2. 关闭SysTick
    SysTick->CTRL = 0;
    // 3. 设置向量表偏移(Cortex-M3/M4)
    SCB->VTOR = APP_ADDRESS;
    // 4. 设置主栈指针
    uint32_t app_stack = *(volatile uint32_t*)APP_ADDRESS;
    __set_MSP(app_stack);
    // 5. 跳转
    pFunction app_entry = (pFunction)(*(volatile uint32_t*)(APP_ADDRESS + 4));
    app_entry();
    // 6. 不应返回
    while(1);
}

App端的配合设置

1. 链接脚本修改

App工程需将Flash起始地址和中断向量表偏移到APP_ADDRESS。以GCC为例:

/* Linker script */
FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 128K

2. 启动文件中的向量表重映射

在App的SystemInit函数或启动文件中,需再次设置VTOR:

void SystemInit(void)
{
    SCB->VTOR = 0x08010000;
    // 其他初始化...
}

3. 中断服务函数注意

App中所有中断服务函数必须正确声明,且向量表偏移已生效,否则中断无法响应。

常见问题与调试技巧

  • 跳转后死机:检查栈指针是否正确,确认App的链接脚本偏移无误。
  • 中断不工作:确认VTOR设置成功,且App的SystemInit已执行。
  • 时钟配置冲突:Bootloader和App可能使用不同时钟,跳转前无需重置时钟,但App应重新初始化所有外设。
  • 使用调试器:在跳转函数处设置断点,单步执行,观察寄存器值。
  • 检查Flash写入:确保App已正确烧录到目标地址,可通过读取内存验证。

总结

向量表重映射是Bootloader跳转的核心,理解其原理可避免诸多隐患。本文通过代码示例演示了Cortex-M3/M4的VTOR方法,并提及M0的替代方案。实际开发中,还需结合具体芯片手册,确保配置正确。掌握此技术,可为IAP(在应用编程)打下坚实基础。