1. 构建最小Bootloader框架
任务说明
本案例实现一个最小可用的STM32 Bootloader,核心功能是:上电后检查应用程序区首字是否为合法栈顶地址,若合法则跳转到应用程序执行。通过一个LED闪烁程序作为应用程序,验证跳转是否成功。
环境与依赖
- 硬件:STM32F103C8T6(Blue Pill)开发板,板载LED(PC13)
- 软件:STM32CubeIDE 或 Keil MDK-ARM(本文以STM32CubeIDE为例)
- 所需库:STM32CubeF1固件包(HAL库)
实现原理
Bootloader与应用程序分别编译,链接到不同的Flash地址。Bootloader位于0x08000000,应用程序位于0x08008000(偏移32KB)。跳转前需完成:
- 检查应用程序首字(栈顶地址)是否在RAM范围内(0x20000000~0x20010000)
- 关闭全局中断
- 设置主栈指针(MSP)为应用程序的栈顶地址
- 获取应用程序复位向量并跳转执行
关键完整代码
Bootloader主程序(main.c核心部分)
/* 应用程序起始地址:Flash偏移32KB */
#define APP_ADDRESS 0x08008000U
/* 跳转函数 */
typedef void (*pFunction)(void);
void JumpToApp(void)
{
uint32_t app_stack_addr;
pFunction app_reset_handler;
/* 1. 检查应用程序首字是否为合法栈顶地址(RAM范围) */
app_stack_addr = *(volatile uint32_t *)APP_ADDRESS;
if ((app_stack_addr & 0x2FFE0000) != 0x20000000)
{
/* 栈顶地址非法,停留在Bootloader */
while (1)
{
/* 可在此处添加错误指示,如LED快闪 */
}
}
/* 2. 关闭全局中断 */
__disable_irq();
/* 3. 设置主栈指针 */
__set_MSP(app_stack_addr);
/* 4. 获取复位向量并跳转 */
app_reset_handler = (pFunction)(*(volatile uint32_t *)(APP_ADDRESS + 4));
app_reset_handler();
}
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 简单延时后跳转(便于调试观察) */
HAL_Delay(100);
JumpToApp();
/* 正常情况下不会执行到这里 */
while (1)
{
}
}
应用程序链接脚本修改(STM32F103C8Tx_FLASH.ld)
/* 修改Flash起始地址和长度 */
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 96K
/* 原为 ORIGIN = 0x08000000, LENGTH = 128K */
应用程序向量表重定位(system_stm32f1xx.c中修改)
/* 在SystemInit函数末尾添加 */
#define VECT_TAB_OFFSET 0x8000U /* 偏移32KB */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
编译与烧录步骤
- 编译Bootloader工程:使用默认链接脚本(起始地址0x08000000),编译生成Bootloader.hex
- 编译应用程序工程:修改链接脚本起始地址为0x08008000,添加向量表重定位代码,编译生成App.hex
- 烧录Bootloader:使用ST-Link或串口ISP将Bootloader.hex烧录到0x08000000
- 烧录应用程序:使用ST-Link将App.hex烧录到0x08008000(注意烧录地址需与链接脚本一致)
验证方法
应用程序代码中让PC13引脚上的LED以1Hz频率闪烁。完成烧录后复位开发板,观察现象:
- 通过标准:复位后约100ms,LED开始以1Hz频率闪烁,说明Bootloader成功跳转到应用程序
- 失败表现:LED不闪烁或保持常亮/常灭,说明跳转失败,可能原因包括:应用程序地址配置错误、向量表未重定位、栈顶地址校验失败
常见问题
- 跳转后程序跑飞:检查应用程序是否已重定位向量表(SCB->VTOR),以及链接脚本起始地址是否与烧录地址一致
- 栈顶地址校验失败:确认应用程序确实烧录在0x08008000,且该地址首字为有效的RAM地址(0x20000000~0x20010000范围内)
- 跳转后中断不工作:跳转前需确保已关闭全局中断,跳转后应用程序的SystemInit会重新配置系统时钟和中断