这里只记录差分升级。详细流程见官网文档。本文章只为简化OTA流程,重点放在端侧设备如何从代码层接入。
系统:openharmony kernel-liteos-m;
SDK:IoT Device SDK Tiny;
MCU:STM32F407ZGT6 HAL库
升级包制作
这里需要用到一个ota_tools的工具,官方文档有详细说明。这里不展开阐述。
diff_packflow.png
升级包打包上传
官方文档有详细说明,这里不展开阐述。
IOT平台升级流程
IOT升级流程.png设备端程序开发说明
升级包存储
img.pngLoader:如果有升级需求,则执行升级(解压以及差分还原)。应该就是bootloader。
Running:设备的应用程序
Flag:存储OTA的一些主要的FLAG(OTA状态、下载升级包大小等,为了防止意外的掉电行为,Flag需要做备份区域)
Backup:用于备份当前的程序,区域大小一般和Running区域大小一致
Download:根据选择解决方案(差分还是全量)来定义其大小
升级执行
SDK提供了三个api供开发者调用。
ota_backup(执行备份操作,将当前运行的程序存储到备份区)
ota_patch(升级还原,根据下载包的内容决定做差分升级还是全量升级)
ota_recover(升级回滚,将备份区的内容拷贝到当前执行区)
对开发者而言,需要先备份然后执行升级、防止意外的升级失败。
进度上报
最后搞
OTA实践
首先,需要思考Running、Flag、Backup、Download和FlagBackup分别存储在什么介质上。
因为我的板子上有w25q64jvssiq芯片。所以Flag、Backup、Download和FlagBackup准备放在片外spi flash中。Loader和Running放在片内flash中。
然后,需要提供两套操作flash的api。
片外SPI FLASH驱动
我这里使用SFUD作为SPI FLASH驱动。关于SFUD的移植见SFUD官方文件或者SFUD移植
片内FLASH驱动
照搬GitHub - LiteOS/LiteOS_Lab at iot_link
中"LiteOS_Lab-iot_link\targets\STM32F429IGTx_FIRE\Src\ota_flash\hal_flash.c"
。源码如下:
#include <string.h>
#include "hal_flash.h"
#include "main.h"
#ifdef HAL_FLASH_MODULE_ENABLED
#define FLASH_SECTOR_ILEGAL 0xFFFFFFFF
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base address of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 Kbytes */
#define ADDR_FLASH_SECTOR_END ((uint32_t)0x08100000) /* End address of Sector 11 */
static uint32_t prv_flash_get_sector(uint32_t addr)
{
uint32_t sector = 0;
if ((addr < ADDR_FLASH_SECTOR_1) && (addr >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_SECTOR_0;
}
else if ((addr < ADDR_FLASH_SECTOR_2) && (addr >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_SECTOR_1;
}
else if ((addr < ADDR_FLASH_SECTOR_3) && (addr >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_SECTOR_2;
}
else if ((addr < ADDR_FLASH_SECTOR_4) && (addr >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_SECTOR_3;
}
else if ((addr < ADDR_FLASH_SECTOR_5) && (addr >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_SECTOR_4;
}
else if ((addr < ADDR_FLASH_SECTOR_6) && (addr >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_SECTOR_5;
}
else if ((addr < ADDR_FLASH_SECTOR_7) && (addr >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_SECTOR_6;
}
else if ((addr < ADDR_FLASH_SECTOR_8) && (addr >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_SECTOR_7;
}
else if ((addr < ADDR_FLASH_SECTOR_9) && (addr >= ADDR_FLASH_SECTOR_8))
{
sector = FLASH_SECTOR_8;
}
else if ((addr < ADDR_FLASH_SECTOR_10) && (addr >= ADDR_FLASH_SECTOR_9))
{
sector = FLASH_SECTOR_9;
}
else if ((addr < ADDR_FLASH_SECTOR_11) && (addr >= ADDR_FLASH_SECTOR_10))
{
sector = FLASH_SECTOR_10;
}
else if ((addr < ADDR_FLASH_SECTOR_END) && (addr >= ADDR_FLASH_SECTOR_11))
{
sector = FLASH_SECTOR_11;
}
else
{
sector = FLASH_SECTOR_ILEGAL;
}
return sector;
}
int hal_flash_read(void *buf, int32_t len, uint32_t location)
{
if (NULL == buf || len < 0 || len >= ADDR_FLASH_SECTOR_END - ADDR_FLASH_SECTOR_0)
{
return -1;
}
if (FLASH_SECTOR_ILEGAL != prv_flash_get_sector(location) && FLASH_SECTOR_ILEGAL != prv_flash_get_sector(location + len))
{
memcpy(buf, (uint8_t *)location, len);
return 0;
}
else
{
return -1;
}
}
int hal_flash_erase(uint32_t addr, int32_t len)
{
uint32_t begin_sector;
uint32_t end_sector;
uint32_t i;
if (len < 0 || len >= ADDR_FLASH_SECTOR_END - ADDR_FLASH_SECTOR_0)
{
return -1;
}
if (HAL_FLASH_Unlock() != HAL_OK)
{
return -1;
}
begin_sector = prv_flash_get_sector(addr);
end_sector = prv_flash_get_sector(addr + len);
if (FLASH_SECTOR_ILEGAL == begin_sector || FLASH_SECTOR_ILEGAL == end_sector)
{
return -1;
}
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
for (i = begin_sector; i <= end_sector; ++i)
{
FLASH_Erase_Sector(i, FLASH_VOLTAGE_RANGE_3);
}
return 0;
}
int hal_flash_write(const void *buf, int32_t len, uint32_t *location)
{
int i;
uint8_t *pbuf;
uint32_t location_cur;
if (NULL == buf || NULL == location || len < 0 || len >= ADDR_FLASH_SECTOR_END - ADDR_FLASH_SECTOR_0)
{
return -1;
}
location_cur = *location;
pbuf = (uint8_t *)buf;
if (FLASH_SECTOR_ILEGAL == prv_flash_get_sector(location_cur) || FLASH_SECTOR_ILEGAL == prv_flash_get_sector(location_cur + len))
{
return -1;
}
for (i = 0; i < len; ++i)
{
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE,
location_cur + i, pbuf[i]) != HAL_OK)
{
return -1;
}
}
*location += len;
return 0;
}
int hal_flash_erase_write(const void *buf, int32_t len, uint32_t location)
{
if (NULL == buf)
{
return -1;
}
if (hal_flash_erase(location, len) != 0)
{
(void)HAL_FLASH_Lock();
return -1;
}
if (hal_flash_write(buf, len, &location) != 0)
{
(void)HAL_FLASH_Lock();
return -1;
}
return 0;
}
void hal_flash_lock(void)
{
(void)HAL_FLASH_Lock();
}
#endif /* HAL_FLASH_MODULE_ENABLED */
在移植片内flash驱动的时候,最好结合HAL库对照看一下MCU芯片(STM32F407ZGT6)的datasheet。以下是官方提供的STM32F407ZGT6的FLASH结构图。只有12个sector。
stm32f407 flash.png
img_demo/img_null.c
img_demo/img_null.c和img_demo/img_memory.c是官方的demo。从这两个文件可以看出,Running、Flag、Backup、Download和FlagBackup可以根据存储介质的不同,提供各自的接口:
static int imgerase_func(uintptr_t arg, int imgsize)
static int imgflush_func(uintptr_t arg, int imgsize)
static int imgread_func(uintptr_t arg, int offset, void *buf, int len)
static int imgwrite_func(uintptr_t arg, int offset,const void *buf, int len)
我们现在需要做的是根据以上4个API,编写片内flash和spi flash对应的4个API,并注册到ota中。复制文件img_null.c为img_flash.c。更改后源码如下:
#include <ota_img.h>
#include "sfud.h"
#include "hal_flash.h"
///< we make the SPI flash as the FLAG DOWNLOAD BACKUP
#ifndef CONFIG_OTAIMG_FLAGSIZE
#define CONFIG_OTAIMG_FLAGSIZE 4096
#endif
#ifndef CONFIG_OTAIMG_DOWNLOADSIZE
#define CONFIG_OTAIMG_DOWNLOADSIZE (512*1024)
#endif
#ifndef CONFIG_OTAIMG_BACKUPSIZE
#define CONFIG_OTAIMG_BACKUPSIZE (512*1024)
#endif
#define CN_OTAIMG_BASEOFFET 0
#define CN_OTAIMG_FLAG_OFFSET (CN_OTAIMG_BASEOFFET)
#define CN_OTAIMG_DOWNLOAD_OFFSET (CN_OTAIMG_FLAG_OFFSET + CONFIG_OTAIMG_FLAGSIZE)
#define CN_OTAIMG_BACKUP_OFFSET (CN_OTAIMG_DOWNLOAD_OFFSET + CONFIG_OTAIMG_DOWNLOADSIZE)
#define CN_OTAIMG_FLAGBACKUP_OFFSET (CN_OTAIMG_BACKUP_OFFSET + CONFIG_OTAIMG_BACKUPSIZE)
/**
* The INNER flash FOR OTA WE USED
*
* |----LOADER(128KB)---|-------RUNNINGIMG(512KB)----------|
* */
#ifndef CONFIG_OTAIMG_RUNNINGSIZE
#define CONFIG_OTAIMG_RUNNINGSIZE (512*1024)
#endif
#ifndef CONFIG_OTAIMG_RUNNINGBASE
#define CONFIG_OTAIMG_RUNNINGBASE 0x08020000
#endif
sfud_flash *w25q64jv;
/*************************************** spi flash *************************************/
static int spiflash_erase(uintptr_t arg, int imgsize)
{
int result = -1;
uint32_t addr;
addr = (uint32_t)arg;
if(sfud_erase(w25q64jv, addr, imgsize) == SFUD_SUCCESS)
{
result = 0;
}
return result;
}
static int spiflash_write(uintptr_t arg, int offset, const void *buf, int len)
{
uint32_t addr;
int result = -1;
addr = (uint32_t)arg + (uint32_t)offset;
if (sfud_write(w25q64jv, addr, len, (const uint8_t *)buf) == SFUD_SUCCESS)
{
result = 0;
}
return result;
}
static int spiflash_read(uintptr_t arg, int offset, void *buf, int len)
{
uint32_t addr;
int result = 0;
addr = (uint32_t)arg + (uint32_t)offset;
if (sfud_read(w25q64jv, addr, len, (uint8_t *)buf) != SFUD_SUCCESS)
{
result = -1;
}
return result;
}
static int spiflash_flush(uintptr_t arg, int imgsize)
{
return 0;
}
/******************************* inner flash ************************************/
static int innerflash_erase(uintptr_t arg, int imgsize)
{
int result = 0;
uint32_t addr;
addr = (uint32_t)arg;
result = hal_flash_erase(addr, imgsize);
return result;
}
static int innerflash_flush(uintptr_t arg, int imgsize)
{
return 0;
}
static int innerflash_read(uintptr_t arg, int offset, void *buf, int len)
{
uint32_t addr;
int result = 0;
addr = (uint32_t)arg + (uint32_t)offset;
result = hal_flash_read(buf, len, addr);
return result;
}
static int innerflash_write(uintptr_t arg, int offset, const void *buf, int len)
{
uint32_t addr;
int result = 0;
addr = (uint32_t)arg + (uint32_t)offset;
result = hal_flash_write(buf, len, &addr);
return result;
}
static const ota_img_t g_otaimg_flag = {
.name = "FLAG",
.size = CONFIG_OTAIMG_FLAGSIZE,
.type = EN_OTA_IMG_FLAG,
.arg = (uintptr_t) CN_OTAIMG_FLAG_OFFSET,
{
.write = spiflash_write,
.read = spiflash_read,
.erase = spiflash_erase,
.flush = spiflash_flush,
},
};
static const ota_img_t g_otaimg_flagbackup = {
.name = "FLAGBACKUP",
.size = CONFIG_OTAIMG_FLAGSIZE,
.type = EN_OTA_IMG_FLAGBACKUP,
.arg = (uintptr_t)CN_OTAIMG_FLAGBACKUP_OFFSET,
{
.write = spiflash_write,
.read = spiflash_read,
.erase = spiflash_erase,
.flush = spiflash_flush,
},
};
static const ota_img_t g_otaimg_backup = {
.name = "BACKUP",
.size = CONFIG_OTAIMG_BACKUPSIZE,
.type = EN_OTA_IMG_BACKUP,
.arg = (uintptr_t)CN_OTAIMG_BACKUP_OFFSET,
{
.write = spiflash_write,
.read = spiflash_read,
.erase = spiflash_erase,
.flush = spiflash_flush,
},
};
static const ota_img_t g_otaimg_download = {
.name = "DOWNLOAD",
.size = CONFIG_OTAIMG_DOWNLOADSIZE,
.type = EN_OTA_IMG_DOWNLOAD,
.arg = (uintptr_t)CN_OTAIMG_DOWNLOAD_OFFSET,
{
.write = spiflash_write,
.read = spiflash_read,
.erase = spiflash_erase,
.flush = spiflash_flush,
},
};
static const ota_img_t g_otaimg_running = {
.name = "RUNNING",
.size = CONFIG_OTAIMG_RUNNINGSIZE,
.type = EN_OTA_IMG_RUNNING,
.arg = (uintptr_t)CONFIG_OTAIMG_RUNNINGBASE,
{
.write = innerflash_write,
.read = innerflash_read,
.erase = innerflash_erase,
.flush = innerflash_flush,
},
};
int ota_img_init()
{
sfud_init();
w25q64jv = sfud_get_device(SFUD_W25Q64JV_DEVICE_INDEX);
ota_img_bind(EN_OTA_TYPE_FOTA, &g_otaimg_flag);
ota_img_bind(EN_OTA_TYPE_SOTA, &g_otaimg_flag);
ota_img_bind(EN_OTA_TYPE_FOTA, &g_otaimg_flagbackup);
ota_img_bind(EN_OTA_TYPE_SOTA, &g_otaimg_flagbackup);
ota_img_bind(EN_OTA_TYPE_FOTA, &g_otaimg_backup);
ota_img_bind(EN_OTA_TYPE_SOTA, &g_otaimg_backup);
ota_img_bind(EN_OTA_TYPE_FOTA, &g_otaimg_running);
ota_img_bind(EN_OTA_TYPE_SOTA, &g_otaimg_running);
ota_img_bind(EN_OTA_TYPE_FOTA, &g_otaimg_download);
ota_img_bind(EN_OTA_TYPE_SOTA, &g_otaimg_download);
return 0;
}
业务层
失败了,RAM不够,暂时先放一放,先把其他功能做稳定之后再尝试
未完待续……
网友评论