天天看点

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

1:开发环境

芯片:NUC977

控制器:FMI

flash芯片:W29N02GVS1AA

2:Nandflash原理

2.1 命名含义

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

2.2 引脚含义

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

CE :chip Enable 片选信号 低电平有效(上面带横杠表示低电平有效)

WE:Write Enable 写使能 低电平有效

RE: Read Enable 读使能 低电平有效

ALE:Address Latch Enable 地址占有使能

CLE:Command Latch Enable 命令占有使能

WP: write Project 写保护 低电平有效

RY/#BY: Read/Busy 空闲与繁忙状态

I/Ox : Input and Output 输入与输出引脚

一般工作得方式是:CE为低选中芯片,WE控制要写入数据 , RE控制读出数据。 当ALE为高时,表示可以写入地址。 当CLE为高时,表示可以协议命令。当ALE和CLE都为低可以经行数据得读写。

当操作都完成后可以通过RY/#BY得状态来判断是否真正得完成。

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

3:内部存储格式

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

3.1 page

再nand flash内部 最小操作单位时page,可以时512, 2K, 4K。这里得一个page是2K。

3.2 OOB/Spare Area / Redundant Area /

nand flash中每一页对应一块区域,用于存放校验的ECC数据和其他一些信息,比如上层文件系统放的和自己文件系统相关的数据。这个区域,在Linux MTD相关系统中,被称作oob(out of band),可以翻译为带外,也就是nand flash的一个页,可以称作一个band,band之外,对应的就是指那个多出来的,特殊的区域了。而nand flash的datasheet中,一般成为spare area,可译为空闲区域,另外,在ID的含义解释中也叫做redundant area,可译为冗余区域,归根结底,都是一个含义。不要被搞糊涂了就好

3.3 block

64个页是一个block,这里的大小是 64 * 2K = 128 K。 block是擦除的最小单元

3.4 Plane

1024个block是一个Plane ,这里的大小为 1024 * 128K = 128M

3.5 Chip

对于chip,其实任何某个型号的flash,都可以称其是一个chip,但是实际上,此处的chip,是针对内部来说的,也就是某型号的flash,内部有几个chip,比如下面会举例说到的,三星的2GB的K9WAG08U1A芯片(可以理解为外部芯片/型号)内部装了2个单片是1GB的K9K8G08U0A,此时就称 K9WAG08U1A内部有2个chip,而有些单个的chip,内部又包含多个plane,比如上面的W29N02GVS1AA内部包含2个单片是2Gb的Plane。只有搞清楚了此处的chip和plane的关系,才能明白后面提到的多页(Multi Plane / Multi Page)编程和交互(interleave)编程的含义。

这里的大小为 2 * 128M = 256M

3.6 Page Register(页寄存器)

nand flash硬件中的一块地方,名字叫做register,实际就是一个数据缓存,一个buffer,用于存放那些从flash读出来或者将要写入到flash中的。其实叫做页缓存,更合适,更容易明白其含义。此页寄存器的大小=页大小+ oob 大小,即pagesize+oob,对于常见的页是2KB的,此页寄存器就是2KB+64=2112字节

4 地址写入循环:

不同的flash,地址写入方式不同,有些把一个地址分为3次写入,有些分为5次写入

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

这个图表示:

有效地址为29位,地址分为5次写入

之所以这样划分起始是为了行地址和列地址,也就是页内的地址和页的地址

A0-A11:在前两次写入。这是因为一个页的大小是2048,用二进制表示就是1000 0000 0000,是12位。也是是说,要访问一个页内的0-2047处的数据,最多需要11位就可以了就可以表示(000 0000 0000 - 111 1111 1111)。

这里用了12位,是为了访问OOB区。当地址A0-A12为1000 0000 0000时,就表示访问该页后面的OOB的数据。

A11-A28:17位就是来表示哪一个页。这里有2102464 个页。 二进制表示10 0000 0000 0000 0000

所以对应的17位就可以访问所有的页

