声明:
主要参考正点原子的IAP章节。
文字里面例程实际是在STM32F407ZGT6开发板上运行测试验证的,实际在STM32F103RCT6也做过升级的项目,文章说到的关键点:中断向量重定位方式,BOOT里面的设置,APP里面的设置都是一样的和芯片型号目前看是没有区别的。
1.固件升级方案综述
单片机的固件升级方式有很多种,
1、ICP:In Circuit Programing,简单说就是在单片机开发时使用烧录器升级程序,比如使用J-Link烧录单片机程序。
2、ISP:In System Programing,在单片机内部实现了基于通信接口(如串口、I2C、SPI等等)的FLASH引导程序,配合厂家提供的烧录软件工具或自行开发的软件实现程序烧录。
3、IAP:In applicating Programing,是指单片机程序开发好之后在运行过程中由外部用户发起的在线升级,这种升级方式一般由用户自行设计升级方案,方案灵活性和自由度较高,在智能家居、汽车电子、物联网设备中常用的OTA(Over The Air)即空中下载技术原理也与之类似。
2.固件升级流程
对于bootloader中升级程序的代码实现思路,可以通过串口与上位机通讯,在串口接收中断中将上位机发来的数据存放到队列中,然后根据升级流程从队列中读取相关内容。
整个升级流程大致分为:
1、握手(联机)
2、擦除app的FLASH空间内容
3、对之前擦除的FLASH写入新的升级程序数据,该步骤又可以细分为:
①接收每次写入flash的起始地址
②接收每次写入flash的数据长度
③接收每次写入的具体数据并进行数据校验
④调用接口将升级数据写入flash
4、升级成功,调用函数接口NVIC_SystemReset()重启系统由bootloader进入app程序。
以上的各步骤都可以和上位机之间添加应答机制。进一步保证通讯的可靠性。
3.固件升级关键点1-BOOT怎么跳转到APP
理论分析:
STM32F407 的内部闪存(FLASH)地址起始于 0x0800 0000,一般情况下,程序文件就从此地址开始写入。此外 STM32F407 是基于 Cortex-M4 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x0800 0004,当中断来临,STM32F407 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
在上图 中,STM32F407 在复位后,先从 0x08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生了中断),此时 STM32F407 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。
当加入 IAP 程序之后,程序运行流程如下图所示:
在上图所示流程中,STM32F407 复位后,还是从 0x08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示,此部分同无IAP流程 一样;在执行完 IAP 以后(即将新的 APP 代码写入STM32F407 的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0x08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,并且注意到此时 STM32F407 的 FLASH,在不同位置上,共有两个中断向量表。
在 main 函数执行过程中,如果 CPU 得到一个中断请求,PC 指针仍然会强制跳转到地址0x08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。
通过以上两个过程的分析,我们知道 IAP 程序必须满足两个要求:
1) 新程序(APP)必须在 IAP 程序(BOOT)之后的某个偏移量为 x 的地址开始;
2) 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;
实现:
1. APP 程序起始地址设置方法
默认的条件下,图中 IROM1 的起始地址(Start)一般为 0x08000000,大小(Size)为 0x100000,即从 0x08000000 开始的 1024K 空间为我们的程序存储区。
上图中,我们设置起始地址(Start)为 0x08010000,即偏移量为 0x10000(64K 字节,即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)为 0x80000-0x10000=0x70000(448KB)。设置好 Start 和 Size,就完成 APP 程序的起始地址设置。IRAM 是内存的地址,APP 可以独占这些内存,我们不需要修改。
注意:需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0x200 的倍数即可(相关知识,请参考:http://www.openedv.com/posts/list/392.htm)。
设置这个地址的作用主要体现在,1. 通过keil点击Download 烧录编译后的BIN或者hex文件到mcu的flash的地址就是0x0801 0000.
若是不通过keil烧录的方式,可以通过在boot里面通过can usart等接口接收bin文件,再调用写flash的api,把bin文件,写到flash起始地址为0x0801 0000的位置。
2. 中断向量表的偏移量设置方法
VTOR 寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定,对于 STM32F407 来说就是指向 0x0800 0000 这个位置,也就是从默认的启动位置加载中断向量等信息,不过 ST 允许重定向这个位置,这样就可以从 Flash 区域的任意位置启动我们的代码了。
我们可以通过调用 sys_nvic_set_vector_table 函数实现,该函数定义如下:
/**
* @brief 设置中断向量表偏移地址
* @param baseaddr
: 基址
* @param offset
: 偏移量
* @retval 无
*/
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
{
/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */
SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);
}
该函数用于设置中断向量偏移,baseaddr 为基地址(整个固件的起始地址,包括BOOT),Offset 为偏移量,需要根据自己的实际情况进行设置。比如 FLASH APP 设置中断向量表偏移量为 0x10000,调用情况如下:
/* 设置中断向量表偏移量为 0x10000 */
sys_nvic_set_vector_table(FLASH_BASE, 0x10000);
通过以上两个步骤的设置,我们就可以生成 APP 程序了,只要 APP 程序的 FLASH 大小不超过我们的设置即可。不过 MDK 默认生成的文件是.hex 文件,并不方便我们用作 IAP更新,我们希望生成的文件是.bin 文件,这样可以方便进行 IAP 升级。这里我们通过 MDK 自带的格式转换工具 fromelf.exe,如果安装在 C 盘的默认路径,它的位置是 D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe,来实现.axf 文件到.bin 文件的转换。该工具在 MDK 的安装目录\ARM\ARMCC\bin 文件夹里面。
fromelf.exe 转换工具的语法格式为:fromelf [options] input_file。
本实验,我们可以通过在 MDK 点击 Options for Target→User 选项卡,在 After Build/Rebuild一栏中,勾选 Run #1,我们推荐使用相对地址,在勾选的同一行后的输入框并写入命令行:
fromelf --bin -o …\Output@L.bin …\Output%L (通用格式)
fromelf --bin --output .\Objects\OLP_X1_IRM.bin .\Objects\OLP_X1_IRM.axf (实际使用)
通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe,…\Output%L 表示当前编译的链接文件(…\是相对路径,表示上级目录,编译器默认从工程文件*.uvprojx 开始查找,根据我的工程文件 Output 的位置就能明白路径的含义),指令–bin –o …\Output@L.bin表示在 Output 目录下生成一个.bin 文件,@L 在 Keil 的下表示 Output 选项卡下的 Name of Executable 后面的字符串,即在 Output 文件夹下生成一个 atk_f407.bin 文件。在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。
最后来看看 APP 程序的生成步骤:
1) 设置 APP 程序的起始地址和存储空间大小
对于在 FLASH 里面运行的 APP 程序,我们只需要设置 APP 程序的起始地址,和存储空间大小即可。
2) 设置中断向量表偏移量
通过调用 sys_nvic_set_vector_table 函数,实现对中断向量表偏移量的设置。
3) 设置编译后运行 fromelf.exe,生成.bin 文件
通过在 User 选项卡,设置编译后调用 fromelf.exe,根据.axf 文件生成.bin 文件,用于 IAP更新。
以上 3 个步骤,就可以得到一个.bin 的 APP 程序,通过 Bootlader 程序即可实现更新。
4.实战例程节选
BOOT里面关键点:
#define FLASH_APP_BASE (0x800A000)
typedef void (*pFunction)(void);
pFunction jump2app;
void iap_load_app(uint32_t appxaddr)
{
if (((*(volatile uint32_t *)appxaddr) & 0x2FFE0000) == 0x20000000)
{/* 检查栈顶地址是否合法.可以放在内部 SRAM 共 64KB(0x20000000) */
/* 最好加着,虽然不加,有些情况不会导致跳转失败,若是大量测试出现少量的升级失败也是影响心情,还查不出原因*/
__disable_irq();
/* 用户代码区第二个字为程序开始地址(复位地址) */
jump2app = (pFunction) * (volatile uint32_t *)(appxaddr + 4);
/* 初始化 APP 堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
__set_MSP(*(volatile uint32_t *)appxaddr);
/* 跳转到 APP */
jump2app();
}
}
APP里面关键点:
main开始就把中断向量重定位。(其实在mian前,汇编里面已经做了中断向量重定位,不过没时间去测试这点,有时间再验证)
在main初始化最后再把中断打开,和boot里面形成配对,有关就有开。
源码链接:
git clone git@gitee.com:xiaoliangliangcong/stm32.git
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yeweiliang_93/article/details/144197061
|