stm32之Bootloader
利用bootloader代码能够实现远程代码更新。
要清楚的认识bootloader,我们就要先了解stm32正常程序运行流程。
在stm32中将所有的中断做成了一张中断向量表(其实就像是一张表格),由上图可知由栈顶地址向下,我们可以大致分为三个部分。
1、中断向量表
2、各个中断程序入口。
3、main函数入口。
整个单片机上电运行的流程为:
上电------复位(从中断向量表中找到复位中断向量)-----执行复位中断服务程序-----执行main函数程序-----main函数中发生中断内请求----PC指针强制跳转到中断向量表处找到相应的中断向量-------执行对应的中断服务程序------返回mian函数
以上就是整个stm32上电运行main函数的流程。
当我们加入bootloader程序之后,在我们单片机的flush里面实际有两个或者多个程序(程序个数取决于flush大小),每个程序有着自己的mian函数,我们的多个程序分别放在flush的不同位置,我们的bootloader放在0X08000000(即是首地址处),其余的程序(app)紧随bootloader之后。
加入bootloader之后的启动流程如下图:
由图我们可以看出每个程序都有着自己的中断向量表等等。。。多个程序的驱动流程和单个的大致相似。。只是在app中发生中断时PC指针指向的是0X08000004这个位置的中断向量表,而非app本身的中断向量表处,,这里的注意,,在执行相应的中断程序时是跳转到app自己的中断程序表位置,,,这里的注意。。。
由此,我们利用bootloader实现代码的更新无非就是在bootloader里面将接受到的代码下载到指定的flush区域替换掉原先的代码,然后将PC指针强制指向app栈顶地址处即可实现代码的跟新。。。。。
同理我也尝试过在app中强制的将pc指针指向另外的app也能实现两个应用程序之间的切换。。。。
bootloader中主要代码为跳转程序:
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
注意:
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) :判断栈顶值是否合法,若没有这一句的话,即使没有下载程序也会进入而导致跑飞。
其中appxaddr表示栈顶地址中的值是栈顶地址中的值而非地址。 栈顶合法值为0x20000000--0x20020000
如何查看一个程序的栈顶值,可以在工程中查看map文件至于怎么查看map文件自行百度。。。。
这是我们app项目生成的map文件可以看到我们的栈顶值为0x2000b360,是一个合法的栈顶值。
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
appxaddr:栈顶地址+4表示的是指向中断向量表位置(也是复位地址)处。
其中iapfun,jump2app都是一个函数指针
typedef void (*iapfun)(void); //定义一个函数类型的参数.
汇编 初始化堆栈指针 MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
楼主实力有限。。。不懂汇编所以这里也不做解释了。。。。照搬
jump2app():把用户代码的复位地址付给PC指针,我看到jump2app()这句代码debug的时候对应的汇编代码是
LDR r0,[pc,#12] ;相对PC的数据加载到函数指针的地址
LDR r0,[r0,#00] ;R0做索引,无偏移,数据装载到R0,这个内容就是函数指针指向的内容,也就是函数的地址了,用户程序的起始地址;
BLX r0 ;这个不解释,说了是跳转
到此整个bootloader的开发流程大致清楚,就可以利用bootloader实现app更新等一系列项目开发。。。
除了使用按键跟新程序,,我们也可以通过标志位等其他方式更新app。。。