本節來學習裸機下的Nand Flash驅動,本節學完后,再來學習Linux下如何使用Nand Flash驅動
Linux中的Nand Flash驅動,鏈接如下:
(分析MTD層以及制作Nand Flash驅動鏈接:http://www.cnblogs.com/lifexy/p/7701181.html)
本節簡單制作一個Nand Flash驅動(只需要初始化Flash以及讀Flash)
打開2440芯片手冊,K9F2G08U0M芯片手冊(因為2440中Nand Flash是用的256MB(2Gb)內存,8個數據引腳)
在芯片手冊中得到K9F2G08U0M=2048塊Block=128K頁Pages=256MB=2Gb
1個設備=2048塊Block
1塊Block=64頁Pages
1頁=(2K+64)B (因為每個地址里都存放了一個字節,所以用B表示)
其中64B是存放ECC的OOB地址,(ECC:存放判斷位反轉的校驗碼)

Nand Flash 缺點:
讀數據容易位反轉
可以通過ECC編碼器值來判斷讀數據是否位反轉,若位反轉則重新讀數據
寫過程:
- 1)寫頁數據
- 2)然后生成ECC
- 3)將ECC寫入到OBB頁地址里(寫數據是不會出現位反轉)
讀過程:
- 1)讀出頁數據,然后生成臨時ECC(此時ECC可能有錯)
- 2)然后讀出OOB頁地址里的ECC
- 3)比較兩個ECC,判斷是否出現位反轉
讀OOB方法:
讀整個Nand Flash時,是讀不出頁里面的OBB地址,比如讀2049這個地址數據時,是讀的第二頁上的第2個地址:

只有讀某一頁時,才能讀出這個頁里面的OOB地址, 比如讀第0頁的2049這個地址數據時,才是讀的第0頁OOB的第2個地址:

Nand Flash芯片硬件引腳圖:

- RnB:就緒(ready)/忙(busy)輸出信號,需要采用上拉電阻(1:表示寫入數據成功,0:表示正在寫入)
- CLE:命令(command)鎖存(latch)使能,(1:表示當前傳的是命令值)
- ALE:地址鎖存使能,(1:表示當前傳的是地址值,當CLE=0和ALE=0,表示傳的是數據)
- nCE:芯片使能(低電平使能) (n:表示低電平有效)
- nWE:寫使能 ,比如寫命令時,當CLE=1,ALE=0時,當nWE來個上升沿,則會將IO數據寫入flash中
- nRE:讀使能,和we類似
- nWP:寫保護(protect) (1:不保護,0:只能讀不能寫),默認接高電平.
1.編寫nand_init()函數
1.1設置通信時序
圖1(nandflash時序表):

圖2(nandflash時序圖):

通過圖2和圖1可以看出:
tCS是等待芯片使能CE的時間, tCS=20nS
tCLS和tALS是等待WE(寫信號)結束的時間, tCLS=tALS=15nS
tWP是WE(寫信號)維持時間, tWP=15nS
tALH是等待命令寫入成功的時間, tALH=5nS
tCLH是等待地址寫入成功的時間, tCLH=5nS
圖3(2440-nandflash時序圖):

