【轉】s3c2410 NandFlash K9F1208U0A/K9F1208U0B的讀取操作

 

【轉】s3c2410 NandFlash K9F1208U0A/K9F1208U0B的讀取操作

我的板子上使用的是SAMSUNG的K9F1208U0B,下面我將對此型號的NandFlash讀取操作做一個講解。
    首先我們先從物理結構上來了解這顆芯片,結構圖如下所示

    正如硬盤的盤片被分爲磁道,每個磁道又被分爲若干扇區,一塊Nand Flash被分爲若干Block,每個Block又被分爲若干Page。
    由上圖我們可以知道flash中Byte(字節),Page(頁),Block(塊)3個單位之間的關係爲
1 Page =512 Bytes Data Field+ 16 Bytes Spare Field
1 Blcok=32 Pages
    我們討論的K9F1208U0B總共有4096 個Blocks,故我們可以知道這塊flash的容量爲4096 *(32 *528)= 69206016 Bytes = 66 MB
    但事實上每個Page上的最後16Bytes是用於存貯檢驗碼用的,並不能存放實際的數據,所以實際上我們可以操作的芯片容量爲
4096 *(32 *512) = 67108864 Bytes = 64 MB
     由上圖所示,1個Page總共由528 Bytes組成,這528個字節按順序由上而下以列爲單位進行排列(1列代表一個Byte。第0行爲第0 Byte ,第1行爲第1 Byte,以此類推,每個行又由8個位組成,每個位表示1個Byte裏面的1bit)。這528Bytes按功能分爲兩大部分,分別是Data Field和Spare Field,其中Spare Field佔528Bytes裏的16Bytes,這16Bytes是用於在讀寫操作的時候存放校驗碼用的,一般不用做普通數據的存儲區,除去這 16Bytes,剩下的512Bytes便是我們用於存放數據用的Data Field,所以一個Page上雖然有528個Bytes,但我們只按512Bytes進行容量的計算。
    Data Field按位置關係又可分爲兩個部分,分別稱爲1st half與2nd half,每個half各佔256個bytes。或許你會感到納悶,爲什麼要把DataField分爲兩個部分?把他們看做一個整體進行操作不就好了嗎?呵呵,凡事都有因果關係,這麼分塊自然有它的道理所在,但現在還不是告訴你答案的時候。我們還是先討論一下它的操作吧。
    對K9F1208U0B的操作是通過向Nand Flash命令寄存器(對於s3c2410來說此寄存器爲NFCMD,內存映射地址爲0x4e000004)發送命令隊列進行的,爲什麼說是命令隊列?就是因爲要完成某個操作的時候發送的不是一條命令,而是連續幾條命令或是一條命令加幾個參數
下面是K9F1208U0B的操作命令集:

    讀命令有兩個,分別是 Read1,Read2其中Read1用於讀取Data Field的數據,而Read2則是用於讀取Spare Field的數據。對於Nand Flash來說,讀操作的最小操作單位爲Page,也就是說當我們給定了讀取的起始位置後,讀操作將從該位置開始,連續讀取到本Page的最後一個 Byte爲止(可以包括Spare Field)

Nand Flash的尋址
    Nand Flash的地址寄存器把一個完整的Nand Flash地址分解成Column Address與Page Address.進行尋址

  • Column Address: 列地址。Column Address其實就是指定Page上的某個Byte,指定這個Byte其實也就是指定此頁的讀寫起始地址。
  • Page Address:頁地址。由於頁地址總是以512Bytes對齊的,所以它的低9位總是0。確定讀寫操作是在Flash上的哪個頁進行的。

Read1命令
    當我們得到一個Nand Flash地址src_addr時我們可以這樣分解出Column Address和Page Address

column_addr=src_addr%512;                         // column address
page_address=(src_addr>>9);                       // page address


    也可以這麼認爲,一個Nand Flash地址的A0~A7是它的column_addr,A9~A25是它的Page Address。(注意地址位A8並沒有出現,也就是A8被忽略,在下面你將瞭解到這是什麼原因)
    Read1命令的操作分爲4個Cycle,發送完讀命令00h或01h(00h與01h的區別請見下文描述)之後將分4個Cycle發送參數, 1st.Cycle是發送Column Address。2nd.Cycle ,3rd.Cycle和4th.Cycle則是指定Page Address(每次向地址寄存器發送的數據只能是8位,所以17位的Page Address必須分成3次進行發送)。
   4個Cycle見下圖所示

    你是否還記得我上文提到過的Data Field被分爲1st half 和2end half兩個部分?而從上面的命令集我們看到Read1的命令裏面出現了兩個命令選項,分別是00h和01h。這裏出現了兩個讀命是否令你意識到什麼呢?是的,00h是用於讀寫1st half的命令,而01h是用於讀取2nd half的命令。現在我可以結合上圖給你說明爲什麼K9F1208U0B的DataField被分爲2個half了。
    如上文我所提及的,Read1的1st.Cycle是發送Column Address,假設我現在指定的Column Address是0,那麼讀操作將從此頁的第0號Byte開始一直讀取到此頁的最後一個Byte(包括Spare Field),如果我指定的Column Address是127,情況也與前面一樣,但不知道你發現沒有,用於傳遞Column Address的數據線有8條(I/O0~I/O7,對應A0~A7,這也是A8爲什麼不出現在我們傳遞的地址位中),也就是說我們能夠指定的 Column Address範圍爲0~255,但不要忘了,1個Page的DataField是由512個Byte組成的,假設現在我要指定讀命令從第256個字節處開始讀取此頁,那將會發生什麼情景?我必須把Column Address設置爲256,但Column Address最大隻能是255,這就造成數據溢出。。。正是因爲這個原因我們才把Data Field分爲兩個半區,當要讀取的起始地址(Column Address)在0~255內時我們用00h命令,當讀取的起始地址是在256~511時,則使用01h命令.假設現在我要指定從第256個byte開始讀取此頁,那麼我將這樣發送命令串

