物联网下的RTOS开发(四)——数据存储

9 篇文章 0 订阅
8 篇文章 0 订阅

数据存储

在window或者linux下开发程序,程序员往往不会关心文件存储的介质。因为操作系统已经为我们封装好了操作硬件(磁盘)的可能会用的绝大部分接口。只要我们调用一下相关IO接口就能完成对磁盘的操作。而目前IOT设备资源很限,用于存储的可以是ram或者flash,ram的容量往往是几K字节到几百K字节,flash往往小于8M字节。这给我们带来没有很好地文件系统支持。磁盘的擦写均匀管理,掉电数据保存等问题,这些都会增加开发的复杂性,带来数据保存稳定性以及代码可移植性的问题。
接下去我会根据工作开发中遇到的经验提出几种现有的方案,以及推荐的做法。

一、存储介质

IOT系统下的存储介质可以简单分为ram和flash。我们知道ram掉电就会丢失,但是注意是掉电而不是重启。这样我们就有机会将重启原因,或者一些必须重启才能完成工作而又要保留flag的一些参数存储在这里。
Flash分为norflash 和 nandflash,IOT的设备这两种都会遇到,如果能上nandflash的系统主芯片能够提供的资源也会相对丰富,存储的接口一般会提供,这里我们主要讨论norflash。flash的特点是,只能将bit为1的地方写成0,这样写之前就要擦除操作。读取flash的速度是很快的,但是写很慢,擦除更慢。这里我用上海复旦微FM25Q16A型号为例,以下图片摘自其datasheet,可以看出擦除一个sector(4K)正常需要35ms的时间。对于MCU来说这个时间已经有点久了。如果程序设计不当,会影响其他任务的执行,甚至可能导致看门狗喂狗不及时而导致系统重启的情况。

在这里插入图片描述在这里插入图片描述

另外还有一种因为故事机和智能音箱而再次兴起的容量相对比较大的spi ram,一般在几M字节。其本质还是ram,这种存储方式在乐鑫推出的ESP32的模组方案应用中做的比较好,乐鑫官方支持了这种应用。
从长远性考虑,我们还需要关注一个参数就是flash的擦写次数,一般为10W次的数量级,需要考虑寿命。这个在本系列的产品设计相关的章节也会有具体说明。

二、物联网场景

数据存储的场景无非是存储临时变量参数,系统配置,业务数据。

  • 临时变量

临时变量一般是存在ram中,写程序的编译器已经帮我们完成了。
这里特别说一下noinit声明的变量。对于需要重启保留上一次临时变量参数用于本次操作的可以使用这种技巧。要实现这个功能需要了解嵌入式系统的初始化启动过程以及编译链接一些知识,把相关的变量指定为noinit关键字就可以。主流的编译工具IAR和MDK都支持noinit的配置。
比如WiFi的正常模式和sniffer模式切换都往往需要重启,这时可以将上一次工作的结果放在ram中而不必每次都写flash来完成。

  • 系统配置

系统配置,存在于掉电不会丢失的flash中。系统上电初始化的时候都被读取,对于这些参数有限是关键配置,有些丢失可以重新获取的。比如串口波特率,在固定场景下通讯就是关心信息,不可丢失。而服务器下发的配置,或者秘钥这些,丢了可以问服务器再要的,这些允许丢失。但是从工程师角度而言我们希望肯定是做到稳定不会丢失,接口调用也简单,移植性强。下文模型设计中重点介绍。

  • 业务数据

在物联网的应用场景中,iot设备大多用于传感接入点。也就是用于数据采集,如温度,电流电压,位置信息等上报。这些数据大多只是存在ram中,然后通过网络发送到服务器做统一存储展示分析。对于上报周期不频繁的数据,比如一个小时一次,一天一次的数据,我们需要考量这个数据的可靠性到达的设计。这种数据的来源可能是在每个小时冻结,每天零点冻结的。冻结是指需要做存储,在接下去的某个时刻需要将此数据上报的,实时性要求不高,但是数据可靠性要求很高,这样我们必须设计存储----上传----应答了。

三、模型设计

存储的应用场景不同,采用的方方法也不尽相同。这里我给出针对flash三类常用的方法,并列举各自的优缺点。

  • 直接存储

直接操作Flash芯片的存储地址,flash驱动提供常规支持以下几个函数
flash_read()
flash_write()
flash_erase()
这个模式的有点就是简单,缺点是操作过程中掉电会引起数据丢失。可以应用于现场配置参数的设定,人员在现场。以后运行过程中只读不写的场景。
此方法的很通用,设计上无需太多考虑,场景的可靠性不高的情况下,可以使用。

  • 可靠性直接存储

为了解决运行过程中数据读写,不丢失的核心思想是备份,写之前先备份。
提供一种乒乓球类似参考。

struct savePara {
    int _magic;        // 用于校验的数据合法性
    int cnt;            // 存储的计数
    
    userStruct para;  // 用户存储数据结构体
};

开辟两个独立空间(sector为单位),magic用于记录数据的合法性,magic可以随意的一个固定值,只要读出来时候对应就可以。cnt用于记录数据的新旧,cnt小的表示数据比较旧。于是有以下读写操作。
数据读取的步骤:

  1. 读取两个区块的cnt,如果magic均合法,cnt大的是最新数据
  2. 如果magic均不合法,则无数据
  3. 如果magic一个合法,则合法为新数据

数据写入步骤:

  1. 读取两个区域的结构体,记录cnt的值,并比较出该数据块的新旧;
  2. 擦除cnt小的一块,以新数据块的cnt++后,将用户数据写入
  • 文件系统应用

其实用户更多关注的业务,存储更多应该交给系统来完成,文件系统就提供了这个功能。他有统一的接口来实现数据的存储和读写,弱化硬件的相关性。另外,上面提到flash的擦写次数是有限的。如果每次存储都对同一个位置进行擦除,那么寿命就是10W次。如果每次写入flash数据的位置不在同一个地方,这样就能增加存储的次数。而这里的难点就是如何实现这个算法。这个算法也叫磨损均衡(Wear-Leveling)。一些文件系统有这个功能,有些则没有。这里推荐LittleFS,最要针对小系统设计,且功能齐全。源代码托管在GitHub上。https://github.com/ARMmbed/littlefs
但是不管怎么说,文件系统仍然带来了ROM和RAM的额外开销,这也是他的弊端。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值