单片机学习小组
直播中

刘军

7年用户 1364经验值
私信 关注

如何为STM32写bootloader实现IAP升级呢

BootLoader是什么?
ARM内核芯片下载程序方式可分为几类?
如何为STM32写bootloader实现IAP升级呢?

回帖(1)

李广旭

2022-2-18 10:18:55
BootLoader是在操作系统内核运行之前运行的,主要功能是初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。BIOS是windows的bootloader,u-boot等为常见的Linux开发版的bootloader。ARM内核芯片下载程序方式可分为三类;
1.下载器(J-Link,ST-Link等)直接下载
2.ISP(in-system programming)下载,以STM32为例,可在不使用下载器而通过如UART、I2C、SPI、USB等通讯接口配合芯片启动代码(.s文件)进行升级
3.IAP(In Application Programming)下载,即编写应用软件实现程序升级,使用bootloader即属于此方法
在产品中,IAP升级的方式较其余两种的优势在于不需要接触芯片,不需要进行硬件配置更改即可实现程序远程升级,有益之处显而易见。物联网技术中的OTA(Over the Air Technology)升级也是基于IAP原理,在bootloader中实现网络通讯与云端配合进行程序升级。
言归正传,为STM32写bootloader实现IAP升级。
首先建立概念,bootloader与应用程序之间是两个独立的程序,在自身运行时互不干扰对方。两者共用的资源是单片机flash。升级Demo以stm32f103c8做实验芯片,编程使用keil MDK,采用HAL库使用cubeMX生成代码基础配置。
第一步:编程实现程序跳转大概需要三个步骤:
1.关闭外设中断  2.重设中断向量表  3.设置起始堆栈地址,将sp指针指向app程序main函数地址
将三个功能放在fun_jump.c函数中,




#include "fun_jump.h"
#include "main.h"

//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
    MSR MSP, r0                         //set Main Stack value
    BX r14
}

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void fun_jump(u32 appxaddr)
{
        if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)        //检查栈顶地址是否合法.
        {
                jump2app=(void(*)())*(vu32*)(appxaddr+4);                //用户代码区第二个字为程序开始地址(复位地址)               
                MSR_MSP(*(vu32*)appxaddr);                                        //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)

                for(int i = 0; i < 8; i++)
                {                       
                        NVIC->ICER = 0xFFFFFFFF;        /* 关闭中断*/
                        NVIC->ICPR = 0xFFFFFFFF;        /* 清除中断标志位 */
                }
                jump2app();                                                                        //跳转到APP.
        }
}
fun_jump.h文件如下,


#ifndef _FUN_JUMP_
#define _FUN_JUMP_

/*-----------------------------------------------
作者:HES
时间:18-5-8
功能:函数跳转。BootLoader使用
------------------------------------------------*/

#define JUMP_ADDR 0x8002000

typedef unsigned int u32;
typedef volatile unsigned int vu32;

//指向指针的函数
static void (*jump2app)();

//跳转到应用程序段
//appxaddr:用户代码起始地址.
void fun_jump(unsigned int appxaddr);

#endif


在符合跳转条件时,调用调转函数fun_jump()即可实现跳转到JUMP_ADDR的地址执行app程序,JUMP_ADDR是app程序在flash中的起始地址。
第二步:设置keil软件
bootloader设置keil option如下,IROM1设置bootloader起始地址及大小,0x2000规定boot大小在8k以内

app程序设置keil option ,设置起始地址为0x8002000,大小可设置为flash size - boot size,此处设置了40k的大小限制。


几个注意点:
1.下载bootloader时,建议将下载器设置为全片擦除flash
2.以上两步操作完成,将下载器设置为部分擦除、下载app程序,bootloader使用延时方式实现直接跳转,可以检测跳转是否能实现
3.app程序中断不能正确触发,需要检查重设中断向量表部分,也可以在app程序初始化时进行重设向量表
SCB->VTOR = FLASH_BASE | 0x2000;   //重设中断向量表 第三步:编写bootloader与电脑端搭配使用的上位机
实际使用时将升级功能集成在了产品上位机中,在进行升级测试时自己用python写了个简陋的上位机,实现电脑与产品之间的串口通讯,实现程序远程升级。
bootloader部分主要要做的工作:
1.跳转判断及跳转功能实现   2.与上位机之间的通讯及协议配合 3.数据传输校验及传输过程中的错误处理,断点续传  4.flash操作,擦除及写入  5.bootloader与程序之间的相互验证
2.与上位机之间的通讯可以采用串口、u***等方式。涉及的几个关键点有.bin文件生成、.bin文件读取及拆分与最后的传输过程。本demo采用自定义的协议,将1k数据拆分为16次进行传输,每次传输包的大小为64bytes. 传输过程进行包头检测、数据长度检测等,确保在.bin文件中的数据与包头刚好相同时,升级可以继续。
3.数据校验是不可缺少且很关键的一步,本demo使用的校验方法有a.每个传输包进行求和取余校验 b.传输完1k字节之后取1k字节数据的头尾进行对比校验 c.程序传输完成后上位机与bootloader之间进行.bin文件大小校验。错误处理即使boot可以将传输状态及时反馈给上位机,上位机对错误状态进行识别后作处理。终止升级or重新对本段数据进行发送。
4.限于flash擦除、写入只支持1k bytes数据整体操作,所以在传输过程中需要将数据积攒到1k之后再进行写入。
第5点,如果是自己测试玩或跳转条件是bootloader上电后延时等待更新信号(类似linux与windows),此步可忽略。但采用读取固定flash位检测标志位的方式决定是否需要更新,则需要慎重考虑。远程程序升级存在的问题之一是如果刷入了错误的不能运行的程序,上电之后程序卡死无法正确执行app或bootloader,那么只能进行拆机重新焼写boot。实现思路大概是bootloader与产品程序之前实现双向验证,app在初始化之前对boot的数据区(flash固定区域)进行识别、校验。校验通过后更改boot数据区的标志,作为app正确运行的标志。下一次上电是boot对标志进行检测,若标志没有被改动,则说明app运行失败或焼写如的错误的app,此时将始终保持boot状态不跳转,等待下次升级。双向验证中,有一方验证失败,则保持在bootloader状态,等待程序升级指令。
第5点涉及公司产品,本文及代码中已删除。
本demo上传至CSDN,实现的效果是LED在bootlaoder下慢速闪烁,在升级成功、跳转成功后闪烁频率变高。
配合上位机的升级流程:
1.下载bootlaoder到单片机,使用u***转ttl连接电脑与单片机串口 USART2 ->PA2 TX  PA3 RX
2.修改upgrade.py文件中的打开bin文件的路径及bin文件名  如下

3.导入upgrade库,并打开串口,调用upgrade()函数

结果如下:

上述文件在附件中可下载.
举报

更多回帖

×
20
完善资料,
赚取积分