7.46. 扩展外部存储器

Overview

为了降低芯片内部FLASH容量, 可以把固件代码和资源文件加载到外部存储器, 芯片启动后搬迁外部存储器代码到可执行代码内存单元运行

7.46.1. 名称定义

  • 外部存储器: 芯片外挂用于加载代码和资源文件的介质, 一般指SD卡, U盘, NAND_FLASH等;

  • 隐藏SDFILE区: 位于外部存储器, 使用跟FLASH内的sdfile结构一致的文件系统(仅缺少UBOOT), 用于存放固件代码以及重要资源文件;

  • 隐藏FAT区: 位于外部存储器的FAT32文件系统, 用于存放PC端盘符不可见的文件;

  • 第二FAT区:位于外部存储器的FAT32文件系统, 用于存放PC端盘符可见的文件;

  • EX_APP.BIN:加载到外部存储器的固件代码可执行文件, 一般就是固件SDK代码;

  • APP.BIN:加载到FLASH的固件代码可执行文件, 在这里的作用是把外部存储器的EX_APP.BIN拷贝到可直接运行代码的内存器如DDR/SDRAM/RAM/PSAM运行;

7.46.2. 外部存储器分区结构

7.46.3. 隐藏SDFILE分区结构

7.46.4. FLASH分区结构

注意:隐藏SDFILE和FLASH中VM区、RESERVED区是相互独立。

7.46.5. 启动流程

7.46.6. SD LOADER流程

sd loader运行在FLASH中,用于初始化外部存储器,加载主工程ex_app.bin到sdram,并完成解密和数据校验工作,校验成功后就会跳转到ex_app.bin中运行。

static volatile unsigned int (*sdramEntryAddr)(void *) = 0x4000120;
static SD_BOOT_INFO sd_info;
#define IRQ_MEM_ADDR    (0x1c7fe00)

void jumpApp(void)
{
    //提供ex_app.bin需要的一些运行参数
    BOOT_DEVICE_INFO *info = (BOOT_DEVICE_INFO *)(((u32)IRQ_MEM_ADDR) + 8);
    info->fs_info = (u32)info + sizeof(BOOT_DEVICE_INFO);
    info->fs_info->FlashSize = boot_info.flash_size;//flash 大小
    info->fs_info->align =  boot_info.vm.align;//vm对齐方式
    info->trim_value = boot_info.trim_value ;//trim值
    info->chip_id = boot_info.chip_id; //chip id
    memcpy(&(info->sfc), &(boot_info.sfc), sizeof(struct sfc_info));  //sfc 相关参数
    memcpy(info->bt_mac_addr, &(boot_info.mac), sizeof(boot_info.mac)); //mac
    memcpy((u32)info + sizeof(BOOT_DEVICE_INFO) * 2 + 32, &sdram_info, sizeof(SDRAM_CFG_INFO)); //sdram相关参数
    memcpy((u32)info + sizeof(BOOT_DEVICE_INFO) * 2 + 32 + sizeof(SDRAM_CFG_INFO), &sd_info, sizeof(SD_BOOT_INFO)); //sd 启动相关参数

    //跳转,跳转后不再返回
    u32 ret = sdramEntryAddr((void *)info);
}

