<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > s3c2440對nandflash的操作

s3c2440對nandflash的操作

作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò ) 收藏
nandflash在對大容量的數據存儲中發(fā)揮著(zhù)重要的作用。相對于norflash,它具有一些優(yōu)勢,但它的一個(gè)劣勢是很容易產(chǎn)生壞塊,因此在使用nandflash時(shí),往往要利用校驗算法發(fā)現壞塊并標注出來(lái),以便以后不再使用該壞塊。nandflash沒(méi)有地址或數據總線(xiàn),如果是8位nandflash,那么它只有8個(gè)IO口,這8個(gè)IO口用于傳輸命令、地址和數據。nandflash主要以page(頁(yè))為單位進(jìn)行讀寫(xiě),以block(塊)為單位進(jìn)行擦除。每一頁(yè)中又分為main區和spare區,main區用于正常數據的存儲,spare區用于存儲一些附加信息,如塊好壞的標記、塊的邏輯地址、頁(yè)內數據的ECC校驗和等。

三星公司是最主要的nandflash供應商,因此在它所開(kāi)發(fā)的各類(lèi)處理器中,實(shí)現對nandflash的支持就不足為奇了。s3c2440不僅具有nandflash的接口,而且還可以利用某些機制實(shí)現直接從nandflash啟動(dòng)并運行程序。本文只介紹如何對nandflash實(shí)現讀、寫(xiě)、擦除等基本操作,不涉及nandflash啟動(dòng)程序的問(wèn)題。

在這里,我們使用的nandflash為K9F2G08U0A,它是8位的nandflash。不同型號的nandflash的操作會(huì )有所不同,但硬件引腳基本相同,這給產(chǎn)品的開(kāi)發(fā)帶來(lái)了便利。因為不同型號的PCB板是一樣的,只要更新一下軟件就可以使用不同容量大小的nandflash。

K9F2G08U0A的一頁(yè)為(2K+64)字節(加號前面的2K表示的是main區容量,加號后面的64表示的是spare區容量),它的一塊為64頁(yè),而整個(gè)設備包括了2048個(gè)塊。這樣算下來(lái)一共有2112M位容量,如果只算main區容量則有256M字節(即256M×8位)。要實(shí)現用8個(gè)IO口來(lái)要訪(fǎng)問(wèn)這么大的容量,K9F2G08U0A規定了用5個(gè)周期來(lái)實(shí)現。第一個(gè)周期訪(fǎng)問(wèn)的地址為A0~A7;第二個(gè)周期訪(fǎng)問(wèn)的地址為A8~A11,它作用在IO0~IO3上,而此時(shí)IO4~IO7必須為低電平;第三個(gè)周期訪(fǎng)問(wèn)的地址為A12~A19;第四個(gè)周期訪(fǎng)問(wèn)的地址為A20~A27;第五個(gè)周期訪(fǎng)問(wèn)的地址為A28,它作用在IO0上,而此時(shí)IO1~IO7必須為低電平。前兩個(gè)周期傳輸的是列地址,后三個(gè)周期傳輸的是行地址。通過(guò)分析可知,列地址是用于尋址頁(yè)內空間,行地址用于尋址頁(yè),如果要直接訪(fǎng)問(wèn)塊,則需要從地址A18開(kāi)始。

由于所有的命令、地址和數據全部從8位IO口傳輸,所以nandflash定義了一個(gè)命令集來(lái)完成各種操作。有的操作只需要一個(gè)命令(即一個(gè)周期)即可,而有的操作則需要兩個(gè)命令(即兩個(gè)周期)來(lái)實(shí)現。下面的宏定義為K9F2G08U0A的常用命令:

#define CMD_READ10x00//頁(yè)讀命令周期1
#define CMD_READ20x30//頁(yè)讀命令周期2
#define CMD_READID0x90//讀ID命令
#define CMD_WRITE10x80//頁(yè)寫(xiě)命令周期1
#define CMD_WRITE20x10//頁(yè)寫(xiě)命令周期2
#define CMD_ERASE10x60//塊擦除命令周期1
#define CMD_ERASE20xd0//塊擦除命令周期2
#define CMD_STATUS0x70//讀狀態(tài)命令
#define CMD_RESET0xff//復位
#define CMD_RANDOMREAD10x05//隨意讀命令周期1
#define CMD_RANDOMREAD20xE0//隨意讀命令周期2
#define CMD_RANDOMWRITE0x85//隨意寫(xiě)命令

