顺便发个广告
工程源代码等资料,欢迎大家索取:
http://item.taobao.com/item.htm?spm=686.1000925.1000774.13.FygESp&id=37435281766
STM32的BootLoader 从SD卡更新固件
by coolweedman
2013-11-13 23:07:26
前言
自从几个月前接触到有Bootloader这回事,就有一种强烈的冲动,想写一个BootLoader出来。很快在飞思卡尔的Cortex-M4单片机上实现,已经是好几个月前的事情了。然后关于BootLoader的事搁在一边好久了,这次弄个STM32的BootLoader出来,Cortex-M3的,顺便发表下博客,跟大家分享一下。
目录
一、什么是BootLoader 3
二、BootLoader的作用 3
三、BootLoader预备知识 3
3.1 复位序列 3
3.2 重定位中断向量表 3
3.3 C语言函数的地址 4
3.4 hex文件和bin文件 5
四、分几步实现BootLoader 7
4.1 跑FAT文件系统 7
4.2 读写Flash程序 7
4.3 跳转到新程序运行 8
五、具体流程 9
5.1 主函数流程 9
5.2 BootLoader流程 9
5.3 跳转到新程序流程 10
六、参考文献 10
这个大家可以上网查一下,我在这里说说我自己比较片面的理解。BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件更新,也就是单片机选择性的自己给自己下程序。可以更新也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行。
BootLoader下载新程序后并不擦除自己,下次启动依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新。
我的大概理解是这样,大家自己网上搜搜详细介绍吧。
BootLoader使单片机能自己给自己下载程序,所以在程序升级方面非常有作用。比如我们的BootLoader是通过GSM更新程序的,我们在升级单片机程序的时候,只要把新程序通过GSM发送给单片机,单片机自己实现程序更新,然后跳转到新程序执行,这样就省去我们很多升级的功夫啦。
可以想象一下如果把单片机安装在非常高的地方,或者危险的工业现场,或者封装得很难拆下来,我们很难直接给单片机下载程序,那么BootLoader的作用就体现出来了。
简单的说,有了BootLoader,我们省心又省力。
我们这里是为ARM的Cortex-M3单片机写的BootLoader,需要了解一下M3内核的架构,并且要了解M3是怎么启动的等等。这个方面的知识,可以参考《Cortex-M3权威指南》,这里的话我只是简单介绍一下,大家有什么不清楚请参考权威指南。并且这里是以STM32为例说明问题的,并且使用的开发环境是RVMDK。
这里参考的是《Cortex-M3权威指南》的3.8节,复位序列。
M3单片机复位后,从0x00000000取栈指针(SP), 从0x00000004取复位向量(PC),有了栈指针和复位向量后,单片机就按照正常流程运行了,在BootLoader里面,我们更新完程序后需要做的步骤之一就是设置栈指针,跳转到复位向量。
这里参考《Cortex-M3权威指南》的7.3节,向量表。
BootLoader是一个完整的程序,下载的新程序也是一个完整的程序。都包含中断向量表,所以的话,我们是有两个中断向量表。单片机启动默认先运行BootLoader,所以默认的中断向量表位置是BootLoader的中断向量表。为了新下载的程序可以正常运行,下载完新的程序后,我们需要把中断向量表重新定位到新下载的程序那里。
Cortex-M3单片机有一个管理中断向量表的寄存器,叫做向量表偏移量寄存器(VTOR)(地址:0xE000_ED08)。具体可以看看截图:
STM332的BootLoader程序一般是在0x08000000,不是在0x00000000是因为STM32的重映射技术(不符合Cortex-M3,有点搞另类的感觉),所以BootLoader的中断向量表在0x08000000那里。如果我们新下载的程序在0x08060000,并且新程序的中断向量表在起始地址,那么下载完程序,为了新程序能正确运行,我们需要把中断向量表重定位到0x08060000那里,再跳转到新程序运行。
这一节设计的内容主要属于分散加载文件,大家具体上网了解,我只是简单介绍。
我们知道C语言名就是函数的地址,并且STM32单片机ROM的其实地址是0x08000000,那么使用编译器编(我使用的是RVMDK)译程序的话,函数的地址默认都在以0x08000000为首的一段内容里面了。比如有可能我们一个函数的地址是0x08000167。
我们需要注意的问题是,如果不修改程序默认的起始地址的话,那么BootLoader和新下载的程序的起始地址都是0x08000000,也就是他们重叠了,这样的话肯定相互之间有影响,程序是不能正常工作的。
我这里的解决方法是,BootLoader程序依然占用0x08000000为首的那段ROM,因为STM32的Cortex-M3默认就是从0x08000000运行程序的,保持BootLoader程序先能正确运行。然后新程序使用除BootLoader占用ROM以外的空间。这里需要知道BootLoader到底占用了多少ROM,很简单,查看MAP文件就行了。这里以我的BootLoader的MAP文件为例说明一下,看截图:
主要是这句话“Base: 0x08000000, Size: 0x00006da4, Max: 0x00080000”,我的BootLoader程序是从0x08000000开始,占用了0x00006DA4大小。只要我们的新程序不要和BootLoader程序冲突就可以了。
编译新程序的时候,我们要修改程序的起始地址,我的修改方法如下(开发环境是RVMDK):
打开Target Option...,切换到Target选项卡,如下
修改IROM1的起始地址和长度:
比如,为了不产生地址冲突,我将将下面的
修改成
再编译新程序。
注意:BootLoader程序是不需要修改的,只是新程序需要修改(新程序就是使用BootLoader下载的程序)。
平时我们用j-Link或者串口下载程序的话,都是打开hex文件下载的,因为hex文件包含地址信息,可以知道应该下载到哪个地址。也就是hex文件是不能直接写进ROM的,一边写需要一边转换。
然后介绍一下bin文件,很好理解,bin文件是直接下载的,也就是bin文件的内容跟下载ROM里面的内容是一样的。但是bin文件没有包含地址信息,所以在下载之前要知道具体下载到哪个地址。
我们的BootLoader下载的是bin文件,直接写进STM32的Flash里面,地址信息的话就是上一节的IROM,0x08070000,连续写入,中间不间断。
下面再介绍使用生成bin文件,工具的话是使用fromelf,熟悉命令行的同学可能会选择直接敲命令,不过这里我是使用RVMDK提供的用户命令。
打开Target Option...,切换到User选项卡,如下
主要是在运行用户命令,Run #1
具体命令是:
C:\Keil\ARM\ARMCC\bin\fromelf.exe --bin -o .\Output\MY_STM32.bin .\Output\MY_STM32.axf
简化一下是fromelf --bin -o xxx.bin xxx.axf
1、fromelf的地址,我的地址是C:\Keil\ARM\ARMCC\bin\fromelf.exe
2、--bin,生成bin文件
3、-o,输出
4、.\Output\MY_STM32.bin,生成的bin文件名
5、.\Output\MY_STM32.axf,源axf文件名,将axf转换成bin
有了前面的基础知识后,应该是比较容易理解BootLoader需要怎么实现了。这一章,我们分几个步骤,慢慢实现BootLoader。
我们的BootLoader是从SD卡更新程序的,把在电脑上编译后的程序,也就是bin文件,复制到SD卡中,然后让单片机可以读取相应的bin文件,就可以实现程序的更新。
我跑的文件系统是FATFS_R0.07c,很经典的一个版本。如果大家对文件系统方面不了解的话,自己网上查找教程,或者说很多同学对这一步应该已经很熟悉啦。
单片机上实现读取bin文件,结合Flash写入程序,就可以实现程序更新。
要实现BootLoader,当然前提是可以写入Flash了。STM32的话是很容易实现的,因为我们有官方库。我使用的是3.0.0版本,参考官方例程,很容易实现Flash的读写,这里简单介绍一下。
第一步是解锁Flash,然后写入前先擦除Flash,再写入,最后读取校验写入是否正确。
调用了几个官方的库函数。
1、void FLASH_Unlock(void),Flash解锁
2、FLASH_Status FLASH_ErasePage(uint32_t Page_Address),Flash擦除
3、FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data),Flash写入
我只是稍微封装了一下几个函数,实现Flash的读写验证,具体我实现的接口函数为一下截图,大家可以大概参考一下:
这一节要结合上面提到过的,Cortex-M3启动做了什么事情,然后我们更新程序后,就去做新程序需要做的事情,就算是调到新程序运行了。主要有三个步骤:
1、重定位中断向量表
2、设置栈指针
3、跳转到复位向量
我的实现如下,看截图:
注意93行的( (void (*)()) (Reset) )();是一去就不返回的,所以95行的return FuncErr;只是形式而已,实际并不运行,而是在93行就直接跳转到新程序运行的。
至此,BootLoader实现步骤完了,相信熟悉了这几个步骤后,大家可以自己给自己的单片机写个BootLoader。顺便说一下,Cortex-M4的BootLoader跟Cortex-M3几乎是一样的。我在STM32上的实现完全是参考自己上次在飞思卡尔Cortex-M4上的实现。下面形式的说一下我的主函数吧,看看具体流程。
先看看我的主函数,再啰嗦一下具体流程,可能有的同学已经有点厌烦啦,其实感觉有点多余。
1、时钟初始化
2、LED初始化(无关紧要)
3、调试接口初始化(无关紧要)
4、Flash初始化(解锁Flash)
5、FAT初始化(挂载文件系统)
6、我们的BootLoader(重点,下面展开继续介绍)
7、主循环(实际不会运行到这里)
老样子,先上截图:
1、打开bin文件,检查文件打开是否正确
2、设置Flash下载起始地址(新程序起始地址)
3、读取文件,检查读取是否正确
4、获取栈指针和复位向量
5、进入循环,条件为如果读取文件字节数不为零
6、将读取到的bin写入Flash,并判写入状态
7、调整Flash地址
8、继续读取bin文件,检查读取是否正确,回到5继续循环
9、这里已经将bin写入Flash完成了,准备跳转到新程序运行
其实上面已经讲过了,这里继续啰嗦,截图:
1、重定位中断向量
2、设置栈指针
3、跳转到复位向量(开始运行新程序)
啰嗦了又一遍,BootLoader完全结束,感谢大家都支持啦~
【1】《CM3权威指南CnR2》Joseph Yiu 著 宋岩 译
【2】ST官方库3.0.0
【3】《C和指针》Kenneth A. Reek著 徐波 译
【4】FATFS文件系统
其他互联网资料