void sd_loader_task(void *priv)
{
    int filelen;
    int rlen;
    char headInfoBuf[SECTOR_SIZE];
    int err;
    JL_FILE_HEAD appBinHead;
    JL_FILE_HEAD appDirHead;
    u32 appAddr;
    u32 caclCrc = 0;
    u32 time_now = jiffies_to_msecs(jiffies);

    //获取ini配置参数,初始化串口
    uart_debug_init();
    logInfo("\r\n\r\n\r\n\r\n\r\n ------------sd loader %s-------------\r\n\r\n\r\n\r\n\r\n", __TIME__);

    //获取ini配置参数,初始化sd设备
    if (sdnand_init()) {
        errInfo("sdnand_init err!\n");
        return;
    }

    err = sdnand_read(headInfoBuf, 0x0, sizeof(headInfoBuf));
    if (!err) {
        errInfo("sdnand_read head info err!\n");
        return;
    }

    int ret = getAppDirHead(headInfoBuf, sizeof(headInfoBuf), &appDirHead);
    if (!ret) {
        logInfo("[%s info]len: 0x%x, appaddr: 0x%x\n", appDirHead.szFileName, appDirHead.u32Length, appDirHead.u32Address);
    }

    //获取ex_app.bin头部信息
    if (0 == getAppAreaHeadInfo(appDirHead.u32Address, "app.bin", &appBinHead)) {
        logInfo("[%s info] len: 0x%x, addr: 0x%x, attr :0x%x\n",
                appBinHead.szFileName,
                appBinHead.u32Length,
                appBinHead.u32Address,
                appBinHead.u8Attribute);

        u32 destAddr = sdramEntryAddr;
        u32 appLen = appBinHead.u32Length;
        u32 offset = appBinHead.u32Address;
        u32 rLen = 0;

        //加载ex_app.bin 到sdram
        err = sdnand_read(destAddr, appBinHead.u32Address + appDirHead.u32Address, appBinHead.u32Length);
        if (!err) {
            errInfo("sdnand_read head info err!\n");
            return;
        }

        //判断是否需要解密,如果开启加密功能,由于是软件解密一定程度上会影响启动速度
        if (appBinHead.u8Attribute == (FILE_ATTR_REG | FILE_ATTR_ENC)) {
            //解密ex_app.bin
            while (rLen < appLen) {
                decode_data_by_user_key(get_chip_id(), destAddr, sizeof(JL_FILE_HEAD), offset, sizeof(JL_FILE_HEAD));
                destAddr += sizeof(JL_FILE_HEAD);
                rLen += sizeof(JL_FILE_HEAD);
                offset += sizeof(JL_FILE_HEAD);
            }
        }

        //校验ex_app.bin
        caclCrc = calc_crc16_with_init_val(caclCrc, sdramEntryAddr, appLen);
        if (caclCrc != appBinHead.u16DataCrc) {
            errInfo("crc err, caclCrc : 0x%x, app crc : 0x%x\n", caclCrc, appBinHead.u16DataCrc);
        } else {
            //更新sfc_base_addr
            sd_info.app_addr = appDirHead.u32Address;

            //关中断,清cache
            __local_irq_disable();
            flush_all_icache();

            logInfo("the time to load app : %d ms\n", jiffies_to_msecs(jiffies) - time_now);
            logInfo("load app OK, jump Now!\n");

            //跳转到ex_app.bin运行
            jumpApp();
        }
    } else {
        errInfo("get app head info err!\n");
    }
}
参数说明:

串口初始化和sd设备初始化时获取ini文件(ini文件由isd_config_rule_loader.c编译时生成)中的相关配置,对应配置说明如下:

  • EX_SDCARD=SD_1_0_1_24XX;[SD_X(SD0:0 SD1:1)_X(A:0 B:1 C:2 D:3)_X(SD 数据宽度)_X(SD 时钟, 一共占4位,其余位补X,如:设置为24XX,即时钟为24 * 1000000);设置为3XXX,即时钟位3 * 1000000] 其中:(SD配置请参考: SD )

  • SDTX=PC00; [配置SD调试输出Pin] //串口tx

  • SDBD=1000000; [配置SD调试波特率]//串口波特率

7.46.7. 外部存储器分区配置使用说明

7.46.7.1. 一般分为四种使用情况:

  • 注意:只有使用以下第2种情况(SD卡分为两个分区(隐藏FAT区,FAT1区,无隐藏SDFILE区)) 需自行进行修改增加内容。使用以下第3和第4中情况只需根据以下说明打开对应宏即可

  • 情况1:常规用法,SD卡只有一个分区,无需配置

  • 情况2:SD卡分为两个分区(隐藏FAT区,FAT1区,无隐藏SDFILE区)

