查看其它库函数说明,请点击此处跳转到SeanLib主页
1. 本篇内容
在需要频繁地向SD卡中写入日志的项目中,为了减少SD卡实际的擦写频次,提高SD卡寿命,特别设计了本函数库,原理是在单片机的备份区SRAM中开辟一块缓存区,多次少量的写入操作,都会将数据暂存在此缓存中,积累到一定数量或距离上一次写入达到一定时间时,再执行实际向SD卡中写入的操作,即落盘。
2. 使用说明
要使用该功能,请将SDCache.h和SDCache.c两个文件添加到项目工程中。
2.1 头文件介绍
头文件中提供了两个自定义的宏,请根据自己的项目情况进行设置:
#defineCACHE_USE_OS1#defineCACHE_SAVE_PERIOD60000CACHE_USE_OS:设置当前工程是否使用os系统,这决定了如何执行落盘、是否创建互斥锁来保护缓存区。
如果使用了os,本函数库将支持发送任务通知,唤醒指定的任务执行落盘,而且本函数库将会自动创建互斥量,以确保线程安全。
如果不使用os,则不会发送任务通知,也不会创建互斥锁。
为何这样设计?
在使用os的项目中,任何任务都可能会添加log,添加log之后都有可能触发落盘,而落盘的过程需要耗费较长的时间(经过实测,写入一个小文件,打开文件、写入文件、关闭文件的过程耗费了40-50ms),因此需要将落盘的代码执行过程放到优先级较低的任务中执行,这样当添加完一条log进入缓存区后,会发送一个任务通知,唤醒落盘任务开始执行,避免影响重要任务的实时性。
CACHE_SAVE_PERIOD:设置自动保存周期
每次添加完一条log,都会记录下当前时刻,在调用Cache_AutoSave执行自动保存时,距离上次添加记录的时间超过该值,将执行落盘,详细过程后面再细讲。
本库提供了下面几个函数:
#ifCACHE_USE_OS//使用os时,支持唤醒任务/******************************************************************************* * 功 能: [可选] 设置唤醒任务,设置后在每次调用 Cache_Write 函数向缓存区中写入数 * 据时,都会检查是否需要执行落盘,若需要则会向唤醒任务发送任务通知,若未 * 设置唤醒任务,也可以通过定时调用 Cache_AutoSave 函数来执行落盘 * 参 数: * Task: 指定一个接收任务通知的任务句柄,通常在该任务中执行落盘 * 返回值: 无 *******************************************************************************/voidCache_SetWeakupTask(xTaskHandle Task);#endif/******************************************************************************* * 功 能: 设置指定文件缓存区的文件保存路径,如果设置的路径与之前存在的路径不同, * 会立即将缓存区中已有的数据存入原来的路径,如果保存失败,旧数据不会丢 * 失,而新路径仍会生效 * 参 数: * FileNum : 指定的文件缓存区,范围0-2 * Path : 文件的完整路径,指定的文件不存在时会自动创建,但目录需要自行创建 * 返回值: 执行成功返回Sean_OK,否则返回Sean_NG *******************************************************************************/Sean_Result_tCache_SetPath(unsignedcharFileNum,char*Path);/******************************************************************************* * 功 能: 向指定文件缓存区中写入数据 * 参 数: * FileNum : 指定的文件缓存区,范围0-2 * format : 格式化字符串 * ... : 可变参数 * 返回值: 执行成功返回Sean_OK,否则返回Sean_NG *******************************************************************************/Sean_Result_tCache_Write(unsignedcharFileNum,constchar*format,...);/******************************************************************************* * 功 能: 自动保存函数,需要定时调用,或在保存任务唤醒后调用,调用该函数并不一定 * 会实际执行落盘,而是先检查是否需要落盘,条件满足(缓存区数据将满或距离 * 上次写入记录时间超过一定值)时才会实际执行落盘,但在系统启动后的第一次 * 调用时一定会保存一次 * 参 数: 无 * 返回值: 执行成功返回Sean_OK,否则返回Sean_NG *******************************************************************************/Sean_Result_tCache_AutoSave(void);注释很详细,就不再细讲了。
另外编写实现一个外部函数,需要在SeanLib.c中实现:
//获取系统滴答计数器,单位ms,裸机程序中用HAL_GetTick, 其它系统中可自定义externunsignedintSean_GetTick(void);2.2 初始化
本库已通过MSH创建一个初始化命令,会自动初始化备份区SRAM并创建三份缓存区,也就是说支持三个文件的缓存写入操作,每个缓存区1K字节。缓存区中设置有初始化标志,不会重复初始化,但当取下VBAT的电池,整个缓存区将会被重置。
用户代码中只需要设置文件保存路径即可:
Cache_SetPath(0,"0:/Log/log_20260701.txt");Cache_SetPath(1,"0:/Log/data_20260701.txt");Cache_SetPath(2,"0:/Log/error_20260701.txt");然后设置唤醒任务:
Cache_SetWeakupTask(Task_Log);2.3 添加log到缓存区
可以编写一些简化的操作宏:
#defineAdd_SysLog(format,...)Cache_Write(0,format,##__VA_ARGS__)在用户任务中按照想要的格式添加log到缓存区:
Add_SysLog("[%02hhu:%02hhu:%02hhu:%03hu] [%s] [%s]: %s\r\n",Log->Hour,Log->Min,Log->Sec,Log->SubSec,Log->LogType,Log->TaskName,Log->LogMsg);2.4 执行落盘
在Task_Log任务中执行自动落盘:
voidStartTaskLog(void*argument)//log保存任务{while(1){Cache_AutoSave();//将本次上电之前已存在的数据落盘ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//等待函数库唤醒任务}}如果没有设置唤醒任务,可以在一个优先级低一些的任务中定时调用Cache_AutoSave函数。
Cache_AutoSave函数中的部分代码:
if(Sean_GetTick()-Cache.Tick>CACHE_SAVE_PERIOD)//需要保存时才调用保存函数{if(Save_One(FileNum)!=Sean_OK){srt=Sean_NG;}}