在這里,隨意讀命令和隨意寫(xiě)命令可以實(shí)現在一頁(yè)內任意地址地讀寫(xiě)。讀狀態(tài)命令可以實(shí)現讀取設備內的狀態(tài)寄存器,通過(guò)該命令可以獲知寫(xiě)操作或擦除操作是否完成(判斷第6位),以及是否成功完成(判斷第0位)。

下面介紹s3c2440的nandflash控制器。s3c2440支持8位或16位的每頁(yè)大小為256字,512字節,1K字和2K字節的nandflash,這些配置是通過(guò)系統上電后相應引腳的高低電平來(lái)實(shí)現的。s3c2440還可以硬件產(chǎn)生ECC校驗碼,這為準確及時(shí)發(fā)現nandflash的壞塊帶來(lái)了方便。nandflash控制器的主要寄存器有NFCONF(nandflash配置寄存器),NFCONT(nandflash控制寄存器),NFCMMD(nandflash命令集寄存器),NFADDR(nandflash地址集寄存器),NFDATA(nandflash數據寄存器),NFMECCD0/1(nandflash的main區ECC寄存器),NFSECCD(nandflash的spare區ECC寄存器),NFSTAT(nandflash操作狀態(tài)寄存器),NFESTAT0/1(nandflash的ECC狀態(tài)寄存器),NFMECC0/1(nandflash用于數據的ECC寄存器),以及NFSECC(nandflash用于IO的ECC寄存器)。

NFCMMD,NFADDR和NFDATA分別用于傳輸命令,地址和數據,為了方便起見(jiàn),我們可以定義一些宏定義用于完成上述操作:

#define NF_CMD(data){rNFCMD= (data); }//傳輸命令
#define NF_ADDR(addr){rNFADDR = (addr); }//傳輸地址
#define NF_RDDATA()(rNFDATA)//讀32位數據
#define NF_RDDATA8()(rNFDATA8)//讀8位數據
#define NF_WRDATA(data){rNFDATA = (data); }//寫(xiě)32位數據
#define NF_WRDATA8(data){rNFDATA8 = (data); }//寫(xiě)8位數據

其中rNFDATA8的定義為(*(volatile unsigned char *)0x4E000010)。

NFCONF主要用到了TACLS、TWRPH0、TWRPH1,這三個(gè)變量用于配置nandflash的時(shí)序。s3c2440的數據手冊沒(méi)有詳細說(shuō)明這三個(gè)變量的具體含義,但通過(guò)它所給出的時(shí)序圖,我們可以看出,TACLS為CLE/ALE有效到nWE有效之間的持續時(shí)間,TWRPH0為nWE的有效持續時(shí)間,TWRPH1為nWE無(wú)效到CLE/ALE無(wú)效之間的持續時(shí)間,這些時(shí)間都是以HCLK為單位的(本文程序中的HCLK=100MHz)。通過(guò)查閱K9F2G08U0A的數據手冊,我們可以找到并計算該nandflash與s3c2440相對應的時(shí)序:K9F2G08U0A中的tWP與TWRPH0相對應,tCLH與TWRPH1相對應,(tCLS-tWP)與TACLS相對應。K9F2G08U0A給出的都是最小時(shí)間,s3c2440只要滿(mǎn)足它的最小時(shí)間即可,因此TACLS、TWRPH0、TWRPH1這三個(gè)變量取值大一些會(huì )更保險。在這里,這三個(gè)值分別取1,2和0。NFCONF的第0位表示的是外接的nandflash是8位IO還是16位IO,這里當然要選擇8位的IO。NFCONT寄存器是另一個(gè)需要事先初始化的寄存器。它的第13位和第12位用于鎖定配置,第8位到第10位用于nandflash的中斷,第4位到第6位用于ECC的配置,第1位用于nandflash芯片的選取,第0位用于nandflash控制器的使能。另外,為了初始化nandflash,還需要配置GPACON寄存器,使它的第17位到第22位與nandflash芯片的控制引腳相對應。下面的程序實(shí)現了初始化nandflash控制器:

void NF_Init ( void )
{
rGPACON = (rGPACON &~(0x3f<<17)) | (0x3f<<17);//配置芯片引腳
//TACLS=1、TWRPH0=2、TWRPH1=0,8位IO
rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
//非鎖定,屏蔽nandflash中斷,初始化ECC及鎖定main區和spare區ECC,使能nandflash片選及控制器
rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
}

為了更好地應用ECC和使能nandflash片選,我們還需要一些宏定義:

#define NF_nFCE_L(){rNFCONT &= ~(1<<1); }
#define NF_CE_L()NF_nFCE_L()//打開(kāi)nandflash片選
#define NF_nFCE_H(){rNFCONT |= (1<<1); }
#define NF_CE_H()NF_nFCE_H()//關(guān)閉nandflash片選
#define NF_RSTECC(){rNFCONT |= (1<<4); }//復位ECC
#define NF_MECC_UnLock(){rNFCONT &= ~(1<<5); }//解鎖main區ECC
#define NF_MECC_Lock(){rNFCONT |= (1<<5); }//鎖定main區ECC
#define NF_SECC_UnLock(){rNFCONT &= ~(1<<6); }//解鎖spare區ECC
#define NF_SECC_Lock(){rNFCONT |= (1<<6); }//鎖定spare區ECC

NFSTAT是另一個(gè)比較重要的寄存器,它的第0位可以用于判斷nandflash是否在忙,第2位用于檢測RnB引腳信號:

#define NF_WAITRB(){while(!(rNFSTAT&(1<<0)));}//等待nandflash不忙
#define NF_CLEAR_RB(){rNFSTAT |= (1<<2); }//清除RnB信號
#define NF_DETECT_RB(){while(!(rNFSTAT&(1<<2)));}//等待RnB信號變高,即不忙

下面就詳細介紹K9F2G08U0A的基本操作,包括復位,讀ID,頁(yè)讀、寫(xiě)數據,隨意讀、寫(xiě)數據,塊擦除等。

復位操作最簡(jiǎn)單,只需寫(xiě)入復位命令即可:

static void rNF_Reset()
{
NF_CE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清除RnB信號
NF_CMD(CMD_RESET);//寫(xiě)入復位命令
NF_DETECT_RB();//等待RnB信號變高,即不忙
NF_CE_H();//關(guān)閉nandflash片選
}

讀取K9F2G08U0A芯片ID操作首先需要寫(xiě)入讀ID命令,然后再寫(xiě)入0x00地址,就可以讀取到一共五個(gè)周期的芯片ID,第一個(gè)周期為廠(chǎng)商ID,第二個(gè)周期為設備ID,第三個(gè)周期至第五個(gè)周期包括了一些具體的該芯片信息,這里就不多介紹:

static char rNF_ReadID()
{
char pMID;
char pDID;
char cyc3, cyc4, cyc5;

NF_nFCE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清RnB信號
NF_CMD(CMD_READID);//讀ID命令
NF_ADDR(0x0);//寫(xiě)0x00地址

//讀五個(gè)周期的ID
pMID = NF_RDDATA8();//廠(chǎng)商ID:0xEC
pDID = NF_RDDATA8();//設備ID:0xDA
cyc3 = NF_RDDATA8();//0x10
cyc4 = NF_RDDATA8();//0x95
cyc5 = NF_RDDATA8();//0x44

NF_nFCE_H();//關(guān)閉nandflash片選

return (pDID);
}

下面介紹讀操作,讀操作是以頁(yè)為單位進(jìn)行的。如果在讀取數據的過(guò)程中不進(jìn)行ECC校驗判斷,則讀操作比較簡(jiǎn)單,在寫(xiě)入讀命令的兩個(gè)周期之間寫(xiě)入要讀取的頁(yè)地址,然后讀取數據即可。如果為了更準確地讀取數據,則在讀取完數據之后還要進(jìn)行ECC校驗判斷,以確定所讀取的數據是否正確。