(1)一个为隐藏FAT区,通过msd(使用msd(USB做masstorge从机方法))用户不可见;
(2)一个为可见FAT区,用户可见;
(3)注意由于没有隐藏SDFILE区,不要开启#define CONFIG_SDFILE_EXT_ENABLE 和 #define CONFIG_DMSDX_ENABLE
(4)使用者需自行在app_config.h增加宏去控制apps/common/system/device_mount.c对于隐藏FAT区和第二FAT区挂载
(5)使用者需自行在cpu/wl82/tools/isd_config_rule_loader.c 中增加隐藏FAT区和第二FAT区配置,默认代码是被CONFIG_SDFILE_EXT_ENABLE所包,但是没有隐藏SDFILE区
(6)修改download.c可以在烧录时将tools文件夹下hfat_dir和fat1_dir内容烧录到对应分区
 //a.在app_config.h中增加
 #define CONFIG_DFAT_ENABLE
 #ifdef CONFIG_DFAT_ENABLE

 #define CONFIG_SDNAND_HFS_LEN_TEXT  0M  //没有SDFILE区,设置为0
 #define CONFIG_SDNAND_HFAT_LEN_TEXT 2G  //hfat隐藏FAT区大小
 #define CONFIG_SDNAND_FAT1_LEN_TEXT 8G  //第二FAT区大小
 #define CONFIG_SDNAND_HFS_LEN       (0llu * 1024 * 1024)         //没有SDFILE区,设置为0
 #define CONFIG_SDNAND_HFAT_LEN      (2llu * 1024 * 1024 * 1024)  //hfat隐藏FAT区大小
 #define CONFIG_SDNAND_FAT1_LEN      (8llu * 1024 * 1024 * 1024)  //第二FAT区大小

 //每个簇有多少个block,比如簇大小设置为32768时,该值为32768 / 512 = 0x40
 #if CONFIG_SDNAND_HFAT_LEN > 0x80000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x40
 #elif CONFIG_SDNAND_HFAT_LEN > 0x40000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x20
 #elif CONFIG_SDNAND_HFAT_LEN > 0x20000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x10
 #elif CONFIG_SDNAND_HFAT_LEN > 0x10000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x8
 #elif CONFIG_SDNAND_HFAT_LEN > 0x8000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x4
 #elif CONFIG_SDNAND_HFAT_LEN > 0x4000000
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x2
 #else
 #define CONFIG_SDNAND_HFAT_CLUSTER_SIZE 0x1
 #endif

 #if CONFIG_SDNAND_FAT1_LEN > 0x80000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x40
 #elif CONFIG_SDNAND_FAT1_LEN > 0x40000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x20
 #elif CONFIG_SDNAND_FAT1_LEN > 0x20000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x10
 #elif CONFIG_SDNAND_FAT1_LEN > 0x10000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x8
 #elif CONFIG_SDNAND_FAT1_LEN > 0x8000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x4
 #elif CONFIG_SDNAND_FAT1_LEN > 0x4000000
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x2
 #else
 #define CONFIG_SDNAND_FAT1_CLUSTER_SIZE 0x1
 #endif

 #endif

