说明
这节提供给用户一份实现更新STM32的程序(兼容STM32f103全系列)
主要说明STM32是如何实现的升级程序.后面的章节都是在这节的基础上进行优化.
测试
1.说明
BootLoader作为引导程序,负责把接收的程序文件写入flash,然后加载执行.
STM32F10xTemplate 是用户程序,这套程序采用串口升级进去.然后执行
2.下载BootLoader程序到单片机(自行下载)

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文件的区别
咱们是直接把程序文件写入了相应的地址里面.
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>
<ignore_js_op>
.Progectuser.bin 就是生成的bin文件名字是 user.bin ,路径是工程目录的 Progect文件夹里面(如果没有则会自动建个文件夹)
所谓工程目录
<ignore_js_op>
.outputProgect.axf 当前工程目录的output文件夹里面的Progect.axf文件
工程都会生成xxxx.axf文件,我的这个文件就生成在上面的目录里面
点击编译便生成了 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>
结语
这节主要目的是让用户彻底了解单片机是如何更新的程序