Nandflash在對(duì)大容量的數(shù)據(jù)存儲(chǔ)中發(fā)揮著重要的作用。相對(duì)于norflash,它具有一些優(yōu)勢(shì),但它的一個(gè)劣勢(shì)是很容易產(chǎn)生壞塊,因此在使用nandflash時(shí),往往要利用校驗(yàn)算法發(fā)現(xiàn)壞塊并標(biāo)注出來(lái),以便以后不再使用該壞塊。nandflash沒(méi)有地址或數(shù)據(jù)總線,如果是8位nandflash,那么它只有8個(gè)IO口,這8個(gè)IO口用于傳輸命令、地址和數(shù)據(jù)。nandflash主要以page(頁(yè))為單位進(jìn)行讀寫(xiě),以block(塊)為單位進(jìn)行擦除。每一頁(yè)中又分為main區(qū)和spare區(qū),main區(qū)用于正常數(shù)據(jù)的存儲(chǔ),spare區(qū)用于存儲(chǔ)一些附加信息,如塊好壞的標(biāo)記、塊的邏輯地址、頁(yè)內(nèi)數(shù)據(jù)的ECC校驗(yàn)和等。 三星公司是最主要的nandflash供應(yīng)商,因此在它所開(kāi)發(fā)的各類處理器中,實(shí)現(xiàn)對(duì)nandflash的支持就不足為奇了。s3c2440不僅具有nandflash的接口,而且還可以利用某些機(jī)制實(shí)現(xiàn)直接從nandflash啟動(dòng)并運(yùn)行程序。本文只介紹如何對(duì)nandflash實(shí)現(xiàn)讀、寫(xiě)、擦除等基本操作,不涉及nandflash啟動(dòng)程序的問(wèn)題。 在這里,我們使用的nandflash為K9F2G08U0A,它是8位的nandflash。不同型號(hào)的nandflash的操作會(huì)有所不同,但硬件引腳基本相同,這給產(chǎn)品的開(kāi)發(fā)帶來(lái)了便利。因?yàn)椴煌吞?hào)的PCB板是一樣的,只要更新一下軟件就可以使用不同容量大小的nandflash。 K9F2G08U0A的一頁(yè)為(2K+64)字節(jié)(加號(hào)前面的2K表示的是main區(qū)容量,加號(hào)后面的64表示的是spare區(qū)容量),它的一塊為64頁(yè),而整個(gè)設(shè)備包括了2048個(gè)塊。這樣算下來(lái)一共有2112M位容量,如果只算main區(qū)容量則有256M字節(jié)(即256M×8位)。要實(shí)現(xiàn)用8個(gè)IO口來(lái)要訪問(wèn)這么大的容量,K9F2G08U0A規(guī)定了用5個(gè)周期來(lái)實(shí)現(xiàn)。第一個(gè)周期訪問(wèn)的地址為A0"A7;第二個(gè)周期訪問(wèn)的地址為A8"A11,它作用在IO0"IO3上,而此時(shí)IO4"IO7必須為低電平;第三個(gè)周期訪問(wèn)的地址為A12"A19;第四個(gè)周期訪問(wèn)的地址為A20"A27;第五個(gè)周期訪問(wèn)的地址為A28,它作用在IO0上,而此時(shí)IO1"IO7必須為低電平。前兩個(gè)周期傳輸?shù)氖橇械刂罚笕齻(gè)周期傳輸?shù)氖切械刂贰Mㄟ^(guò)分析可知,列地址是用于尋址頁(yè)內(nèi)空間,行地址用于尋址頁(yè),如果要直接訪問(wèn)塊,則需要從地址A18開(kāi)始。 #include "2440addr.h" #define CMD_READ1 0x00 #define CMD_READ2 0x30 #define CMD_READID 0x90 #define CMD_RESET 0xFF #define CMD_WRITE1 0x80 #define CMD_WRITE2 0x10 #define CMD_BLOCKERASE1 0x60 #define CMD_BLOCKERASE2 0xD0 #define CMD_RANDOMWRITE 0x85 #define CMD_RANDOMREAD1 0x05 #define CMD_RANDOMREAD2 0xE0 #define CMD_READSTATE 0x70 #define NF_CMMD(cmd) rNFCMD = cmd #define NF_ADDR(addr) rNFADDR = addr #define NF_WRDATA(data) rNFDATA = data #define NF_WRDATA8(data) rNFDATA8 = data #define NF_RDDATA() rNFDATA #define NF_RDDATA8() rNFDATA8 #define NF_CE_L() rNFCONT &= "(0x1<<1) #define NF_CE_H() rNFCONT |= 0x1<<1 #define NF_MECC_LOCK() rNFCONT |= 0x1<<5 #define NF_MECC_ULOCK() rNFCONT &= "(0x1<<5) #define NF_SECC_LOCK() rNFCONT |= 0x1<<6 #define NF_SECC_ULOCK() rNFCONT &= "(0x1<<6) #define NF_RESETECC() rNFCONT |= 0x1<<4 #define NF_WAITRB() while(!(rNFSTAT&0x1)) #define NF_CLEARRB() rNFSTAT |= 0x1<<2 #define NF_DETECT() while(!(rNFSTAT&0x1<<2)) #define TACLS 1 #define TWRPH0 1 #define TWRPH1 1 #define U32 unsigned int #define U8 unsigned char U8 buffer[2048], Ecc[6]; U8 cmd, data, command; U32 block, add, pagenumber, count; U8 NF_BlockErase(U32 block){ //擦除以塊為單位 U8 state; NF_CE_L(); //打開(kāi)nandflash片選 NF_CLEARRB(); //等待R/nB信號(hào)就緒 NF_CMMD(CMD_BLOCKERASE1); NF_ADDR((block<<6)&0xff); NF_ADDR((block>>2)&0xff); NF_ADDR((block>>10)&0xff); NF_CMMD(CMD_BLOCKERASE2); NF_WAITRB(); NF_CMMD(CMD_READSTATE); do{ state = NF_RDDATA8(); }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x40; return 0x40; //0x40塊擦除失敗 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x60; return 0x60; //0x60塊擦除成功 } } U8 NF_PageWrite(U32 pagenumber){ U32 i, mecc, secc; U8 state; NF_CE_L(); NF_RESETECC(); //復(fù)位ECC NF_CLEARRB(); NF_CMMD(CMD_WRITE1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); //先解鎖main區(qū),然后在main區(qū)讀寫(xiě),產(chǎn)生main區(qū)ECC校驗(yàn),然后鎖定ECC,這樣ECC就被硬件寫(xiě)入rNFMECC0/1,我們將它讀到spare //區(qū)應(yīng)該存放校驗(yàn)的位置2048"2051。在讀寫(xiě)spare區(qū)的時(shí)候,即產(chǎn)生spare區(qū)的ECC,硬件把它自動(dòng)寫(xiě)入rNFSECC中,會(huì)產(chǎn)生spare區(qū) //的校驗(yàn),兩個(gè)字節(jié),寫(xiě)到2052"2053,在讀取的時(shí)候,我們將main區(qū)的ECC和spare區(qū)的ECC讀出來(lái),放入rNFMECCD0/1和rNFSECC中, //硬件完成rNFMECC0/1,rNFSECC和rNFMECCD0/1,rNFSECCD的校驗(yàn)。 NF_MECC_ULOCK(); //解鎖main區(qū)的ECC for(i = 0; i < 2048; i++){ NF_WRDATA8((char)(i+1)); //這個(gè)過(guò)程中產(chǎn)生ECC } NF_MECC_LOCK(); //鎖定main區(qū)ECC mecc = rNFMECC0; //讀取main區(qū)ECC Ecc[0] = (U8)(mecc&0xff); Ecc[1] = (U8)((mecc>>8)&0xff); Ecc[2] = (U8)((mecc>>16)&0xff); Ecc[3] = (U8)((mecc>>24)&0xff); NF_SECC_ULOCK(); //解鎖main區(qū)的ECC for(i = 0; i < 4; i++){ NF_WRDATA8(Ecc[ i]); //將maina區(qū)的ECC寫(xiě)入spare前4個(gè)字節(jié),這個(gè)過(guò)程產(chǎn)生spare區(qū)的ECC } NF_SECC_LOCK(); //鎖定spare區(qū)的ECC secc = rNFSECC; //讀取spare區(qū)的ECC Ecc[4] = (secc)&0xff; Ecc[5] = (secc>>8)&0xff; for(i = 4; i < 6; i++){ NF_WRDATA8(Ecc[ i]); //將spare區(qū)的ECC寫(xiě)入spare } NF_CMMD(CMD_WRITE2); NF_DETECT(); //等待R/nB信號(hào)變高,即不忙 NF_CMMD(CMD_READSTATE); //發(fā)讀狀態(tài)命令, 0x70 do{ state = NF_RDDATA8(); //檢查狀態(tài) I/O 位0為0 是寫(xiě)成功 1 是失敗, I/O 位6為0表示忙 為1是就緒 }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x43; return 0x43; //0x43隨機(jī)寫(xiě)失敗 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x63; return 0x63; //0x63隨機(jī)寫(xiě)成功 } } U8 NF_PageRead(U32 pagenumber){ U32 i, mecc, secc; NF_CE_L(); NF_RESETECC(); NF_CLEARRB(); NF_CMMD(CMD_READ1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_READ2); NF_WAITRB(); NF_MECC_ULOCK(); for(i = 0; i < 2048; i++){ buffer[ i] = NF_RDDATA8(); } NF_MECC_LOCK(); NF_SECC_ULOCK(); mecc = NF_RDDATA(); NF_SECC_LOCK(); rNFMECCD0 = ((mecc&0xff00)<<8) | (mecc&0xff); //讀取剛才的ECC 讓rNFMECCD0/1,rNFSECCD與rNFMECC0/1,RNFSECC比較,看是否發(fā)生錯(cuò)誤 rNFMECCD1 = ((mecc&0xff000000)>>8) | ((mecc&0xff0000)>>16); //校驗(yàn)是因?yàn)閚andflash很容易發(fā)生位反轉(zhuǎn),壞塊 secc = NF_RDDATA(); rNFSECCD = ((secc&0xff00)<<8)|(secc&0xff); NF_CE_H(); if((rNFESTAT0 & 0x0f) == 0x0){ //如果低4位都是0,說(shuō)明沒(méi)有錯(cuò)誤 while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x66; for(i = 0; i < 8; i++){ while(!(rUTRSTAT0&0x4)); rUTXH0 = buffer[ i]; } return 0x66; } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x44; return 0x44; } } void NF_Init(){ //GPA 17~22接在nandflash上 rGPACON = rGPACON & ("(0x3f<<17)) | (0x3f<<17); //TACLS為CLE/ALE有效到nWE有效之間的持續(xù)時(shí)間,TWRPH0為nWE的有效持續(xù)時(shí)間,TWRPH1為nWE無(wú)效到CLE/ALE無(wú)效之間的持續(xù)時(shí)間,這些時(shí)間都是以HCLK為單位的(本文程序中的HCLK=100MHz) rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0); //非鎖定,屏蔽nandflash中斷,初始化ECC及鎖定main區(qū)和spare區(qū)ECC,使能nandflash片選及控制器 rNFCONT = (0<<13)|(0<<12)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0); } void NF_Reset(){ NF_CE_L(); NF_WAITRB(); NF_CMMD(CMD_RESET); NF_DETECT(); NF_CE_H(); } void NF_ReadID(){ char MID, DID, cyc3, cyc4, cyc5; NF_CE_L(); NF_CLEARRB(); NF_CMMD(CMD_READID); NF_ADDR(0x00); MID = NF_RDDATA8(); //廠商ID while(!(rUTRSTAT0&0x4)); rUTXH0 = MID; DID = NF_RDDATA8(); //設(shè)備ID while(!(rUTRSTAT0&0x4)); rUTXH0 = DID; cyc3 = NF_RDDATA8(); //其他信息 while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc3; cyc4 = NF_RDDATA8(); while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc4; cyc5 = NF_RDDATA8(); while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc5; NF_CE_H(); } U8 NF_RandomWrite(U32 pagenumber, U32 add, U8 data){ //根據(jù)時(shí)序來(lái)就行 U8 state; NF_CE_L(); NF_CMMD(CMD_WRITE1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_RANDOMWRITE); NF_ADDR(add&0xff); //確定頁(yè)內(nèi)地址 NF_ADDR((add>>8)&0xff); NF_WRDATA8(data); //寫(xiě)數(shù)據(jù) NF_CMMD(CMD_WRITE2); NF_WAITRB(); NF_CMMD(CMD_READSTATE); do{ state = NF_RDDATA8(); }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x44; return 0x44; //0x44隨機(jī)寫(xiě)失敗 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x66; return 0x66; //0x66隨機(jī)寫(xiě)成功 } } U8 NF_RandomRead(U32 pagenumber, U32 add){ //根據(jù)時(shí)序來(lái)就行 char ch; NF_CE_L(); NF_CMMD(CMD_READ1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_READ2); NF_WAITRB(); NF_CMMD(CMD_RANDOMREAD1); NF_ADDR(add&0xff); NF_ADDR((add>>8)&0xff); NF_CMMD(CMD_RANDOMREAD2); ch = NF_RDDATA8(); //讀數(shù)據(jù) while(!(rUTRSTAT0&0x4)); rUTXH0 = ch; NF_CE_H(); return ch; } U8 NF_IsBadBlock(U32 block){ U8 result; result = NF_RandomRead(block*64, 2054); //0"2047是main區(qū)數(shù)據(jù),2048"2051是main區(qū)的ECC校驗(yàn),2052"2053是spare區(qū)的ECC校驗(yàn) if(result == 0x33){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x62; return 0x62; //0x62是壞塊 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x42; return 0x42; //0x42不是壞塊 } } U8 NF_MarkBadblock(U32 block){ U8 result; result = NF_RandomWrite(block*64, 2054, 0x33); //0x33標(biāo)記壞塊 block*64得到的是block塊第一個(gè)頁(yè)的地址 if(result == 0x66){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x61; return 0x61; //0x61標(biāo)記壞塊成功 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x41; return 0x41; //0x41標(biāo)記壞塊失敗 } } void __irq UART0_ISR(void){ U8 ch; rSRCPND |= (0x1<<28); rINTPND |= (0x1<<28); rSUBSRCPND |= 0x1<<0; if(rUTRSTAT0 & 0x1){ ch = rURXH0; while(!(rUTRSTAT0&0x4)); rUTXH0 = ch; cmd = ch; } } int Main(){ count = 0; rULCON0 = 0x3; rUCON0 = (1<<11)|(1<<2)|(1<<0); rUBRDIV0 = 26; rSRCPND = 0x1<<28; rINTPND = 0x1<<28; rSUBSRCPND = 0x1<<0; rINTMSK &= "(0x1<<28); rINTSUBMSK &= "(0x1<<0); pISR_UART0 = (U32)UART0_ISR; NF_Init(); while(1){ switch(cmd){ case 0x11: NF_Reset(); break; case 0x22: NF_ReadID(); break; case 0x33: NF_RandomRead(2001*64,0x3); break; case 0x44: NF_RandomWrite(2001*64,2052,0xbc); break; case 0x55: NF_PageRead(2001*64); break; case 0x66: while(!(rUTRSTAT0&0x4)); rUTXH0 = 0xba; NF_PageWrite(2001*64); break; case 0x77: NF_BlockErase(2001); break; case 0x88: NF_IsBadBlock(2001); break; case 0x99: NF_MarkBadblock(2001); break; } cmd = 0; } return 0; } 作者:李萬(wàn)鵬 |