//b.在apps/common/system/device_mount.c
static int mount_sd_to_fs(const char *sd_name)
 ...
 /* #ifdef CONFIG_SDFILE_EXT_ENABLE */
 #if defined CONFIG_SDFILE_EXT_ENABLE || defined CONFIG_DFAT_ENABLE //增加app_config.h中增加的宏CONFIG_DFAT_ENABLE去挂载隐藏FAT区和第二FAT区
 ...

 //c.在cpu/wl82/tools/isd_config_rule.c
 ...
 //#if defined CONFIG_SDFILE_EXT_ENABLE //源代码
 #if defined CONFIG_SDFILE_EXT_ENABLE || defined CONFIG_DFAT_ENABLE //增加CONFIG_DFAT_ENABLE宏判断
 #if TCFG_SD0_ENABLE
 #if TCFG_SD_PORTS == 'A'
 EX_SDCARD=SD_0_0_1_1_24;
 #elif TCFG_SD_PORTS == 'B'
 ...
 ...
 [BURNER_CONFIG]
 SIZE=32;
 //文件最后位置加入以下内容
 #if defined CONFIG_DFAT_ENABLE
 [SDCARD_CONFIG]
 HFAT_FILE=jl_hfat.bin
 HFAT_ADR=AUTO
 HFAT_LEN=CONFIG_SDNAND_HFAT_LEN_TEXT
 HFAT_OPT=1

 FAT1_FILE=jl_fat1.bin
 FAT1_ADR=AUTO
 FAT1_LEN=CONFIG_SDNAND_FAT1_LEN_TEXT
 FAT1_OPT=1
 #endif

 //d.在download.c中增加
 //使用linux编译 在#ifdef __SHELL__部分修改增加
 #if defined CONFIG_SDFILE_EXT_ENABLE
 echo -n "fat_image_tool.exe --size " >> ${PROJ_BUILD}
 ...
 echo "isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000 -div1 -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -extend-bin -output-bin jl_hfs.bin %UPDATE_FILES% -no-app-bin-enc" >> ${PROJ_BUILD}
 #elif defined CONFIG_DFAT_ENABLE  //CONFIG_DFAT_ENABLE 宏包着内容为需自行增加内容
 echo -n "fat_image_tool.exe --size " >> ${PROJ_BUILD}
 echo -n CONFIG_SDNAND_HFAT_LEN_TEXT >> ${PROJ_BUILD}
 echo -n " --sectors-per-cluster " >> ${PROJ_BUILD}
 echo -n CONFIG_SDNAND_HFAT_CLUSTER_SIZE >> ${PROJ_BUILD}
 echo " --lfn true --n-root 512 --volume-name HFAT_IMG --output jl_hfat.bin --fat-dir hfat_dir" >> ${PROJ_BUILD}
 echo -n "fat_image_tool.exe --size " >> ${PROJ_BUILD}
 echo -n CONFIG_SDNAND_FAT1_LEN_TEXT >> ${PROJ_BUILD}
 echo -n " --sectors-per-cluster " >> ${PROJ_BUILD}
 echo -n CONFIG_SDNAND_FAT1_CLUSTER_SIZE >> ${PROJ_BUILD}
 echo " --lfn true --n-root 512 --volume-name FAT1_IMG --output jl_fat1.bin --fat-dir fat1_dir" >> ${PROJ_BUILD}
 echo "isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000 -uboot uboot.boot -app app.bin" >> ${PROJ_BUILD}
 echo "isd_download.exe isd_config.ini -tonorflash -dev wl82 -boot 0x1c02000 -div1 -wait 300 -uboot uboot.boot -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -reboot 500 %UPDATE_FILES% -extend-bin" >> ${PROJ_BUILD}
 #elif defined CONFIG_SFC_ENABLE
 echo "isd_download.exe isd_confi
 ....

 //使用windows编译的话,在#ifdef __SHELL__对应的 #else下面修改
 #if defined CONFIG_SDFILE_EXT_ENABLE
 fat_image_tool.exe --size CONFIG_SDNAND_HFAT_LEN_TEXT --sectors-per-cluster CONFIG_SDNAND_HFAT_CLUSTER_SIZE --lfn true --n-root 512 --volume-name HFAT_IMG --output jl_hfat.bin --fat-dir hfat_dir
 fat_image_tool.exe --size CONFIG_SDNAND_FAT1_LEN_TEXT --sectors-per-cluster CONFIG_SDNAND_FAT1_CLUSTER_SIZE --lfn true --n-root 512 --volume-name FAT1_IMG --output jl_fat1.bin --fat-dir fat1_dir
 isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000 -div1 -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -extend-bin -output-bin jl_hfs.bin %UPDATE_FILES% -no-app-bin-enc
 #elif defined CONFIG_DFAT_ENABLE  //CONFIG_DFAT_ENABLE 宏包着内容为需自行增加内容
 fat_image_tool.exe --size CONFIG_SDNAND_HFAT_LEN_TEXT --sectors-per-cluster CONFIG_SDNAND_HFAT_CLUSTER_SIZE --lfn true --n-root 512 --volume-name HFAT_IMG --output jl_hfat.bin --fat-dir hfat_dir
 fat_image_tool.exe --size CONFIG_SDNAND_FAT1_LEN_TEXT --sectors-per-cluster CONFIG_SDNAND_FAT1_CLUSTER_SIZE --lfn true --n-root 512 --volume-name FAT1_IMG --output jl_fat1.bin --fat-dir fat1_dir
 isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000 -uboot uboot.boot -app app.bin
 isd_download.exe isd_config.ini -tonorflash -dev wl82 -boot 0x1c02000 -div1 -wait 300 -uboot uboot.boot -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% %CFG_FILE% -reboot 500 %KEY_FILE% %UPDATE_FILES% -extend-bin
 #elif defined CONFIG_SFC_ENABLE
 ...
  • 情况3:开启#define CONFIG_SDFILE_EXT_ENABLE //外挂多分区存储器支持 SD卡分为三个区,分别为:

(1)隐藏SDFILE区,用于存放主工程资源代码,通过运行flash中sd loader将其加载到sdram中运行;
(2)隐藏FAT区,通过msd用户不可见;
(3)第二FAT区(可见FAT区),用户可见;
(4)情况3与情况2独立,需使用三个分区直接开启宏CONFIG_SDFILE_EXT_ENABLE即可,不需情况2中自行增加内容
  • 情况4:在情况三的基础上开启#define CONFIG_DMSDX_ENABLE //msd多分区显示支持 时SD分为三个区:

(1)隐藏SDFILE区
(2)隐藏FAT区此时通过msd用户可见
(3)第二FAT区可见
(4)使用其它板级时,参考apps/wifi_story_machine/board/wl82/board_7916A.c中CONFIG_DMSDX_ENABLE宏所包内容进行两个FAT区的偏移量设置和sdx.0和sdx.1设备的注册
//app_config.h
#define CONFIG_SDFILE_EXT_ENABLE         //外挂多分区存储器支持
#define CONFIG_DMSDX_ENABLE              //msd多分区显示支持 需定义CONFIG_SDFILE_EXT_ENABLE 打开外挂多分区存储器前提下使用

#ifdef CONFIG_SDFILE_EXT_ENABLE

