关于STM32 IAP
源码下载链接:
https://gitee.com/yang456/STM32_IAP_Learn.git
下载bootloader程序 我用stlink下载哈,方便
选择程序文件
首先需要发送一条指令
updata start
然后发送程序文件
说明接收并运行了咱写的APP程序.
IAP详细说明:
所谓单片机的升级实质上就是把手动用软件下载程序,改为咱人为通过某种方式写到Flash
打开下面的程序
如果编译出错,可能是这里
我这里是让软件帮忙生成bin文件
我写的是
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Progect\Progect.bin .\output\Progect.axf
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o 这是命令,意思是让软件生成bin文件
前面这个路径和自己安装软件的路径有关,下面是我的路径
.\Progect\Progect.bin 就是生成的bin文件名字是 Progect.bin ,路径是工程目录的 Progect文件夹里面(如果没有则会自动建个文件夹)
所谓工程目录
.\output\Progect.axf 当前工程目录的output文件夹里面的Progect.axf文件
.axf这个文件也是由软件生成的,这个文件里面有咱要的bin文件数据,当然还有一些调试信息
我设置的是生成的文件名字是Progect.axf
我自己设置的 让文件生成到output文件夹
其实整理下就是
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o .\Progect\Progect.bin .\output\Progect.axf
用fromelf.exe里面的--bin -o指令,把Progect.axf文件里面的bin数据提取出来以后生成一个新的文件 Progect.bin
现在看bin文件 和 hex文件的区别
看没看到hex比bin文件多了前面一部分,和后面一部分
大家下载单片机程序应该都知道是下载hex文件
但是大家了解整个的下载过程不
其实咱用软件下载的时候首先单片机需要知道下载的这段程序下载到哪个地址上(把程序数据写到哪个Flash地址)
所以hex文件的前面部分就是地址信息,就是告诉芯片我后面的代码段存储到哪个地址上
当然为了保险起见,数据需要加校验,其实hex文件的最后一位就是校验位
像51单片机,STM32的串口下载,下载的时候需要断电上电,或者复位一下,其实咱的单片机里面有一段程序(接收单片机程序,写入Flash) 就是咱所说的bootloader
记不记得都是先点一下下载软件的下载按钮,然后再复位单片机
其实点一下下载软件,下载软件就一直串口输出下载命令呢!单片机一启动是先执行里面内嵌的bootloader,bootloader一检测到下载命令,就开始执行下载操作了,接收下载软件过来的数据,然后写到Flash里面.....写完了,有的单片机重启下才运行,有的就直接运行了
好,现在说bin文件为啥去掉了前面的地址信息
记住,咱自己更新的时候咱就规定好了程序的运行位置
注:大家应该知道0x08000000 和 0x8000000 是相等的
STM32默认一开始是从0x8000000开始运行的,程序默认也写到这里(注意这个是不可改变的)
但是呢咱可以手写个代码把一个完整的程序文件写到一个位置(假设写到 0x8004000开始)
然后呢!调用STM32给的跳转函数,填上跳转地址(0x8004000),程序就跳转到新地址运行了
我这几代码呢!是这样做的,第一部分代码(简称:bootloader),什么也不配置,就是默认运行在 0x8000000地址
bootloader 代码主要做的就是串口接收程序文件(简称APP,注意是bin文件哈),然后把程序bin文件写到指定的Flash地址,写完之后,跳转到这个地址执行
不过呢!APP代码除了上面说的是下载bin文件以外,还需要配置
我先说明 APP程序写到哪个地址其实和bootloader程序有关
注意没
咱的bootloader 程序需要占用10KB的空间,也就是说
10KB = 10*1024 = 10240字节 = 0x2800
那么咱的APP程序一开始写入的地址必须大于 0x8002800
如果写入的地址占用了bootloader的地址,那么程序就崩了
我的bootloader 代码主要做的就是串口接收程序文件(简称APP,注意是bin文件哈),然后把程序bin文件写到指定的Flash地址,写完之后,跳转到这个地址执行,你可不能让这个代码崩....程序一开始就是执行bootloader,如果这个程序有问题了,整个就崩了...
现在看用户程序的其它配置
假设我想让APP个程序运行在0x8004000地址,那么需要让软件生成可以运行在这个地址的bin文件才可以,所以
默认是0x8000000
还需要修改下SIZE,上面写的是0x10000 也就是65536字节 = 64KB 我用的单片机是C8T6哈,Flash就是64KB的
咱改为 0x8004000
0x4000 就是 16384字节 其实整个FLASH还剩下 65536 - 16384 = 49152 字节 也就是 0xC000
好,现在生成的程序就是可以运行在 0x8004000的程序了
但是呢还有个问题!
中断问题!
咱可以在bootloader MAP文件里面看到
往下还有各种中断
如果咱不在APP程序里面做一下设置,那么APP程序里面的中断其实会跳进 bootloader 里面(假设bootloader 也使用了一样的中断)
这个我的视频会给大家演示
咱需要让APP里面生成自己单独的一套中断
所以呢咱需要设置下中断向量偏移,一般都是咱的APP程序相对于Flash地址偏移了多少,咱就设置中断向量偏移多少
SCB->VTOR = FLASH_BASE | 0x4000;
这样的话,所有的APP里面分配的中断函数地址最起码是在0x8004000地址的基础上的
其实现在APP程序就准备好了,
不过大家百度STM32 IAP,有很多人在bootloader里面这样写
if(((*(vu32*)(UpdateAddrCopy+4))&0xFF000000)==0x08000000 && ((*(vu32*)UpdateAddrCopy)&0x2FFE0000)==0x20000000)
这就是对接收bin数据的(看的bootloader的bin数据)
这两个位置进行判断
前面的四位是说明咱的整个程序占用的RAM空间最高地址 注意:STM32是小端模式,低位存在低位,高位存在高位
所以上面的是 0x20000978
大家应该知道咱的全局变量和局部变量都是存在RAM里面的
咱的STM32 RAM的首地址是0x20000000 我的后面是0x5000 = 20480字节 = 20KB (20480/1024)
咱可以看一下那个MAP
其实咱知道咱用的RAM空间是 0x20005000
其实在细致一点就是判断高16位是不是 0x2000 然后低16位是不是小于0x5000
而程序中只是 ((*(vu32*)UpdateAddrCopy)&0x2FFE0000)==0x20000000
然后还有个 ((*(vu32*)(UpdateAddrCopy+4))&0xFF000000)==0x08000000
大家看MAP文件
bootloader 的MAP文件为例
0x08001F35
实际上这个Flash地址里面存储的是复位中断入口地址,得到这个地址以后执行复位中断服务程序
复位中断入口地址是0x08001F35
这个数据存在了 Flash的 0x08000004 (1F35) 和 0x08000006 (0800) 地址里面
注意:所有的中断入口地址都存在了Flash里面,咱定义的函数的入口地址也是在Flash里面
咱编译的时候,软件就给中断函数和咱写的普通函数分配空间 (从0x08000XXX)开始分,注意不是从0x08000000开始哈,具体前面的空间具体给谁我也不知道,但是呢!看上面就知道第一个32位(0x08000000 - 0x08000003),存储的是堆栈栈顶地址(整个程序占用的RAM空间最高地址)
第二个32位(0x08000004 - 0x08000007) 存储的是复位中断入口地址
可以看下MAP,这是分配的地址
分完以后函数的首地址就有了
然后再把这些地址存到Flash里面,从0x08000000开始存
不过Cortex-M3内核规定,Flash起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址
其它的我就不知道按照什么规则存了
当然后面的
不可屏蔽中断,就是说系统出现了不可挽回的错误的时候会进去这个中断
还有
硬件错误中断,像数组越界,内存溢出等等都会进去
然后,发现后面基本上都是各种中断函数...如果大家想判断的更准确的话,就多判断几个08
其实各种中断函数的地址都存在了Flash里面,由于一开始是初始化各种中断,所以才出现这么多08
接收完程序就可以跳转了
其实呢跳转是固定形式,上面有注释
所有涉及到的主要的IAP知识点就说完了
现在说下我的源码思路
我为了方便我修改用户程序软件上的配置
我设置的bootloader使用了16K,实际上我的bootloader只用了10K
其实只要内存充足,自己愿意定义多少就定义多少,前提看APP程序占用多少空间,只要预留的够APP使用就可以
总共 64K 去掉 16K 还有 48K...足够用
然后我再主函数里面打印了一下
其实就对应用户程序
这样方便一点
bootloader函数接收到 updata start以后先擦除APP程序所用的Flash地址空间
然后咱发bin数据的时候
环形队列: https://www.cnblogs.com/yangfengwu/p/6822984.html
环形队列实际上就是用一些函数操作数组,可以一边往里面存数据,一边取数据
我就是用5字节大小的数组,来接收的APP程序(21K),然后写入Flash
由于读环形队列然后写入其实挺快的,有时候串口接收数据的速度赶不上,所以
其实就是类似于空闲中断,10ms的时间内串口都没有接收到数据,就认为接收到一条完整的数据
其实用户程序也就是上面说的那些配置
大家有不明白的再问吧.