什么是OTA?
简单来说,就是无需u盘或者各种外接设备,只需要能够联网,就能够进行在线升级的模式就是OTA。
OTA有什么好处?
- 便捷性:用户无需将设备送到专业维修点,可以随时随地通过无线网络进行软件升级。
- 时效性:厂商可以快速推送更新,及时修复软件漏洞,提高系统的稳定性和安全性。
- 成本节约:减少了物料和物流成本,对于厂商和用户来说,都节省了因物理更换带来的经济负担。
- 用户体验:通过不断更新,可以持续优化用户界面和增加新功能,提升用户体验。
- 问题修复:能够及时修复系统中出现的问题,减少系统崩溃的风险。
- 数据收集:厂商可以通过OTA更新来收集设备信息,帮助改善产品。
- 环保:减少了电子垃圾的产生,有利于环保
OTA原理和逻辑?
第一步.
上位机发送升级包,单片机进行版本和数据包的校验,以及本次文件的大小。
第二步.
分为两种情况:
第一种:版本和数据包校验失败,版本相同无需进行更新。通知上位机无需更新
第二种:版本和数据包校验成功,通过Xmodem协议进行数据传输。传输的数据通过SPI协议和分区管理,下载的W25Q128分区中。
第三步.
下载完成后通过软件复位单片机,进入到Bootloader
第四步.
Bootloader检测到有更新后,会将W25Q128中的程序下载到单片机中。下载完成后会自动跳转去执行程序。
关于版本回退.
因为在W25Q128中存在两个分区,其中一个分区的程序是旧版,可以通过Bootloader和分区管理进行版本回退。
演示效果
开机进入Bootloader程序
打印出版本信息 : Firmware version:version:v1.6.0size:14176w25q:Bup:N
可以看到最后一串字符 up:N 代表当前没有程序更新
需要更新的时候发送以下字符:version:v1.6.0size:14176
解析:
版本号 version:xxxxx 。固定格式6个字节
数据包大小:size:xxxxxx。固定格式:size:你的程序大小
返回数据:An update was detected;//检测一个版本更新
Look for the partition address //正在寻找分区地址
OK//代表可以发送文件了。
因为目前我没有使用文件传输协议,所以发送数据的时候要有间隔。后面会加上,传输协议采用Xmodem,关于Xmodem的代码在Xmodem.c中已经写好了,直接使用即可。
发送文件后会输出当前数据下载进度。
数据解析:
downloading = 93.91 %//当前数据下载进度
b:version:v1.6.0version:size:14176w25q:Aup:Y //分区信息
Download //下载完成
Restarting the device.... //正在重启设备
重启设备后会进入Bootloader程序中
可以看到:up:Y//代表有程序要更新.
Bootloader : A program update was detected //一个程序要更新
Updating = 100.00 % //更新进度
更新完成后会自动跳转地址
jump 80081a1 success //跳转到APP程序
代码部分
代码分为两部分
Bootloader代码和APP代码
OTA_APP为APP中运行的程序
OTA_Bootloader为Bootloader程序
SecureCRT为文件传输软件
ota.vsdx为OTA思维导图
Bootloader部分代码
关于地址跳转部分的代码,通过设置新的堆栈地址,和新的复位函数,即可以实现跳转
/******************************************************************************
函数名称:Bootloader_Jump_APP
函数描述:Bootloader跳转APP程序
开发时间:2024.6.150
版本编号:V0.01
使用状态:正在使用
作者 :深情D先森
@param:
APP起始地址
1.uint32_t address
@retval:
1.void;
******************************************************************************/
void Bootloader_Jump_APP(uint32_t address)
{
JUMP_A Jump_APP1;
uint32_t Jump_address;
if((*(uint32_t *)address>=0x20000000)&&(*(uint32_t *)address<=0x2002FFFF))//判断是否在RAM的大小之内
{
__set_PRIMASK(1);//关闭NMI(不可屏蔽中断)和硬fault之外的所有中断
Jump_address = *(volatile uint32_t*)(address+4);//将复位向量地址转换为函数指针
Jump_APP1 = (JUMP_A)Jump_address;
printf("jump %lx success \r\n",Jump_address);
Periph_Deinit();
__set_MSP(*(volatile uint32_t*)address); //设置主堆栈
// msr_sp(*(uint32_t*)address);//编译器不同采用不同的指令
Jump_APP1();
while(1)
{
printf("jump %lx error \r\n",Jump_address);
}
}
while(1)
{
printf("jump %lx error \r\n",Jump_address);
}
}
为什么要这么做?
串口输出的跳转地址为0X080081a1
可以看到我们复位函数的地址也是0X080081a1
在STM32启动文件中有这样一段代码
__initial_sp ; Top of Stack//栈顶
加4个字节的偏移就是复位向量所在的地址
APP部分代码
暂时不放了,有需要可以私我.