#undef CONFIG_SFC_ENABLE
#undef __FLASH_SIZE__
#define __FLASH_SIZE__    (16 * 1024 * 1024)

//隐藏SDFILE区大小、隐藏FAT区大小和第二FAT区大小设置需对应的两个参数同时修改
#define CONFIG_SDNAND_HFS_LEN_TEXT  16M    //隐藏SDFILE区大小
#define CONFIG_SDNAND_HFAT_LEN_TEXT 4G     //隐藏FAT区大小
#define CONFIG_SDNAND_FAT1_LEN_TEXT 8G     //第二FAT区大小
#define CONFIG_SDNAND_HFS_LEN       (16llu * 1024 * 1024)       //隐藏SDFILE区大小
#define CONFIG_SDNAND_HFAT_LEN      (4llu * 1024 * 1024 * 1024) //隐藏FAT区大小
#define CONFIG_SDNAND_FAT1_LEN      (8llu * 1024 * 1024 * 1024) //第二FAT区大小
...
#endif
  • 两个FAT区的读写示例

#include "fs/fs.h"
static void sd_fs_test(void)
{
    extern int storage_device_ready(void);

    while (!storage_device_ready()) {//等待sd文件系统挂载完成
            os_time_dly(2);
    }

    //读写测试
    u8 *test_data = "123456789";
    u8 *test_data1 = "abcdefghijkadcp";
    u8 read_buf[32];
    u8 read_buf1[32];
    memset(read_buf,0,sizeof(read_buf));
    memset(read_buf1,0,sizeof(read_buf1));
    int len;

    //1.第二FAT区创建写读文件
    //路径storage/sd0/C
    FILE *fd = fopen("storage/sd0/C/test11.txt", "w+");
    if (fd) {
            fwrite(test_data, 1, strlen(test_data), fd);
            fclose(fd);
    }

    fd = fopen("storage/sd0/C/test11.txt", "r");
    if (fd) {
            len = flen(fd);
            fread(read_buf, 1, len, fd);
            fclose(fd);
            printf("FAT1 read file : %s \n", read_buf);
    }


//2.hfat 创建读写
//路径storage/sdh/C
FILE *fd1 = fopen("storage/sdh/C/test22.txt", "w+");
    if (fd1) {
            fwrite(test_data1, 1, strlen(test_data1), fd1);
            fclose(fd1);
    }

    fd1 = fopen("storage/sdh/C/test22.txt", "r");
    if (fd1) {
            len = flen(fd1);
            fread(read_buf1, 1, len, fd1);
            fclose(fd1);
            printf("HFAT read file : %s \n", read_buf1);
    }

    while (1) {
            os_time_dly(2);
    }

}
static int c_main(void)
{
        os_task_create(sd_fs_test, NULL, 12, 1000, 0, "sd_fs_test");
        return 0;
}
late_initcall(c_main);

(1)SD卡只有一个分区

为常规用法,正常配置使用即可;

(2)SD卡分为两个区,隐藏FAT区和第二FAT区(可见FAT区)

  • download.bat(由download.c编译生成,因此需要在对应位置增加配置)应存在以下配置:

//步骤一:生成FAT镜像文件。
fat_image_tool.exe --size 4G --sectors-per-cluster 0x40 --lfn true --n-root 512 --volume-name FAT_IMG --output jl_hfat.bin --fat-dir hfat_dir
主要参数说明:
--size :为fat镜像大小,需要和isd_config_rule_loader.c配置文件中保持一致。
--sectors-per-cluster:簇大小配置。
--output:输出的fat镜像文件。
--fat-dir:目录资源文件,会一同打包到fat镜像文件中。

//步骤二:将FAT镜像烧录到SD卡。注意:步骤二需再步骤三上面,即先烧录FAT镜像再烧录jl_isd.bin
isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000

//步骤三:将jl_isd.bin烧录到flash
isd_download.exe isd_config.ini -tonorflash -dev wl82 -boot 0x1c02000 -div1 -wait 300 -uboot uboot.boot -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -reboot 500 -update_files normal -extend-bin

