1、Nandflash接口从原理图可以看出
Nand的型号和命名(K9F2G08为例子)
K9F为三星公司一个系列
2G表示Nand的大小是2Gbit(256MB)
数据线有八根
注意:Nand的数据线上传递的不止是要存取的数据,也有命令、地址
2、Nand的结构
这个Nand共128k页,
1页=(2K + 64)Byte
- 其中2K代表带内数据,用于存取有效信息 64代表带外数据,用于ECC校验,坏块标记等,因此,真实计算Nandflash容量时是不包含带外数据的,因为它不存放有效信息。
1块= 64页
1Nand设备容量 = 2048块 = 2048 x 64页 = (2K )x 2048 x 64页 Byte = 256MB
在Nand中,页是读写的最小单位,块是擦除的最小单位
Nand芯片中主要包含2部分:Nand存储颗粒+Nand接口电路。存储颗粒就是纯粹的Nand原理的存储单元,类似于仓库;Nand接口电路是用来管理存储颗粒,并且给外界提供一个统一的Nand接口规格的访问接口的。
Nand中有多个存储单元,每个单元都有自己的地址(地址是精确到字节的)。所以Nand是地址编排精确到字节,但是实际读写却只能精确到页(所以Nand的很多操作都要求给的地址是页对齐的,譬如2K、4K、512K等这样的地址,不能给3000B这样的地址)。Nand读写时地址传递是通过IO线发送的,因为地址有30位而IO只有8位,所以需要多个cycle才能发送完毕。一般的Nand都是4cycle或者5cycle发送地址(从这里把Nand分为了4cycle Nand和5cycle Nand)。
总结:Nand芯片内部有存储空间,并且有电路来管理这些存储空间,向外部提供统一的Nand接口的访问规则,然后外部的SoC可以使用Nand接口时序来读写这个Nand存储芯片。Nand接口是一种公用接口,是一种标准,理论上来说外部SoC可以直接模拟Nand接口来读写Nand芯片,但是实际上因为nand接口对时序要求非常严格,而且时序很复杂,所以一般的SoC都是通过专用的硬件的Nand控制器(这些控制器一般是作为SoC的内部外设来存在的)来操控Nand芯片的。
3、Nand的单元组织:block与page(大页Nand与小页Nand)
(1)Nand的页和以前讲过的块设备(尤其是硬盘)的扇区是类似的。 扇区最早在磁盘中是512字节,后来也有些高级硬盘扇区不是512字节而是1024字节/2048字节/4096字节等。Nand也是一样,不同的Nand的页的大小是不同的,也有512字节/1024字节/2048字节/4096字节等。但是,操作系统里软件编程的时候,扇区的大小永远是当作512字节对待的。
(2)一个block等于多少page也是不定的,不同的Nand也不同。一个Nand芯片有多少block也是不定的,不同的Nand芯片也不同。
总结:Nand的组织架构挺乱的,接口时序也不同,造成结构就是不同厂家的Nand芯片,或者是同一个厂家的不同系列型号存储容量的nand接口也不一样。所以nand有一个很大的问题就是一旦升级容量或者换芯片系列则硬件要重新做、软件要重新移植。
4、带内数据和带外数据(ECC与坏块标记)
(1)Nand的每个页由2部分组成,这2部分各自都有一定的存储空间。譬如K9F2G08中为2K+64字节。其中的2K字节属于带内数据,是我们真正的存储空间,将来存储在Nand中的有效数据就是存在这2K范围内的(我们平时计算nand的容量时也是只考虑这2KB);64字节的带外数据不能用来存储有效数据,是作为别的附加用途的(譬如用来存储ECC数据、用来存储坏块标志等····)
(2)什么是ECC:(error correction code,错误校验码)。因为nand存储本身出错(位反转)概率高(Nand较Nor最大的缺点就是稳定性),所以当我们将有效信息存储到Nand中时都会同时按照一定算法计算一个ECC信息(譬如CRC16等校验算法),将ECC信息同时存储到Nand这个页的带外数据区。然后等将来读取数据时,对数据用同样的算法再计算一次ECC,并且和从带外数据区读出的ECC进行校验。如果校验通过则证明Nand的有效数据可信,如果校验不通过则证明这个数据已经被损坏(只能丢弃或者尝试修复)。
(3)坏块标志:Nand芯片用一段时间后,可能某些块会坏掉(这些块无法擦除了,或者无法读写了),nand的坏块非常类似于硬盘的坏道。坏块是不可避免的,而且随着Nand的使用坏块会越来越多。当坏块还不算太多时这个Nand都是可以用的,除非坏块太多了不划算使用了才会换新的。所以我们为了管理Nand发明了一种坏块标志机制。Nand的每个页的64字节的带外数据中,我们(一般是文件系统)定义一个固定位置(譬如定位第24字节)来标记这个块是好的还是坏的。文件系统在发现这个块已经坏了没法用了时会将这个块标记为坏块,以后访问nand时直接跳过这个块即可。
5、Nand的地址时序
(1)nand的地址有多位,分4/5周期通过IO引脚发送给Nand芯片来对Nand进行寻址。寻址的最小单位是字节,但是读写的最小单位是页。
(2)nand的地址在写代码时要按照Nand要求的时序和顺序去依次写入。
6、Nand的命令码
(1)外部SoC要想通过Nand控制器来访问Nand(实质就是通过Nand接口),就必须按照Nand接口给nand发送命令、地址、数据等信息来读写Nand。
(2)Nand芯片内部的管理电路本身可以接收外部发送的命令,然后根据这些命令来读写Nand内容与外部SoC交互。所以我们对nand进行的所有操作(擦除、读、写···)都要有命令、地址、数据的参与才能完成,而且必须按照Nand芯片规定的流程来做。
7、NandFlash的常见操作及流程分析
1、坏块检查
(1)Flash使用之前要先统一擦除(擦除的单位是块)。Flash类设备擦除后里面全是1,所以擦干净之后读出来的值是0xff。
(2)检查坏块的思路就是:先块擦除,然后将整块读出来,依次检测各字节是否为0xff,如果是则表明不是坏块,如果不是则表明是坏块。
2、页写(program)操作
(1)写之前确保这个页是被擦除干净的。如果不是擦除干净的(而是脏的、用过的)页,写进去的值就是错的,不是你想要的结果,类似黑板。
(2)写操作(write)在flash的操作中就叫编程(program)
(3)SoC写Flash时通过命令线、IO线依次发送写命令、写页地址、写数据等进入NandFlash。
(4)写的过程:SOC通过Nand控制器和Nand芯片完成顺序对接,然后按照时序要求将一页数据发给Nand芯片内部的接口电路。接口电路先接收收据到自己的缓冲区,然后再集中写入Nand芯片的存储区域中。Nand接口电路将一页数据从缓冲区中写入Nand存储系统中需要一定的时间,这段时间Nand芯片不能再响应SOC发过来的其他命令,所以SoC要等待Nnad接口电路忙完。等待方法是SoC不断读取状态寄存器(这个状态寄存器有2种情况:一种是SoC的Nand控制器自带的,另一种是SoC通过发命令得到命令响应得到的),然后通过检查这个状态寄存器的状态位就能知道Nand接口电路刚才写的那一页数据写完了没、写好了没。直到SoC收到正确的状态寄存器响应才能认为刚才要写的那一页数据已经ok。(如果SoC收到的状态一直不对,可以考虑重写或者认为这一页所在的块已经是坏块,或者整个Nand芯片已经挂掉了)。
(5)正常情况下到了第四步就已经完了。但是因为Nand的读写有不靠谱情况,因此我们为了安全会去做ECC校验。ECC校验有硬件式校验和软件式校验2种。软件式校验可以采用的策略有很多,其中之一(Nand芯片手册上推荐的方式是):将刚才写入的1页数据读出来,和写入的内容进行逐一对比。如果读出的和写入的完全一样,说明刚才的写入过程正确完成了;如果读出来的和写入的不完全一样那就说明刚才的写入有问题。
(6)硬件式ECC:SoC的Nand控制器可以提供硬件式ECC(这个也是比较普遍的情况)。硬件式ECC就是在Nand的控制器中有个硬件模块专门做ECC操作。当我们操作Nand芯片时,只要按照SoC的要求按时打开ECC生成开关,则当我们写入Nand芯片时SoC的Nand控制器的ECC模块会自动生成ECC数据放在相应的寄存器中,然后我们只需要将这生成的ECC数据写入Nand芯片的带外数据区即可;在将来读取这块Nand芯片时,同样要打开硬件ECC开关,然后开始读,在读的过程当中硬件ECC会自动计算读进来的一页数据的ECC值并将之放到相应的寄存器中。然后我们再读取带外数据区中原来写入时存入的ECC值,和我们刚才读的时候得到的ECC值进行校验。校验通过则说明读写正确,校验不通过则说明不正确(放弃数据或者尝试修复)。
3、擦除(erase)操作
(1)擦除时必须给块对齐的地址。如果给了不对齐的地址,结果是不可知的(有些Nand芯片没关系,它内部会自动将其对齐,而有些Nand会返回地址错误)。
(2)读写时给的地址也是一样,要求是页对齐地址。如果给了不对齐的,也是有可能对有可能错。
4、页读(read)操作
8、S5PV210的NandFlash控制器
SoC的Nand控制器的作用
(1)Nand芯片本身通过Nand接口电路来存取数据,Nand接口电路和SOC之间通过Nand接口时序来通信。Nand接口时序相对复杂,如果要SoC完全用软件来实现Nand接口时序有一些不好(主要是:第一很难保证时序能满足、容易不稳定;第二代码很难写)。解决方案是:在SoC内部集成一个Nand控制器(实质就是一块硬件电路,这个硬件电路完全满足Nand接口时序的操作,然后将接口时序的操作寄存器化)。
(2)SOC和Nand芯片之间通信,在SoC没有Nand控制器时需要SoC自己来处理接口时序,编程很麻烦,需要程序员看Nand芯片的接口时序图,严格按照接口时序图中编程(尤其要注意各个时间参数);在SoC有Nand控制器时SoC只需要编程操控Nand控制器的寄存器即可,Nand控制器内部硬件会根据寄存器值来生成合适的Nand接口时序和Nand芯片通信。所以在有Nand控制器时编程要简单很多,我们读写Nand芯片时再也不用关注Nand接口时序了,只要关注SoC的Nand控制器的寄存器即可。
(3)扩展来讲,现在的技术趋势就是:几乎所有的外设在SoC内部都有对应的控制器来与其通信,那么SoC内部集成的各种控制器(也就是各种内部外设)越多,则SoC硬件能完成的功能越多,将来用这个SoC来完成相应任务时软件编程越简单。譬如说图形处理和图像处理领域,2D图像编码(jpeg编码)、视频编码(h.264编码),现在大部分的application级别的SoC都有集成的内部编码器(像S5PV210就有、更复杂的譬如4418、6818就更不用说了,只会更多更先进),我们可以利用这些硬件编码器来进行快速编解码,这样软件工作量和难度降低了很多(这就是所谓的硬件加速)。
9、Nandflash控制器结构框图
(1)结构框图中关键点:SFR(我们后续编程的关键,编程时就是通过读写SFR来产生Nand接口时序以读写Nand芯片的) + Nand interface(硬件接口,将来和Nand芯片的相应引脚进行连接) + ECC生成器
10、S5PV210的Nand控制器的主要寄存器
NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFMECCD0&NFMECCD1、NFSECCD、NFSTAT
11、总结:
(1)像NandFlash这类芯片,通过专用的接口时序和SoC内部的控制器相连(这种连接方式是非常普遍的,像LCD、DDR等都是类似的连接)。这种接法和设计对我们编程来说,关键在于两点:SoC的控制器的寄存器理解和Nand芯片本身的文档、流程图等信息。
(2)对于我们来说,学习NandFlash,要注意的是:
第一,要结合SoC的数据手册、Nand芯片的数据手册、示例代码三者来理解。
第二,初学时不要尝试完全不参考自己写出Nand操作的代码,初学时应该是先理解实例代码,知道这些代码是怎么写出来的,必要时对照文档来理解代码。代码理解之后去做实践,实践成功后以后再考虑自己不参考代码只参考文档来写出nand操作的代码。
12、Nand操作代码
//Nand.h文件
void nand_init(void);
void nand_read_id(void);
int nand_block_erase(unsigned long block_num);
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length);
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length);
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data);
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata);
//Nand.c文件
#include "nand.h"
#include "stdio.h"
#define rNFCONF ( *((volatile unsigned long *)0xB0E00000) )
#define rNFCONT ( *((volatile unsigned long *)0xB0E00004) )
#define rNFCMMD ( *((volatile unsigned long *)0xB0E00008) )
#define rNFADDR ( *((volatile unsigned long *)0xB0E0000C) )
#define rNFDATA ( *((volatile unsigned long *)0xB0E00010) )
#define rNFDATA8 ( *((volatile unsigned char *)0xB0E00010) )
#define rNFSTAT ( *((volatile unsigned long *)0xB0E00028) )
#define rMP0_1CON ( *((volatile unsigned long *)0xE02002E0) )
#define rMP0_2CON ( *((volatile unsigned long *)0xE0200300) )
#define rMP0_3CON ( *((volatile unsigned long *)0xE0200320) )
#define MAX_NAND_BLOCK 8192 /*定义nand最大块数:8192块 */
#define NAND_PAGE_SIZE 2048 /*定义一页的容量:2048 byte */
#define NAND_BLOCK_SIZE 64 /*定义block大小:64页 */
#define TACLS 1 // 12ns /* 时序相关的设置 */
#define TWRPH0 4
#define TWRPH1 1
#define NAND_CMD_READ_1st 0x00 /* 命令 */
#define NAND_CMD_READ_2st 0x30
#define NAND_CMD_READ_CB_1st 0x00
#define NAND_CMD_READ_CB_2st 0x35
#define NAND_CMD_RANDOM_WRITE 0x85
#define NAND_CMD_RANDOM_READ_1st 0x05
#define NAND_CMD_RANDOM_READ_2st 0xe0
#define NAND_CMD_READ_ID 0x90
#define NAND_CMD_RESET 0xff
#define NAND_CMD_READ_STATUS 0x70
#define NAND_CMD_WRITE_PAGE_1st 0x80
#define NAND_CMD_WRITE_PAGE_2st 0x10
#define NAND_CMD_BLOCK_ERASE_1st 0x60
#define NAND_CMD_BLOCK_ERASE_2st 0xd0
#define ECC_EN (1<<4)
#define CONTROL_EN (1<<0)
static void nand_reset(void);
static void nand_wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void nand_send_cmd(unsigned long cmd);
static void nand_send_addr(unsigned long addr);
static unsigned char nand_read8(void);
static void nand_write8(unsigned char data);
static unsigned int nand_read32(void);
static void nand_write32(unsigned int data);
typedef struct nand_id_info
{
//marker code
unsigned char IDm;
//device code
unsigned char IDd;
unsigned char ID3rd;
unsigned char ID4th;
unsigned char ID5th;
}nand_id_info;
// 复位
void nand_reset(void)
{
nand_select_chip();
nand_send_cmd(NAND_CMD_RESET);
nand_wait_idle();
nand_deselect_chip();
}
// 等待就绪
void nand_wait_idle(void)
{
unsigned long i;
while( !(rNFSTAT & (1<<4)) )
for(i=0; i<10; i++);
}
// 发片选
void nand_select_chip(void)
{
unsigned long i;
rNFCONT &= ~(1<<1);
for(i=0; i<10; i++);
}
// 取消片选
void nand_deselect_chip(void)
{
unsigned long i = 0;
rNFCONT |= (1<<1);
for(i=0; i<10; i++);
}
// 发命令
void nand_send_cmd(unsigned long cmd)
{
unsigned long i = 0;
rNFCMMD = cmd;
for(i=0; i<10; i++);
}
// 发地址
void nand_send_addr(unsigned long addr)
{
unsigned long i;
unsigned long col, row;
// 列地址,即页内地址
col = addr % NAND_PAGE_SIZE;
// 行地址,即页地址
row = addr / NAND_PAGE_SIZE;
// Column Address A0~A7
rNFADDR = col & 0xff;
for(i=0; i<10; i++);
// Column Address A8~A11
rNFADDR = (col >> 8) & 0x0f;
for(i=0; i<10; i++);
// Row Address A12~A19
rNFADDR = row & 0xff;
for(i=0; i<10; i++);
// Row Address A20~A27
rNFADDR = (row >> 8) & 0xff;
for(i=0; i<10; i++);
// Row Address A28~A30
rNFADDR = (row >> 16) & 0xff;
for(i=0; i<10; i++);
}
unsigned int nand_read32(void)
{
return rNFDATA;
}
void nand_write32(unsigned int data)
{
rNFDATA = data;
}
// 读一个字节的数据
unsigned char nand_read8(void)
{
return rNFDATA8;
}
// 写一个字节的数据
void nand_write8(unsigned char data)
{
rNFDATA8 = data;
}
unsigned char nand_read_status(void)
{
unsigned char ch;
int i;
// 1. 发出片选信号
nand_select_chip();
// 2. 读状态
nand_send_cmd(NAND_CMD_READ_STATUS);
for(i=0; i<10; i++);
ch = nand_read8();
// 3. 取消片选
nand_deselect_chip();
return ch;
}
// nandflash 初始化
void nand_init(void)
{
// 1. 配置nandflash
rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0);
rNFCONT = (0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x3<<1)|(1<<0);
// 2. 配置引脚
rMP0_1CON = 0x22333322;
rMP0_2CON = 0x00002222;
rMP0_3CON = 0x22222222;
// 3. 复位
nand_reset();
}
// 读芯片ID
void nand_read_id(void)
{
nand_id_info nand_id;
// 1. 发片选
nand_select_chip();
// 2. 读ID
nand_send_cmd(NAND_CMD_READ_ID);
nand_send_addr(0x00);
nand_wait_idle();
nand_id.IDm = nand_read8();
nand_id.IDd = nand_read8();
nand_id.ID3rd = nand_read8();
nand_id.ID4th = nand_read8();
nand_id.ID5th = nand_read8();
printf("nandflash: makercode = %x\r\n devicecode = %x\r\n ID3rd = %x\r\n ID4rd = %x\r\n ID5rd = %x\r\n", nand_id.IDm, nand_id.IDd, nand_id.ID3rd, nand_id.ID4th, nand_id.ID5th);
nand_deselect_chip();
}
// 擦除块,参数为块号(0 ~ MAX_NAND_BLOCK-1)
int nand_block_erase(unsigned long block_num)
{
unsigned long i = 0;
// 获得row地址,即页地址
unsigned long row = block_num * NAND_BLOCK_SIZE;
// 1. 发出片选信号
nand_select_chip();
// 2. 擦除:第一个周期发命令0x60,第二个周期发块地址,第三个周期发命令0xd0
nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
for(i=0; i<10; i++);
// Row Address A12~A19
rNFADDR = row & 0xff;
for(i=0; i<10; i++);
// Row Address A20~A27
rNFADDR = (row >> 8) & 0xff;
for(i=0; i<10; i++);
// Row Address A28~A30
rNFADDR = (row >> 16) & 0xff;
rNFSTAT |= (1<<4); // clear RnB bit
nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
for(i=0; i<10; i++);
// 3. 等待就绪
nand_wait_idle();
// 4. 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// statas[0] = 1,表示擦除失败,详见NAND Flash数据手册中 READ STATUS一节的描述
// 取消片选信号
nand_deselect_chip();
printf("masking bad block %d\r\n", block_num);
return -1;
}
else
{
// status[0] = 0,表示擦除成功,返回0
nand_deselect_chip();
return 0;
}
}
int nand_erase(unsigned long block_addr)
{
int i = 0;
if((nand_read_status() & 0x80) == 0)
{
printf("Write protected.\n");
return -1;
}
unsigned long row = block_addr >> 18;
// 1. 发出片选信号
nand_select_chip();
// 2. 擦除:第一个周期发命令0x60,第二个周期发块地址,第三个周期发命令0xd0
nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
for(i=0; i<10; i++);
// Row Address A12~A19
rNFADDR = row & 0xff;
for(i=0; i<10; i++);
// Row Address A20~A27
rNFADDR = (row >> 8) & 0xff;
for(i=0; i<10; i++);
// Row Address A28~A30
rNFADDR = (row >> 16) & 0x01; // 只要最低1bit为 A28
for(i=0; i<10; i++);
rNFSTAT |= (1<<4); // clear RnB bit
nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
for(i=0; i<10; i++);
// 3. 等待就绪
nand_wait_idle();
// 4. 读状态
unsigned char status = nand_read_status();
if (status & 1)
{
// statas[0] = 1,表示擦除失败,详见NAND Flash数据手册中 READ STATUS一节的描述
// 取消片选信号
nand_deselect_chip();
printf("masking bad block %d\r\n", block_addr);
return -1;
}
else
{
// status[0] = 0,表示擦除成功,返回0
nand_deselect_chip();
return 0;
}
}
// 从nand中读数据到sdram
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
unsigned long i = 0;
// 1. 发出片选信号
nand_select_chip();
// 2. 从nand读数据到sdram,第一周期发命令0x00,第二周期发地址nand_addr,第三个周期发命令0x30,可读一页(2k)的数据
while(length)
{
nand_send_cmd(NAND_CMD_READ_1st);
nand_send_addr(nand_addr);
rNFSTAT = (rNFSTAT)|(1<<4);
nand_send_cmd(NAND_CMD_READ_2st);
nand_wait_idle();
// 列地址,即页内地址
unsigned long col = nand_addr % NAND_PAGE_SIZE;
i = col;
// 读一页数据,每次拷1byte,共拷2048次(2k),直到长度为length的数据拷贝完毕
for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
{
*sdram_addr = nand_read8();
sdram_addr++;
nand_addr++;
}
}
// 3. 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 取消片选信号
nand_deselect_chip();
printf("copy nand to sdram fail\r\n");
return -1;
}
else
{
nand_deselect_chip();
return 0;
}
}
// 从sdram中写数据到nand
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
unsigned long i = 0;
// 1. 发出片选信号
nand_select_chip();
// 2. 从sdram读数据到nand,第一周期发命令0x80,第二周期发地址nand_addr,第三个周期写一页(2k)数据,第四周期发0x10
while(length)
{
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
nand_send_addr(nand_addr);
// 列地址,即页内地址
unsigned long col = nand_addr % NAND_PAGE_SIZE;
i = col;
// 写一页数据,每次拷1byte,共拷2048次(2k),直到长度为length的数据拷贝完毕
for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
{
nand_write8(*sdram_addr);
sdram_addr++;
nand_addr++;
}
rNFSTAT = (rNFSTAT)|(1<<4);
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
nand_wait_idle();
}
// 3. 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 取消片选信号
nand_deselect_chip();
printf("copy sdram to nand fail\r\n");
return -1;
}
else
{
nand_deselect_chip();
return 0;
}
}
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length)
{
int i = 0;
// 1 发出片选信号
nand_select_chip();
// 2 写页读命令1st
nand_send_cmd(NAND_CMD_READ_1st);
// 3 写入页地址
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = pgaddr&0xff;
rNFADDR = (pgaddr>>8)&0xff;
rNFADDR = (pgaddr>>16)&0xff;
// 4 clear RnB
rNFSTAT |= (1<<4);
// 5 写页读命令2st
nand_send_cmd(NAND_CMD_READ_2st);
// 6 等待空闲
nand_wait_idle();
// 7 连续读取2KB的Page main区数据 (继续读取可读出64B的spare area数据)
for (i=0; (i<NAND_PAGE_SIZE) && (length!=0); i++,length--)
*buf++ = nand_read8();
// 8 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 读出错,取消片选信号,返回错误码-1
nand_deselect_chip();
printf("nand random read fail\r\n");
return -1;
}
else
{
// 读正确,取消片选,返回0
nand_deselect_chip();
return 0;
}
}
int nand_page_read32(unsigned int pgaddr, unsigned int *buf, unsigned int lengthB)
{
int i = 0;
// 1 发出片选信号
nand_select_chip();
// 2 写页读命令1st
nand_send_cmd(NAND_CMD_READ_1st);
// 3 写入页地址
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = pgaddr&0xff;
rNFADDR = (pgaddr>>8)&0xff;
rNFADDR = (pgaddr>>16)&0xff;
// 4 clear RnB
rNFSTAT |= (1<<4);
// 5 写页读命令2st
nand_send_cmd(NAND_CMD_READ_2st);
// 6 等待空闲
nand_wait_idle();
// 7 连续读取2KB的Page main区数据 (继续读取可读出64B的spare area数据)
for (i=0; (i<NAND_PAGE_SIZE/4) && (lengthB!=0); i++,lengthB--)
*buf++ = nand_read32();
// 8 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 读出错,取消片选信号,返回错误码-1
nand_deselect_chip();
printf("nand random read fail\r\n");
return -1;
}
else
{
// 读正确,取消片选,返回0
nand_deselect_chip();
return 0;
}
}
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length)
{
int i = 0;
// 1 发出片选信号
nand_select_chip();
// 2 write cmd 1st
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
// 3 write page addr
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = pgaddr&0xff;
rNFADDR = (pgaddr>>8)&0xff;
rNFADDR = (pgaddr>>16)&0xff;
// 4 写入一页内容
for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
nand_write8(*buf++);
// 5 clear RnB
rNFSTAT = (rNFSTAT)|(1<<4);
// 6 write cmd 2
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
// 7 wait idle
nand_wait_idle();
// 8 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 取消片选信号
nand_deselect_chip();
printf("nand random write fail\r\n");
return -1;
}
else
{
nand_deselect_chip();
return 0;
}
}
/*
* 函数功能: 随机读数据
* 参数: pgaddr 为页地址, offset为页内偏移地址,data为返回值
* 返回值: 0表示读取成功,1表示读取失败
*/
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data)
{
unsigned char readdata;
// 1. 发出片选信号
nand_select_chip();
// 2. 随机读页内某个地址的值
nand_send_cmd(NAND_CMD_READ_1st);
//写入页地址
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = pgaddr&0xff;
rNFADDR = (pgaddr>>8)&0xff;
rNFADDR = (pgaddr>>16)&0xff;
rNFSTAT |= (1<<4);
nand_send_cmd(NAND_CMD_READ_2st);
nand_wait_idle();
nand_send_cmd(NAND_CMD_RANDOM_READ_1st);
//写入页内偏移地址
rNFADDR = offset&0xff;
rNFADDR = (offset>>8)&0xff;
rNFSTAT = (rNFSTAT)|(1<<4);
nand_send_cmd(NAND_CMD_RANDOM_READ_2st);
readdata = nand_read8();
// 3. 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 取消片选信号
nand_deselect_chip();
printf("nand random read fail\r\n");
return -1;
}
else
{
nand_deselect_chip();
*data = readdata;
return 0;
}
}
/*
* 函数功能: 随机写数据
* 参数: pgaddr 为页地址, offset为页内偏移地址,wrdata为要写入的数据
* 返回值: 0表示写入成功,1表示写入失败
* 测试结论: 1、random write一次只能写入一个字节,因此内部只能使用 nand_write8,使用nand_write32就会出错
*/
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata)
{
// 1. 发出片选信号
nand_select_chip();
// 2. 随机写页内某个地址的值
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = pgaddr&0xff;
rNFADDR = (pgaddr>>8)&0xff;
rNFADDR = (pgaddr>>16)&0xff;
nand_send_cmd(NAND_CMD_RANDOM_WRITE);
//写入页内偏移地址
rNFADDR = offset&0xff;
rNFADDR = (offset>>8)&0xff;
nand_write8(wrdata);
rNFSTAT = (rNFSTAT)|(1<<4);
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
nand_wait_idle();
// 3. 读状态
unsigned char status = nand_read_status();
if (status & 1 )
{
// 取消片选信号
nand_deselect_chip();
printf("nand random write fail\r\n");
return -1;
}
else
{
nand_deselect_chip();
return 0;
}
}
// nand_test 使用的是BLOCK_NO块中的首页
#define BLOCK_NO 10
#define PAGE_NO (BLOCK_NO * NAND_BLOCK_SIZE)
#define PAGE_ADDR (PAGE_NO * NAND_PAGE_SIZE)
#define OFFSET0 4
#define OFFSET1 5
#define OFFSET2 6
#define OFFSET3 7
void nand_test(void)
{
int ret = 0;
unsigned char data1, data2, data3, data4;
unsigned char buf[8];
unsigned int bufInt[2];
nand_init();
nand_read_id();
#if 0
// 得先擦除才能写啊
if ((ret = nand_erase(PAGE_ADDR)) == 0)
printf("success to erase block %d\r\n", BLOCK_NO);
else
printf("fail to erase block %d\r\n", BLOCK_NO);
#endif
// 先给丫random写4个byte
#if 1
nand_random_write(PAGE_ADDR, OFFSET0, 'a');
nand_random_write(PAGE_ADDR, OFFSET1, 'b');
nand_random_write(PAGE_ADDR, OFFSET2, 'c');
nand_random_write(PAGE_ADDR, OFFSET3, 'd');
#endif
#if 0
nand_random_write(PAGE_ADDR, OFFSET0, 0xba);
nand_random_write(PAGE_ADDR, OFFSET1, 0xde);
nand_random_write(PAGE_ADDR, OFFSET2, 0xc0);
nand_random_write(PAGE_ADDR, OFFSET3, 0xde);
#endif
// 然后再用三种方法,读出来看看对不对得上
nand_random_read(PAGE_ADDR, OFFSET0, &data1);
nand_random_read(PAGE_ADDR, OFFSET1, &data2);
nand_random_read(PAGE_ADDR, OFFSET2, &data3);
nand_random_read(PAGE_ADDR, OFFSET3, &data4);
printf("PAGE_ADDR: \r\n", PAGE_ADDR);
printf("4 byte data from nand_random_read: %x, %x, %x, %x\r\n", data1, data2, data3, data4);
ret = nand_page_read(PAGE_ADDR, buf, sizeof(buf));
if (ret != 0)
printf("nand_page_read error!\r\n");
else
printf("4 byte data form nand_page_read: %x, %x, %x, %x\r\n", buf[OFFSET0], buf[OFFSET1], buf[OFFSET2], buf[OFFSET3]);
ret = nand_page_read32(PAGE_ADDR, bufInt, sizeof(bufInt)/sizeof(unsigned int));
if (ret != 0)
printf("nand_page_read32 error!\r\n");
else
printf("1 word data form nand_page_read32: %x\r\n", bufInt[OFFSET0]);
}