1. Bootloader基础框架搭建

Bootloader基础框架搭建 学习卡片

任务目标

本案例实现一个基于STM32F103系列MCU的最小Bootloader框架,包含三个核心能力:启动代码初始化、中断向量表重映射、以及从Bootloader跳转到应用程序。最终通过串口打印和LED翻转验证跳转是否成功。

环境依赖

  • 硬件:STM32F103C8T6最小系统板(蓝丸),板载LED接PC13,USART1接PA9/PA10
  • 软件:STM32CubeIDE 1.13+ 或 Keil MDK 5.30+,STM32标准外设库或HAL库均可
  • 应用区起始地址:0x08008000(Bootloader占用前32KB)

关键完整代码

// main.c - Bootloader主程序
#include "stm32f1xx_hal.h"

#define APP_ADDRESS 0x08008000U
#define APP_FLAG_ADDR 0x20000000U  // RAM中跳转标志

typedef void (*pFunction)(void);

// 跳转标志写入(由应用层在复位前设置)
void SetJumpFlag(void) {
    *(__IO uint32_t*)APP_FLAG_ADDR = 0xDEADBEEF;
}

// 跳转至应用程序
void JumpToApp(void) {
    pFunction app_entry;
    uint32_t app_stack = *(volatile uint32_t*)APP_ADDRESS;
    
    // 检查栈顶地址合法性(RAM范围0x20000000-0x20010000)
    if ((app_stack & 0x2FFE0000) != 0x20000000) {
        Error_Handler();
    }
    
    // 设置MSP为应用栈顶
    __set_MSP(app_stack);
    
    // 获取复位向量并跳转
    app_entry = (pFunction)(*(volatile uint32_t*)(APP_ADDRESS + 4));
    app_entry();
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    
    // 初始化LED和串口
    LED_Init();
    UART_Init();
    
    // 检查跳转标志
    if (*(__IO uint32_t*)APP_FLAG_ADDR == 0xDEADBEEF) {
        // 清除标志并跳转
        *(__IO uint32_t*)APP_FLAG_ADDR = 0;
        HAL_UART_Transmit(&huart1, "Jump to APP...\r\n", 17, 100);
        HAL_Delay(10);
        JumpToApp();
    }
    
    // Bootloader主循环(无应用时运行)
    while (1) {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        HAL_UART_Transmit(&huart1, "Bootloader running...\r\n", 24, 100);
        HAL_Delay(500);
    }
}

// system_stm32f1xx.c - 中断向量表重映射(关键片段)
// 在SystemInit()函数末尾添加:
void SystemInit(void) {
    // ...原有代码...
    
    // 若当前运行在Bootloader且需要重映射到APP
    if ((*(volatile uint32_t*)APP_FLAG_ADDR) == 0xDEADBEEF) {
        // 设置向量表偏移(仅适用于具有VTOR的Cortex-M3 r2p1+)
        SCB->VTOR = APP_ADDRESS;
    }
}

链接脚本配置

/* STM32F103C8Tx_FLASH.ld - Bootloader链接脚本关键修改 */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K  /* Bootloader占前32KB */
  RAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 20K
}

运行与验证

  1. 编译并烧录Bootloader到0x08000000(默认烧录地址)
  2. 打开串口助手(115200-8-N-1),复位开发板,应周期性收到Bootloader running...
  3. 在应用工程中调用SetJumpFlag()后执行软复位(NVIC_SystemReset),Bootloader检测到标志后跳转至0x08008000
  4. 跳转成功标志:串口输出Jump to APP...后,LED翻转频率变化(应用区程序控制)

通过判定标准

  • Bootloader独立运行时LED以2Hz翻转,串口持续输出提示信息
  • 设置跳转标志并复位后,程序能进入应用区执行(应用区LED闪烁频率不同)
  • 若应用区无效(栈顶地址非法),Bootloader不跳转并继续运行自身循环

常见问题

  • 跳转后死机:检查应用工程中断向量表偏移是否设置为0x08008000,且编译链接地址正确
  • 外设初始化冲突:跳转前需反初始化Bootloader使用的外设(HAL_UART_DeInit等),避免应用初始化失败
  • VTOR不可用:Cortex-M3 r2p0及更早版本需在启动文件中手动复制向量表到RAM并重映射
Flash 内存布局 Bootloader 0x08000000 - 0x08007FFF Application 0x08008000 - 0x0800FFFF 跳转流程 上电复位 检查跳转标志 跳转至APP APP主循环 ① 检查RAM中标志 0xDEADBEEF ② 设置MSP为APP栈顶 ③ 获取复位向量地址 ④ 跳转执行 32KB 32KB

上述SVG展示了Bootloader与应用程序在Flash中的分区布局,以及跳转时的关键执行步骤。Bootloader占用低地址区(含中断向量表),应用区从0x08008000开始,跳转时需重新定位栈指针和入口地址。