总结:地址可以分成2部分,后面3次的地址用来寻找在哪一个页。前面2次的地址用力寻找在该页的哪一个位置。

5 操作的命令

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

5.1 读ID READ ID(0x90)

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

基本过程就是:

1:先发出命令0x90

2:再发出地址0x00

3:再读取5 byte数据即可

void read_nand_id(void)
{
	char id[6]={0};
	int i;
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000); //CS enable
	outpw(REG_NANDCMD, NAND_CMD_READID);
	outpw(REG_NANDADDR, 0x80000000); //这里地址最高位置1 来表示时最后一笔地址
//	outpw(REG_NANDADDR, 0x80000020);
	
	for(i = 0; i< 6; i++) {
		id[i] = (unsigned char)inpw(REG_NANDDATA);
	}
	for(i = 0; i< 6; i++) {
		rt_kprintf("id[%d] = 0x%02x\n", i, id[i]);
	}
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))); //CS disable
}
MSH_CMD_EXPORT(read_nand_id, read_nand_id);
           

5.2 读数据 PAGE READ (00h-30h)

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

过程:

1:先发出读命令0x00

2: 发出5次地址

3:发出命令0x30

4:等待RY/#BY变成1,表示数据由flash中的page读到page buffer这个动作结束

5:读出数据

int nand_read(int argc, char *argv[])
{
	rt_uint8_t *pBuffer;;
	rt_uint32_t StartAddress;
	rt_uint32_t NumByteToRead;
	rt_uint32_t i;
	rt_uint32_t column,  page_addr;
	
	if(argc > 2) {
		StartAddress =  htoi(argv[1]);
		NumByteToRead =  htoi(argv[2]);
	}else{
		rt_kprintf("please input >> nand_read <addr> <length>\n");
		return RT_ERROR;
	}
	pBuffer = rt_malloc(NumByteToRead);
	
	column = StartAddress % 2048;
	page_addr = StartAddress / 2048;
	
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000); //CS enable
	
	outpw(REG_NANDCMD, NAND_CMD_READ0); // read0 cmd
	
	outpw(REG_NANDADDR, column&0xff);
	outpw(REG_NANDADDR, column >> 8);
	outpw(REG_NANDADDR, page_addr&0xFF);
	outpw(REG_NANDADDR, (page_addr >> 8)&0xFF);
	outpw(REG_NANDADDR, ((page_addr >> 16)&0xFF)|ENDADDR);
	
	outpw(REG_NANDCMD, NAND_CMD_READSTART); // cmd read start

	while (!(inpw(REG_NANDINTSTS) & READYBUSY)); //wait
	for(i = 0; i<NumByteToRead; i++){
		pBuffer[i] = (unsigned char)inpw(REG_NANDDATA);
	}
	
	rt_kprintf("data:\n");
	for(i = 0; i<NumByteToRead; i++) {
		if(i%16 == 0) {
			rt_kprintf("\n");
			rt_kprintf("0x%08x: ", StartAddress+i);
		}
		rt_kprintf("0x%02x ", pBuffer[i]);
	}
	rt_kprintf("\n");
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))); //CS disable
	return RT_EOK;
}
MSH_CMD_EXPORT(nand_read, read_nand test);
           

5.3 写操作 PAGE PROGRAM (80h-10h)

通常flash 用program(编程)来表示写

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

过程:

1:发送命令0x80

2:发送5次地址

3:发送数据

4:发送命令0x10

5:等待RY/#BY变成1,表示数据由page buffer写入到flash中的page这个动作结束