在上文中我們已經(jīng)介紹過(guò),nandflash的每一頁(yè)有兩區:main區和spare區,main區用于存儲正常的數據,spare區用于存儲其他附加信息,其中就包括ECC校驗碼。當我們在寫(xiě)入數據的時(shí)候,我們就計算這一頁(yè)數據的ECC校驗碼,然后把校驗碼存儲到spare區的特定位置中,在下次讀取這一頁(yè)數據的時(shí)候,同樣我們也計算ECC校驗碼,然后與spare區中的ECC校驗碼比較,如果一致則說(shuō)明讀取的數據正確,如果不一致則不正確。ECC的算法較為復雜,好在s3c2440能夠硬件產(chǎn)生ECC校驗碼,這樣就省去了不少的麻煩事。s3c2440即可以產(chǎn)生main區的ECC校驗碼,也可以產(chǎn)生spare區的ECC校驗碼。因為K9F2G08U0A是8位IO口,因此s3c2440共產(chǎn)生4個(gè)字節的main區ECC碼和2個(gè)字節的spare區ECC碼。在這里我們規定,在每一頁(yè)的spare區的第0個(gè)地址到第3個(gè)地址存儲main區ECC,第4個(gè)地址和第5個(gè)地址存儲spare區ECC。產(chǎn)生ECC校驗碼的過(guò)程為:在讀取或寫(xiě)入哪個(gè)區的數據之前,先解鎖該區的ECC,以便產(chǎn)生該區的ECC。在讀取或寫(xiě)入完數據之后,再鎖定該區的ECC,這樣系統就會(huì )把產(chǎn)生的ECC碼保存到相應的寄存器中。main區的ECC保存到NFMECC0/1中(因為K9F2G08U0A是8位IO口,因此這里只用到了NFMECC0),spare區的ECC保存到NFSECC中。對于讀操作來(lái)說(shuō),我們還要繼續讀取spare區的相應地址內容,已得到上次寫(xiě)操作時(shí)所存儲的main區和spare區的ECC,并把這些數據分別放入NFMECCD0/1和NFSECCD的相應位置中。最后我們就可以通過(guò)讀取NFESTAT0/1(因為K9F2G08U0A是8位IO口,因此這里只用到了NFESTAT0)中的低4位來(lái)判斷讀取的數據是否正確,其中第0位和第1位為main區指示錯誤,第2位和第3位為spare區指示錯誤。

下面就給出一段具體的頁(yè)讀操作程序:

U8 rNF_ReadPage(U32 page_number)
{
U32 i, mecc0, secc;

NF_RSTECC();//復位ECC
NF_MECC_UnLock();//解鎖main區ECC

NF_nFCE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清RnB信號

NF_CMD(CMD_READ1);//頁(yè)讀命令周期1

//寫(xiě)入5個(gè)地址周期
NF_ADDR(0x00);//列地址A0~A7
NF_ADDR(0x00);//列地址A8~A11
NF_ADDR((page_number) & 0xff);//行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff);//行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff);//行地址A28

NF_CMD(CMD_READ2);//頁(yè)讀命令周期2

NF_DETECT_RB();//等待RnB信號變高,即不忙

//讀取一頁(yè)數據內容
for (i = 0; i < 2048; i++)
{
buffer[i] =NF_RDDATA8();
}

NF_MECC_Lock();//鎖定main區ECC值

NF_SECC_UnLock();//解鎖spare區ECC

mecc0=NF_RDDATA();//讀spare區的前4個(gè)地址內容,即第2048~2051地址,這4個(gè)字節為main區的ECC
//把讀取到的main區的ECC校驗碼放入NFMECCD0/1的相應位置內
rNFMECCD0=((mecc0&0xff00)<<8)|(mecc0&0xff);
rNFMECCD1=((mecc0&0xff000000)>>8)|((mecc0&0xff0000)>>16);

NF_SECC_Lock();//鎖定spare區的ECC值

secc=NF_RDDATA();//繼續讀spare區的4個(gè)地址內容,即第2052~2055地址,其中前2個(gè)字節為spare區的ECC值
//把讀取到的spare區的ECC校驗碼放入NFSECCD的相應位置內
rNFSECCD=((secc&0xff00)<<8)|(secc&0xff);

NF_nFCE_H();//關(guān)閉nandflash片選

//判斷所讀取到的數據是否正確
if ((rNFESTAT0&0xf) == 0x0)
return 0x66;//正確
else
return 0x44;//錯誤

}

