bootloader地址跳转实现
前言
实验硬件:STM32F407ZGT6,sd卡,USB线
本文目标:在STM32中实现IAP本地升级,通过USB MSC连接电脑放置bin文件包
技术栈:bootloader、IAP、FATFS、USB MSC
软件:Keil、CubeMX
实现功能:实现一个bootloader,在bootloader程序内通过USB MSC将sd卡连接PC对sd卡进行读写,STM32通过读取sd卡中的bin文件对内部flash的app版本进行更新,同时在更新过程中实现老版本回溯功能防止更新失败导致app奔溃
本节先实现bootloader中地址跳转到app功能
OTA升级也可借鉴本文
一、IAP介绍
IAP (In-Application Programming),即应用内编程,是一种允许微控制器在不依赖外部编程器的情况下更新自身固件的技术。在STM32等MCU中,IAP通过将Flash存储器划分为引导加载程序(Bootloader)区和应用程序区两部分来实现。
Bootloader作为固定不变的底层程序,负责检测升级条件、验证新固件的完整性、擦写Flash并将新固件写入应用程序区。应用程序则是实际执行业务功能的代码,可以通过IAP机制进行更新。
当系统需要升级时,新固件可通过各种接口(如UART、CAN、以太网、SD卡等)传输到设备,Bootloader接收并验证固件后,将其写入指定区域
以stm32为例,程序从0x0800 0000开始执行,我们可以将bootloader烧录至0x0800 0000-0x08007FFF(占用32kb)区域,将app(用户代码)烧录至0x08008000区域,在bootloader中可以检测预设的条件是否触发(如flash中的标志位、按键是否按下等)判断是否直接跳转至app主程序
简而言之就是写两段代码,从第一段代码中跳转到第二段代码的地址,内部flash地址分配如上图,也就是说我们要创建两个工程。
二、跳转功能实现
首先我们要创建两个工程,一个作为我们的app,一个作为bootloader,用CubeMX生成两个工程,CubeMX里打开usart1进行串口打印,重定向printf后在while循环中分别打印boot与app用于测试(创建工程这边就不写了,没什么好写的),此时若直接烧录则会在串口调试助手循环接收到APP或boot
bootloader工程设置
将bootloader程序的起始地址设为0x0800 0000,Size设为0x8000,代表该程序ROM选择这一段地址,同时修改debug中擦除相关信息防止烧录bootloader时将app擦除造成程序奔溃
app工程设置
将app的起始地址设为0x0800 8000,Size设为0xF800,即bootloader之后剩余的地址,同时修改debug中擦除相关信息将app烧录到正确位置
Jump_to_APP函数实现
bootloader跳转流程
1、关中断
2、设置MSP
3、跳转
#define FLASH_APP_ADDR (0x08008000U)
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
void Jump_to_APP(uint32_t app_addr)
{
uint32_t JumpAddress;
printf("jump to app: %#x\r\n", FLASH_APP_ADDR);
//判断应用程序区的首地址是否存放了有效的初始栈顶指针
if (((*(volatile uint32_t*)FLASH_APP_ADDR) & 0x2FFE0000 ) == 0x20000000)
{
__set_PRIMASK(1); //关总中断 0打开总中断
JumpAddress = *(volatile uint32_t*) (FLASH_APP_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;
printf("jump %#x success \r\n",FLASH_APP_ADDR);
__set_MSP(*(volatile uint32_t*) FLASH_APP_ADDR); //设置SP指针,复位指针
Jump_To_Application(); //开始跳转
}
else
{
printf( "erorr [0x%08x]\r\n",(*(volatile uint32_t*)FLASH_APP_ADDR) );
}
}
__set_MSP((volatile uint32_t) FLASH_APP_ADDR);是将FLASH_APP_ADDR处的值直接写入SP寄存器,设置主堆栈指针
Jump_To_Application是一个函数指针,Jump_To_Application = (pFunction) JumpAddress;将应用程序入口地址(Reset_Handler)强制转换为函数指针,赋值给 Jump_To_Application
FLASH_APP_ADDR + 4是app的Reset_Handler(复位入口)的地址
app工程设置
在app工程开头打开所有中断并且设置中断向量表的基地址为 0x08008000
将bootloader与app分别烧录后可以可以看到bootloader直接跳转至app你可以像我一样进入boot时打印一串文字,进入app时打印一串文字或者点个灯啥的判断程序是否成功跳转
三、常见问题检查方案
.map文件
.map 文件是Keil等嵌入式开发工具在编译链接过程中自动生成的链接映射文件。它详细记录了程序中各个函数、变量、段(section)在最终可执行文件中的内存分布和地址分配情况。
map文件的主要作用
查看内存分布:了解代码段、数据段、堆栈等在Flash和RAM中的分布情况。
定位符号地址:查找全局变量、函数等的绝对地址,便于调试和定位问题。
分析内存占用:统计各个模块、文件、函数的内存占用,优化资源分配。
排查链接错误:定位重定义、未定义符号等链接问题。
如果app无法正常跳转,可以查看app工程的.map文件看生成的地址是否正确(在哪里自己去查下资料),Reset_Handler函数在flash实际地址,如果你Reset_Handler函数地址不在app地址内,则说明你app工程设置有问题。
如果你Reset_Handler函数地址没有问题,那么你去bootloader工程跳转前打个断点,看LR或PC指针是否指向Reset_Handler地址,如果未指向Reset_Handler地址,则说明你bootloader工程有问题。
.sct文件
.sct 文件是Keil MDK(ARM编译环境)中用于描述程序各个代码段、数据段在目标芯片存储器中分布的“分散加载文件”(Scatter File)。
它告诉链接器(Linker)每一段代码和数据应该放在Flash还是RAM的哪个具体地址,是嵌入式开发中非常重要的配置文件
一般情况下bootloader工程不会有问题,app工程如果怎么编译怎么设置.map文件的地址都不对,可以查看下app工程的.sct文件是否配置正确
在.sct文件中设置正确ROM地址后再进行编译烧录
内部flash查看
还是不行可以用keil的debug查看下内部flash是否正确,0x0800 0000地址是否为正确的bootloader文件,0x0800 8000是否为正确的app文件,keil的debug具体使用方法网上自己找下,这边不做介绍
成功实现地址跳转IAP最麻烦的部分基本就完成了,剩下的功能我们之后添加