首先查看2440芯片手冊里nandflash時序圖,如上圖,可以看出需要設置TACLS,TWRPH0和TWRPH1,這三個參數
TACLS:屬於等待WE(寫信號)就緒的時間,對比圖2得出TACLS= tCLS- tWP=0nS
TWRPH0:屬於WE(寫信號)的時間, 對比圖2得出TWRPH0= tWP=15nS
TWRPH1:屬於等待命令寫入成功的時間,對比圖2得出TWRPH1=tALH=tCLH=5nS
最后,在NFCONF寄存器中設置這三個參數
TACLS[13:12]
表示Duration(持續時間)=HCLK*TACLS,由於Duration=0nS,所以TACLS=0
TWRPH0 [10:8]
表示Duration(持續時間)=HCLK*( TWRPH0+1),由於Duration=15nS,HCLK=10nS(100Mhz),所以TWRPH0 =1.
TWRPH1 [6:4]
表示Duration(持續時間)= HCLK*( TWRPH1 +1),由於Duration=5nS,HCLK=10nS(100Mhz),所以TWRPH1 =0
1.2然后定義全局變量,並實現nand_init()初始化:
/* nand flash 時序 */
#define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 /* nand flash 寄存器 */ #define NFCONF *((unsigend int *)0X4E000000); //配置寄存器(用來設置時序) #define NFCONT *((unsigend int *)0X4E000000); //控制寄存器(用來使能nandflash控制器以及ECC編碼器,還有控制芯片使能CE腳) #define NFCMMD *((unsigend char *)0X4E000000);//發送命令寄存器(命令只有8位) #define NFADDR *((unsigend char *)0X4E000000);//發送地址寄存器(地址只有8位) #define NFDATA *((unsigend int *)0X4E000000);//讀/寫數據寄存器(數據只有8位) #define NFSTAT *((unsigend int *)0X4E000000);//運行狀態寄存器(用於判斷RnB腳) /*因為Nand Flash只有8位I/O腳,所以NFCMMD/ NFADDR/ NFDATA三個寄存器值都是unsigend char型 */
1.3 nand_init()函數初始化
void nand_init(void) { /* 設置時序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* bit4=1:初始化ECC, bit1=1:禁止片選 bit0=1:啟動nandflash控制器*/ NFCONT = (1<<4)|(1<<1)|(1<<0); }
2編寫nand_read()函數
2.1編寫nand_read()函數需要以下幾個子函數:
2.1.1片選使能函數(在讀寫FLASH之前都要選中片選)
nand_select() //使能片選 { int i; NFCONT&=~(1<<1); // NFCONT控制器位1置0 for(i=0;i<10;i++); //等待芯片使能成功 }
2.1.2取消片選函數(退出讀寫FLASH時需要取消片選)
nand_deselect() //取消片選 { int i; NFCONT&=~(1<<1); // NFCONT控制器位1置0 for(i=0;i<10;i++); //等待芯片使能成功 }
2.1.3讀命令函數
nand_cmd(unsigned char cmd) { int i; NFCMMD= cmd; // 向NFCMMD寄存器寫入命令 for(i=0;i<10;i++); //等待寫入命令成功 }
2.1.4判斷RnB狀態函數(在寫入所有命令后都要判斷RnB腳是否為高電平就緒)
nand_waite_idle() { int i; while(!(NFSTAT&0X01)) // 等待NFSTAT寄存器位0置1 for(i=0;i<10;i++); }
2.1.5讀數據函數
nand_read_data() { unsigend char p=NFDATA; //讀取NFDATA寄存器 return p; //返回 }
2.1.6 編寫寫入地址函數 (分5個周期)
首先Nand Flash引腳只有8位,然而地址共有2048(塊)*64(頁)*2KB,為了讀出多個地址,如下圖,所以需要分5個周期來實現發送地址

如上圖,其中 A10~A0對應頁大小(列),由於nandflash每頁2048B,所以只用到A10~A0
A28~A11對應頁目錄(行),表示共有2048塊*64(每塊有64頁)個目錄
例如,4097 地址就是:
A10~A0=4097%2048= 1(A0=1,其余為0)
A28~A11=4097/2048=2(A13=1,其余為0)
所以nand_write_nand()函數如下:
void nand_read_addr(unsigned int addr) { unsigned int col = addr % 2048; unsigned int page = addr / 2048; volatile int i; NFADDR=(col>>0)&0xff; //A7~A0,第1周期 for(i=0;i<10;i++); NFADDR=(col>>8)&0x0f; //A10~A8,第2周期 for(i=0;i<10;i++); NFADDR=(page>>0)&0xff; //A18~A11,第3周期 for(i=0;i<10;i++); NFADDR=(page>>8)&0xff; //A26~A19,第4周期 for(i=0;i<10;i++); NFADDR=(page>>16)&0xff; //A27~A28,第5周期 for(i=0;i<10;i++); }
2.2Nand Flash命令圖:

如上圖,例如:當要reset復位nand flash時
1) 使能片選nand_select();
2) 發送0XFF復位命令nand_cmd(0xFF);
3) 等待RnB狀態是否就緒 nand_wait_idle();
4) 取消片選 nand_deselect();
2.3Nand Flash讀數據時序圖:

從上圖可以看出nand flash 讀數據分為了以下幾個步驟:
(1) 使能片選CE,將CLE置1,等待發送命令
(2) 將WE置低,將IO置為0X00,然后拉高WE,觸發一次上升沿,則將把0x00寫入flash中
(3) 將CLE置0,表示發送地址(分為5個周期)
(4) 發送讀命令0X30
(5) 等待RnB信號為高電平
(6) 讀數據
(在同一頁里,數據可以連續讀,讀下一頁時,需要重新發送新的地址才行例如:讀1000地址到2050地址時,
1.發出1000地址,到達頁0的1000地址上,然后再連續讀(2048-1000)次,直到讀到頁0的2047處.
2.再發出2048地址,到達頁1的0地址上,然后連續讀(2051-2048)次,直到讀到2050為止)
(7) 取消片選nCE
2.4 所以nand_read()函數如下:
void nand_read(unsigned int src,unsigned char *dest,unsigned int len) /* src:源地址,為32位地址,所以用unsigend int表示 *dest:目的地址內容,由於這里是將數據讀出到目的地址內容中,所以需要用到*指針,因為每個地址里存的是一個字節,所以用unsigend char 型 */ { int col=src%2048; //第一次讀,可能不是讀的頁首地址,所以需要記錄當前頁的位置 int i=0; //當前讀了0次 nand_select(); //1使能片選nCE while(i<len) { nand_cmd(0X00); //2發送讀命令0X00 nand_write_addr(src); // 3發送yuan地址(分為5個周期) nand_cmd(0X30); //4發送讀命令0X30 nand_wait_idle(); //5等待RnB信號為高電平 for(;(col<2048)&&(i<len);col++) //連續讀頁內數據 {dest[i]=nand_read_data(); //6.讀數據 i++;
src++;} col=0;} nand_deselect(); // 取消片選nCE }