//参考示例:某工程下编译时生成的download.bat示例
set OBJDUMP=C:\JL\pi32\bin\llvm-objdump.exe
set OBJCOPY=C:\JL\pi32\bin\llvm-objcopy.exe
set ELFFILE=sdk.elf
%OBJCOPY% -O binary -j .text %ELFFILE% text.bin
%OBJCOPY% -O binary -j .data %ELFFILE% data.bin
%OBJCOPY% -O binary -j .ram0_data  %ELFFILE% ram0_data.bin
%OBJCOPY% -O binary -j .cache_ram_data  %ELFFILE% cache_ram_data.bin
%OBJDUMP% -section-headers -address-mask=0x1ffffff %ELFFILE%
%OBJDUMP% -t %ELFFILE% > symbol_tbl.txt
copy /b text.bin+data.bin+ram0_data.bin+cache_ram_data.bin app.bin
packres\packres.exe -n tone -o packres/AUPACKRES audlogo
fat_image_tool.exe --size 8G --sectors-per-cluster 0x40 --lfn true --n-root 512 --volume-name FAT_IMG --output jl_hfat.bin --fat-dir hfat_dir
isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000
isd_download.exe isd_config.ini -tonorflash -dev wl82 -boot 0x1c02000 -div1 -wait 300 -uboot uboot.boot -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -reboot 500 -update_files normal -extend-bin
fw_add.exe -noenc -fw jl_isd.fw -add script.ver -out jl_isd.fw
ufw_maker.exe -fw_to_ufw jl_isd.fw
copy jl_isd.ufw update.ufw
ping /n 2 127.1>null
del cache_ram_data.bin
del data.bin
del ram0_data.bin
del text.bin
TIMEOUT /T 3
exit /b 0
  • isd_config.ini(由isd_config_rule.c编译生成,因此需要在对应位置增加配置)应存在以下配置:

//步骤一:增加SD卡配置参数,工具会读取该参数来初始化SD卡
EX_SDCARD=SD_0_1_1_24XX;

//步骤二:增加FAT区配置
[SDCARD_CONFIG]
/* 隐藏FAT区配置*/
HFAT_FILE=jl_hfat.bin                //隐藏FAT区bin文件
HFAT_ADR=AUTO                        //在外部存储器中的偏移地址,auto为由工具自动分配,即该区域会紧随着隐藏SDFILE区
HFAT_LEN=CONFIG_SDNAND_HFAT_LEN_TEXT //隐藏FAT区大小
HFAT_OPT=1

/* 第二FAT区配置*/
FAT1_ADR=AUTO                       //在外部存储器中的偏移地址,auto为由工具自动分配,即该区域会紧随着隐藏FAT区
FAT1_LEN=CONFIG_SDNAND_FAT1_LEN_TEXT//第二FAT区大小
FAT1_OPT=1

(3)SD卡分为三个区:SDFILE区、隐藏FAT区和第二FAT区:

  • loader_tools目录下isd_config.ini(由isd_config_rule_loader.c编译生成,因此需要在对应位置增加配置)应存在以下配置:

//步骤一:增加SD卡配置参数,工具会读取该参数来初始化SD卡
EX_SDCARD=SD_0_1_1_24XX;


//步骤二:增加SDFILE区配置和FAT区配置
/* 隐藏SDFILE区配置*/
HFS_FILE=jl_hfs.bin                  //隐藏SDFILE区bin文件
HFS_ADR=0G                           //在外部存储器中的偏移地址
HFS_LEN=CONFIG_SDNAND_HFS_LEN_TEXT   //隐藏SDFILE区大小
HFS_OPT=1

/* 隐藏FAT区配置*/
HFAT_FILE=jl_hfat.bin                //隐藏FAT区bin文件
HFAT_ADR=AUTO                        //在外部存储器中的偏移地址,auto为由工具自动分配,即该区域会紧随着隐藏SDFILE区
HFAT_LEN=CONFIG_SDNAND_HFAT_LEN_TEXT //隐藏FAT区大小
HFAT_OPT=1

/* 第二FAT区配置*/
FAT1_ADR=AUTO                       //在外部存储器中的偏移地址,auto为由工具自动分配,即该区域会紧随着隐藏FAT区
FAT1_LEN=CONFIG_SDNAND_FAT1_LEN_TEXT//第二FAT区大小
FAT1_OPT=1
  • download.bat(由download.c编译生成,因此需要在对应位置增加配置)应存在以下配置:

fat_image_tool.exe --size 4G --sectors-per-cluster 0x40 --lfn true --n-root 512 --volume-name FAT_IMG --output jl_hfat.bin --fat-dir hfat_dir
主要参数说明:
--size :为fat镜像大小,需要和isd_config_rule_loader.c配置文件中保持一致。
--sectors-per-cluster:簇大小配置。
--output:输出的fat镜像文件。
--fat-dir:目录资源文件,会一同打包到fat镜像文件中。