這段程序是把某一頁(yè)的內容讀取到全局變量數組buffer中。該程序的輸入參數直接就為K9F2G08U0A的第幾頁(yè),例如我們要讀取第128064頁(yè)中的內容,可以調用該程序為:rNF_ReadPage(128064);。由于第128064頁(yè)是第2001塊中的第0頁(yè)(128064=2001×64+0),所以為了更清楚地表示頁(yè)與塊之間的關(guān)系,也可以寫(xiě)為:rNF_ReadPage(2001*64);。

頁(yè)寫(xiě)操作的大致流程為:在兩個(gè)寫(xiě)命令周期之間分別寫(xiě)入頁(yè)地址和數據,當然如果為了保證下次讀取該數據時(shí)的正確性,還需要把main區的ECC值和spare區的ECC值寫(xiě)入到該頁(yè)的spare區內。然后我們還需要讀取狀態(tài)寄存器,以判斷這次寫(xiě)操作是否正確。下面就給出一段具體的頁(yè)寫(xiě)操作程序,其中輸入參數也是要寫(xiě)入數據到第幾頁(yè):

U8 rNF_WritePage(U32 page_number)
{
U32 i, mecc0, secc;
U8 stat, temp;

temp = rNF_IsBadBlock(page_number>>6);//判斷該塊是否為壞塊
if(temp == 0x33)
return 0x42;//是壞塊,返回

NF_RSTECC();//復位ECC
NF_MECC_UnLock();//解鎖main區的ECC

NF_nFCE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清RnB信號

NF_CMD(CMD_WRITE1);//頁(yè)寫(xiě)命令周期1

//寫(xiě)入5個(gè)地址周期
NF_ADDR(0x00);//列地址A0~A7
NF_ADDR(0x00);//列地址A8~A11
NF_ADDR((page_number) & 0xff);//行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff);//行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff);//行地址A28

//寫(xiě)入一頁(yè)數據
for (i = 0; i < 2048; i++)
{
NF_WRDATA8((char)(i+6));
}

NF_MECC_Lock();//鎖定main區的ECC值

mecc0=rNFMECC0;//讀取main區的ECC校驗碼
//把ECC校驗碼由字型轉換為字節型,并保存到全局變量數組ECCBuf中
ECCBuf[0]=(U8)(mecc0&0xff);
ECCBuf[1]=(U8)((mecc0>>8) & 0xff);
ECCBuf[2]=(U8)((mecc0>>16) & 0xff);
ECCBuf[3]=(U8)((mecc0>>24) & 0xff);

NF_SECC_UnLock();//解鎖spare區的ECC
//把main區的ECC值寫(xiě)入到spare區的前4個(gè)字節地址內,即第2048~2051地址
for(i=0;i<4;i++)
{
NF_WRDATA8(ECCBuf[i]);
}

NF_SECC_Lock();//鎖定spare區的ECC值
secc=rNFSECC;//讀取spare區的ECC校驗碼
//把ECC校驗碼保存到全局變量數組ECCBuf中
ECCBuf[4]=(U8)(secc&0xff);
ECCBuf[5]=(U8)((secc>>8) & 0xff);
//把spare區的ECC值繼續寫(xiě)入到spare區的第2052~2053地址內
for(i=4;i<6;i++)
{
NF_WRDATA8(ECCBuf[i]);
}

NF_CMD(CMD_WRITE2);//頁(yè)寫(xiě)命令周期2

delay(1000);//延時(shí)一段時(shí)間,以等待寫(xiě)操作完成

NF_CMD(CMD_STATUS);//讀狀態(tài)命令

//判斷狀態(tài)值的第6位是否為1,即是否在忙,該語(yǔ)句的作用與NF_DETECT_RB();相同
do{
stat = NF_RDDATA8();
}while(!(stat&0x40));

NF_nFCE_H();//關(guān)閉nandflash片選

//判斷狀態(tài)值的第0位是否為0,為0則寫(xiě)操作正確,否則錯誤
if (stat & 0x1)
{
temp = rNF_MarkBadBlock(page_number>>6);//標注該頁(yè)所在的塊為壞塊
if (temp == 0x21)
return 0x43//標注壞塊失敗
else
return 0x44;//寫(xiě)操作失敗
}
else
return 0x66;//寫(xiě)操作成功
}

