文章目录
Bootloader升级(程序升级)
Bootloader概念
本文不讨论芯片自带的BootLoader,只以用户自己编写的为例
- Bootloader 是应用程序运行前执行的一段固化在微控制器中的代码程序。
- Bootloader 是底层硬件与上层应用软件之间的一个中间接口软件。
- BootLoader 独立于用户应用程序,可以被编译、连接并下载到 ECU 中,不能与用户应用程序同时运行。
- Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式系统中。
- 在嵌入式系统中难以建立一个通用的 Boot Loader。
- 单片机出厂时会自带一个固化在ROM中的bootloader
- Bootloader就是在系统上电或复位后运行的一段小程序。这段程序将系统的软硬件环境带到一个合适的状态,为最终调用应用程序准备好正确的环境。
升级概念
本文中升级所指的是升级用户编写的除Boot区外,单片机长时间运行的代码(APP)。
- 单片机在在APP运行时接收到执行升级命令后会跳转至Boot区,在Boot区对APP区的FLASH进行擦除,再将要升级的执行文件写入到该部分FLASH,写入成功后进行复位,从BootLoader跳转回APP区,系统执行升级后的程序,升级结束。
程序启动及中断向量表
一般来说,系统从Flash地址起始位置取指令并执行,那么Flash中的第一句指令应该就是main.c中的第一句指令吧?但实际上执行文件中的起始指令并非是main.c中的第一句指令,以下图为例,该程序的起始地址为0x08007800,那么main.c应该就在0x08007800吧?但是在仿真中我们看到main.c的位置处在0x0803b030,这和我们的常识相违背了,那Flash的起始地址放的是什么指令?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnaOXDiL-1656472553845)(file:///C:/Users/Administrator/AppData/Roaming/marktext/images/2022-06-28-17-05-50-image.png)]
我们跳转到0x08007800地址看一下,发现这个地址是一个叫__vector_table的东西,这是什么?我们编写的程序中好像没有这个东西,实际上这是中断向量表,存在于.s文件中,那么芯片上电后执行的第一条指令应该就是这个表格里的第一个指令,进入.s文件中看一看这个第一条指令是什么。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLSE5WdH-1656472553846)(file:///C:/Users/Administrator/AppData/Roaming/marktext/images/2022-06-28-17-11-52-image.png)]
这是.s文件中的中断向量表,每个向量占用4个字节,我们可以看到第一条指令是一个叫做sfe的东西,sfe(CSTACK)是IAR汇编器段操作,用于获取段(section)的结束地址,由于本文使用的芯片是基于Cortex-M架构的,该系列芯片的堆栈模型是满减栈,堆栈从高地址向低地址增长,因此堆栈的基地址是CSTACK的结束地址,IAR在链接器脚本(*.icf)文件中定义堆栈,实际是定义了一个名为“CSTACK”的空闲块(block),我们知道第一个指令是用于定义块的了,那么下一句指令Reset_Handler是做什么用的?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j3pR7Y42-1656472553846)(file:///C:/Users/Administrator/AppData/Roaming/marktext/images/2022-06-28-17-17-13-image.png)]
我们在.s文件中可以找到Reset_Handler这个函数,这个函数只做了两件事,系统初始化和IAR程序启动,也就是说芯片上电之后执行的第一条命令是跳转到SystemInit这里初始化,然后执行__iar_program_start程序,执行完这句话后才开始执行用户编写的代码。
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
从上边我们可以知道芯片上电后会先执行中断向量表中的Reset_Handler函数,也大概知道了程序执行的方式,那么我们是不是也可以使用一个芯片烧录两个单独程序,通过一个芯片实现多种功能?答案是:可以的!那么我们应该怎么实现跳转呢?
程序跳转
我们知道,程序在Flash中的位置是固定的已知的,那么我们只需要让芯片从另一个程序中的Reset_Handler位置开始执行程序,是不是就可以实现两个程序的相互跳转了?那么如何实现跳转呢?我们现在已经知道要执行的代码的位置了,那是不可以使用指针来进行跳转?
设置指针
从上边我们知道了程序执行文件中有中断向量表,堆栈和主程序区,那我们只需要将这几个部分通过指针进行跳转就可以实现程序切换了,本文所使用的芯片基于arm cortex m4内核,该系列芯片都是从Flash地址0处开始执行程序,那么就必须在地址为0处烧录一个程序,然后在这个程序中实现跳转,在地址为0x4000处烧录第二个程序,那么我们只需要创建一个void类型的函数指针,将其地址设置为0x4004,也就是第二个程序的Reset_Handler函数位置,再执行该地址的函数,应该就行了吧。
void (*pFunction)(void);
pFunction = (void (*)(void))(* ( __IO uint32_t * )(0X00004000 + 4));
pFunction();//跳转
return ;
我们将这段程序烧录进0位置上,将一个点灯程序烧录进0x4000位置上,按下复位键,理论上我们应该能够见到灯被点亮,实验结果和预想的一样,灯被点亮了,也就证明我们的程序成功跳转到了APP区域。不过该程序仍有很多疏漏,比如中断跳转,堆栈指针设置,不建议用于实际使用。