杰里蓝牙软件掉电保存功能 实现杰里蓝牙掉电保存参数功能主要有两种方式。原文参考杰里技术支持给的链接NOTE: 记忆、保存数据到内部flash · Issue #I6N47C · 珠海杰理科技/fw-AC63_BT_SDK - Gitee.com一杰里SDK提供VM区域杰里sdk已经做好机制双备份存储循环写方式擦除均衡机制。#defineCONFIG_VM_LEAST_SIZE 8K//必须按照8K的倍数递增最小只能定8K假如定8K大小实际最大只能存4K数据最好是定义16K以上的。syscfg_id.c/* --------------------------------------------------------------------------*/ /** * brief 读取对应配置项的内容 * * param [in] item_id 配置项ID号 * param [out] buf 用于存储read数据内容 * param [in] len buf的长度(byte), buf长度必须大于等于read数据长度 * * return 1)执行正确: 返回值等于实际上所读到的数据长度(大于0); * 2)执行错误: 返回值小于等于0, 小于0表示相关错误码; */ /* --------------------------------------------------------------------------*/ int syscfg_read(u16 item_id, void *buf, u16 len);/* --------------------------------------------------------------------------*/ /** * brief 写入对应配置项的内容 * * param [in] item_id 配置项ID号 * param [in] buf 用于存储write数据内容 * param [in] len buf的长度(byte), buf长度必须大于等于write数据长度 * * return 1)执行正确: 返回值等于实际上所读到的数据长度(大于0); * 2)执行错误: 返回值小于等于0, 小于0表示相关错误码; */ /* --------------------------------------------------------------------------*/ int syscfg_write(u16 item_id, void *buf, u16 len);举个例子保存设置的蓝牙名称memcpy(g_bak_run_par.dev_name, cmd-pars[1], strlen(cmd-pars[1])); \\获取设置蓝牙名称ble_at_set_name(g_bak_run_par.dev_name,strlen(g_bak_run_par.dev_name)); \\设置蓝牙名称syscfg_write(CFG_USER_DEV_NAME,g_bak_run_par.dev_name,sizeof(g_bak_run_par.dev_name));查询蓝牙名称syscfg_read(CFG_USER_DEV_NAME,g_bak_run_par.dev_name,sizeof(g_bak_run_par.dev_name));write和read都使用同一个IDCFG_USER_DEV_NAME根据杰里参考文档1-59是个用户自定义设置IDCFG_USER_DEV_NAME我给设置的是8,注意每个ID最多只能写512字节数据 最多10ID 单个数据大于512B的采用第二种方式二用户自定义flash区域2.1自定义VM区域大小在ac632n_sdk\cpu\bd19\tools\isd_config_rule.c下添加注意添加4K对齐的定义操作flash必须按照4K的操作禁用页256的操作方式。FORCE_4K_ALIGN YES; // force aligin with 4k bytesSPECIAL_OPT 0; // only generate one flash.bin//COMPACT_SETTING YES; // USERIF_FILE100kb.bin;需要指定文件时需要加这项//区域的大小必须是4K的倍数//放在 [RESERVED_CONFIG] 内不能放到 [RESERVED_EXPAND_CONFIG] 里//#USERIF_ADR0x50000;USERIF_ADRAUTO; //AUTO表示自动经跟上一个区域后面USERIF_LEN0x4000; //16KUSERIF_OPT1;添加成功后编译出的蓝牙软件是这样的带USERIF_RESERVED_SIZE,大小和自己设置一样是16k。2.2代码驱动flash.c#if 1 // flash_param.c #include jm_flash.h #include system/includes.h #include asm/includes.h #include jm_default.h #include ../../../../include_lib/driver/cpu/bd19/asm/crc16.h #define LOG_TAG_CONST BOARD #define LOG_TAG [BOARD] #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include debug.h #if USER_VM_LAST_FLASH #if 0 #define log_info(x, ...) log_prints(\n[LE_AT_CHAR_COM] x , ## __VA_ARGS__) #else #define log_info(x, ...) #endif //用户自定义区域大小 16K #define FLASH_AREA_NAME SDFILE_APP_ROOT_PATH USERIF #define CFG_OFFSET 0 // 从区域开头存放 #define SECTOR_SIZE 512 // 4KB 扇区 extern u16 CRC16(const void *ptr, u32 len); static FILE *g_flash_fp NULL; // 初始化 Flash 区域句柄懒加载 static FILE *jm_get_flash_fp(void) { if (g_flash_fp NULL) { g_flash_fp fopen(FLASH_AREA_NAME, rw); if (g_flash_fp NULL) { log_info(open %s fail!\n, FLASH_AREA_NAME); } } return g_flash_fp; } // 计算配置体的 CRC排除 crc32 字段本身 static u16 jm_compute_cfg_crc(const backup_runing_parameter_struct *cfg) { // 计算从 magic 到 mac_addr 的 CRC即总大小减去 crc32 字段 u32 data_size sizeof(g_bak_run_par) - sizeof(cfg-crc16); return CRC16((const void *)cfg, data_size); } // 擦除整个用户区域按 4K 扇区 int jm_flash_cfg_erase(void) { FILE *fp jm_get_flash_fp(); if (!fp) return -1; struct vfs_attr attr {0}; fget_attrs(fp, attr); u32 flash_addr sdfile_cpu_addr2flash_addr(attr.sclust); u32 total_size attr.fsize; log_info(erase flash area: 0x%x, size%dKB\n, flash_addr, total_size / 1024); while (total_size 0) { clr_wdt(); if (!sfc_erase(SECTOR_ERASER, flash_addr)) { log_info(erase failed at 0x%x\n, flash_addr); return -2; } flash_addr SECTOR_SIZE; total_size - SECTOR_SIZE; } fseek(fp, 0, SEEK_SET); return 0; } // 保存配置先擦后写 int jm_cfg_save(const backup_runing_parameter_struct *cfg) { if (!cfg) return -1; FILE *fp jm_get_flash_fp(); if (!fp) return -2; // 1. 构造临时配置体带 CRC static backup_runing_parameter_struct tmp_cfg __attribute__((aligned(4))); memset(tmp_cfg,0,sizeof(backup_runing_parameter_struct)); jm_cfg_load(tmp_cfg); if(!memcmp(cfg, tmp_cfg ,sizeof(backup_runing_parameter_struct))) { goto end; } else { log_info(save start); } memcpy(tmp_cfg, cfg, sizeof(backup_runing_parameter_struct)); // 2. 清除旧 CRC计算新 CRC tmp_cfg.crc16 0; tmp_cfg.crc16 jm_compute_cfg_crc(tmp_cfg); // 3. 擦除简化擦整个区域 int ret jm_flash_cfg_erase(); if (ret) return ret; // 4. 写入带 CRC 的完整结构体 fseek(fp, CFG_OFFSET, SEEK_SET); int wrote fwrite(fp, tmp_cfg, sizeof(backup_runing_parameter_struct)); if (wrote ! sizeof(backup_runing_parameter_struct)) { log_info(write cfg fail! %d\n, wrote); return -3; } log_info(save cfg with CRC0x%08x\n, tmp_cfg.crc16); end: log_info(save end); return 0; } // 加载配置带校验 int jm_cfg_load(backup_runing_parameter_struct *cfg) { if (!cfg) return -1; FILE *fp jm_get_flash_fp(); if (!fp) return -2; static backup_runing_parameter_struct tmp_cfg __attribute__((aligned(4))); fseek(fp, CFG_OFFSET, SEEK_SET); int read fread(fp, tmp_cfg, sizeof(backup_runing_parameter_struct)); if (read ! sizeof(backup_runing_parameter_struct)) { log_info(short read: %d\n, read); return -3; } // 1. 校验魔数和版本 if (tmp_cfg.magic ! USER_CFG_MAGIC) { log_info(magic/version mismatch\n); return -4; } // 2. 校验 CRC u16 expected_crc tmp_cfg.crc16; tmp_cfg.crc16 0; // 清除 CRC 字段再计算 u16 actual_crc jm_compute_cfg_crc(tmp_cfg); if (actual_crc ! expected_crc) { log_info(CRC error! expect0x%08x, actual0x%08x\n, expected_crc, actual_crc); return -5; // CRC 校验失败 } memcpy(cfg, tmp_cfg, sizeof(backup_runing_parameter_struct)); log_info(load cfg ok, CRC0x%08x\n, actual_crc); return 0; } void track_cfg_parameter_save(void) { jm_cfg_save(g_bak_run_par); } #endif void track_cfg_parameter_init(void) //先读 读到错误的值 按照default参数来 { #if USER_VM_LAST_FLASH if(jm_cfg_load(g_bak_run_par)0) { log_info(parameter_Init_OK); } #endif } #endifflash.h#ifndef _JM_FLSH_ID_H_ #define _JM_FLSH_ID_H_ #include stdint.h #include app_config.h #include jm_gpio.h #include system/includes.h #include device/key_driver.h #include asm/chargestore.h #include asm/charge.h #include asm/power/p33.h #include rtc_alarm.h #include asm/pwm_led.h #include user_cfg.h #include usb/otg.h #include norflash.h #include board_ac632n_demo_cfg.h #include os/os_api.h #include stdint.h #define USER_CFG_MAGIC 0x5A5AA5A5 #if USER_VM_LAST_FLASH typedef enum _FLASH_ERASER { CHIP_ERASER, BLOCK_ERASER, SECTOR_ERASER, PAGE_ERASER, } FLASH_ERASER; // 接口声明 int jm_cfg_load(backup_runing_parameter_struct *cfg); int jm_cfg_save(const backup_runing_parameter_struct *cfg); int jm_flash_cfg_erase(void); void track_cfg_parameter_save(void); void track_cfg_parameter_init(void); #else #define CFG_USER_STRUCT1 2 #define CFG_USER_STRUCT2 3 #define CFG_USER_STRUCT3 4 #define CFG_USER_STRUCT4 5 #define CFG_USER_STRUCT5 6 #define CFG_USER_MACADRESS 7 #define CFG_USER_DEV_NAME 8 #define CFG_USER_STRUCT6 9 #endif #endif两种方式都可以实现掉电参数保存逻辑第二种方式可以在board_ac632n_demo_global_build_cfg.h中加入以下功能宏。#define USER_VM_LAST_FLASH 0想使用第二种方式直接置1就行建议使用第二种方式track_cfg_parameter_init一定要放在杰里case启动初始化要不一些参数加载不对我现在放的地方是atchar_app_start这个接口函数下。