打印
[MCU]

聊聊数据存储

[复制链接]
3533|33
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dj狂人|  楼主 | 2014-12-18 17:19 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 dj狂人 于 2014-12-18 17:23 编辑

                                                聊聊数据存储
之前学51的时候,对数据存储没什么概念,主要是做过的东西对这方面没这个需求自己也没往这方面考虑过。而对于STM32来说,当程序写大特别是跑OS之后数据存储就变成了不得不考虑的事情,那么我们现在就来聊聊数据存储。
假如我们在程序中定义了这么五个变量:
int Frequency = 8;
int Speed = 5;
int Power = 60;
int Voltage = 12;
int Temperature = 25;
这五个变量在程序运行的时候是可以通过按键或者其它方式人为的改变它们的值的,现在要求在人为改变它们之后对它们进行保存,那么这个时候我们该怎么去编写程序呢。
    如果是51单片机的的话,在单片机不支持IAP技术的条件下,我们一般会考虑外扩,但是在STM32上,我们可以把这五个变量保存到Flash ROM里面,在有数据变动的时候再进行重新擦写就可以了,但保存到FlashROM中会有一定的限制,那就是它不允许我们进行字节擦除,如果我们想要对其中的某个数据进行修改的话只能先把里面的数据都取出来,修改好之后把Flash ROM整页擦除再把数据写进去。这是因为Flash ROM在没有写入任何数据之前里面都是‘1’,我们写入数据只需要把相应的‘1’写成‘0’就可以了,但是它是不允许也不能把‘0’再写成‘1’的,这就是为什么Flash ROM不支持字节擦写的原因。
    那么好,确定用Flash ROM来存储数据之后,我们下面开始数据改以什么样的形式进行存储。对于上面五个变量可不可以直接这样存放进去呢
这样是可以的,但我们要考虑到虽然我们知道里面哪个地址存的是什么变量的数据,但单片机是不知道它现在取的是什么数据的,这样就要求我们为每个变量都赋一个标志,比如用0x00代表Frequency变量,0x01代表Speed变量...以此类推,那么单片机取出数据的时候先找到标志,如先找到0x00,那么它就知道这次取的是Frequency变量的数据了,如果找到0x01,它就知道这次取出的是Speed变量的数据了。
解决好这个问题之后,我们再来看看有数据变动需要修改的情况。
    如上图,现在我们的五个变量和相应的标志已经存放到FlashROM里面去了,假如程序在运行的时候,使用者通过按键修改了Speed变量的值,修改成了
