天天看點

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);