欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32单片机BootLoader

程序员文章站 2024-02-23 22:38:16
...

STM32单片机BootLoader

前言
今天我们借用正点原子的的IAP试验例程来分析一下bootloader的原理以及详细过程,为今后相关的IAP功能奠定一定的基础,今后IAP相关的功能都是以此简单原理为基础的。

直接上菜吧,Let‘s go!

一、APP文件的来源
通常我们现在BootLoader工程APP固件文件都是用bin文件,原因我就不多说了,有兴趣的话自行百度吧(这不是今天的重点)。

二、APP文件的传输方式
最简单的方式就是串口直接传输了,就如正点原子的IAP方式,没有加入任何传输协议,简单明了,这是给我们抛砖引玉用的,我们实际项目肯定不会用这种方式去传输文件,因为不加协议的话很容易因为外界干扰导致数据被错误,再一个项目的扩展性和叠加性很差!项目里面有用Ymoden协议的,有用USB的,有用WIFI,GPRS模块等等…大家有兴趣可以自行百度或者等待我后期更新文章。

三、APP文件的载入方式
APP文件的载入方式分为两种:
1.FLASH更新方式
2.SRAM更新方式

下面分别对这两种方式做介绍:
1.FLASH更新方式
直接上代码介绍吧

if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{  
	iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码   
}
 if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
 {  
    	iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
 }

里一开始我不太明白为什么要

((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000

这样判断一下呢?
网上查阅资料之后原子哥告诉我:

(1)第一个4个字节是MSP地址,第二个4个字节,才是复位中断向量的入口地址。
(2)&0xFF000000就是取最高8位。因为FLASH的地址范围是0X0800 0000开始的。这可以一定程度上确保地址范围正常。

这就是为什么要判断一下的原因,是为了确保APP文件的正确性!

插一句话:

u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));

这里解释了为什么将串口缓存写入FALSH对应地址的时候是判断0X20001000+4地址位置上的数据,是因为定义串口BUF的时候为这个变量分配了变量地址。这也是C语言定义变量的一种方法,不懂的回去看看C语言哈…

我们再来看一下iap_write_appbin函数

iapfun jump2app; 
u16 iapbuf[1024];
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
 u16 t;
 u16 i=0;
 u16 temp;
 u32 fwaddr=appxaddr;//当前写入的地址
 u8 *dfu=appbuf;
 for(t=0;t<appsize;t+=2)
 {          
  temp=(u16)dfu[1]<<8;
  temp+=(u16)dfu[0];   
  dfu+=2;//偏移2个字节
  iapbuf[i++]=temp;     
  if(i==1024)
  {
   i=0;
   STMFLASH_Write(fwaddr,iapbuf,1024); 
   fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
  }
 }
 if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.  
}

这里可以看出缓存数据写入片内FLASH的全部过程,先是以1024个字节为单位进行写入,然后将不足1024个字节部分数据写入。

我们再来看一下iap_load_app函数

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
 if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
 { 
  jump2app=(iapfun)*(vu32*)(appxaddr+4);  //用户代码区第二个字为程序开始地址(复位地址)  
  MSR_MSP(*(vu32*)appxaddr);     //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
  jump2app();         //跳转到APP.
 }
} 

跳转APP之前做的最重要的两件事:
(1)设置复位地址的开始地址;
(2)初始化MSP指针。
确保这两项工作OK之后跳转到APP程序就OK了

再多说一句,在实际项目中,我一般会在这个函数之前关掉所有中断和清掉或者屏蔽所有中断标志,确保APP不被中断打断正确无误的运行!

例如:

__set_PRIMASK(1);

2.SRAM更新方式

SRAM更新方式和FLASH方式差不多,区别就是更新的地址不一样,APP程序不能固化在单片机内部,外部掉电之后程序就没了,每次上次都要自行载入一次程序,当然在程序加密的角度上又有优势,个人觉得,不喜勿喷!

当然大部分建议用FLASH方式!
所用到的函数在上面已经做了介绍
唯一不同的就是调用不一样

 if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.
 {  
    iap_load_app(0X20001000);//SRAM地址
 }

四、APP工程的设置方法

前面的描述我们已经可以完成IAP工程的构建
下面对APP工程做一定的说明

1.FLASH更新方式

#define FLASH_APP1_ADDR  0x08010000   //第一个应用程序起始地址(存放在FLASH)
           //保留0X08000000~0X0800FFFF的空间为IAP使用

这是IAP工程里面的FALSH地址设置

APP工程设置详细方式:
(1)将下面这句话放到main函数的最前面

SCB->VTOR = FLASH_BASE | 0x10000; /* Vector Table Relocation in Internal FLASH. */ 

注意和上面FLASH地址是对应的。

(2)将Target->Read/Only Memory Areas 里面的IROM1改成如下这样,注意和上面对应
STM32单片机BootLoader
(3)将Target->User里面After Build/Rebuid里面Run#1设置如下
STM32单片机BootLoader

D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe  --bin -o  ..\OBJ\RTC.bin ..\OBJ\RTC.axf

//D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe
//上面这一段需要根据自己电脑安装的MDK位置进行更改,其他不变

每次编译完工程,就可以自动生成我们需要的bin文件

这样,APP工程就设置好了!

2.SRAM更新方式

大部分设置方式和上面一样,改动的地方,下面做说明:

(1)将下面这句话放到main函数的最前面

 SCB->VTOR=SRAM_BASE|0x1000;

(2)将Target->Read/Only Memory Areas 里面的IROM1改成如下这样,注意和上面对应
将Target->Read/Write Memory Areas 里面的IRAM1改成如下这样,注意和上面对应
STM32单片机BootLoader这里做一下补充说明:
SRAM更新方式是用RAM区域来装程序,所以IROM1起始地址和大小要改成RAM的地址,而不是FLASH地址,IRAM1区域的起始地址也要相对应放到程序区域的后面。
上面程序空间占了0x20001000到0x2000E000,所以IRAM1起始地址在其后面。

第(3)设置和上面一样,就不做重复性说明了。

这样下来,我们BootLoader项目就设置好了,实际项目中我们都是在这个基础上进行修改。

如果有描述不到位的地方,欢迎大家来交流指正!

最后说明:
这次博文的代码来自正点原子的战舰V3 HAL库代码,也在战舰V3上做过实际测试!