NandFlash驱动设计

2019-04-14 16:47发布

NandFlash种类

nandflash在嵌入式系统中的角 {MOD}和硬盘在电脑中的角 {MOD}一样。
根据物理结构的区别,nandflash主要分为两类:
SLC(single level cell):单层式存储
MLC(multi level cell):多层式存储
nandflash存储的基本存储单元称为存储格,SLC在存储格上只存一位数据,而MLC则存放两位数据。 这里写图片描述 MLC与SLC对比:
1.价格:由于MLC采用了更高密度的存储方式,因此同容量的MLC价格上远低于SLC。
2.访问速度:SLC的访问速度一般要比MLC快3倍以上。
3.使用寿命:SLC能进行10万次的擦写,MLC能进行1万次。
4.功耗:MLC功耗比SLC高15%左右。

访问方式

内存采用统一编址:cpu有地址空间,在这个地址空间给内存分配了地址。因此,要访问内存只需要访问相应的地址就可以。内存需要用地址线和数据线和cpu连接。 Nandflash采用独立编址:与内存不同,cpu并没有给nandflash分配地址空间,nandflash处于cpu地址空间之外,因此称为独立编址。 在cpu内部有nandflash控制器,该控制器与nandflash连接。当要访问nandflash某个地址时,
①首先,nandflash控制器通过连接线将地址发送给nandflash(地址线位数不够时通过多次发送将地址传送)
②传送命令,用于判断是读数据,写数据,还是擦除数据
③数据传输 nandflash控制器中有地址寄存器、命令寄存器、数据寄存器,需要访问nandflash时程序只需往这三个寄存器写入相应的地址、命令、数据即可。

nandflash结构

这里写图片描述 一个nandflash可分为多个blocks,每个block可分为多个pages,每个page分为数据区和空闲区,数据区用于存放数据,空闲区用于存放校验码等信息。如上图共有2048个blocks,每个block分为64pages,一页分为2K和64B两个区域。
nandflash地址分为行地址和列地址:行地址为页的编号,列地址为地址在该页的偏移量。 nandflash信号引脚:
1.CLE(command latch enable):命令锁存允许
2.ALE(address latch enable):地址锁存允许
3.CE:芯片选择
4.RE:读允许
5.WE:写允许
6.WP:在写或擦除期间,提供写保护
7.R/B:读/忙
8.I/O引脚

读取nandflash数据

