1:開發環境
晶片:NUC977
控制器:FMI
flash晶片:W29N02GVS1AA
2:Nandflash原理
2.1 命名含義
2.2 引腳含義
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得狀态來判斷是否真正得完成。
3:内部存儲格式
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次寫入
這個圖表示:
有效位址為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 操作的指令
5.1 讀ID READ ID(0x90)
基本過程就是:
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)
過程:
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(程式設計)來表示寫
過程:
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)
過程:
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);