内部FLASH空间添加用户自定义区域
📎 原始文档:https://www.kdocs.cn/l/cvVDkgDEyHZO 内部FLASH空间添加用户自定义区域
内部FLASH空间添加用户自定义区域
参考资料: 添加用户自定义区域 :https://kdocs.cn/l/cszLZSN40zWI
补充说明如下:
- 修改 isd_config.ini 文件, 配置 user_info 区域大小和名称 注意:有些SDK是修改isd_config_rule.c,编译的时候由isd_config_rule.c去生成isd_config.ini 注意修改的位置
isd_config.ini 修改如下:
USER_ADR=AUTO;
USER_LEN=0x1000;
USER_OPT=1;
添加 user 存储区域描述, 名称为”USER”。需要跟程序上的fopen对应。如下图。
USER_ADR=AUTO; 表示地址由工具自动分配, 默认即可, 用户不需要修改;
USER_LEN=0x1000; 表示区域大小, 用户可以根据需求配置;
USER_OPT=1; 表示下载代码时对该区域的操作, 用户一般不需要修改;
2. 修改 sdk_ld 文件中的链接地址 
- 如果是0x1E00020的话,就改成0x1E00040 3. 添加 user_info_demo.c 文件中的代码测试 相关接口:读写接口跟文件系统接口一致,但由于flash的特性需要写之前需要自己去管理擦除user_info区域。
//打开文件
fp = fopen("mnt/sdfile/app/user", "wr"); //字符: mnt/sdfile/app/ 固定; 字符user对应ini文件里配置的名称
//文件偏移
fseek(user_fp, -wbuf_len, SEEK_CUR); //把文件指针移到刚写的位置
//把buf写入区域
ret = fwrite(user_fp, wbuf, wbuf_len);
//读出写入buf内容
fread(user_fp, rbuf, rbuf_len);
//擦除区域操作(物理地址)
norflash_ioctl(NULL, erase_cmd, erase_addr);
#if 1//for test
FILE *user_fp; //区域读写句柄
struct vfs_attr attr = {0}; //user_info区域属性, sclust: 区域地址, fsize: 区域大小
extern FILE *user_info_zone_init(struct vfs_attr *attr);
user_fp = user_info_zone_init(&attr);
int ret = 0; //区域操作返回值
u8 r_test_buf[80] = {0};
u8 test_buf[32] = {0};//test buffer
for(int i=0;i<sizeof(test_buf);i++) {
test_buf[i]=i;//给数组赋值
}
while(1)
{
os_time_dly(100);
//写 从起始地址开始写
ret = fwrite(user_fp, test_buf, sizeof(test_buf));
printf("write len = %d\r\n",ret);
if(ret != sizeof(test_buf)) {
printf("write err\r\n");
printf("earse all\r\n");
user_info_zone_earse(user_fp, &attr);
}
// 读出刚才写入的内容, 检查是否写入正确;
fseek(user_fp, -sizeof(test_buf), SEEK_CUR); //把文件指针移到刚写的位置
ret = fread(user_fp, r_test_buf, sizeof(test_buf));//读32个字节
if(ret != sizeof(test_buf)) {
printf("read err\r\n");
}
put_buf(r_test_buf,sizeof(r_test_buf));
}
#endif
- 自定义区域保存音频文件,并播放出来demo 测试代码:
- 添加从提示音资源区copy文件到自定义区接口
int user_copy_file(const char *src_file_name,const char *dest_file_name){
FILE *src_file = NULL;
FILE *dest_file = NULL;
char buff[100];
int ret;
src_file=fopen(src_file_name,"rb");
if(!src_file)return -1;
dest_file=fopen(dest_file_name,"wb");
if(!dest_file)return -1;
struct vfs_attr attr;
ret=fget_attrs(src_file,&attr);
g_printf("ret -- %d fsize=%d\r\n",ret,attr.fsize);
do{
ret=fread(src_file,buff,sizeof(buff));
r_printf("read file ret=%d\r\n",ret);
if(ret>0)fwrite(dest_file,buff,ret);
}while(ret>0);
fclose(src_file);
fclose(dest_file);
return 0;
}
- 初始化调用添加接口
//提示音写入 user_info
//ini RESERVED_CONFIG 区路径 "mnt/sdfile/app/"
//ini RESERVED_EXPAND_CONFIG 区路径 ""mnt/sdfile/EXT_RESERVED/"
int uret=user_copy_file("mnt/sdfile/res/tone/0.wtg","mnt/sdfile/app/user");
- 播放自定义接口中保存的提示音
//ini RESERVED_CONFIG 区路径 "mnt/sdfile/app/"
//ini RESERVED_EXPAND_CONFIG 区路径 ""mnt/sdfile/EXT_RESERVED/"
tone_play_by_path("mnt/sdfile/app/user", 0);
- ini文件配置为下载程序时擦除USER区域
[RESERVED_CONFIG]
USER0_ADR=AUTO;
USER0_LEN=0xf000;
USER0_OPT=0;
[RESERVED_EXPAND_CONFIG]
USER1_ADR=AUTO;
USER1_LEN=0xf000;
USER1_OPT=0;
- 如果需要换提示音需要擦除一遍该区域再进行更改数据,参考内部FLASH空间添加用户自定义区域
- DEMO:
696:下载、 内部
695:下载、 内部 (下载代码之后需要换库修复进music死机问题)
- 703soundbox 200P01 SDK 内部flash读写参考代码 测试SDK版本:
参考代码包括:
- 文件目录的初始化
- 文件的创建