column_addr=256;
NF_CMD=0x01;                                        從2nd half開始讀取
NF_ADDR=column_addr&0xff;                       1st Cycle
NF_ADDR=page_address&0xff;                      2nd.Cycle
NF_ADDR=(page_address>>8)&0xff;             3rd.Cycle
NF_ADDR=(page_address>>16)&0xff;           4th.Cycle

    其中NF_CMD和NF_ADDR分別是NandFlash的命令寄存器和地址寄存器的地址解引用,我一般這樣定義它們,

#define rNFCMD        (*(volatile unsigned char *)0x4e000004)        //NADD Flash command
#define rNFADDR        (*(volatile unsigned char *)0x4e000008)        //NAND Flash address


    事實上,當NF_CMD=0x01時,地址寄存器中的第8位(A8)將被設置爲1(如上文分析,A8位不在我們傳遞的地址中,這個位其實就是硬件電路根據 01h或是00h這兩個命令來置高位或是置低位),這樣我們傳遞column_addr的值256隨然由於數據溢出變爲1,但A8位已經由於NF_CMD =0x01的關係被置爲1了,所以我們傳到地址寄存器裏的值變成了
A0 A1 A2 A3 A4 A5 A6 A7 A8
1     0    0     0    0     0    0    0    1

這8個位所表示的正好是256,這樣讀操作將從此頁的第256號byte(2nd half的第0號byte)開始讀取數據。

Read2
    Read2則是指定讀取Spare Field的內容
    其實Read1和Read2都是讀命令,他們的區別相當於對一個讀指針進行不同區域的定位。如圖所示

nand_flash.c中包含3個函數
void nf_reset(void);
void nf_init(void);
void nf_read(unsigned int src_addr,unsigned char *desc_addr,int size);

    nf_reset()將被nf_init()調用。
    nf_init()是nand_flash的初始化函數,在對nand flash進行任何操作之前,nf_init()必須被調用。
     nf_read(unsigned int src_addr,unsigned char *desc_addr,int size);爲讀函數,src_addr是nand flash上的地址,desc_addr是內存地址,size是讀取文件的長度。
    在nf_reset和nf_read函數中存在兩個宏
NF_nFCE_L();
NF_nFCE_H();

    你可以看到當每次對Nand Flash進行操作之前NF_nFCE_L()必定被調用,操作結束之時NF_nFCE_H()必定被調用。這兩個宏用於啓動和關閉Flash芯片的工作(片選/取消片選)。
    至於nf_reset()中的
rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
    這一行代碼是對NandFlash的控制寄存器進行初始化配置,rNFCONF是Nand Flash的配置寄存器,各個位的具體功能請參閱s3c2410數據手冊。

    現在舉一個例子,假設我要從Nand Flash中的第5000字節處開始讀取1024個字節到內存的0x30000000處,我們這樣調用read函數
nf_read(5000, 0x30000000,1024);
我們來分析5000這個src_addr.
根據
column_addr=src_addr%512;       
page_address=(src_addr>>9);
       
我們可得出column_addr=5000%512=392
page_address=(5000>>9)=9
於是我們可以知道5000這個地址是在第9頁的第392個字節處,於是我們的nf_read函數將這樣發送命令和參數
column_addr=5000%512;
page_address=(5000>>9);
NF_CMD=0x01;                                           從2nd half開始讀取
NF_ADDR= column_addr &0xff;                     1st Cycle
NF_ADDR=page_address&0xff;                      2nd.Cycle
NF_ADDR=(page_address>>8)&0xff;             3rd.Cycle
NF_ADDR=(page_address>>16)&0xff;           4th.Cycle

向NandFlash的命令寄存器和地址寄存器發送完以上命令和參數之後,我們就可以從rNFDATA寄存器(NandFlash數據寄存器)讀取數據了.
我用下面的代碼進行數據的讀取.
for(i=column_addr;i<512;i++)
{
        *buf++=NF_RDDATA();
}

每當讀取完一個Page之後,數據指針會落在下一個Page的0號Column(0號Byte).
附件裏是完整的NandFlash讀取操作代碼.請配合本文使用.

nand_flash_read.rar
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章