add ota_quick_start document

This commit is contained in:
mculover666
2020-06-28 17:58:55 +08:00
parent 1e81a1a07c
commit f9658804e6
25 changed files with 361 additions and 0 deletions

View File

@@ -0,0 +1,361 @@
# 1. TencentOS-tiny OTA
## 1.1. OTA与IAP
IAP全称 In Application Programming什么是在应用编程呢
正常开发流程如下:
- 使用Keil MDK工具编写应用程序
- 编译出.axf可执行程序或者.bin文件或者.hex文件
- 使用Keil MDK工具直接烧录.axf文件到STM32 Flash中或者使用类似STM32CubeProg之类的软件烧录.bin文件到STM32 Flash中
- 复位单片机从Flash起始地址处读取程序并执行
当产品开发完成用于生产后通常不会再引出下载器接口给用户所以也就无法连接各种各样的下载器更别说用Keil或者STM32CubeProg这些工具下载程序如果发现应用程序有漏洞或者要修改部分功能都已经无法完成。
### 1.1.1. IAP设计思想
IAP巧妙的解决了这个问题即在应用程序正常工作的时候还可以接收新的固件并烧录到Flash中。
- 应用程序如何接收新的固件?
一般产品设计时都会留一个用于打印日志信息的串口可以直接利用此串口来接收PC发送到新固件传输协议大多都采用Y-Modem协议。
- 接收之后的新固件存放到哪里?
固件大小一般都有几十KB或者上百KB应用程序接收到新的固件之后如果是以数组的形式暂存在RAM空间中不仅会浪费RAM空间而且容易发生固件数据丢失所以直接存储到空闲的Flash空间中是最好的方式。
eg. 官方开发板使用的芯片是STM32L431RCT6Flash空间有256KB如果应用程序的大小只有100KB那么接收进来的新固件就可以存放在剩余的156KB空间中。
同时会人为的对Flash划分分区也就是对某一段地址空间起个名字如下图
![iap-partition-no-bootloader](image/OTA/iap-partition-no-bootloader.jpg)
- 如何将老固件替换为为新固件?
上电之后CPU会从Flash的起始地址处读取数据开始执行这点无法改变正在运行的应用程序如果进行自己擦除自己的操作设备直接变砖。
bootloader可以完美的解决这一问题即写一小段应用程序放在Flash的起始位置处使得每次上电之后最先运行bootloader完成两个功能
- 检查升级标志是否有新的固件被下载到IAP分区中
- 如果无升级标志则跳转到ActiveAPP分区开始执行应用程序
- 如果有升级标志则先将IAP分区中的固件拷贝到ActiveAPP分区中然后跳转到ActiveAPP分区开始执行新的应用程序
加入bootloader之后整个Flash的分区情况如下
![iap-partition](image/OTA/iap-partition.jpg)
### 1.1.2. OTA设计思想
在IAP的这种设计思想之上因为当前的物联网设备都已经具备基本的网络通信功能所以无需通过串口传输新的固件直接通过网络传输新的固件即可称之为OTA IAP(Over The Air In Application Programming)也就是平常所说的空中升级简称OTA。
## 1.2. OTA组件概述、特性及优势
TencentOS-tiny中提供的OTA组件完全开源免费相较于其他的OTA功能TencentOS-tiny OTA组件提供更小的差分升级包耗用更少的传输流量占用更小的内存空间提供更可靠的升级服务。
TencentOS-tiny OTA组件有以下的特性
- 传输差分升级包,而不是整个固件
- 灵活的分区表功能
- 支持原地升级
- 支持乒乓升级,可以在升级中断的情况下进行版本回滚
- 支持通过HTTP从自建服务器拉取固件
- 支持从腾讯云平台进行统一的固件拉取和管理
## 1.3. OTA组件开发调试推荐流程
OTA的整个流程调试较复杂如果分步来做会比较容易
(1)调试bootloader是否可以正常读取分区表信息
(2)调试bootloader是否可以正常跳转到Active APP;
(3)调试bootloader是否直接可以根据patch包进行升级;
(4)调试Active APP是否可以通过网络拉取patch升级包并下载到 OTA 分区;
(5)综合调试。
# 2. 分区规划及分区表的生成、烧录
## 2.1. 分区规划
TencentOS-tiny支持两种升级方式两种升级方式对应有不同的分区表
原地升级方式中bootloader直接根据旧固件+patch升级包生成新的固件一旦开始升级无论升级成功与否都无法回滚到上一个版本主要有五个分区
- Bootloader分区
- Active APP分区当前正在运行的固件存放分区
- OTA分区新固件存放分区
- KV分区记录系统升级中的一些环境变量
- 分区表存放分区表固定大小32B一般放在Flash的最后一个扇区中
乒乓升级方式中bootloader在升级之前会将原来的固件拷贝一份一旦开始升级无论新固件升级成功与否都可以回滚到上一个版本主要有六个分区
- Bootloader分区
- Active APP分区当前正在运行的固件存放分区
- **Backup分区**:备份当前正在运行的固件;
- OTA分区新固件存放分区
- KV分区记录系统升级中的一些环境变量
- 分区表存放分区表固定大小32B一般放在Flash的最后一个扇区中
在实际应用中两种方式的选择要根据设备需求设备Flash空间大小设备Flash扇区大小综合考虑。
本文接下来如无特殊说明都是以第一种方式原地升级为例官方板的Flash大小为256KB可以如下分区
![](./image/OTA/ota_partition_ip.jpg)
## 2.2. 分区表工具的使用方法
TencentOS-tiny 源码中提供了一个生成分区表的命令行工具需要先使用gcc编译没有gcc环境可以安装MinGW。
### 2.2.1. 编译
进入`\components\ota\tools\partition_table`目录执行make命令开始编译编译之后进入到当前目录下的`out\target`即可看到工具 `ptbl.exe`
```bash
cd out/target/
```
该工具有如下参数:
- `-h`:打印帮助信息
- `-p <ip/pp>`:选择升级方式,ip或者pp
- `-a <start,end>`Active APP分区的起始和结束地址
- `[-b <start,end>]`Backup 分区的起始和结束地址选了pp方式才需要
- `-o <start,end>`OTA分区的起始和结束地址
- `-k <start,end>`KV分区的起始和结束地址
- `-v <major>.<minor>`:初识固件版本号
为官方板生成分区表文件的命令如下:
```bash
./ptbl.exe -p ip -a 0x08007800,0x0802D000 -o 0x0802D000,0x08039800 -k 0x08039800,0x0803F800 -v 0.1 ptbl.bin
```
运行之后即可在当前目录下看到生成的分区表文件`ptbl.bin`
## 2.3. 分区表烧录方法
生成的分区表文件`ptbl.bin`可以通过STM32CubeProg直接烧录到Flash中按照之前规划的分区烧录位置为 0x0803F800 ,烧录方法如下:
(1)使用下载器连接开发板到PC打开STM32CubeProg工具点击Connect连接到开发板
![stm32cubeprog_01](./image/OTA/stm32cubeprog_01.jpg)
(2)如果是**第一次使用OTA**,点击左侧的烧录按钮,点击全片擦除:
![stm32cubeprog_02](./image/OTA/stm32cubeprog_02.jpg)
>第一次使用的时候KV分区中的数据不确定可靠起见直接选择全片擦除。
(3)点击左侧的烧录按钮,选择刚刚生成的`ptbl.bin`,下载地址填写 0x0803F800点击烧录
![stm32cubeprog_03](./image/OTA/stm32cubeprog_03.jpg)
出现烧录成功弹窗即可。
# 3. 测试Bootloader程序
进入`board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota`目录打开官方提供的bootloader示例工程`ota_bootloader_recovery`
bootloader其实是一个裸机程序在 main 函数中主要有三部分,分别对应三个功能:
- 读取分区表
- 根据patch包进行升级
- 跳转到Active APP分区开始执行
接下来将完成 1.3 节所给出的OTA组件开发调试推荐流程前三步。
## 3.1. 测试bootloader是否可以读取分区表
首先需要指定分区表地址:
```c
uint32_t partition_addr = 0x0803f800;
```
完善初始化环境变量的代码在初始化之后手动添加打印分区表的代码最后的while(1)用于停止程序,防止进行后面的功能:
```c
if ((ret = ota_env_init(OTA_UPDATE_IN_POSITION, partition_addr, &stm32l4_norflash_onchip_drv_ota, &stm32l4_norflash_onchip_prop_ota)) != OTA_ERR_NONE) {
printf("env init failed!OTA errcode = %d\n", ret);
return -1;
} else {
printf("env init successfully!\r\n");
printf("+-------------------------+\r\n");
printf("|Active APP | 0x%08x |\r\n", ota_partition_start(OTA_PARTITION_ACTIVE_APP));
printf("| OTA | 0x%08x |\r\n", ota_partition_start(OTA_PARTITION_OTA));
printf("| KV | 0x%08x |\r\n", ota_partition_start(OTA_PARTITION_KV));
printf("| Version | %d.%d |\r\n", ota_partition_init_version_get().major, ota_partition_init_version_get().minor);
printf("+-------------------------+");
}
while(1);
```
修改程序编译地址和大小与Bootloader分区的划分相对应
![](./image/OTA/bootloader_option.jpg)
编译程序。
编译成功之后修改下载设置选择扇区擦除方式、设置程序烧录起始地址、设置程序烧录大小同样与bootloader分区的划分相对应
![](./image/OTA/bootloader_prog_option.jpg)
下载程序,在串口助手中观察输出:
![](./image/OTA/bootload_partition_read_result.jpg)
## 3.2. 测试bootloader是否可以跳转到Active APP分区
打开和bootloader在一个目录下的app工程`ota_application_download_through_http`
目前我们只是测试bootloader是否可以跳转过来执行此程序所以如图所示修改代码不要触发OTA固件拉取功能
![](./image/OTA/app_mofidy.jpg)
另外,因为此程序被烧录到 Active APP 分区是从Active APP分区的起始地址开始运行的所以需要修改中断向量表的偏移地址如图
![](./image/OTA/app_mofidy_2.jpg)
同样修改程序编译的起始地址和大小,与规划的 Active APP分区信息相对应
![](./image/OTA/actice_app_option.jpg)
编译程序。
编译成功之后修改下载设置烧录到Actice APP分区中
![](./image/OTA/actice_app_prog_option.jpg)
最后再修改bootloader程序屏蔽while(1)屏蔽OTA固件升级功能开启跳转功能
![](./image/OTA/bootloader_modify.jpg)
再次编译下载bootloader复位开发板查看串口助手中的输出可以看到bootloader成功跳转到Actice APP开始执行
![](./image/OTA/bootload_jump_result.jpg)
## 3.3. 差分patch包生成方法
### 3.3.1. 准备老固件和新固件
官方示例工程已经在MDK中配置编译后生成.bin固件如图
![](./image/OTA/actice_app_bin_option.jpg)
编译之后会在工程目录下生成TencentOS_tiny.bin文件拷贝一份出来重命名为:TencentOS_tiny_0_1.bin表示这是0.1版本的固件。
修改app工程将打印信息中的0.1改为0.2,以验证是否升级成功:
![](./image/OTA/actice_app_0_2_option.jpg)
重新编译生成新固件TencentOS_tiny.bin同样拷贝一份重命名为TencentOS_tiny_0_2表示这是0.2版本的固件。
### 3.3.2. 差分升级包生成工具的使用
>编译此工具同样需要gcc环境。
进入`\components\ota\tools\diff`目录执行make命令开始编译编译之后进入到当前目录下的`out\target`即可看到工具 `diff.exe`
```bash
cd out/target/
```
该工具有如下参数:
- `-h`:打印帮助信息
- `-v`:打印更多信息
- `-s`<待定...>
- `-b <n>`指定扇区大小单位字节Byte
- `-n <major>.<minor>`:新的固件版本号
- `-o <major>.<minor>`:旧的固件版本号
将3.3.1节准备的两个固件拷贝到当前工具所在目录下。
根据前面准备的两个新旧固件,制作差分升级包的命令如下:
```bash
./diff.exe -v -b 2048 -n 0.2 -o 0.1 TencentOS_tiny_0_1.bin TencentOS_tiny_0_2.bin patch.bin
```
运行之后即可在当前目录下看到生成的差分升级包`patch.bin`
## 3.4. 测试bootloader是否可以根据patch进行升级
使用STM32CubeProg直接将差分升级包patch.bin下载到OTA分区
![](./image/OTA/patch_prog.jpg)
下载成功后因为bootloader通过检测KV分区中的环境变量new_version来判断是否有新的固件但是环境变量在app程序拉取固件成功之后才会设置所以我们手动在bootloader中来设置此环境变量。
在main.c中引入KV的头文件
```c
#include "tos_kv.h"
```
在环境变量初始化代码之后,升级代码之前添加如下的代码:
```c
ota_img_vs_t new_version;
new_version.major = 0;
new_version.minor = 2;
tos_kv_set("new_version", &new_version, sizeof(ota_img_vs_t));
```
添加之后编译,下载,在串口助手中查看输出:
![](./image/OTA/bootload_recovery_result.jpg)
可以看到bootloader成功根据patch差分包和旧的固件还原出了新的固件并成功升级。
# 4. 使用HTTP方式获取固件并升级
TencentOS-tiny OTA组件支持使用 HTTP 协议拉取差分升级patch包并烧写到OTA分区中。
## 4.1. 准备HTTP服务器
HTTP服务器的选择非常多常用选择有
- 在Windows上使用类似MyWebServer的小工具开启HTTP服务
- 优点:简单方便,用于测试
- 缺点无公网ip只能在同一个局域网内被访问
![](./image/OTA/my_webserver.jpg)
- 在云服务器上安装类似Nginx的工具开启HTTP访问
- 优点性能强劲、有公网ip、支持高并发大量设备同时开机拉取固件
- 缺点:安装过程较复杂
除了这两种方式外还有很多的方式可供选择只需要开启HTTP服务即可本文中我使用第一种方式。
开启之后将第3步中生成的差分升级固件 patch.bin 上传/复制到HTTP服务目录中然后使用浏览器访问`http://<已经开启HTTP服务PC的ip>:<HTTP服务端口>/patch.bin`,测试正常获取之后方可进行后续操作。
>开启HTTP服务之后记得关闭各种防火墙比如Win10网络防火墙云服务器安全组等。
## 4.2. HTTP获取固件并升级
(1)使用STM32CubeMX Prog全片擦除烧写分区表到0x0803f800;
(2)修改bootloader程序去除手动设置new_version环境变量代码编译下载**不要复位**:
![](./image/OTA/ota_bootloader_modify.jpg)
(3)修改Actice APP工程(`ota_application_download_through_http`)开启HTTP固件拉取和升级功能:
![](./image/OTA/ota_active_app_modify.jpg)
![](./image/OTA/ota_active_app_netinfo_modify.jpg)
修改之后编译,下载。
特别注意,**此时0.1版本的程序已经被我们修改所以之前生成的patch包无法完成升级**重新在0.1版本的基础上修改处0.2版本的程序并制作patch包。
重复一遍之前的制作流程即可:
- ① 将当前固件拷贝出来,重命名为`TencentOS_tiny_0_1.bin`;
- ② 修改打印提示表示这是0.2版本的程序:
```c
printf("do sth(v0.2)...\n");
```
- ③ 将新固件拷贝出来命名为`TencentOS_tiny_0_2.bin`;
- ④ 使用diff命令行工具或者可视化工具制作差分升级包patch.bin
- ⑤ 将制作出的差分升级包拷贝/上传到HTTP服务器目录
此时一切准备就绪复位设备即可在串口助手中看到OTA的升级状况
![](./image/OTA/ota_http_result.jpg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB