本文简要介绍一下本人在Cortex-M3系统的STM32F10x芯片上开发的一个日志文件系统(与其说是系统,不如说是小小的库)。该库的特点是将在STM32F10x芯片上处理数据(历史记录)变得简单可靠。因为我所做的项目基本上都为监控系统,需要记录各种各样的日志,并可随时上传至中心服务器。利用该库就可以很容易的使用该接口完成数据的初始化、读取、写入和删除。而且随着该库的应用,稳定性也得到了验证,应用到其它项目中也更有底气了。本库分两部分,一部分支持内部FLASH,一部分支持外部FLASH。由于硬件设计的成本考虑,我们经常需要考虑使用内部FLASH或者外部FLASH的情况,在存储数据量不大,而选择的芯片内部FLASH空间足够时,就可以将数据存储在内部FLASH,当数据量大时,却可以选择存储在外部FLASH,对于应用程序而言,只需要修改一下宏定义来映射不同的库函数即可。
通过该库在我的项目团队中应用,感觉对于数据存储部分,大家都形成了共识,代码基本上不用考虑FLASH地址如何计算,如果确保擦写平均等……通过应用此库,并规划好空间分析,并设定,即可开始记录的操作了。
以下是关于此库的简要说明:
内部FLASH采用的是NOR FLASH,它的特点是芯片内执行,这样应用程序就可以直接在FLASH闪存内运行,不必再把代码读到系统的RAM中。日志文件系统的主要功能是以内部FLASH地址作为存储空间(直接使用内部FLASH空间,减少硬件成本),在Cortex-M3系统CPU芯片上,按页建立的一个日志文件系统。可以使用一个或者多个连续页建立一个文件系统,一个文件系统可以存储一个定长的数据,对于不定长的数据,可以按最长长度建立。日志文件系统提供统一的API接口来访问内部FLASH中的数据,同时提供了FLASH空间的平均擦除算法,提高FLASH的使用寿命。日志文件系统按记录方式进行存储,对于需要提供记录方式的应用来说,利用此文件系统可以带来很大的便利性。例如:某监控系统需要记录历史告警信息,又或某系统需要记录操作日志记录等。
内部文件系统按扇区建立记录引导表,通过扇区引导表可快速定位记录位置,同时,通过扇区记录表的信息可以快速建立文件的信息(记录数量,第一条记录位置)等。一般情况下,每个扇区为2K,为充分利用文件系统的优势,记录长度应该远小于2K,以确保合适的空间利用率。建立扇区数据存储可支持不定长记录,同时存储了数据的校验和,同时数据写入后会立马会立即校验,以确保数据成功写入,失败时会继续向后写入,以应对各种异常情况。数据存储时会写入的数据长度,读取时也会有返回写入的长度,写入时会生成校验,并在写入和读取时都会校验。
本文件系统具有任意时间掉电恢复功能,由于操作FLASH过程中,程序出现意外死机或者掉电,程序可最大限度的保障用户数据的安全,并对数据进行恢复。日志文件系统使用连续的空间作为其存储的条件,因此,在日志文件系统中的记录是有序的,不能从中间删除记录,以确保日志记录的完整性。
本说明书编写的目的是介绍内部FLASH日志文件系统包含的API函数信息、各函数的作用、各函数如何调用和它们之间的关联以及一些结构体和常数的定义。本说明可作为初级程序员使用本文件系统的程序员在使用FLASH时的参考手册。
主要优势:
1、 日志化记录结构,方便操作和维护
2、 平均擦除算法,提高FLASH使用寿命
3、 异常掉电自恢复功能
4、 数据校验功能
5、 无需操作系统支持,简单易用
日志文件系统使用的方式非常简单,通过简单三步即可完成日志文件系统的使用。
(1) 定义一个文件描述句柄对象
定义文件描述句柄对象,是使用前必须完成的一步,该描述定义了本文件系统使用扇区开始地址(注意:不般在程序用不到的空间之外的连续空间)、使用扇区的数量、本文件的区别ID,本日志文件记录的最大数据长度,提供了上述信息之后,就已经定义好了一个日志文件了。
文件描述句柄对象一般为全局变量,一般可在文件范围内使用,同时必须保存句柄在整个程序运行期内有效。
(2) 初始化文件句柄
定义的文件描述句柄仅仅指定了文件需要使用的空间位置,日志记录大小,区别ID等信息,为正式开始使用此日志文件,需要对其进行初始化,包括扫描指定FLASH地址的各扇区,确保与文件描述句柄描述一致,当出现不一致的扇区时,将会重新初始化此扇区。同时初始化会扫描扇区分区表信息,获取扇区内记录数量。
(3) 使用文件句柄
当初始化成功后,就可以开始使用此日志文件了。包括取得日志文件中包括的记录数量,读取、删除其中的记录,向日志文件记录中添加记录等操作。
一段简单应用的示例:
// 从FLASH中加载某一配置,成功返回0,否则返回1 // 通过使用日志文件系统的方式,将可以占用一个扇区来存放配置(实际上你必须这么做- // FLASH只能按扇区擦除),如此,当多次配置变更时,通过写入一条新的配置,并删除 // 前一条配置(置删除标志),正至配置写完整个区域,才会开始删除扇区,并开始新的写入。 // 以下示例占用一个扇区的空间作为配置空间,写的配置的大小由参数设定(cfg) lfsin_t lfs_cfg = { FLASH_OFFSET_CFG, // 开始地址偏移(FLASH的偏移地址) 0, // 开始页 1, // 结束页[start, end),占用一页空间 LFS_TYPE_CFG, // 该文件系统的类型标识 sizeof(cfg_t), // 记录的日志最大大小 -1, // 用于内部表示开始的头(写的位置),不需要指定 -1, // 用于内部表示开始的尾(读的位置) ,不需要指定 0, // 可用日志记录条数,不需要指定 0 // 当前读的头位置(扇区的ID位置) ,不需要指定 }; int LoadCfg(void) { int rdlen; int buf[10]; unsigned char cfg_load = 0; lfsin_load(&lfs_cfg); // 初始化日志文件(扫描记录区) // 读取第一条记录(如果有一条配置信息),buf用来存放读出数据的缓冲区 rdlen = lfsin_read(&lfs_cfg, 0, buf, sizeof(cfg_t)) // 判断读取的数据是否符合要求 if (rdlen == sizeof(cfg_t)){ cfg_load = 1; // 成功加载配置 return 0; }else{ cfg_load = 0; //加载配置失败 return 1; } }