該段程序先判斷該頁(yè)所在的壞是否為壞塊,如果是則退出。在最后寫(xiě)操作失敗后,還要標注該頁(yè)所在的塊為壞塊,其中所用到的函數rNF_IsBadBlock和rNF_MarkBadBlock,我們在后面介紹。我們再總結一下該程序所返回數值的含義,0x42:表示該頁(yè)所在的塊為壞塊;0x43:表示寫(xiě)操作失敗,并且在標注該頁(yè)所在的塊為壞塊時(shí)也失??;0x44:表示寫(xiě)操作失敗,但是標注壞塊成功;0x66:寫(xiě)操作成功。

擦除是以塊為單位進(jìn)行的,因此在寫(xiě)地址周期是,只需寫(xiě)三個(gè)行周期,并且要從A18開(kāi)始寫(xiě)起。與寫(xiě)操作一樣,在擦除結束前還要判斷是否擦除操作成功,另外同樣也存在需要判斷是否為壞塊以及要標注壞塊的問(wèn)題。下面就給出一段具體的塊擦除操作程序:

U8 rNF_EraseBlock(U32 block_number)
{
char stat, temp;

temp = rNF_IsBadBlock(block_number);//判斷該塊是否為壞塊
if(temp == 0x33)
return 0x42;//是壞塊,返回

NF_nFCE_L();//打開(kāi)片選
NF_CLEAR_RB();//清RnB信號

NF_CMD(CMD_ERASE1);//擦除命令周期1

//寫(xiě)入3個(gè)地址周期,從A18開(kāi)始寫(xiě)起
NF_ADDR((block_number << 6) & 0xff);//行地址A18~A19
NF_ADDR((block_number >> 2) & 0xff);//行地址A20~A27
NF_ADDR((block_number >> 10) & 0xff);//行地址A28

NF_CMD(CMD_ERASE2);//擦除命令周期2

delay(1000);//延時(shí)一段時(shí)間

NF_CMD(CMD_STATUS);//讀狀態(tài)命令

//判斷狀態(tài)值的第6位是否為1,即是否在忙,該語(yǔ)句的作用與NF_DETECT_RB();相同
do{
stat = NF_RDDATA8();
}while(!(stat&0x40));

NF_nFCE_H();//關(guān)閉nandflash片選

//判斷狀態(tài)值的第0位是否為0,為0則擦除操作正確,否則錯誤
if (stat & 0x1)
{
temp = rNF_MarkBadBlock(page_number>>6);//標注該塊為壞塊
if (temp == 0x21)
return 0x43//標注壞塊失敗
else
return 0x44;//擦除操作失敗
}
else
return 0x66;//擦除操作成功
}

該程序的輸入參數為K9F2G08U0A的第幾塊,例如我們要擦除第2001塊,則調用該函數為:rNF_EraseBlock(2001)。

K9F2G08U0A除了提供了頁(yè)讀和頁(yè)寫(xiě)功能外,還提供了頁(yè)內地址隨意讀、寫(xiě)功能。頁(yè)讀和頁(yè)寫(xiě)是從頁(yè)的首地址開(kāi)始讀、寫(xiě),而隨意讀、寫(xiě)實(shí)現了在一頁(yè)范圍內任意地址的讀、寫(xiě)。隨意讀操作是在頁(yè)讀操作后輸入隨意讀命令和頁(yè)內列地址,這樣就可以讀取到列地址所指定地址的數據。隨意寫(xiě)操作是在頁(yè)寫(xiě)操作的第二個(gè)頁(yè)寫(xiě)命令周期前,輸入隨意寫(xiě)命令和頁(yè)內列地址,以及要寫(xiě)入的數據,這樣就可以把數據寫(xiě)入到列地址所指定的地址內。下面兩段程序實(shí)現了隨意讀和隨意寫(xiě)功能,其中隨意讀程序的輸入參數分別為頁(yè)地址和頁(yè)內地址,輸出參數為所讀取到的數據,隨意寫(xiě)程序的輸入參數分別為頁(yè)地址,頁(yè)內地址,以及要寫(xiě)入的數據。