Speed = 2;那么我们要更新保存在Flash ROM里的Speed的值的时候,需要在RAM里面定义一个数组,把Flash ROM里面这五个变量的值和标志取出来放到数组里面,然后通过标志找到Speed变量的数据,对它的数据进行更新,也就是把‘5’擦写成‘2’,之后对Flash ROM进行整页擦除,最后把数组里面的数据再重新写进去就完成了数据的更新。
上面介绍的方法是初学者比较常用的方法,这种方法简单,容易理解。但是有点致命的缺陷,我们都知道Flash 是有寿命的,虽然现在普遍上的Flash 可达10万次擦写,但如果在运行的时候使用者经常通过按键修改变量,频繁的对Flash进行整页擦除,会导致Flash的使用寿命大大减少。那么要解决这个问题就需要我们合理科学的使用Flash。那么对Flash又该怎么进行合理科学的使用呢。
在我们STM32的中小容量型号芯片中,一般一页有1K的内存空间,而上面我们存储五个变量加五个标志之后还剩下一大片的空间没用到,不得不说这是种浪费,如果把剩下的空间利用起来,那么有数据变动的时候我们可以先不对原数据进行更改,而是采用追加的方式保存到后面去:
这样的话我们只要在这页空间快存满或者合理的时候对这些数据进行整理,把无效旧的数据清理掉再重新写入Flash就可以了。
这样合理的使用Flash之后,我们再来看看怎么对无效数据进行清理。
如上图,当我们的一页空间里快存满或者我们想要进行清理的时候,我们可以在RAM里面先定义一个数组,把Flash中的数据取出来放到数组里面,在这个取出来的过程中,我们进行初步处理,就是判断哪些是无效数据。后面追加的数据肯定是要留下,而前面一点的数据则是要清理的,那么要清理的数据我们把它们的标志位置零,接着对Flash进行整页擦除,然后把数组里面标志不为零的数据再写入Flash里面。这样就完成了数据的清理。
    上面这种清理无效数据的方法算是比较常用的,但也有缺陷,因为我们的Flash采用追加的方式之后,里面的数据还是比较多的,这就要求我们在RAM里面定义一个相当大的数组,如果我们的程序把RAM已经使用的很大,那么剩下的空间不足够我们定义这么大的数组,就会造成数据溢出或者是把其他数据给覆盖掉,这是很致命的。那我们又该怎样解决这个问题呢。方法很简单,只要我们在要往Flash里追加数据的时候先通过标志寻到旧的数据,然后把它的标志清零再追加新数据,这样我们在整理数据的时候只需要定义一个很小的数组把Flash中标志不为零的数据取出来,对Flash进行整页擦除之后再把数据写进去就可以了。那这种方法又是否完美了?肯定不是。
    我们刚那种方法最大的缺陷就是寻找无效数据的时候太花时间,如果每改动一个数据都要遍历一页Flash一次或几次的话,在某些场合往往是无法忍受的,就像RTOS拒绝delay一样。如果不用这种方法又该怎样做才能更好的满足实时的需求又很好的避免在RAM中定义一个大数组的尴尬呢。相信也有很多程序编写者在思考这个问题,他们也肯定会有自己的处理方法。我在msOS里面就看到了一种比较新颖的处理方式。那么他是怎么处理的呢?
    在msOS这个系统中,他充分利用了变量在RAM中原有的存储空间。我们都知道程序中的指令都是存储在程序存储空间也就是Flash里面,变量是存放在数据存储空间(RAM)里面的,那么我们整理数据的时候定义的数组只用来存放标志(注意,这个标志直接就是变量在RAM中的地址),数据放回它在RAM中原有的数据存储空间去(虽然这样做会把初始化时的初始值覆盖掉,这个有好处也有坏处,看情况而定)。这样我们定义的数组就只是之前的一半就够了。
    好,数据存储就聊到这。以上都是个人在学习msOS时接触到的东西,如果有说的不对的或者不透彻的还请各位指正



相关帖子

沙发
ayb_ice| | 2014-12-18 17:26 | 只看该作者
要么提高寿命

要么减少写次数,

这是本质

这都可以简单实现

使用特权

评论回复
板凳
dj狂人|  楼主 | 2014-12-18 17:32 | 只看该作者
ayb_ice 发表于 2014-12-18 17:26
要么提高寿命

要么减少写次数,

“要么减少写次数”估计意义不大,减少整页擦除的次数,把有限空间合理的利用起来才是编程应该考虑的

使用特权

评论回复
地板
李富贵| | 2014-12-18 22:55 | 只看该作者
星宿派都有重点实验室了,星宿老仙自封的吗?

使用特权

评论回复
5
dj狂人|  楼主 | 2014-12-18 23:00 | 只看该作者
李富贵 发表于 2014-12-18 22:55
星宿派都有重点实验室了,星宿老仙自封的吗?

星宿派?

使用特权

评论回复
6
qin552011373| | 2014-12-18 23:01 | 只看该作者
李富贵 发表于 2014-12-18 22:55
星宿派都有重点实验室了,星宿老仙自封的吗?

;P

使用特权

评论回复
7
dj狂人|  楼主 | 2014-12-18 23:01 | 只看该作者
本帖最后由 dj狂人 于 2014-12-18 23:15 编辑
李富贵 发表于 2014-12-18 22:55
星宿派都有重点实验室了,星宿老仙自封的吗?

楼上指的是凤舞天吗

使用特权

评论回复
8
李富贵| | 2014-12-18 23:22 | 只看该作者
dj狂人 发表于 2014-12-18 23:01
楼上指的是凤舞天吗

follow me

星宿老仙,法力无边,神通广大,法驾中原!
星宿老仙,法力无边,攻无不胜 ,战无不克!
星宿老仙,法力无边;神功盖世,威力无限!

使用特权

评论回复
9
dj狂人|  楼主 | 2014-12-18 23:25 | 只看该作者
李富贵 发表于 2014-12-18 23:22
follow me

星宿老仙,法力无边,神通广大,法驾中原!

:)

使用特权

评论回复
10
ayb_ice| | 2014-12-19 08:02 | 只看该作者
dj狂人 发表于 2014-12-18 17:32
“要么减少写次数”估计意义不大,减少整页擦除的次数,把有限空间合理的利用起来才是编程应该考虑的 ...

“要么减少写次数”估计意义不大,

开机后在RAM里操作,掉电,关机保存,你说意义大不

