调试数据存储到nandflash的那些事儿(四):nandflash底层的简介

调试数据存储到nandflash的那些事儿(一):关于sizeof()的一些坑

调试数据存储到nandflash的那些事儿(二): 存入nandflash的方式比较

调试数据存储到nandflash的那些事儿(三): 关于移位存入,强制类型转换读出的讨论

调试数据存储到nandflash的那些事儿(四):nandflash底层的简介

调试数据存储到nandflash的那些事儿(五):对nandflash的寻址说明


前言:

在一个系统中,有三大类:数据、交互、质控。而数据则是根本中的根本,大部分嵌入式设备都是围绕着数据向用户提供着服务。而数据又分为采集、处理、存储、上传等具体细节。在我的设备中数据的存储是用nandflash进行存储的。所以研究nandflash,编写操作nandflash就显得极为重要。

那么接下来有必要大概介绍一下nandflash:

1.我们的板子配的为  IC_H27U1G8F2CBI,他是128MB的空间。空间内默认都是0xFF,为什么呢?

NAND flash和NOR flash的区别详解

有时间可以复习一下,没时间的话就记住在没有写入或擦除nandflash后都是0xFF。写的过程就是给某一个点写0,所以在判断块是好块还是坏块就是先擦除判断是否都为0XFF,然后再写入,再读看是不是和写入的一样。

2.为什么是128MB呢?nandflsh的结构共有1024块,1块=64页,1页=2048bytes 所以1024*64*(2*1024)= 128MB.

下图为对“页”的说明

根据我的理解,块和页的概念可以这样理解,整个nandflash是一个书架,这个书架上有1024本书(块),每本书64页(页)。每页2048行。后面还有64行注释(备用区)。正儿八经的2048行是用来存储数据的,注释是用来辅助用的。

当然,不要忘了一点:这个书架有些特殊,想要修改某一行的数据前需要擦除整本书,也就是需要先擦除所在块,才能写入(最小擦除单位就是一整个块,毕竟nandflash是应用于大量存储的,这样更节省时间)。如果要写入的地方已经不是0XFF了,那么是写不进去的。

3.每个“页”的逻辑结构,前面512Bx4 = 2048B 是主数据区,后面16Bx4是备用区,备用区的空间不算在128MB中。

实际在使用时几乎不需要考虑到扇区的存在,我们就认为每一页有2048个字节也可以。

备用区一般在什么时候会用到呢?由于nandflash存在坏块。芯片出厂时,厂商保证芯片的第1个块是好块。如果是坏块,则在该块的第1个PAGE的第1个字节或者第2个PAGE(当第1个PAGE坏了无法标记为0xFF时)的第1个字节写入非0xFF值。坏块标记值是随机的,软件直接判断是否等于0xFF即可。

4.nandflash的坏块管理是很需要花费精力的,前面我们提到nandflash最小擦除单位是块,也就是说如果一个块是坏块,那么擦除后某一位置也可能也不为0XFF,那么我接下来我写到这个位置就会失败。所以记录下哪些是坏块非常有必要。

我们已知一共有1024个块,所以我们建立一个链表用来记录哪些是坏块,在使用时避开使用这些坏块不就好了吗?

那如果1024个块是物理块的话,每次在使用时我都检查一下有哪些好块可用实在太麻烦了。用户调用nandflash的接口时只想着用,你让他还考虑坏块是哪些,实在是太麻烦了。这部分工作不如让我们在nandflash底层就给处理好。

不如创建一个逻辑块的概念,理想情况下没有坏块,逻辑块1对应物理块1,逻辑块2对应物理块2,...逻辑块1024对应物理块1024。如果第100的物理块坏了,那么我让逻辑块100指向物理块101,逻辑块101指向物理块102...逻辑块1024指向空。这样的好处在于,用户只要通过维护这个链表,就可以简单的使用逻辑块找到可以使用的物理块。

所以在一上电时,我们初始化nandflash时,对整个nandflash进行坏块判断,创建链表。最大1024所以当然是uint16_t。