U8 rNF_RamdomRead(U32 page_number, U32 add)
{
NF_nFCE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清RnB信號

NF_CMD(CMD_READ1);//頁(yè)讀命令周期1

//寫(xiě)入5個(gè)地址周期
NF_ADDR(0x00);//列地址A0~A7
NF_ADDR(0x00);//列地址A8~A11
NF_ADDR((page_number) & 0xff);//行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff);//行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff);//行地址A28

NF_CMD(CMD_READ2);//頁(yè)讀命令周期2

NF_DETECT_RB();//等待RnB信號變高,即不忙

NF_CMD(CMD_RANDOMREAD1);//隨意讀命令周期1
//頁(yè)內地址
NF_ADDR((char)(add&0xff));//列地址A0~A7
NF_ADDR((char)((add>>8)&0x0f));//列地址A8~A11
NF_CMD(CMD_RANDOMREAD2);//隨意讀命令周期2

return NF_RDDATA8();//讀取數據
}

U8 rNF_RamdomWrite(U32 page_number, U32 add, U8 dat)
{
U8 temp,stat;

NF_nFCE_L();//打開(kāi)nandflash片選
NF_CLEAR_RB();//清RnB信號

NF_CMD(CMD_WRITE1);//頁(yè)寫(xiě)命令周期1

//寫(xiě)入5個(gè)地址周期
NF_ADDR(0x00);//列地址A0~A7
NF_ADDR(0x00);//列地址A8~A11
NF_ADDR((page_number) & 0xff);//行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff);//行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff);//行地址A28

NF_CMD(CMD_RANDOMWRITE);//隨意寫(xiě)命令
//頁(yè)內地址
NF_ADDR((char)(add&0xff));//列地址A0~A7
NF_ADDR((char)((add>>8)&0x0f));//列地址A8~A11

NF_WRDATA8(dat);//寫(xiě)入數據

NF_CMD(CMD_WRITE2);//頁(yè)寫(xiě)命令周期2

delay(1000);//延時(shí)一段時(shí)間

NF_CMD(CMD_STATUS);//讀狀態(tài)命令

//判斷狀態(tài)值的第6位是否為1,即是否在忙,該語(yǔ)句的作用與NF_DETECT_RB();相同
do{
stat =NF_RDDATA8();
}while(!(stat&0x40));

NF_nFCE_H();//關(guān)閉nandflash片選

//判斷狀態(tài)值的第0位是否為0,為0則寫(xiě)操作正確,否則錯誤
if (stat & 0x1)
return 0x44;//失敗
else
return 0x66;//成功
}

下面介紹上文中提到的判斷壞塊以及標注壞塊的那兩個(gè)程序:rNF_IsBadBlock和rNF_MarkBadBlock。在這里,我們定義在spare區的第6個(gè)地址(即每頁(yè)的第2054地址)用來(lái)標注壞塊,0x44表示該塊為壞塊。要判斷壞塊時(shí),利用隨意讀命令來(lái)讀取2054地址的內容是否為0x44,要標注壞塊時(shí),利用隨意寫(xiě)命令來(lái)向2054地址寫(xiě)0x33。下面就給出這兩個(gè)程序,它們的輸入參數都為塊地址,也就是即使僅僅一頁(yè)出現問(wèn)題,我們也標注整個(gè)塊為壞塊。

U8 rNF_IsBadBlock(U32 block)
{
return rNF_RamdomRead(block*64, 2054);
}

U8 rNF_MarkBadBlock(U32 block)
{
U8 result;

result = rNF_RamdomWrite(block*64, 2054, 0x33);

if(result == 0x44)
return 0x21;//寫(xiě)壞塊標注失敗
else
return 0x60;//寫(xiě)壞塊標注成功
}

關(guān)于nandflash的基本操作就講解到這里,當然nandflash還有一些其他復雜的操作,如邏輯地址與物理地址的轉換,壞塊的替代等,這些內容本文就不再介紹了


關(guān)鍵詞: s3c2440nandflas

評論


技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>