nandflash有两种读数据方式:
①页读:提供某页的地址,即某页的编号,将该页数据区所有数据读取出来(只需提供行地址)
②随机读:提供行地址和列地址,读取该地址的数据
通过阅读相应型号的nandflash数据手册,可以得到指令表格和读数据的信号流程(以K9F2G08U0A为例)
指令表格: 这里写图片描述 页读信号流程: 这里写图片描述 从指令表格可看出,00h和30h这两个指令是读数据指令,所以页读流程中发送的指令为00h和30h。从I/O引脚可看出,页读第一步是发送00h,第二步是分5轮发送地址,第三步是发送命令30h,第四步是等待R/B信号,当其从低电平变为高电平时开始读取数据。判断R/B信号从低电平变为高电平是通过读取一个寄存器的某一位的值来判断的,因此在等待前需要对该寄存器进行清除;在对nandflash进行操作前需要选中nandflash芯片,操作完成后需要取消选中的nandflash芯片,因此页读流程为:
①选中nandflash芯片
②清除R/B
③发送命令0x00
④发送地址
⑤发送命令0x30
⑥等待R/B
⑦读取数据
⑧取消选中nandflash芯片
其中①⑧是对nandflash操作的共通步骤。
接下来说明以上8步骤的实现方法。查阅S3C2440数据手册,可得关于nandflash控制的寄存器: 这里写图片描述 1.选中与取消选中nandflash芯片
NFCONT寄存器用于控制nandflash芯片,该寄存器Reg_nCE位用于选中与取消选中nandflash芯片 这里写图片描述 2.清除R/B与判断等待R/B是否结束
NFSTAT寄存器显示nandflash运行状态,可通过RnB_TransDetect位的值判断RnB是否从低电平变为高电平啊,当从低电平变为高电平时该位被设为1,在等待R/B信号前对该位进行清除是为了防止RnB_TransDetect位在我们对nandflash进行操作之前就已经是1的情况。对该位进行清除是往该位写入1. 这里写图片描述 3.发送命令
NFCMD寄存器用于存储指令,要发送命令只需要往该寄存器写入命令
4.发送地址
分5轮发送地址,先发送2轮列地址,再发送3轮行地址。 这里写图片描述 因为是页读,所以偏移量为0,即发送列地址为0;发送行地址是先发送最低8位,最后才发送最高8位
NFADDR寄存器用于发送地址。 这里写图片描述 5.接收数据
NFDATA寄存器用于接收数据
以上是nandflash读数据的步骤,但读数据前需要初始化nandflash。初始化分为三步:初始化NFCONF寄存器、初始化NFCONT寄存器、复位。
要想nandflash正常工作则nandflash必须满足它的时序图。其中twp、tcls、tclh这三个时序非常重要。 这里写图片描述 这里写图片描述 由上图可知,twp>12ns,tcls>12ns,tclh>5ns.
这三个时序是通过NFCONF寄存器配置的。 这里写图片描述 这里写图片描述 其中TACLS=tcls-twp,TWRPH0=twp,TWRPH1=tclh
NFCONT寄存器需要设置的是其最低两位MODE,Reg_nCE.初始化是设置MODE为使能模式,Reg_nCE为未选中模式。
复位:重新配置后要对nandflash进行一次复位,复位操作为 这里写图片描述 因此步骤为:
①选中nandflash
②清除RnB
③发送0xff
④等待RnB
⑤取消选中nandflash
之前代码搬迁时我们是将代码从iram复制到内存,现在初始化好nandflash,我们可以将代码从nandflash搬移到内存了。代码搬移使用的是页读。因为使用的是c语言,因此在reset中需要将初始化c语言环境搬到代码搬移前面。 reset: bl set_svc bl disable_watchdog bl disable_interrupt bl disable_mmu bl init_clock bl init_sdram bl init_stack bl nand_init bl copy_to_ram bl clean_bss ldr pc, =gboot_main copy_to_ram: mov r0, #0 @r0的值会作为nand_to_ram的第一个参数传递过去 ldr r1, =_start @r1的值会作为nand_to_ram的第二个参数传递过去 ldr r2, =bss_end sub r2,r2,r1 @r3的值会作为nand_to_ram的第三个参数传递过去 mov ip,lr bl nand_to_ram mov lr, ip mov pc,lr void nand_to_ram(unsigned long start_addr, unsigned char* sdram_addr, int size) { int i; for( i=(start_addr >>11); size>0;) { NF_PageRead(i,sdram_addr); size -= 2048; sdram_addr += 2048; i++; } }

往nandflash写数据

与nandflash读数据相对,写数据也有两种方式,页写和随机写。
从nandflash数据手册可以得到页写的信号流程为: 这里写图片描述 由上可以得出页写的步骤流程:
①选中nandflash芯片
②清除R/B
③发送命令0x80
④发送列地址
⑤发送行地址
⑥写入数据
⑦发送命令0x10
⑧等待RnB
⑨发送命令0x70
⑩读取写入结果
⑪取消选中nandflash芯片
但有了以上步骤还无法实现写数据,对于nandflash,若要进行写数据则必须先对nandflash进行擦除。擦除流程图为: 这里写图片描述 以上是块擦除方式,也即不仅仅擦除该页的数据,还擦除该页所在的块的数据。
代码为: int NF_Erase(unsigned long addr) { int ret; //选中flash芯片 select_chip(); //清除RnB clear_RnB(); //发送命令0x60 send_cmd(0x60); //发送行地址 send_addr(addr&0xff); send_addr((addr>>8)&0xff); send_addr((addr>>16)&0xff); //发送命令D0 send_cmd(0xD0); //等待RnB wait_RnB(); //发送命令0x70 send_cmd(0x70); //读取擦除结果 ret = NFDATA; //取消选中flash芯片 deselect_chip(); return ret; } int NF_WritePage(unsigned long addr, unsigned char* buff) { int ret; int i; //选中flash芯片 select_chip(); //清除RnB clear_RnB(); //发送命令0x80 send_cmd(0x80); //发送列地址 send_addr(0x00); send_addr(0x00); //发送行地址 send_addr(addr&0xff); send_addr((addr>>8)&0xff); send_addr((addr>>16)&0xff); //写入数据 for(i=0;i<2048;i++) { NFDATA = buff[i]; } //发送命令0x10 send_cmd(0x10); //等待RnB wait_RnB(); //发送命令0x70 send_cmd(0x70); //读取写入结果 ret = NFDATA; //取消选中flash芯片 deselect_chip(); return ret; }