rt_uint32_t nand_write(int argc, char *argv[])
{
	rt_uint8_t *pBuffer;
	rt_uint8_t data;
	rt_uint32_t StartAddress;
	rt_uint32_t NumByteToWrite;
	rt_uint32_t i;
	rt_uint32_t column,  page_addr;
	if(argc > 3) {
		StartAddress =  htoi(argv[1]);
		NumByteToWrite =  htoi(argv[2]);
		data =  htoi(argv[3]);
	}else{
		rt_kprintf("please input >> nand_write <addr> <length> <data>\n");
		return RT_ERROR;
	}
	column = StartAddress % 2048;
	page_addr = StartAddress / 2048;
	rt_kprintf("Write addr: 0x%02x, length:%d, data:%x\n\n", StartAddress, NumByteToWrite, data);
	pBuffer = rt_malloc(NumByteToWrite);
	
	for(i = 0; i<NumByteToWrite; i++){
		pBuffer[i] = data;
	}
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000); //CS enable
	outpw(REG_NANDCMD, NAND_CMD_SEQIN); // pargram cmd
	
	outpw(REG_NANDADDR, column&0xff);
	outpw(REG_NANDADDR, column >> 8);
	outpw(REG_NANDADDR, page_addr&0xFF);
	outpw(REG_NANDADDR, (page_addr >> 8)&0xFF);
	outpw(REG_NANDADDR, ((page_addr >> 16)&0xFF)|ENDADDR);
	
	for(i = 0; i<NumByteToWrite; i++){
		outpw(REG_NANDDATA, pBuffer[i]);
	}
	
	outpw(REG_NANDCMD, NAND_CMD_PAGEPROG); // data input

	while (!(inpw(REG_NANDINTSTS) & READYBUSY)); //wait
	
	outpw(REG_NANDCMD, NAND_CMD_STATUS); // write status pass: bit0 = 0  fail: bit0 = 1
	if(inpw(REG_NANDDATA) & 0x1 ){
		rt_kprintf("write data to address: 0x%08x failed\n", StartAddress);
		rt_kprintf("status:0x%02x\n", inpw(REG_NANDDATA));
	}else{
		rt_kprintf("write data to address: 0x%08x success\n", StartAddress);
	}

	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))); //CS disable
	return RT_EOK;
}
MSH_CMD_EXPORT(nand_write, read_nand test);
           

5.4 块擦除 BLOCK ERASE (60h-D0h)

Nandflash原理W29N02GVS1AA1:开发环境2:Nandflash原理3:内部存储格式4 地址写入循环:5 操作的命令

过程:

1:写入命令0x60

2:写入3次地址,表示页的地址,会删除这个页所在的block

3:写入命令0xDO

4:等待RY/#BY变成1,表示擦删完成

5:写入命令0x70

6:读出1byte数据,查看读出数据的bit0 是不是位0

int nand_erase(int argc, char *argv[])
{
	rt_uint32_t StartAddress = 0x00030000;
	rt_uint32_t column,  page_addr;
//	column = StartAddress % 2048;
	
	if(argc > 1) {
		StartAddress =  htoi(argv[1]);
	}else{
		rt_kprintf("please input >> nand_erase <addr> <length> <data>\n");
		return RT_ERROR;
	}
	page_addr = StartAddress / 2048;
	
	
	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))|0x04000000); //CS enable
	outpw(REG_NANDCMD, NAND_CMD_ERASE1); // erase page
	
	outpw(REG_NANDADDR, page_addr&0xFF);
	outpw(REG_NANDADDR, (page_addr >> 8)&0xFF);
	outpw(REG_NANDADDR, ((page_addr >> 16)&0xFF)|ENDADDR);
	
	
	outpw(REG_NANDCMD, NAND_CMD_ERASE2); // erase confirm command

	while (!(inpw(REG_NANDINTSTS) & READYBUSY)); //wait
	
	outpw(REG_NANDCMD, NAND_CMD_STATUS); // write status pass: bit0 = 0  fail: bit0 = 1
	if(inpw(REG_NANDDATA) & 0x1 ){
		rt_kprintf("erase address: 0x%08x failed\n", page_addr);
		rt_kprintf("status:0x%02x\n", inpw(REG_NANDDATA));
	}else{
		rt_kprintf("erase address: 0x%08x success\n", page_addr);
	}

	outpw(REG_NANDCTL, (inpw(REG_NANDCTL)&(~0x06000000))); //CS disable
	return RT_EOK;
}
MSH_CMD_EXPORT(nand_erase, read_nand test);