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卡。