- 703soundbox 200P02 SDK 内部flash读写参考代码
- demo cfg_private.c

- 注意点:
- 接口说明 /**
- 此cfg_private_write接口有如下问题:
- 使用resfile_seek接口偏移后resfile_get_pos获取到的值不正确,在改写数据时达不到想要的效果
*/
- 使用resfile_seek接口偏移后resfile_get_pos获取到的值不正确,在改写数据时达不到想要的效果
/**
* 此cfg_private_write接口有如下问题:
* 1. 使用resfile_seek接口偏移后resfile_get_pos获取到的值不正确,在改写数据时达不到想要的效果
*/
int cfg_private_write(RESFILE *file, void *buf, u32 len)
{
struct resfile_attrs attrs = {0};
//获取当前资源文件的属性信息
resfile_get_attrs(file, &attrs);
/* r_printf(">>>[test]:attrs.sclust = 0x%x\n", attrs.sclust); */
//地址映射 cpu addr -> flash addr
attrs.sclust = sdfile_cpu_addr2flash_addr(attrs.sclust);
//获取当前资源文件读写指针位置
u32 fptr = resfile_get_pos(file);
/* r_printf(">>>[test]:addr = 0x%x, fsize = %d, fptr = %d, w_len = %d\n", attrs.sclust, attrs.fsize, fptr, len); */
if (len + fptr > attrs.fsize) {
r_printf(">>>[test]:error, write over!!!!!!!!\n");
return -1;
}
char *buf_temp = (char *)malloc(cp_hdl->cfg_private_align_size);
int res = len;
for (int wlen = 0; len > 0;) {
//计算写地址对应的flash对齐块地址
u32 align_addr = (attrs.sclust + fptr) / cp_hdl->cfg_private_align_size * cp_hdl->cfg_private_align_size;
//计算块内偏移地址
u32 w_pos = attrs.sclust + fptr - align_addr;
//计算写数据的长度
wlen = cp_hdl->cfg_private_align_size - w_pos;
/* y_printf(">>>[test]:wpos = %d, wlen = %d\n", w_pos, wlen); */
//判断块数据长度大于写的数据长度,如果小于需要再次写一个块单位的数据
if (wlen > len) {
wlen = len;
}
//判断数据是否需要擦除
norflash_read(NULL, (void *)buf_temp, cp_hdl->cfg_private_align_size, align_addr);
if (0 == cfg_private_check(buf_temp, cp_hdl->cfg_private_align_size)) {
cfg_private_erase(align_addr, cp_hdl->cfg_private_align_size);
}
//把需要些的数据复制到文件最后
memcpy(buf_temp + w_pos, buf, wlen);
/* put_buf(buf_temp, cp_hdl->cfg_private_align_size); */
//写一个块单位的数据
norflash_write(NULL, (void *)buf_temp, cp_hdl->cfg_private_align_size, align_addr);
fptr += wlen;
len -= wlen;
}
__exit:
//是否临时申请到的内存
free(buf_temp);
//文件偏移到末尾
resfile_seek(file, fptr, SEEK_SET);
return res;
}
2. 参考修改(未经过测试)
int cfg_private_write(RESFILE *file,u32 offset, void *buf, u32 len)
{
struct resfile_attrs attrs = {0};
//获取当前资源文件的属性信息
resfile_get_attrs(file, &attrs);
/* r_printf(">>>[test]:attrs.sclust = 0x%x\n", attrs.sclust); */
//地址映射 cpu addr -> flash addr
attrs.sclust = sdfile_cpu_addr2flash_addr(attrs.sclust);
//获取当前资源文件读写指针位置
u32 fptr = resfile_get_pos(file);
//error
if(offset>fptr)return -1;
if(offset && offset<fptr)fptr=offset;
/* r_printf(">>>[test]:addr = 0x%x, fsize = %d, fptr = %d, w_len = %d\n", attrs.sclust, attrs.fsize, fptr, len); */
if (len + fptr > attrs.fsize) {
r_printf(">>>[test]:error, write over!!!!!!!!\n");
return -1;
}
char *buf_temp = (char *)malloc(cp_hdl->cfg_private_align_size);
int res = len;
for (int wlen = 0; len > 0;) {
//计算写地址对应的flash对齐块地址
u32 align_addr = (attrs.sclust + fptr) / cp_hdl->cfg_private_align_size * cp_hdl->cfg_private_align_size;
//计算块内偏移地址
u32 w_pos = attrs.sclust + fptr - align_addr;
//计算写数据的长度
wlen = cp_hdl->cfg_private_align_size - w_pos;
/* y_printf(">>>[test]:wpos = %d, wlen = %d\n", w_pos, wlen); */
//判断块数据长度大于写的数据长度,如果小于需要再次写一个块单位的数据
if (wlen > len) {
wlen = len;
}
//判断数据是否需要擦除
norflash_read(NULL, (void *)buf_temp, cp_hdl->cfg_private_align_size, align_addr);
if (0 == cfg_private_check(buf_temp, cp_hdl->cfg_private_align_size)) {
cfg_private_erase(align_addr, cp_hdl->cfg_private_align_size);
}
//把需要些的数据复制到文件最后
memcpy(buf_temp + w_pos, buf, wlen);
/* put_buf(buf_temp, cp_hdl->cfg_private_align_size); */
//写一个块单位的数据
norflash_write(NULL, (void *)buf_temp, cp_hdl->cfg_private_align_size, align_addr);
fptr += wlen;
len -= wlen;
}
__exit:
//是否临时申请到的内存
free(buf_temp);
//文件偏移到末尾
resfile_seek(file, fptr, SEEK_SET);
return res;
}
- AC210N_le_audio_1.3.1 内部flash自定义区的读写 AC210N音箱问题整理.otl
注:
1. FLASH的对齐方式可以从code block的编译输出信息中看到(256Bytes对齐、4K对齐),如下图:

b.4K对齐的FLASH如果使用page擦除,会导致擦除的sector出现不可预知的错误,所以操作FLASH的时候请考虑操作的规范性