isd_download.exe isd_config.ini -gen2 -to-sdcard -dev wl82 -boot 0x1c02000 -div1 -app app.bin cfg_tool.bin -res %AUDIO_RES% %UI_RES% cfg -extend-bin -output-bin jl_hfs.bin -update_files normal
主要参数说明:
-output-bin :输出的隐藏SDFILE区镜像文件,改文件由uboot、app.bin、cfg_tool.bin和res资源文件打包生成。
-to-sdcard/-tonorflash:-to-sdcard为生成sd卡镜像,-tonorflash为生成flash镜像。
-no-app-bin-enc:存在该参数时不对app.bin加密。app.bin加密后,sd loader将app.bin加载到sdram后会进行软件解密,因此会增加ex_app.bin的启动时间。

copy isd_config_loader.ini loader_tools\isd_config.ini
copy jl_hfs.bin loader_tools\jl_hfs.bin
copy jl_hfs.bin.clear loader_tools\jl_hfs.bin.clear
copy jl_hfat.bin loader_tools\jl_hfat.bin

del jl_hfs.bin
del jl_hfs.bin.clear
del jl_hfat.bin
cd loader_tools
call download.bat
  • 工程示例

wifi_story_machine工程已提供示例,通过在app_config.h中定义#define CONFIG_SDFILE_EXT_ENABLE进行使能。其他工程需要使用到该方式时,请参考进行移植。

  • SD LOADER更改说明

sd loader工程和主工程为两个独立工程,出于开发需要需要对sd loader进行更新。此时只需要将sd loader编译生成的sdk.elf替换掉主工程”cpu\wl82\tools\loader_tools\”目录下的sdk.elf即可。

  • SD卡镜像文件生成方法

SD卡镜像文件用于对SD卡进行外部烧录,修改”cpu\wl82\tools\loader_tools\download.bat”文件,添加配置-output-sdcrad-image jl_sdcard.img。如下:

set OUTPUT_IMAGE=-output-sdcrad-image jl_sdcard.img
isd_download.exe isd_config.ini -gen2 -tonorflash -dev wl82 -boot 0x1c02000 -div1 -wait 300 -uboot uboot.boot -app app.bin cfg_tool.bin -reboot 500 -update_files normal -extend-bin %OUTPUT_IMAGE%

7.46.8. OTA升级

扩展外部存储器升级涉及到FLASH升级和外部存储器升级,因此需要将两个升级固件进行打包,该项目中提供了一个简易的打包工具(路径:”cpu\wl82\tools\packufw\”),同时提供源代码,由用户根据自己的需求进行修改使用;提供的例程中,打包架构如下:

/*JLpack打包格式*/
// |首头部| |输入文件1头部| |输入文件2头部| |输入文件1数据| |输入文件2数据|

//首头部和输入文件的头部结构如下:
#define JLFILE_VERSION_LEN 32
typedef struct {
    unsigned short headCrc;           //头部crc
    unsigned short dataCrc;           //数据crc
    unsigned int   address;           //数据地址
    unsigned int   length;            //数据长度

    unsigned char u8Attribute;        //文件属性
    unsigned char res;                //保留
    unsigned short fileNum;           //文件个数,这里代表携带了多少问文件数据
    char fileName[16];                //名称
    char version[JLFILE_VERSION_LEN]; //版本号
} JLExtUpateHead;

//固件打包命令
JLpack.exe -f exAppUpdate.ufw -v 3.4.5 -f appUpdate.ufw -v 2.1.2  -o otaUpdate.ufw
其中:
-f : Input file
-v : File version, -f must be followed by -v
-o : output file

7.46.9. OTA升级流程

