美文网首页
华为云OTA升级

华为云OTA升级

作者: xEndLess | 来源:发表于2022-04-21 23:08 被阅读0次

    这里只记录差分升级。详细流程见官网文档。本文章只为简化OTA流程,重点放在端侧设备如何从代码层接入。
    系统:openharmony kernel-liteos-m;
    SDK:IoT Device SDK Tiny;
    MCU:STM32F407ZGT6 HAL库

    升级包制作

    这里需要用到一个ota_tools的工具,官方文档有详细说明。这里不展开阐述。


    diff_packflow.png

    升级包打包上传

    官方文档有详细说明,这里不展开阐述。

    IOT平台升级流程

    IOT升级流程.png

    设备端程序开发说明

    升级包存储

    img.png

    Loader:如果有升级需求,则执行升级(解压以及差分还原)。应该就是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不够,暂时先放一放,先把其他功能做稳定之后再尝试
    未完待续……

    相关文章

      网友评论

          本文标题:华为云OTA升级

          本文链接:https://www.haomeiwen.com/subject/vawisrtx.html