uint16_t s_usLUT[NAND_BLOCK_COUNT];                /* 逻辑块号映射表 */

这样一来,用户在使用nandflash时只需要告诉我们逻辑块,我们去对应物理块实际操作。

5.看来我们需要在每次上电都建立一遍映射表,把他存入铁电也不合适,要保持独立性嘛。但是每次上电我们都要判断一遍坏块吗?那可是得擦除块的啊,那我们还怎么存储数据呢。

这下就体现了备用区的作用了,在格式化时我们就能知道哪些是坏块吧,我们在该块的第一页的备用区的第一个字节写上任意值(非0XFF)。然后之后我们在上电初始化nandflash时,只需要去读每一个块的第一页的备用区的第一个字节是否为0XFF就知道是否为坏块了。然后在平常使用时出现坏块的时候就标记一下,更新下映射表即可。

6.那为什么备用区还需要写上对应的逻辑块号呢?反正我知道了坏块都有哪些了,按照一个既定的方式给逻辑块赋值物理块号不就可以吗?比如第一个坏块是100,那么逻辑块100对应物理块101。

如果你认为也没什么毛病,那你可能忽略了一点,有些块是用着用着才坏的,如果有一天你发现物理块101也坏了,难道让逻辑块100去对应物理块102吗?那物理块102之前可存着逻辑块101的数据呢。

明白了吗?物理块和逻辑块是一一对应的关系,也就是从第一次使用开始后,他们彼此就一一对应,除非物理块坏了。所以在备用区也留着存逻辑块号的位置。在格式化时就把逻辑块号记录下来,今后每次上电初始化时根据这个记给映射表赋值。

7.还剩下一个问题,就是nandflash在写和读时需要知道具体位置的嘛,也就是寻址。寻址根据具体应用情况不同而不同,比如我需要存储一年的分钟数据,那我用时间来寻址。再比如,我如果想存储每次的检测结果,那我用序号寻址更合理。但对于nandflash的读写而言,他只需要知道你想让他往哪写从哪读。寻址方式是你应用层用户需要考虑的事情。

那么问题来了,由于寻址方式是一定的,比如我存数据时,在一分钟的开头存储了一遍数据,但我此时重启设备,设备在同一分钟又要存储一遍数据,位置是同样的。我们知道,存储nandflash时,如果碰到要写入的位置已有数据是写不进去的,需要擦除整个块,才能接着写。那我总不能,每次重启都把前一段时间的数据都擦了吧。

有人说你可以通过逻辑判断啊,存过一次就不要再存了,不就没事吗?

好吧,如果我以一年为周期,假设从1月1日00:00存到第一个块开始,存过一年后,我再此来到1月1日00:00这个位置,那我就只能把第一个块擦除才能接着存,那我不就把去年的1月2日的数据给擦除了吗?但是这还没过一年啊,我应该保存住1月2日的。

所以需要考虑下,我们该怎么面对这个问题。

我们想要达到的目的是,用户本身只是想在某个位置存储一定长度的数据而已,人家并不想把其他位置的数据擦除掉。

刚碰到这个问题时,我想到的方案是,不是要存一年的数据即可吗?我让nandflash分为两年,奇数年存第一年,偶数年存第二年。所以在跨年的时候相当于擦的是前年的数据,就不影响一年数据的完整性了。但是这个方案存在一个弊端,明明可以存两年的你只能声称存一年,而且万一一年的数据特别大,不够存两年的怎么办。

然后有一个同事是这样的想法,安富莱也是这样的:在一个块a若写着写着发现不为空,则先将整个块搬到一个空闲块b。将a擦除,再把b从位置0到要写入的位置之间的数据搬回a,再接着写。这样就不会影响之前的数据了。

我现在就是根据这个想法,写了这部分函数。在写函数的时候判断要写入的位置和之后的空间是否为空(0XFF),若不为空则执行上述的操作。


总结:这篇主要介绍nandflash的底层驱动,之后有时间会继续补充细节。

下面是bsp_nandflash.c和bsp_nandflash.h下载

https://download.csdn.net/download/nianzhu2937/11589092


 


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值