//具体请参考"apps/common/example/update/http_upgrade/example3/ex_ota_example.c" 。
static void download_task(void *priv)
{
    struct download_hdl *hdl = priv;
    void *update_fd = NULL;
    int ret = 0;
    int err = 0;
    char sock_err = 0;
    int total = 0;
    JLExtUpateHead extHead;


_reconnect_:
    //发起连接请求,建立socket连接
    ret = hdl->download_ops->init(&hdl->ctx);
    if (ret != HERROR_OK) {
        if (hdl->ctx.req_exit_flag == 0) {
            if (hdl->reconnect_cnt < MAX_RECONNECT_CNT) {
                hdl->reconnect_cnt++;
                goto _reconnect_;
            } else {
                log_e("download reconnect upto max count.\n");
                goto _out_;
            }
        }
    }

    /* 获取打包固件首头部信息 */
    ret = hdl->download_ops->read(&hdl->ctx, (char *)&extHead, sizeof(JLExtUpateHead));
    if (ret !=  sizeof(JLExtUpateHead)) {
        printf("get extHead err!");
        sock_err = 1;
        goto _out_;
    }

    put_buf(&extHead, sizeof(JLExtUpateHead));
    if (ext_head_vaild_check(&extHead)) {
        printf("check extHead err!\n");
        sock_err = 1;
        goto _out_;
    } else {
        /* 判断是否是ext_update 升级接口*/
        if (0 == memcmp(extHead.fileName, "EXT_UPDATE", strlen("EXT_UPDATE"))) {
            printf("get EXT_UPDATE head\n");
        } else {
            printf("EXT_UPDATE information not found\n");
            sock_err = 1;
            goto _out_;
        }
    }

    /* 保存需要升级的固件头部信息 */
    char *r_buf = (char *)&gupdateTable;
    char r_len = extHead.fileNum * sizeof(JLExtUpateHead);
    int len = 0;
    while (1) {
        ret = hdl->download_ops->read(&hdl->ctx, r_buf + len, r_len - len);
        if (ret < 0) {
            sock_err = 1;
            goto _out_;
        } else {
            len += ret;
        }

        if (len >= r_len) {
            break;
        }
    }

    /* 对需要升级的设备进行升级 */
    int cnt = 0;
    while (cnt < extHead.fileNum) {
        if (ext_head_vaild_check(&gupdateTable[cnt])) {
            printf("ext_head_vaild_check gupdateTable head err!\n");
            sock_err = 1;
            goto _out_;
        } else {
            if (0 == memcmp(gupdateTable[cnt].fileName, "appUpdate.ufw", strlen("appUpdate.ufw"))) {
                printf("find %s\n", gupdateTable[cnt].fileName);

                /* 升级flash时需要转换到flash设备 */
                switch_upgrade_dev(FLASH_DEV_TYPE);//flash
            } else if (0 == memcmp(gupdateTable[cnt].fileName, "exAppUpdate.ufw", strlen("exAppUpdate.ufw"))) {
                printf("find %s\n", gupdateTable[cnt].fileName);

                /* 升级SD时需要转换到SD设备 */
                switch_upgrade_dev(SD_DEV_TYPE);//sd
            } else {
                printf("unable to find the upgrade firmware\n");
                sock_err = 1;
                goto _out_;
            }

            /* 获取当前设备的固件版本号 */
            get_current_version(g_currentVersion);
            if (0 == strcmp(gupdateTable[cnt].version, g_currentVersion)) {
                printf("the upgrade firmware version information is the same\n");
                strcpy(get_latest_version(), gupdateTable[cnt].version);
                cnt++;
                continue;
            } else {
                //版本号不一致更新版本信息
                strcpy(get_latest_version(), gupdateTable[cnt].version);
            }

            update_fd = net_fopen(CONFIG_UPGRADE_OTA_FILE_NAME, "w");
            if (!update_fd) {
                log_e("open update_fd error\n");
                goto _out_;
            }

            int dsize = 0;
            hdl->file_size = gupdateTable[cnt].length;
            hdl->download_len = 0;
            while (hdl->ctx.req_exit_flag == 0) {
                dsize = (hdl->file_size - hdl->download_len) > hdl->recv_buf_size ? hdl->recv_buf_size : (hdl->file_size - hdl->download_len);
                ret = hdl->download_ops->read(&hdl->ctx, (char *)hdl->recv_buf, dsize);//最大接收为recv_buf_size
                if (ret <  0) {  //读取数据失败
                    sock_err = 1;
                    goto _out_;
                } else {
                    hdl->download_len += ret;
                    err = net_fwrite(update_fd, hdl->recv_buf, ret, 0);
                    if (err != ret) {
                        goto _out_;
                    }
                }

                if (hdl->download_len >= hdl->file_size) {
                    printf("download %s success!\n", gupdateTable[cnt].fileName);
                    total++;
                    sock_err = 0;
                    net_fclose(update_fd, sock_err);
                    update_fd = NULL;
                    break;
                }
            }

            hdl->download_len = 0;
            cnt++;
        }
    }

_out_:

    if (total) {
        system_reset();
    }

    //关闭网络连接
    hdl->download_ops->close(&hdl->ctx);

    if (update_fd) {
        net_fclose(update_fd, sock_err);
        update_fd = NULL;
    }
    free(hdl->url);
    free(hdl->recv_buf);
    free(hdl);

}

注意事项:

扩展外部存储器升级涉及到FLASH升级和外部存储器升级,其isd_config.ini配置文件分别由isd_config_rule.c和isd_config_rule_loader.c编译生成,在编译升级固件时需要对对应ini文件中BR22_TWS_VERSION版本号进行递增,否则会出现升级完成后再次在线烧录程序时跑的还是旧程序的情况,这时候需要重新擦除整块flash或重新格式化SD卡。