这篇文档主要讲解Cortex-M3内核的芯片怎样实现带有BOOT和APP分区程序功能,通过BOOT来给APP固件升级。
先列出主要步骤:
在BOOT中:
- 重定位中断向量表到BOOT区首地址(必须)
- 在跳转之前重置所有内部外设时钟包括Systick时钟,避免跳转时产生意外中断(可选)
- 更改PC指针和SP堆栈指针,实现跳转(必须)
在APP中:
- 重定位中断向量表到APP区首地址(必须)
接下来详细说明,先讲解一下我们的程序为什么需要有BOOT和APP这样的程序分布,已经明白的可以跳过下面几段,直接到“分割线”下面。
做过单片机开发的可能知道,通常做一个开发板或者普通的不需要升级固件的产品来说,是不需要BOOT的,但如果一个产品需要经常升级固件,而产品本身的电路板是不可能随意让你拆除,也不可能搞一个JTAG或者SWD烧录器进行烧写时,这时候就需要一个很方便的固件升级方式,比如通过串口升级(借助WIFI转串口、蓝牙转串口或者其它直连的方式)或者通过网口升级都行,只要能跟芯片通信就行,最简单的一般是通过串口来升级。
一般外部的升级软件通过跟芯片的BOOT通信,将APP的bin文件分块传输并写入到芯片FLASH区的APP指定的起始地址处,写入完毕之后,往往还需要对整个写入的APP固件进行校验,确保写入的APP是正确的之后,就可以更改系统的一些配置参数,这样下次程序启动的时候去读取配置参数,然后去正确的启动APP;而实际的升级比这可能还要复杂,比如程序升级到了一半突然被意外中断或干脆断电该怎么处理?产品会不会变“砖”?等问题。
其实防砖也好处理,主要思想就是先清掉升级标志,然后等全部升级完毕,最后更改标志,BOOT启动时根据该标志决定是否跳转到APP。
在升级之前我们在“配置参数区”的某个指定位置写入特定的几个字节数据,来标记升级成功如否的标志,只有当我们把APP程序升级完毕,所有校验检查都通过之后,在复位重启之前,更改那几个特殊标记的字节,来表示升级成功,这样如果没有升级或者升级一半断电的话,该标志就不会被更改,那下次BOOT启动之后先去检查该标志,如果不满足启动APP条件,则不会进行APP跳转,程序仍然可以停留在BOOT代码中,给用户继续升级的机会,而不是跳转到APP中,而APP区又是损坏的,变成了一个“砖头”;通过这种方法我们就很好的实现了防砖设计。
----------------------------------------------------分割线----------------------------------------------
下面我们回到正题,在从BOOT跳转到APP这个动作以及做这个“跳转”动作之前,我们需要做的事情,这里我已经列出了主要的步骤:
- 在BOOT里面,主要是重定位中断向量表和实现跳转功能
- 在APP里面,主要是重定位中断向量表
在BOOT代码中,main开始运行的起始,我们需要做重定位中断向量表到BOOT的起始区,中断向量表就是一系列中断的入口地址,发生中断以后,程序会跳转到中断向量表中对应的中断入口区,去寻找该中断函数真正的入口地址,然后跳到中断函数去执行中断处理,如果中断向量表的入口地址不对,那么执行的中断入口地址就不对了,你执行的甚至都不是中断函数了,所以BOOT和APP区必须都有自己的“中断向量表”,中断向量表一般位于程序的起始地址处,程序进入BOOT或者APP的main之后,第一件事情就是要重定位中断向量表地址。
STM32和LPC1768都是Cortex-M3内核的,操作中断向量表的寄存器就是VTOR寄存器
,重定位中断向量表,说白了就是向VTOR寄存器0xE000ED08地址写入BOOT区或者APP区的起始地址值。下面看下该寄存器的说明
APP跳转函数,使用了C中嵌入汇编,下面是跳转代码
__asm void f_jmp_app(U32 address)
{
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
在需要跳转的位置,执行 f_jmp_app(APP_Start);
比如STM32,我的APP起始地址为 0x0810000 地址,则写成 f_jmp_app(0x08100000)即可。
在f_jmp_app函数中,有2句汇编指令,第一句 LDR SP, [R0] , SP为栈顶指针, R0为函数的第一个参数,该函数只有一个就是 address ,所以R0就等于0x08100000, 而第二句就是 LDR PC, [R0, #4] , 该指令的功能是将 address+4的新地址作为PC程序指针,原因请往下接着看,下面是Cortex-M3内核的寄存器组,SP堆栈指针就是R13, PC程序指针就是R15。
至于为什么需要上面那样操作,就需要去看Cortex-M3权威指南了,里面有关于SP和PC的详细介绍,这里我贴出主要内容
总结:
在BOOT中,main起始处写:
SCB->VTOR = 0x0 & 0x1FFFFF80; //重定位中断向量表
在需要跳转的位置写上:
f_jmp_app(app_start);
而f_jmp_app 的函数定义为:
__asm void f_jmp_app(U32 address)
{
LDR SP, [R0] ;Load new stack pointer address
LDR PC, [R0, #4] ;Load new program counter address
}
在APP中,main起始处写:
SCB->VTOR = app_start & 0x1FFFFF80; //重定位中断向量表
注:app_start起始就是app程序起始地址。