说明 这节提供给用户一份实现更新STM32的程序(兼容STM32f103全系列) 主要说明STM32是如何实现的升级程序.后面的章节都是在这节的基础上进行优化.
测试 1.说明
BootLoader作为引导程序,负责把接收的程序文件写入flash,然后加载执行. STM32F10xTemplate 是用户程序,这套程序采用串口升级进去.然后执行 2.下载BootLoader程序到单片机(自行下载)
3.调整开发板拨动开关如下:电脑串口和STM32串口1通信
4.打开串口调试助手 发送 updata start 单片机擦除flash以后返回 wait updata...
5.发送程序文件STM32F10xTemplate 用户程序的 bin文件
用户程序每隔1S打印 run user app <ignore_js_op>
6.在用户程序执行的时候再次发送 updata start 等待返回 wait updata... 可以再次发送程序文件,执行更新. <ignore_js_op>
发送程序文件 <ignore_js_op>
用户程序每隔1S打印 run user app <ignore_js_op>
整体说明 <ignore_js_op>
首先用户需要明白,无论是什么单片机实现更新程序,实质上就是把程序文件写到单片机的存储里面 然后调用单片机提供的函数运行而已!! 对于当前的STM32程序而言就是把程序文件从0x08004000这个位置开始,把程序文件写到里面 然后把0x08004000这个地址给一个函数执行. 1.下载完BootLoader以后,当用户发送 updata start 程序擦除用户程序运行区的flash <ignore_js_op>
2.当用户发送程序文件时 把接收的程序从0x08004000这个位置开始,把程序文件写到flash里面 <ignore_js_op>
3.当检测到接收到用户程序,打印下有没有错误信息,然后重启 <ignore_js_op>
4.一旦有了用户程序,则加载用户程序 <ignore_js_op>
5.在用户程序里面,如果接收到updata start 则设置一个更新标志(存储在flash里面) <ignore_js_op>
6.BootLoader 判断有更新标志以后,擦除用户程序运行区的flash 然后就是如此循环 <ignore_js_op>
细节说明(bin文件) 1.什么是bin文件? 大家肯定知道hex文件 打开这节的hex文件和bin文件 <ignore_js_op>
<ignore_js_op>
(我使用的UltraEdit这个软件)
<ignore_js_op>
注意看hex文件和bin文件的区别
hex比bin文件多了前面一部分,和后面一部分
大家下载单片机程序应该都知道是下载hex文件
但是大家了解整个的下载过程不
其实咱用软件下载的时候首先单片机需要知道下载的这段程序下载到哪个地址上(把程序数据写到哪个Flash地址)
所以hex文件的前面部分就是地址信息,就是告诉芯片我后面的代码段存储到哪个地址上
当然为了保险起见,数据需要加校验,其实hex文件的最后一位就是校验位
像51单片机,STM32的串口下载,下载的时候需要断电上电,或者复位一下,其实咱的单片机里面有一段程序(接收单片机程序,写入Flash) 就是咱所说的bootloader
记不记得都是先点一下下载软件的下载按钮,然后再复位单片机
其实点一下下载软件,下载软件就一直串口输出下载命令呢!单片机一启动是先执行里面内嵌的bootloader,
bootloader一检测到下载命令,就开始执行下载操作了,接收下载软件过来的数据,然后写到Flash里面.....写完了,有的单片机重启下才运行,有的就直接运行了
好,现在说bin文件为啥去掉了前面的地址信息
记住,咱自己更新的时候咱就规定好了程序的运行位置
咱们是直接把程序文件写入了相应的地址里面. 2.如何制作bin文件 2.1 概述
在做升级之前,上面的flash存储位置是事先规定好的 stm32的flash地址是从0x08000000开始,默认下载程序的时候都是把程序文件从0x08000000开始写入 这节规定了从0x08000000地址到0x08004000这段flash留给BootLoader使用(16KB) 这个具体留多少要看BootLoader程序bin文件大小,后面有具体说明. 2.1 设置中断偏移地址,程序文件从哪开始执行,就设置偏移多少. SCB->VTOR = FLASH_BASE | 0x4000; 为什么需要设置中断偏移? 记住一句话:BootLoader里面配置的程序,即使执行了用户程序同样有效! 记住另一句话:所有的中断函数都有固定的地址入口! 假设BootLoader使用了某个中断,用户程序也使用了某个中断,如果不设置这个偏移, 那么用户程序和BootLoader就使用了同一个中断函数! <ignore_js_op>
2.2 设置该程序文件运行的地址,及其大小 0x8004000 0xB800 解释: 想让这个程序运行在某个地址上,必须在软件上设置一下,然后生成的文件才可以运行在这个地址上. 0x8004000 :这节的程序就希望程序运行在这个地址上 0xB800 : 我使用的是64KB的flash,从0x08000000 到 0x08004000 (16KB) 用于存储BootLoader程序 从 0x0800f800 到 0x0801000 (最后2KB) 用于存储其它数据 用户程序剩余 = 64KB - 16KB - 2KB = 46KB = 46*1024字节 = 47104字节 = 0xB800(16进制表示) <ignore_js_op>
2.3让软件生成bin文件 <ignore_js_op>
我写的是
E:MDK5ARMARMCCbinfromelf.exe --bin -o .Progectuser.bin .outputProgect.axf
E:MDK5ARMARMCCbinfromelf.exe --bin -o 这是命令,意思是让软件生成bin文件
前面这个路径和自己安装软件的路径有关,下面是我的路径
根据自己软件安装的路径修改,否则编译报错!
根据自己软件安装的路径修改,否则编译报错!
根据自己软件安装的路径修改,否则编译报错!
<ignore_js_op>
.Progectuser.bin 就是生成的bin文件名字是 user.bin ,路径是工程目录的 Progect文件夹里面(如果没有则会自动建个文件夹) 所谓工程目录
<ignore_js_op>
.outputProgect.axf 当前工程目录的output文件夹里面的Progect.axf文件 工程都会生成xxxx.axf文件,我的这个文件就生成在上面的目录里面
整理下就是
E:MDK5ARMARMCCbinfromelf.exe --bin -o .Progectuser.bin .outputProgect.axf
用fromelf.exe里面的--bin -o指令,把Progect.axf文件里面的bin数据提取出来以后生成一个新的文件 user.bin
点击编译便生成了 user.bin文件 <ignore_js_op>
如何分配flash 1.首先需要明确,BootLoader程序是烧写到单片机里面永远不变的! 写完BootLoader程序以后,生成bin文件,看一下bin文件大小 <ignore_js_op>
<ignore_js_op>
BootLoader是10KB,那么可以规定12KB(高容量单片机是2KB作为一页,用偶数可兼容全系列) 2.接下来是确定下存储其它用户数据所用flash大小 这个需要根据自己的项目自行规定大小,一般都是把数据放到最后的地址存储 3.以上的确定下来之后,剩余中间部分就是作为用户程序的了 为了便于修改,我做了以下程序 <ignore_js_op>
在BootLoader程序开始运行,会打印下当前的配置
<ignore_js_op>
<ignore_js_op>
程序具体怎么写入的flash并运行 1.一开始得到了程序写入的地址 <ignore_js_op>
2.接收到 updata start ;擦除用户程序区; UpdataStartFlage = 1; <ignore_js_op>
3.串口助手发送程序数据时,把程序数据写入了环形队列 关于环形队列系列文章: https://mnifdv.cn/forum.php?mod=forumdisplay&fid=53 <ignore_js_op>
4.主函数取出数据拼接成16位数据以后写入flash 从0x08004000开始写入,地址每次累加2 注:STM32写入flash每次需要写16位数据 <ignore_js_op>
5.接收完数据,打印下相应的错误,重启. <ignore_js_op>
6.重启以后,判断了用户地址里面有用户程序,加载用户程序 <ignore_js_op>
细节说明 1.环形队列大小5字节 就是说,只使用了5字节就接收处理了全部的程序文件! <ignore_js_op>
<ignore_js_op>
2.关于 if(((*(vu32*)(UpdateAddr 4))&0xFF000000)==0x08000000 && ((*(vu32*)UpdateAddr)&0x2FFE0000)==0x20000000)这句话是判断程序文件 实际上就是判断的程序文件的这两个位置
<ignore_js_op>
前面的四位flash地址记录的值 00 0E 00 20 是记录的整个程序占用RAM空间的最高地址(STM32默认的) STM32的RAM是从 20000000 开始 注意:STM32是小端模式,低位存在低位,高位存在高位 上面的 20 00 0E 00 就是说总共使用了 0E 00 (3584字节的RAM) 0x20000E00 ((*(vu32*)UpdateAddr)&0x2FFE0000)==0x20000000 上面的判断其实就是判断了下是不是 20 00 后面的 08 00 6C 35 实际上这个存储的是复位中断入口的地址.(STM32默认的后面四位存储的是复位中断入口的地址) 当然再往后08 00 41 45 是不可屏蔽中断函数的地址 再往后 08 00 41 47 是硬件错误中断函数的地址 咱所有的程序都是存储在flash里面的,复位中断函数也不例外 其实是下面这个样子 <ignore_js_op>
BootLoader和用户程序的flash里面具体存储的都是按照上面的图示. 程序是存储在flash里面的,flash的地址肯定是从 0x08000000 - XXXXXX,所以才会有了 ((*(vu32*)(UpdateAddr 4))&0xFF000000)==0x08000000 主要判断的是不是 最高位是不是08 3.一定要记住一件事情: 凡是BootLoader里面用到的中断回调函数,用户程序里面全部重新写一遍.可以不用,但是必须重写! 我上面说过一句话: BootLoader里面配置的程序,即使执行了用户程序同样有效! 仔细考虑这句话! 假设在BootLoader里面使用了中断定时器,用户程序里面没有使用,跳转到用户程序以后定时器还在运行! 但是由于所有的变量全部重新分配,导致凡是定时器中断里面的变量都没有了!从而导致死机! BootLoader程序使用了滴答定时器中断 <ignore_js_op>
用户程序里面没有使用滴答定时器中断,但是重写了其回调函数! <ignore_js_op>
结语 这节主要目的是让用户彻底了解单片机是如何更新的程序
这节的程序只能作为学习参考不能作为项目应用!
后面的章节将为用户提供稳定可靠的升级方案!