一、概述

1、NandFlash

  NAND结构能提供极高的单元密度,可以达到高存储密度,比如能达到256M,并且写入和擦除的速度也很快。应用NAND的困难在于flash的管理需要特殊的系统接口。

2、NandFlash和NorFlash对比

 Nor Nand
容量1MB~32MB16M~512MB
XIPYesNo
擦除非常慢(5S)快(3ms)
慢       快
可靠性比较高,位反转的比例小于NandFlash的10%比较低,位反转比较常见,必须有校验措施,比如TNR必须有坏块管理措施
可擦除次数10000~100000              100000~1000000
生命周期     低于NandFlash的10%             是NorFlash的10倍以上    
接口      与RAM接口相同                   I/O接口
访问方法随机访问顺序访问
易用性容易复杂
主要用途常用于保存代码和关键数据用于保存数据
价格便宜

二、NandFlash常见引脚

1、输入输出(I/O0~I/O7)

  数据输入/输出

2、芯片使能(Chip Enable,CE#)

  如果没有检测到CE信号,那么NAND器件就保持待机模式,不对任何控制信号作出响应
3、写使能(Write Enable, WE#)

  WE#负责将数据、地址或指令写入NAND之中
4.读使能(Read Enable, RE#)

  允许输出数据缓冲器。
5.指令锁存使能(Command Latch Enable, CLE)

   当CLE为高时,在WE#信号的上升沿,指令被锁存到NAND指令寄存器中
6.地址锁存使能(Address Latch Enable, ALE)

  当ALE为高时,在W6.地址锁存使能(Address Latch Enable, ALE)E#信号的上升沿,地址被锁存到NAND地址寄存器中
7.就绪/忙(Ready/Busy, R/B#)

  如果NAND器件忙,R/B#信号将变低。该信号是漏极开路,需要采用上拉电阻

三、NandFlash的物理结构

  以下内容皆以K9F2G08U0B为例进行说明。

1、结构框图

NandFlash-LMLPHP

2、存储结构

NandFlash-LMLPHP

1 Page = (2K+64)Bytes

1 Block = 64 Pages

       = (128K + 4K) Byte

1 Device= 2048 Blocks

        = 128K Pages

    = (256M + 8M)Byte

    =  2112 Mbits

ECC

  Linux系统中,一般叫做OOB(Out Of Band)。NandFlash在操作过程中容易出错,为了保证数据的正确性,需要有检测和纠错机制,此机制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),因此设计了多余的区域,用于放置数据的校验值。

3、地址序列

NandFlash-LMLPHP

A0~A11 页内寻址 不仅可以寻址到有效数据,还能寻址到校验区

A12~A28 页寻址

其中:

  A12~A17 块内页寻址

  A18~A28 块寻址

、NandFlash相对于RAM使用的特殊性 

RAM

NandFlash

读取/写入的叫法

读取/写入

读取/编程(Program)①

读取/写入的最小单位

字节

Page/页

擦除(Erase)操作的最小单位

字节

Block/块②

擦除操作的含义

将数据删除/全部写入0

将整个块都擦除成全是1,也就是里面的数据都是0xFF ③

对于写操作

直接写即可

在写数据之前,要先擦除,然后再写

注:

①之所以将写操作叫做编程,是因为,flash 和之前的EPROM,EEPROM继承发展而来,而之前的EEPROM(Electrically Erasable Programmable Read-Only Memory),往里面写入数据,就叫做编程Program,之所以这么称呼,是因为其对数据的写入,是需要用电去擦除/写入的,就叫做编程。

②对于目前常见的页大小是2K/4K的Nand Flash,其块的大小有128KB/256KB/512KB等。而对于Nor Flash,常见的块大小有64K/32K等。

③在写数据之前,要先擦除,内部就都变成0xFF了,然后才能写入数据,也就是将对应位由1变成0。

五、实验代码

1、擦除操作

/* *********************擦除函数*******************************
参数说明:
block 要擦除的块
返回参数:
0 擦除失败
1 擦除成功
*/
unsigned char EraseBlock(unsigned int block)
{
unsigned int blockpage = ;
blockpage = block << ;
/* 选中芯片 */
nand_select_chip();
/* 发出WRITE0命令 */
write_cmd(0x60);
/* Write Address */
NFADDR = blockpage & 0xff; /* Row Address A12~A19 */
NFADDR = (blockpage >> ) & 0xff; /* Row Address A20~A27 */
NFADDR = (blockpage >> ) & 0x01; /* Row Address A28 */
write_cmd(0xD0);
wait_idle();
write_cmd(0x70);
wait_idle();
if(read_data()&0x1) // Page write error
{
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
else
{
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
}

2、页读操作

/* *********************页读函数*******************************
参数说明:
buf 存放读取数据的目的地址
addr 从nandflash中读取数据的源地址
*/
static unsigned char ReadPage(unsigned char * buf,unsigned int addr)
{
int j;
if (addr & NAND_BLOCK_MASK) {
return ; /* 地址或长度不对齐 */
}
/* 选中芯片 */
nand_select_chip(); /* 发出READ0命令 */
write_cmd();
/* Write Address */
write_addr(addr);
write_cmd(0x30);
wait_idle(); for(j=;j < NAND_SECTOR_SIZE; j++) {
*buf = read_data();
buf++; /*i每经过一个大循环就增加NAND_SECTOR_SIZE*/
}
/* 取消片选信号 */
nand_deselect_chip();
return ;
}

3、页写操作

/* *********************页写函数*******************************
参数说明:
buf 源地址
addr 写入nandflash的目的地址
*/
static unsigned char WritePage(unsigned char * buf,unsigned int addr)
{
unsigned int i = ;
if (addr & NAND_BLOCK_MASK) {
return ; /* 地址或长度不对齐 */
}
/* 选中芯片 */
nand_select_chip();
/* 发出WRITE0命令 */
write_cmd(0x80);
/* Write Address */
write_addr(addr);
/* Write Data */
for(i=;i<;i++)
{
write_data(*buf);
buf++;
}
write_cmd(0x10);
wait_idle();
write_cmd(0x70);
wait_idle();
if(read_data()&0x1) // Page write error
{
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
else
{
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
}

4、打印页内容(调试用)

/* *********************打印出指定地址的一个页的内容********************
*/
void nand_printf()
{ unsigned int i;
unsigned char block=, page=;
unsigned char * downPt;
downPt=(unsigned char *)_NONCACHE_STARTADDRESS; printf("\n\rK9F2G08 NAND Page Read.\n");
printf("\nBlock # to read: ");
block = getc()-;
printf("%d\n\r", block);
printf("\nPage # to read: ");
page = getc()-;
printf("%d\n\r", page); if(ReadPage(downPt,((block<<)+page)<<)==) {
printf("\nRead error.\n");
} else {
printf("\nRead OK.\n");
}
// Print data.
printf("Read data(%d-block,%d-page)\n", block, page); for(i=; i<; i++) {
if((i%)==) printf("\n\r%4x: ", i);
printf("%02x ", *downPt++);
}
printf("\n");
}

5、发送地址函数

static void write_addr(unsigned int addr)
{
int i;
unsigned int col, page; col = addr & NAND_BLOCK_MASK;
page = addr / NAND_SECTOR_SIZE; NFADDR = col & 0xff; /* Column Address A0~A7 */
for(i=; i<; i++); NFADDR = (col >> ) & 0x0f; /* Column Address A8~A11 */
for(i=; i<; i++); NFADDR = page & 0xff; /* Row Address A12~A19 */
for(i=; i<; i++); NFADDR = (page >> ) & 0xff; /* Row Address A20~A27 */
for(i=; i<; i++); NFADDR = (page >> ) & 0x01; /* Row Address A28 */
for(i=; i<; i++);
}

  着重强调一下,“for(i=0; i<10; i++); ”延时代码绝不能省略。笔者发现,当S3C2440的主频在12MHz时,如果没有延时代码,NandFlash发送地址还能正常工作;一旦主频在400MHz,没有延时代码,地址就不能正常发送。

参考资料:如何编写linux下nand flash驱动-1

nandflash内存详解

附:实现NandFlash程序下载的代码(nand.zip

04-13 12:49