<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

          s3c2440對nandflash的操作

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

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

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

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

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

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

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

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

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

          #define NF_CMD(data){rNFCMD= (data); }//傳輸命令
          #define NF_ADDR(addr){rNFADDR = (addr); }//傳輸?shù)刂?br />#define NF_RDDATA()(rNFDATA)//讀32位數(shù)據(jù)
          #define NF_RDDATA8()(rNFDATA8)//讀8位數(shù)據(jù)
          #define NF_WRDATA(data){rNFDATA = (data); }//寫32位數(shù)據(jù)
          #define NF_WRDATA8(data){rNFDATA8 = (data); }//寫8位數(shù)據(jù)

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

          NFCONF主要用到了TACLS、TWRPH0、TWRPH1,這三個變量用于配置nandflash的時序。s3c2440的數(shù)據(jù)手冊沒有詳細說明這三個變量的具體含義,但通過它所給出的時序圖,我們可以看出,TACLS為CLE/ALE有效到nWE有效之間的持續(xù)時間,TWRPH0為nWE的有效持續(xù)時間,TWRPH1為nWE無效到CLE/ALE無效之間的持續(xù)時間,這些時間都是以HCLK為單位的(本文程序中的HCLK=100MHz)。通過查閱K9F2G08U0A的數(shù)據(jù)手冊,我們可以找到并計算該nandflash與s3c2440相對應(yīng)的時序:K9F2G08U0A中的tWP與TWRPH0相對應(yīng),tCLH與TWRPH1相對應(yīng),(tCLS-tWP)與TACLS相對應(yīng)。K9F2G08U0A給出的都是最小時間,s3c2440只要滿足它的最小時間即可,因此TACLS、TWRPH0、TWRPH1這三個變量取值大一些會更保險。在這里,這三個值分別取1,2和0。NFCONF的第0位表示的是外接的nandflash是8位IO還是16位IO,這里當(dāng)然要選擇8位的IO。NFCONT寄存器是另一個需要事先初始化的寄存器。它的第13位和第12位用于鎖定配置,第8位到第10位用于nandflash的中斷,第4位到第6位用于ECC的配置,第1位用于nandflash芯片的選取,第0位用于nandflash控制器的使能。另外,為了初始化nandflash,還需要配置GPACON寄存器,使它的第17位到第22位與nandflash芯片的控制引腳相對應(yīng)。下面的程序?qū)崿F(xiàn)了初始化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區(qū)和spare區(qū)ECC,使能nandflash片選及控制器
          rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
          }

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

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

          NFSTAT是另一個比較重要的寄存器,它的第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的基本操作,包括復(fù)位,讀ID,頁讀、寫數(shù)據(jù),隨意讀、寫數(shù)據(jù),塊擦除等。

          復(fù)位操作最簡單,只需寫入復(fù)位命令即可:

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

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

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

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

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

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

          return (pDID);
          }

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

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

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

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

          NF_RSTECC();//復(fù)位ECC
          NF_MECC_UnLock();//解鎖main區(qū)ECC

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

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

          //寫入5個地址周期
          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);//頁讀命令周期2

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

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

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

          NF_SECC_UnLock();//解鎖spare區(qū)ECC

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

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

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

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

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

          }

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

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

          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();//復(fù)位ECC
          NF_MECC_UnLock();//解鎖main區(qū)的ECC

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

          NF_CMD(CMD_WRITE1);//頁寫命令周期1

          //寫入5個地址周期
          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

          //寫入一頁數(shù)據(jù)
          for (i = 0; i < 2048; i++)
          {
          NF_WRDATA8((char)(i+6));
          }

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

          mecc0=rNFMECC0;//讀取main區(qū)的ECC校驗碼
          //把ECC校驗碼由字型轉(zhuǎn)換為字節(jié)型,并保存到全局變量數(shù)組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區(qū)的ECC
          //把main區(qū)的ECC值寫入到spare區(qū)的前4個字節(jié)地址內(nèi),即第2048~2051地址
          for(i=0;i<4;i++)
          {
          NF_WRDATA8(ECCBuf[i]);
          }

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

          NF_CMD(CMD_WRITE2);//頁寫命令周期2

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

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

          //判斷狀態(tài)值的第6位是否為1,即是否在忙,該語句的作用與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);//標(biāo)注該頁所在的塊為壞塊
          if (temp == 0x21)
          return 0x43//標(biāo)注壞塊失敗
          else
          return 0x44;//寫操作失敗
          }
          else
          return 0x66;//寫操作成功
          }

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

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

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

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

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

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

          //寫入3個地址周期,從A18開始寫起
          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);//延時一段時間

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

          //判斷狀態(tài)值的第6位是否為1,即是否在忙,該語句的作用與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);//標(biāo)注該塊為壞塊
          if (temp == 0x21)
          return 0x43//標(biāo)注壞塊失敗
          else
          return 0x44;//擦除操作失敗
          }
          else
          return 0x66;//擦除操作成功
          }

          該程序的輸入?yún)?shù)為K9F2G08U0A的第幾塊,例如我們要擦除第2001塊,則調(diào)用該函數(shù)為:rNF_EraseBlock(2001)。

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

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

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

          //寫入5個地址周期
          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);//頁讀命令周期2

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

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

          return NF_RDDATA8();//讀取數(shù)據(jù)
          }

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

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

          NF_CMD(CMD_WRITE1);//頁寫命令周期1

          //寫入5個地址周期
          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);//隨意寫命令
          //頁內(nèi)地址
          NF_ADDR((char)(add&0xff));//列地址A0~A7
          NF_ADDR((char)((add>>8)&0x0f));//列地址A8~A11

          NF_WRDATA8(dat);//寫入數(shù)據(jù)

          NF_CMD(CMD_WRITE2);//頁寫命令周期2

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

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

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

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

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

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

          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;//寫壞塊標(biāo)注失敗
          else
          return 0x60;//寫壞塊標(biāo)注成功
          }

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


          關(guān)鍵詞: s3c2440nandflas

          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();