×
嵌入式 > 技术百科 > 详情

nRF5 SDK模块深度解析:FDS用法及常见问题应对策略

发布时间:2020-06-13 发布时间:
|

FDS,全称Flash Data Storage,用来访问芯片内部Flash的。当你需要把数据存储在Flash中,或者读取Flash中的用户数据,或者更新或者删除Flash中的数据,那么FDS模块是你最好的选择。FDS采用文件和记录方式来组织Flash数据,也就是说,真正的数据是放在一条记录中,而多条记录组成一个文件。根据应用的需要,整个系统可以只有一个文件,也可以包含多个文件。文件采用文件ID来标示,文件ID为2个字节(注:不能取值为0xFFFF)。一个文件下面可以放一条记录,也可以放多条记录,记录是通过记录key来标示的,记录key也是2个字节长度(注:不能取值为0x0000)。这里需要注意的是,同一个文件下面的两条或者多条记录他们的key可以是一样的,比如我们可以建立如下文件系统:文件1包含2条记录,文件2包含3条记录,文件2包含2条key为0x0003的记录。

注:如果你可以保证一个文件下面所有记录的key都不一样,那么文件系统会变得更简洁一些,尤其在find记录的时候,只会返回一条记录,可以简化很多应用逻辑。如前所述,这个不是强制要求:同一个文件下记录key可以相同。

FDS用法

一般而言,按照如下步骤使用FDS模块:

1)修改FDS的默认配置参数,比如总共分配多少Flash空间(默认只分配了8kB Flash空间给用户使用),请到sdk_config.h文件中修改如下默认配置项:

2)通过fds_register注册FDS事件回调函数及通过fds_init初始化FDS模块。FDS模块的初始化,写记录,更新记录,删除记录以及垃圾回收,这些API都是异步的。也就是说调用这些FDS操作的API,只是把相应操作放入队列然后立即返回(队列大小由上述的FDS_OP_QUEUE_SIZE控制),真正的Flash操作结果是通过事件回调函数通知你的。注:现在的FDS模块可以进行多次初始化。示例代码如下所示:

3)通过fds_record_write创建新的记录,即写记录。注意写记录的时候,必须保证输入参数是全局变量或者static的局部变量,推荐使用全局变量!由于record key可以重复,所以连续调用两次相同的fds_record_write,将生成两条同样key的记录。前面也提及过,fds_record_write是异步的,所以它的返回值为success只是表示操作入队成功,真正的flash操作结果是通过前面注册的fds_evt_handler来通知的。示例代码如下所示:

4)通过fds_record_open来读记录。读记录之前必须先找到这条记录,这个是通过fds_record_find来实现的,由于同一个文件可以包含多条key相同的记录,所以通过多次调用同一个fds_record_find,可以找到所有相关记录。示例代码如下所示:

5)操作记录,比如fds_record_update,fds_record_delete等,update和delete操作,必须先找到相应记录,然后才能去update或者delete。fds_record_delete不是真得把记录删除,而是将记录标示为无效。而fds_record_update实际包含2步:先找到之前的记录然后将其标记为无效(即delete操作),然后write一条新记录。记住:delete并不会回收Flash空间,无效记录仍然占据着Flash空间,这些无效记录占据着的Flash空间只有经过垃圾回收(fds_gc)才能再次给新记录使用。请注意fds_record_find只会去寻找有效记录,而不会将无效记录返回给用户的。另外,fds_record_ update和fds_record_delete是异步的,所以它们的返回值为success只是表示操作入队成功,真正的flash操作结果是通过前面注册的fds_evt_handler来通知的。示例代码如下所示:

6)当Flash不够用时,即FDS写记录或者更新记录操作返回错误FDS_ERR_NO_SPACE_IN_FLASH,请调用垃圾回收函数:fds_gc进行垃圾回收。fds_gc是一个非常耗时的操作过程(请确保操作过程中不会掉电,否则Flash行为未知),它会一个page一个page操作,然后将该page中的有效记录拷贝到swap page,然后擦除该page,并标记该page为swap page,而之前的swap page则变为data page,如此往复,直到把所有page都回收完。只有经过fds_gc后,之前无效记录占据的Flash空间才会释放,这个时候才会有多余的Flash空间给用户去操作。

建议大家直接参考SDK里面自带的fds例子来编写自己的fds应用代码,SDK自带的fds例子所在目录为:SDK安装目录\examples\peripheral\flash_fds(注:从SDK14之后才有fds例子)

  理解FDS

FDS作为上层模块,它是通过调用fstorage API来实现自己的功能,fstorage又是通过调用NVMC外设驱动或者softdevice Flash访问API来达到操作Flash的目的,调用关系图如下所示:

当softdevice存在的时候,建议使用nrf_fstorage_sd后端;没有softdevice的时候,请使用nrf_fstorage_nvmc后端。

根据有无bootloader,FDS将操作不同的Flash空间,如下:

当你通过FDS把数据写入Flash中,除了数据本身,FDS还会在这条记录中加入额外的信息:记录头header,一条记录在Flash中完整的格式如下所示:

所以,在计算记录总共占用多少Flash空间的时候,记得一定要把每条记录的header(3个word)也加上。

  FDS使用常见问题

大家在使用FDS模块时,经常碰到的问题有如下几种:

1.FDS不支持掉电保护,所以在Flash操作过程中出现了掉电,FDS行为将未知。

2.OTA的时候,新固件的FDS page数目一定要等于老固件的FDS page数,否则将出现不可知行为。

3.fds_record_write或者fds_record_update后,强烈建议回读该记录,以确保记录的确write或者update成功。

4.忘了给参数清0。Nordic提供的API输入参数很多都是结构体变量,这些变量使用之前,记得一定要通过memset先清0。如果忘了清0,就会出现一些匪夷所思的现象。

fds_record_desc_t desc;//={0};//错误,忘了清0

fds_find_token_t tok;//={0};//错误,忘了清0

5.忘了使用全局变量或者静态局部变量。因为write和update操作都是异步的,所以record.data.p_data必须指向全局变量或者静态局部变量,以保证Flash操作过程中p_data指向的内容不会更改。

6.变量起始地址必须字对齐。Flash操作是以word为单位的,所以要求write和update操作的p_data指向的变量的起始地址必须word对齐,大家可以使用伪汇编指令“__ALIGN(sizeof(uint32_t))”来保证该变量起始地址是word对齐的。

7.Update或者delete之前必须先find。fds_record_update或者fds_record_delete会用到参数descriptor,这个descriptor必须是通过fds_record_find返回的。

8.忘了使用fds_gc导致Flash fatal error或者其他奇奇怪怪的问题。当write或者update报FDS_ERR_NO_SPACE_IN_FLASH错误时,记得一定要调用fds_gc。或者当delete record或者update record达到一定次数后,主动调用fds_gc。或者通过查看fds_stat得到dirty record数目达到某个值后,主动调用fds_gc。

9.SDK已知问题。每个版本SDK都有或多或少的问题,这些问题都可以在Nordic devzone上查到。比如SDK12.2.0 fds_gc在某些情况下,就会有问题。所以,一般建议大家使用最新版SDK,最新版SDK会把之前发现的问题都修复掉,它的稳定性和可靠性都是最高的。

10.FDS不支持掉电保护,所以在FDS操作过程中,尤其是垃圾回收过程中,发生了掉电,那么Flash内容将变得不可靠。所以强烈建议大家:在每一次write或者update之后,都把相应记录读出来,跟原始内容进行比对,以确保记录真的写成功或者更新成功了。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
越南制造.会成为中国制造的最大敌手吗?