使用特权

评论回复
11
YingziSeek| | 2014-12-19 08:22 | 只看该作者

使用特权

评论回复
12
dj狂人|  楼主 | 2014-12-19 09:41 | 只看该作者
ayb_ice 发表于 2014-12-19 08:02
“要么减少写次数”估计意义不大,

开机后在RAM里操作,掉电,关机保存,你说意义大不 ...

楼上误解了我的意思,也脱离了原来讨论的话题,原来楼上说的是减少“减少写次数”,然后我说“减少写次数意义不大”因为我**的是不要在存储空间还有剩余的情况下随随便便就进行整页擦除而是以追加的形式保存新数据把有限的存储空间利用起来,但我没说掉电保护意义不大:)

使用特权

评论回复
13
天师猫神| | 2014-12-19 10:55 | 只看该作者
进来学习下,,,,,,,,,,,,,,

使用特权

评论回复
14
mohanwei| | 2014-12-19 12:44 | 只看该作者
你哪里学来的文学青年存储格式,太强大了……

普通青年是这样存取的:
typedef struct
{
int Frequency;
int Speed;
int Power;
int Voltage;
int Temperature;
}PaStruct;
PaStruct pa;//在RAM定义一个参数结构体

#define _FlashAddr_Pa (127*1024) //分配一个地址
//读取:
ReadFlash(_FlashAddr_Pa, &pa, sizeof(pa));//当做一个完整的数组读出

写入:
WriteFlash(_FlashAddr_Pa, &pa, sizeof(pa));//当作一个完整的数组写入

访问:
pa.Frequency = 9999;
printf("Frequency=%d",(int)pa.Frequency);
……

现在的MCU基本都可以保证20万次以上的擦写循环,如果只是设置参数时保存一次,放心的擦-写(一年才能擦写多少次?Flash寿命没到期你的产品都该淘汰了)

使用特权

评论回复
15
lxyppc| | 2014-12-19 12:46 | 只看该作者
看了半天没看懂
看起来很厉害的样子

使用特权

评论回复
16
lxyppc| | 2014-12-19 12:49 | 只看该作者
mohanwei 发表于 2014-12-19 12:44
你哪里学来的文学青年存储格式,太强大了……

普通青年是这样存取的:

文学青年不都是用st那个flash模拟eeprom的库么

使用特权

评论回复
17
mohanwei| | 2014-12-19 12:56 | 只看该作者
lxyppc 发表于 2014-12-19 12:46
看了半天没看懂
看起来很厉害的样子

在C/C++里,结构体内部的成员肯定是在分布在连续的地址上的(可能会有大小端、对齐的问题,但不影响访问),所以一个结构体可以当作一个数组来处理。

使用特权

评论回复
18
lxyppc| | 2014-12-19 13:05 | 只看该作者
mohanwei 发表于 2014-12-19 12:56
在C/C++里,结构体内部的成员肯定是在分布在连续的地址上的(可能会有大小端、对齐的问题,但不影响访问 ...

我那是回复楼主的

你的普通青年版本我看懂了

使用特权

评论回复
19
dj狂人|  楼主 | 2014-12-19 13:10 | 只看该作者
mohanwei 发表于 2014-12-19 12:44
你哪里学来的文学青年存储格式,太强大了……

普通青年是这样存取的:

哈哈   欢迎拍砖

“typedef struct
{
int Frequency;
int Speed;
int Power;
int Voltage;
int Temperature;
}PaStruct;”

首先这个确实不错,可是我们现在要保存的是有更改的数据,但是程序运行的时候我们是不知道使用者将要改动那个变量,按照楼上的思路,不管使用者改动了哪个数据,我都把PAStruct中的所有数据都追加到Flash立面,这无疑浪费了部分空间

再有对楼上的这个
“现在的MCU基本都可以保证20万次以上的擦写循环,如果只是设置参数时保存一次,放心的擦-写(一年才能擦写多少次?Flash寿命没到期你的产品都该淘汰了)”
我只能呵呵了

使用特权

评论回复
20
dj狂人|  楼主 | 2014-12-19 13:15 | 只看该作者
lxyppc 发表于 2014-12-19 12:49
文学青年不都是用st那个flash模拟eeprom的库么

敢发表出来就不怕拍砖,如果觉得哪里不妥或不对的,也希望大伙给予指正,万分感谢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:dj的世界一直很好 河池学院-msOS重点实验室

15

主题

201

帖子

0

粉丝