STM32的BootLoader 从SD卡更新固件
2015-08-05 17:47
316 查看
预备知识 我们这里是为ARM的Cortex-M3单片机写的BootLoader,需要了解一下M3内核的架构,并且要了解M3单片机是怎么启动的等等。这个方面的知识,可以参考《Cortex-M3权威指南》,这里的话我只是为了实现BootLoader简单介绍一下,大家有什么不清楚的请参考权威指南。并且这里是以STM32为例说明问题的,使用的开发环境是RVMDK(Keil)。
这里参考的是《Cortex-M3权威指南》的3.8节,复位序列。
M3单片机复位后,从0x00000000取栈指针(SP), 从0x00000004取复位向量(PC),有了栈指针和复位向量后,单片机就按照正常流程运行了,在BootLoader里面,我们更新完程序后需要做的步骤之一就是设置栈指针,跳转到复位向量。
栈是一种数据结构,后进先出LIFO。借用百度百科的解释:栈由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。它使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放。
复位向量是一个函数地址,在Cortex M3单片机里是复位函数的地址。也就是单片机启动后第一个执行的函数。
这里参考《Cortex-M3权威指南》的7.3节,向量表。
BootLoader是一个完整的程序,下载的新程序(以下称为App)也是一个完整的程序。都包含中断向量表,所以的话,我们是有两个中断向量表,相信因为有两个向量表,大家都知道我们应该需要对这两个向量表做点什么吧。
的中断向量表 我们只看前16个向量,因为其余的向量属于外设使用,与Cortex M3内核无关。
__Vectors DCD __initial_spTop ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
__initial_spTop就是栈指针,Reset_Handler是复位向量。这里只显示了16个向量,CortexM3单片机的话总共有256个向量,也就是从栈指针的地址开始有1KB的区域属于中断向量表。
单片机启动默认先运行BootLoader,所以默认的中断向量表位置是BootLoader的中断向量表。为了App可以正常运行,下载完App后,我们还需要把中断向量表重新定位到App程序那里。根据《Cortex M3权威指南》,介绍一下怎样重定位中断向量表。
Cortex-M3单片机有一个管理中断向量表的寄存器,叫做向量表偏移量寄存器(VTOR)(地址:0xE000_ED08)。具体可以看看截图:
STM332程序的起始地址一般在0x08000000。所以BootLoader程序是在0x08000000,不是在0x00000000是因为STM32的重映射技术(不符合Cortex-M3的设计,有点搞另类的感觉)。所以BootLoader的中断向量表在0x08000000那里。如果我们的App程序起始地址在0x08070000,并且App的中断向量表在起始地址,那么BootLoader程序下载App后,为了App程序能正确运行,开始App程序的运行后第一步,就要把中断向量表重定位到0x08070000那里。
具体实现下面会再介绍,接下来介绍分散加载文件相关内容。
这一节涉及的内容主要属于分散加载文件,大家具体上网了解,这里只是介绍了能够实现BootLoader的一小部分。
语言的函数地址 我们知道C语言的函数名就是函数的地址,并且STM32单片机ROM的起始地址是在0x08000000,那么使用编译器编译程序的话(这里使用的是RVMDK),函数的地址默认都在以0x08000000为首的一段ROM里面了。比如我们一个函数Delay(),它的地址可以是0x08000167(Cortex
M3中函数的地址0bit位一般是1),也就是Delay函数的代码在0x08000167,C语言函数调用Delay时,就是执行0x08000167的代码。
占用的ROM 我们需要注意的问题是,如果不修改程序默认的起始地址的话,那么BootLoader和新App程序的起始地址都是0x08000000,也就是他们重叠了(代码重叠),这样的话肯定相互之间有影响,程序是不能正常工作的。
这里的解决方法是,BootLoader程序依然占用0x08000000为首的那段ROM,因为STM32的默认就是从0x08000000运行程序的。保持BootLoader程序先能正确运行。然后App使用除BootLoader占用ROM以外的空间。这里需要知道BootLoader到底占用了多少ROM,很简单,查看MAP文件就行了。这里以我的BootLoader的MAP文件为例说明一下,看截图:
Memory Map of the image
Image Entry point : 0x08000131
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00006da4, Max: 0x00080000, ABSOLUTE)
Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00006d54, Max: 0x00080000, ABSOLUTE)
主要是这句话“Base: 0x08000000, Size: 0x00006da4, Max: 0x00080000”,这句话说明了我的BootLoader程序是从0x08000000开始,占用了0x00006DA4大小。只要我们的App不要和BootLoader程序占用的空间冲突就可以了。我的App程序的起始地址选择为0x08070000,不与BootLoader程序冲突。具体怎么修改ROM起始地址,下面介绍。
起始地址 编译新程序的时候,我们要修改程序的起始地址,我的修改方法如下(开发环境是RVMDK):打开Target Option...,切换到Target选项卡,如下
修改IROM1的起始地址和长度:
比如,为了不产生地址冲突,我将起始地址0x08000000修改成0x0807000,将ROM长度0x80000修改成0x10000。如下图所示(左图为修改前、右图为修改后):
注意:BootLoader程序是不需要修改的,只是App需要修改(App就是使用BootLoader下载的程序)。
文件和bin文件 文件 平时我们用j-Link或者串口下载程序的话,都是打开hex文件下载的,因为hex文件包含地址信息,下载程序的时候知道程序下载到ROM的哪个区域。从另一个角度上说,也就是hex文件是不能直接写进ROM的,一边写需要一边转换(解码出地址信息,将对应内容写入ROM)。
文件 bin文件的话,很好理解,是直接的可执行代码。也就是bin文件的内容跟下载ROM里面的内容是一样的。bin文件是没有包含地址信息的,所以在下载之前要知道bin文件是要下载到ROM的那个区域。
我们的BootLoader下载的是bin文件,直接写进STM32的Flash里面,地址信息的话就是上一节的IROM,0x08070000,从0x08070000开始连续写入,中间不间断。
文件生成 默认情况下编译后生成的是hex文件,不过很轻松可以生成bin文件。介绍具体怎么生成bin文件,工具的话是使用fromelf.exe(目录一般是在Keil安装目录里面,本人的fromelf.exe目录是在C:KeilARMARMCCbin),我们是使用fromelf工具将axf文件转换为bin文件。
熟悉命令行的同学可能会选择直接敲命令,不过这里介绍使用RVMDK提供的用户命令(编译时可以自动生成bin,省去每次生成bin文件都要敲命令的过程)。
打开Target Option...,切换到User选项卡,如下
主要是在运行用户命令,Run #1
具体命令是(记得在Run #1前打勾,才会在编译后执行用户命令生成bin文件):
C:KeilARMARMCCbinfromelf.exe
--bin
-o
.OutputMY_STM32.bin
.OutputMY_STM32.axf
命令可以分为五部分,简化后是fromelf --bin -o xxx.bin xxx.axf,需要注意的是命令的五个部分之间要有空格。还需要说明的是路径问题,这里的路径都是相对.uvproj文件的,下面是我的目录(注意MY_STM32.uvproj文件和Output文件夹)。
我的bin文件和axf文件都在Output文件夹里面,并且路径是相对MY_STM32.uvproj的,Output文件夹里的bin文件(MY_STM32.bin)相对于MY_STM32.uvproj应该写成“.OutputMY_STM32.bin”。
l 第一部分
这部分是fromelf.exe文件的路径,根据自己的安装目录而变。我这里因为Keil是安装在C盘的,所以我的路径如下所示。
参考命令:C:KeilARMARMCCbinfromelf.exe
l 第二部分
这部分是固定的,--bin表示生成bin文件。
参考命令:--bin
l 第三部分
这部分也是固定的,-o表示输出。
参考命令:-o
l 第四部分
这部分是生成文件的目录和文件名,我是输出在Output文件夹的,也就是bin文件在Output文件夹里面。
参考命令:.OutputMY_STM32.bin
l 第五部分
这部分是axf文件的目录和文件名,我们的bin文件是根据axf文件生成的,也就是说axf文件相当于输入,bin文件相当于输出。我的axf文件也在Output文件夹的。
参考命令:.OutputMY_STM32.axf
介绍了这些基本知识后,我们可以来实现BootLoader了。
有了前面的基础知识后,应该是比较容易理解BootLoader需要怎么实现了。这一章,我们分几个步骤,一步一步实现BootLoader。
文件系统 我们的BootLoader是从SD卡更新程序的,把在电脑上编译后的App程序,也就是bin文件,复制到SD卡中,然后让单片机读取相应的bin文件,就可以实现程序的更新。需要注意的是,App程序需要修改ROM的起始地址,再编译,并且要生成bin文件才支持正常下载。
我跑的文件系统是FATFS_R0.07c,很经典的一个版本。如果大家对文件系统方面不了解的话,请自己网上查找教程,或者说很多同学对这一步应该已经很熟悉啦。
只要单片机上实现读取bin文件,结合Flash写入程序,就可以实现程序更新。下面介绍读写Flash。
程序 要实现BootLoader,还有一个前提是可以写入Flash了。如果是STM32单片机的话是很容易实现的,因为我们有官方库。本人使用的是3.0.0版本,参考官方例程,很容易实现Flash的读写,这里同样是为了实现BootLoader简单介绍一下。
写入步骤 l 解锁Flash
l 擦除Flash
l 写入Flash
l 验证读写是否正确
调用的库函数 l void FLASH_Unlock(void) Flash解锁
l FLASH_Status FLASH_ErasePage(uint32_t Page_Address) Flash擦除
l FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data) Flash写入
读写 稍微封装一下STM32的官方库函数,就能实现Flash的读写,并验证读写是否正确,具体我实现的接口函数为以下截图,大家可以参考一下:
来到这里,我们可以实现在bin文件写入Flash了,写入完后,就要跳转到App程序执行了,接下来继续介绍。
这一节要结合上面提到过的,Cortex-M3启动做了什么事情,然后我们的BootLoader下载App程序后,App程序就需要做同样的事情。主要有三个步骤,其中BootLoader程序需要做的是:
l 跳转到复位向量
App需要做的是:
l 重定位中断向量表
l 设置栈指针
BootLoader程序需要做的是跳转到复位向量,具体实现可以参考以下代码。
( (void (*)()) (Reset) )(); //跳转到复位向量
注意 ( (void (*)()) (Reset) )();是一去就不返回的,执行完这条语句,单片机就直接跳转到App程序运行的,所以BootLoader程序下载完App后,做一些简单的处理(根据自己的应用,也可以不做任何处理),就用这条语句跳转到App执行。
开始运行 BootLoader跳转到App后,App需要做的是先设置栈指针,然后重定位中断向量表地址,具体可以参考以下代码。
__set_MSP( Msp ); //设置栈指针
NVIC_SetVectorTable( base, offset ); //重定位中断向量表
其中Msp是栈指针,也就是中断向量表第一个字的内容,我们这里的内容是*( (uint32_t)(0x08070000) )。
base是中断向量表的基地址,一般情况下就是ROM的起始地址,这里是0x08070000。
至此,BootLoader实现步骤完了,相信熟悉了这几个步骤后,大家可以自己给自己的单片机写个BootLoader。顺便说一下,Cortex-M4的BootLoader跟Cortex-M3几乎是一样的。我在STM32上的实现完全是参考自己上次在飞思卡尔Cortex-M4上的实现。下面说一下我的主函数吧,我们再看看具体的BootLoader流程,再熟悉一下BootLoader。
具体流程 先看看我的主函数,再啰嗦一下具体流程,可能有的同学已经有点厌烦啦,其实感觉有点多余。
先看截图。
主函数的流程如下所示:
l 时钟初始化
l LED初始化(无关紧要)
l 调试接口初始化(无关紧要)
l Flash初始化(解锁Flash)
l FAT初始化(挂载文件系统)
l 我们的BootLoader(重点,下面展开继续介绍)
l 主循环(实际不会运行到这里)
然后在具体讲解BootLoader_FromSDCard函数,这就是我们的重点,传说中STM32的BootLoader从SD卡更新固件。
流程 老样子,先上截图:
具体流程如下所示:
l 打开bin文件,检查文件打开是否正确
l 设置Flash下载起始地址(App程序起始地址)
l 读取bin文件,检查读取是否正确
l 获取栈指针SP和复位向量PC
l 进入循环(这里是第5步),条件为如果读取bin文件字节数不为零
l 将读取到的bin写入Flash,并判写入状态
l 调整Flash地址,根据写入字节调整
l 继续读取bin文件,检查读取是否正确,回到5继续循环
来到这里已经是退出循环了,也就是说我们已经将bin写入Flash完成了,准备跳转到新程序运行
其实上面已经讲过了,这里继续啰嗦,截图:
l 重定位中断向量
l 设置栈指针
l 跳转到复位向量(开始运行App程序)
说明一下,在这里重定位中断向量其实是多余的,App程序执行初始化后,又回到STM32初始状态,所以在App程序中需要执行重定位中断向量表操作,具体同以上操作相同。
啰嗦了又一遍,BootLoader完全结束,感谢大家都支持啦~
#include "main.h"
int main(void)
{
SystemInit(); //配置系统时钟为72M
LED_GPIO_Config(); //初始化LED端口
Debug_TraceIOEnable(); //使能调试printf的IO口
Flash_Init(); //初始化Flash
FAT_Init(); //初始化文件系统
BootLoader_FromSDCard(); //Bootloader从SD卡更新固件
while(1)
{
LED_StatShow( FuncErr ); //LED显示Bootloader状态
}
}
这里参考的是《Cortex-M3权威指南》的3.8节,复位序列。
M3单片机复位后,从0x00000000取栈指针(SP), 从0x00000004取复位向量(PC),有了栈指针和复位向量后,单片机就按照正常流程运行了,在BootLoader里面,我们更新完程序后需要做的步骤之一就是设置栈指针,跳转到复位向量。
栈是一种数据结构,后进先出LIFO。借用百度百科的解释:栈由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。它使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放。
复位向量是一个函数地址,在Cortex M3单片机里是复位函数的地址。也就是单片机启动后第一个执行的函数。
这里参考《Cortex-M3权威指南》的7.3节,向量表。
BootLoader是一个完整的程序,下载的新程序(以下称为App)也是一个完整的程序。都包含中断向量表,所以的话,我们是有两个中断向量表,相信因为有两个向量表,大家都知道我们应该需要对这两个向量表做点什么吧。
的中断向量表 我们只看前16个向量,因为其余的向量属于外设使用,与Cortex M3内核无关。
__Vectors DCD __initial_spTop ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
__initial_spTop就是栈指针,Reset_Handler是复位向量。这里只显示了16个向量,CortexM3单片机的话总共有256个向量,也就是从栈指针的地址开始有1KB的区域属于中断向量表。
单片机启动默认先运行BootLoader,所以默认的中断向量表位置是BootLoader的中断向量表。为了App可以正常运行,下载完App后,我们还需要把中断向量表重新定位到App程序那里。根据《Cortex M3权威指南》,介绍一下怎样重定位中断向量表。
Cortex-M3单片机有一个管理中断向量表的寄存器,叫做向量表偏移量寄存器(VTOR)(地址:0xE000_ED08)。具体可以看看截图:
STM332程序的起始地址一般在0x08000000。所以BootLoader程序是在0x08000000,不是在0x00000000是因为STM32的重映射技术(不符合Cortex-M3的设计,有点搞另类的感觉)。所以BootLoader的中断向量表在0x08000000那里。如果我们的App程序起始地址在0x08070000,并且App的中断向量表在起始地址,那么BootLoader程序下载App后,为了App程序能正确运行,开始App程序的运行后第一步,就要把中断向量表重定位到0x08070000那里。
具体实现下面会再介绍,接下来介绍分散加载文件相关内容。
这一节涉及的内容主要属于分散加载文件,大家具体上网了解,这里只是介绍了能够实现BootLoader的一小部分。
语言的函数地址 我们知道C语言的函数名就是函数的地址,并且STM32单片机ROM的起始地址是在0x08000000,那么使用编译器编译程序的话(这里使用的是RVMDK),函数的地址默认都在以0x08000000为首的一段ROM里面了。比如我们一个函数Delay(),它的地址可以是0x08000167(Cortex
M3中函数的地址0bit位一般是1),也就是Delay函数的代码在0x08000167,C语言函数调用Delay时,就是执行0x08000167的代码。
占用的ROM 我们需要注意的问题是,如果不修改程序默认的起始地址的话,那么BootLoader和新App程序的起始地址都是0x08000000,也就是他们重叠了(代码重叠),这样的话肯定相互之间有影响,程序是不能正常工作的。
这里的解决方法是,BootLoader程序依然占用0x08000000为首的那段ROM,因为STM32的默认就是从0x08000000运行程序的。保持BootLoader程序先能正确运行。然后App使用除BootLoader占用ROM以外的空间。这里需要知道BootLoader到底占用了多少ROM,很简单,查看MAP文件就行了。这里以我的BootLoader的MAP文件为例说明一下,看截图:
Memory Map of the image
Image Entry point : 0x08000131
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00006da4, Max: 0x00080000, ABSOLUTE)
Execution Region ER_IROM1 (Base: 0x08000000, Size: 0x00006d54, Max: 0x00080000, ABSOLUTE)
主要是这句话“Base: 0x08000000, Size: 0x00006da4, Max: 0x00080000”,这句话说明了我的BootLoader程序是从0x08000000开始,占用了0x00006DA4大小。只要我们的App不要和BootLoader程序占用的空间冲突就可以了。我的App程序的起始地址选择为0x08070000,不与BootLoader程序冲突。具体怎么修改ROM起始地址,下面介绍。
起始地址 编译新程序的时候,我们要修改程序的起始地址,我的修改方法如下(开发环境是RVMDK):打开Target Option...,切换到Target选项卡,如下
修改IROM1的起始地址和长度:
比如,为了不产生地址冲突,我将起始地址0x08000000修改成0x0807000,将ROM长度0x80000修改成0x10000。如下图所示(左图为修改前、右图为修改后):
注意:BootLoader程序是不需要修改的,只是App需要修改(App就是使用BootLoader下载的程序)。
文件和bin文件 文件 平时我们用j-Link或者串口下载程序的话,都是打开hex文件下载的,因为hex文件包含地址信息,下载程序的时候知道程序下载到ROM的哪个区域。从另一个角度上说,也就是hex文件是不能直接写进ROM的,一边写需要一边转换(解码出地址信息,将对应内容写入ROM)。
文件 bin文件的话,很好理解,是直接的可执行代码。也就是bin文件的内容跟下载ROM里面的内容是一样的。bin文件是没有包含地址信息的,所以在下载之前要知道bin文件是要下载到ROM的那个区域。
我们的BootLoader下载的是bin文件,直接写进STM32的Flash里面,地址信息的话就是上一节的IROM,0x08070000,从0x08070000开始连续写入,中间不间断。
文件生成 默认情况下编译后生成的是hex文件,不过很轻松可以生成bin文件。介绍具体怎么生成bin文件,工具的话是使用fromelf.exe(目录一般是在Keil安装目录里面,本人的fromelf.exe目录是在C:KeilARMARMCCbin),我们是使用fromelf工具将axf文件转换为bin文件。
熟悉命令行的同学可能会选择直接敲命令,不过这里介绍使用RVMDK提供的用户命令(编译时可以自动生成bin,省去每次生成bin文件都要敲命令的过程)。
打开Target Option...,切换到User选项卡,如下
主要是在运行用户命令,Run #1
具体命令是(记得在Run #1前打勾,才会在编译后执行用户命令生成bin文件):
C:KeilARMARMCCbinfromelf.exe
--bin
-o
.OutputMY_STM32.bin
.OutputMY_STM32.axf
命令可以分为五部分,简化后是fromelf --bin -o xxx.bin xxx.axf,需要注意的是命令的五个部分之间要有空格。还需要说明的是路径问题,这里的路径都是相对.uvproj文件的,下面是我的目录(注意MY_STM32.uvproj文件和Output文件夹)。
我的bin文件和axf文件都在Output文件夹里面,并且路径是相对MY_STM32.uvproj的,Output文件夹里的bin文件(MY_STM32.bin)相对于MY_STM32.uvproj应该写成“.OutputMY_STM32.bin”。
l 第一部分
这部分是fromelf.exe文件的路径,根据自己的安装目录而变。我这里因为Keil是安装在C盘的,所以我的路径如下所示。
参考命令:C:KeilARMARMCCbinfromelf.exe
l 第二部分
这部分是固定的,--bin表示生成bin文件。
参考命令:--bin
l 第三部分
这部分也是固定的,-o表示输出。
参考命令:-o
l 第四部分
这部分是生成文件的目录和文件名,我是输出在Output文件夹的,也就是bin文件在Output文件夹里面。
参考命令:.OutputMY_STM32.bin
l 第五部分
这部分是axf文件的目录和文件名,我们的bin文件是根据axf文件生成的,也就是说axf文件相当于输入,bin文件相当于输出。我的axf文件也在Output文件夹的。
参考命令:.OutputMY_STM32.axf
介绍了这些基本知识后,我们可以来实现BootLoader了。
有了前面的基础知识后,应该是比较容易理解BootLoader需要怎么实现了。这一章,我们分几个步骤,一步一步实现BootLoader。
文件系统 我们的BootLoader是从SD卡更新程序的,把在电脑上编译后的App程序,也就是bin文件,复制到SD卡中,然后让单片机读取相应的bin文件,就可以实现程序的更新。需要注意的是,App程序需要修改ROM的起始地址,再编译,并且要生成bin文件才支持正常下载。
我跑的文件系统是FATFS_R0.07c,很经典的一个版本。如果大家对文件系统方面不了解的话,请自己网上查找教程,或者说很多同学对这一步应该已经很熟悉啦。
只要单片机上实现读取bin文件,结合Flash写入程序,就可以实现程序更新。下面介绍读写Flash。
程序 要实现BootLoader,还有一个前提是可以写入Flash了。如果是STM32单片机的话是很容易实现的,因为我们有官方库。本人使用的是3.0.0版本,参考官方例程,很容易实现Flash的读写,这里同样是为了实现BootLoader简单介绍一下。
写入步骤 l 解锁Flash
l 擦除Flash
l 写入Flash
l 验证读写是否正确
调用的库函数 l void FLASH_Unlock(void) Flash解锁
l FLASH_Status FLASH_ErasePage(uint32_t Page_Address) Flash擦除
l FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data) Flash写入
读写 稍微封装一下STM32的官方库函数,就能实现Flash的读写,并验证读写是否正确,具体我实现的接口函数为以下截图,大家可以参考一下:
来到这里,我们可以实现在bin文件写入Flash了,写入完后,就要跳转到App程序执行了,接下来继续介绍。
这一节要结合上面提到过的,Cortex-M3启动做了什么事情,然后我们的BootLoader下载App程序后,App程序就需要做同样的事情。主要有三个步骤,其中BootLoader程序需要做的是:
l 跳转到复位向量
App需要做的是:
l 重定位中断向量表
l 设置栈指针
BootLoader程序需要做的是跳转到复位向量,具体实现可以参考以下代码。
( (void (*)()) (Reset) )(); //跳转到复位向量
注意 ( (void (*)()) (Reset) )();是一去就不返回的,执行完这条语句,单片机就直接跳转到App程序运行的,所以BootLoader程序下载完App后,做一些简单的处理(根据自己的应用,也可以不做任何处理),就用这条语句跳转到App执行。
开始运行 BootLoader跳转到App后,App需要做的是先设置栈指针,然后重定位中断向量表地址,具体可以参考以下代码。
__set_MSP( Msp ); //设置栈指针
NVIC_SetVectorTable( base, offset ); //重定位中断向量表
其中Msp是栈指针,也就是中断向量表第一个字的内容,我们这里的内容是*( (uint32_t)(0x08070000) )。
base是中断向量表的基地址,一般情况下就是ROM的起始地址,这里是0x08070000。
至此,BootLoader实现步骤完了,相信熟悉了这几个步骤后,大家可以自己给自己的单片机写个BootLoader。顺便说一下,Cortex-M4的BootLoader跟Cortex-M3几乎是一样的。我在STM32上的实现完全是参考自己上次在飞思卡尔Cortex-M4上的实现。下面说一下我的主函数吧,我们再看看具体的BootLoader流程,再熟悉一下BootLoader。
具体流程 先看看我的主函数,再啰嗦一下具体流程,可能有的同学已经有点厌烦啦,其实感觉有点多余。
先看截图。
主函数的流程如下所示:
l 时钟初始化
l LED初始化(无关紧要)
l 调试接口初始化(无关紧要)
l Flash初始化(解锁Flash)
l FAT初始化(挂载文件系统)
l 我们的BootLoader(重点,下面展开继续介绍)
l 主循环(实际不会运行到这里)
然后在具体讲解BootLoader_FromSDCard函数,这就是我们的重点,传说中STM32的BootLoader从SD卡更新固件。
流程 老样子,先上截图:
具体流程如下所示:
l 打开bin文件,检查文件打开是否正确
l 设置Flash下载起始地址(App程序起始地址)
l 读取bin文件,检查读取是否正确
l 获取栈指针SP和复位向量PC
l 进入循环(这里是第5步),条件为如果读取bin文件字节数不为零
l 将读取到的bin写入Flash,并判写入状态
l 调整Flash地址,根据写入字节调整
l 继续读取bin文件,检查读取是否正确,回到5继续循环
来到这里已经是退出循环了,也就是说我们已经将bin写入Flash完成了,准备跳转到新程序运行
其实上面已经讲过了,这里继续啰嗦,截图:
l 重定位中断向量
l 设置栈指针
l 跳转到复位向量(开始运行App程序)
说明一下,在这里重定位中断向量其实是多余的,App程序执行初始化后,又回到STM32初始状态,所以在App程序中需要执行重定位中断向量表操作,具体同以上操作相同。
啰嗦了又一遍,BootLoader完全结束,感谢大家都支持啦~
#include "main.h"
int main(void)
{
SystemInit(); //配置系统时钟为72M
LED_GPIO_Config(); //初始化LED端口
Debug_TraceIOEnable(); //使能调试printf的IO口
Flash_Init(); //初始化Flash
FAT_Init(); //初始化文件系统
BootLoader_FromSDCard(); //Bootloader从SD卡更新固件
while(1)
{
LED_StatShow( FuncErr ); //LED显示Bootloader状态
}
}
相关文章推荐
- JS获得鼠标位置
- ios js call ios
- 计数排序
- 计数排序
- android布局--Android fill_parent、wrap_content和match_parent的区别
- Ubuntu 12.04中文输入法的安装
- js实现网页抽奖实例
- java中==与equals的区别
- jquery获取URL参数
- Java泛型总结
- 曾国藩的克己养成
- 4、C#进阶:MD5加密、进程、线程、GDI+、XML、委托
- 加载 XML 文档
- AddressBookUI Framework in iOS9 beta4
- ArcGIS Engine调用GP服务
- 推荐文章(内存对齐,Git使用...)
- Oracle数据库导入导出
- iOS开发中常用到的宏
- 设计模式3
- Sudoku Solver