版權(quán)聲明:本文為小斑馬學(xué)習(xí)總結(jié)文章,技術(shù)來(lái)源于韋東山著作,轉(zhuǎn)載請(qǐng)注明出處!![]
一、NAND_FLASH操作原理

NAND FLASH原理圖
NAND FLASH是一個(gè)存儲(chǔ)芯片
那么: 這樣的操作很合理"讀地址A的數(shù)據(jù),把數(shù)據(jù)B寫(xiě)到地址A"
問(wèn)1. 原理圖上NAND FLASH和S3C2440之間只有數(shù)據(jù)線,怎么傳輸?shù)刂罚?br>
答1.在DATA0~DATA7上既傳輸數(shù)據(jù),又傳輸?shù)刂樊?dāng)ALE為高電平時(shí)傳輸?shù)氖堑刂罚?/strong>
那么在數(shù)據(jù)線上是不是只傳輸數(shù)據(jù)和只傳輸?shù)刂纺兀?br>
我們參考NAND FLASH的芯片手冊(cè)可以知道,對(duì)NAND FLASH的操作還需要發(fā)出命令,下面有個(gè)NAND FLASH的命令表格

問(wèn)2. 從NAND FLASH芯片手冊(cè)可知,要操作NAND FLASH需要先發(fā)出命令怎么傳入命令?
答2.在DATA0~DATA7上既傳輸數(shù)據(jù),又傳輸?shù)刂?,也傳輸命令?/strong>
- 當(dāng)ALE為高電平時(shí)傳輸?shù)氖堑刂贰?/li>
- 當(dāng)CLE為高電平時(shí)傳輸?shù)氖敲睢?/li>
- 當(dāng)ALE和CLE都為低電平時(shí)傳輸?shù)氖菙?shù)據(jù)。
問(wèn)3. 數(shù)據(jù)線既接到NAND FLASH,也接到NOR FLASH,還接到SDRAM、DM9000等等
那么怎么避免干擾?
答3. 這些設(shè)備,要訪問(wèn)之必須"選中",沒(méi)有選中的芯片不會(huì)工作,相當(dāng)于沒(méi)接一樣。
問(wèn)4. 假設(shè)燒寫(xiě)NAND FLASH,把命令、地址、數(shù)據(jù)發(fā)給它之后,NAND FLASH肯定不可能瞬間完成燒寫(xiě)的,怎么判斷燒寫(xiě)完成?
答4. 通過(guò)狀態(tài)引腳RnB來(lái)判斷:它為高電平表示就緒,它為低電平表示正忙
問(wèn)5. 怎么操作NAND FLASH呢?
答5. 根據(jù)NAND FLASH的芯片手冊(cè),一般的過(guò)程是:
- 發(fā)出命令
- 發(fā)出地址
- 發(fā)出數(shù)據(jù)/讀數(shù)據(jù)
看上面的命令表格,不容易看,我們看一下讀ID的時(shí)序圖,

每個(gè)NAND FLASH都內(nèi)嵌一些ID(譬如:廠家ID,設(shè)備ID),時(shí)序圖從左往右看,縱向放是一列一列的看。
對(duì)于s3c2440來(lái)說(shuō),內(nèi)部集成了一個(gè)NAND FLASH控制器,2440和外設(shè)連接的簡(jiǎn)易圖,如下圖所示

NAND FLASH控制器,幫我們簡(jiǎn)化了對(duì)NAND FLASH的操作,下面來(lái)分析一下不使用NAND FLASH控制器和使用NAND FLASH控制器對(duì)外設(shè)NAND FLASH的操作
發(fā)命令:
| NAND FLASH | S3C2440 |
|---|---|
| 選中芯片 | NFCMMD=命令值 |
| CLE設(shè)為高電平 | |
| 在DATA0~DATA7上輸出命令值 | |
| 發(fā)出一個(gè)寫(xiě)脈沖 |
發(fā)地址:
| NAND FLASH | S3C2440 |
|---|---|
| 選中芯片 | NFADDR=地址值 |
| ALE設(shè)為高電平 | |
| 在DATA0~DATA7上輸出地址值 | |
| 發(fā)出一個(gè)寫(xiě)脈沖 |
發(fā)數(shù)據(jù):
| NAND FLASH | S3C2440 |
|---|---|
| 選中芯片 | NFDATA=數(shù)據(jù)值 |
| ALE,CLE設(shè)為低電平 | |
| 在DATA0~DATA7上輸出數(shù)據(jù)值 | |
| 發(fā)出一個(gè)寫(xiě)脈沖 |
讀數(shù)據(jù) :
| NAND FLASH | S3C2440 |
|---|---|
| 選中芯片 | val=NFDATA |
| 發(fā)出讀脈沖 | |
| 讀DATA0~DATA7的數(shù)據(jù) |
用UBOOT來(lái)體驗(yàn)NAND FLASH的操作:
1.讀ID
| S3C2440 | u-boot | |
|---|---|---|
| 選中 | NFCONT的bit1設(shè)為0 | md.l 0x4E000004 1; mw.l 0x4E000004 1 |
| 發(fā)出命令0x90 | NFCMMD=0x90 | mw.b 0x4E000008 0x90 |
| 發(fā)出地址0x00 | NFADDR=0x00 | mw.b 0x4E00000C 0x00 |
| 讀數(shù)據(jù)得到0xEC | val=NFDATA | md.b 0x4E000010 1 |
| 讀數(shù)據(jù)得到device code | val=NFDATA | md.b 0x4E000010 1 |
| 退出讀ID的狀態(tài) | NFCMMD=0xff | mw.b 0x4E000008 0xff |
下圖是讀操作時(shí)序圖

對(duì)于存儲(chǔ)為256M的NAND FLASH,需要28條地址線,來(lái)表示這個(gè)地址值,根據(jù)原理圖可以,只用8根地址線,所以需要4個(gè)周期的地址,為了兼容更大容量的NAND FLASH,要發(fā)出5個(gè)周期的地址:(如下圖所示)
1.讀數(shù)據(jù)
| S3C2440 | u-boot | |
|---|---|---|
| 選中 | NFCONT的bit1設(shè)為0 | md.l 0x4E000004 1; mw.l 0x4E000004 1 |
| 發(fā)出命令0x00 | NFCMMD=0x00 | mw.b 0x4E000008 0x00 |
| 發(fā)出地址0x00 | NFCMMD=0x00 | mw.b 0x4E00000C 0x00 |
| 發(fā)出地址0x00 | NFCMMD=0x00 | mw.b 0x4E00000C 0x00 |
| 發(fā)出地址0x00 | NFCMMD=0x00 | mw.b 0x4E00000C 0x00 |
| 發(fā)出地址0x00 | NFCMMD=0x00 | mw.b 0x4E00000C 0x00 |
| 發(fā)出地址0x00 | NFCMMD=0x00 | mw.b 0x4E00000C 0x00 |
| 發(fā)出命令0x30 | NFCMMD=0x30 | mw.b 0x4E000008 0x30 |
| 讀數(shù)據(jù)得到0x17 | val=NFDATA | md.b 0x4E000010 1 |
| 讀數(shù)據(jù)得到0x00 | val=NFDATA | md.b 0x4E000010 1 |
| 讀數(shù)據(jù)得到0x00 | val=NFDATA | md.b 0x4E000010 1 |
| 讀數(shù)據(jù)得到0xea | val=NFDATA | md.b 0x4E000010 1 |
| 退出讀狀態(tài) | NFCMMD=0xff | mw.b 0x4E000008 0xff |
二、NandFlash時(shí)序及初始化
| 存儲(chǔ)芯片的編程 | NAND FLASH存儲(chǔ)芯片編程 |
|---|---|
| 初始化 | 主控芯片的NAND FLASH控制器的初始化 |
| 識(shí)別 | 讀取ID |
| 讀操作 | 一次讀一個(gè)頁(yè)(page) |
| 寫(xiě)操作 | 一次寫(xiě)一個(gè)頁(yè)(page) |
| 擦除 | 一次擦除一個(gè)塊(block) |

NAND FLASH時(shí)序圖,如下所示:




所以NFCONF寄存器設(shè)置如下
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/*設(shè)置NAND FLASH的時(shí)序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
到此設(shè)置NAND FLASH的時(shí)序已經(jīng)設(shè)置完了,我們接著來(lái)使能,使能實(shí)在NFCONT。
MODE [0]: 設(shè)置為1,使能NAND FLASH。
Reg_nCE [1]: 設(shè)置為1,禁止片選。因?yàn)槲覀儸F(xiàn)在還沒(méi)有使用。為例錯(cuò)誤的操作。
InitECC [4]: 初始化ECC的編碼器,后邊要使用,我們?cè)O(shè)置為1,來(lái)初始化。
所以NFCONF寄存器設(shè)置如下:
/*使能NAND FLASH控制器,初始化ECC,禁止片選*/
NFCONT = (1<<4) | (1<<1) | (1<<0);
三、NandFlash的芯片id讀取
參考NAND FLASHh的芯片手冊(cè),如下圖所示:(NAND FLASH讀操作時(shí)序圖)

一般先操作片選使能,只有片選使能之后才能進(jìn)行后邊的操作,片選是能代碼如下:
void nand_select(void)
{
/*使能片選*/
NFCONT &=~(1<<1);
}
有使能片選,一定有禁止片選,禁止片選的代碼如下:
void nand_deselect(void)
{
/*禁止片選*/
NFCONT |= (1<<1);
}
讀ID的操作時(shí)序圖,如下所示
按照從左往右的時(shí)間點(diǎn),來(lái)分析,片選信號(hào)像一個(gè)總開(kāi)關(guān),只有使能了片選信號(hào),后續(xù)的操作才會(huì)有意義,我們使能片選信號(hào)之后,片選引腳nCE后續(xù)一直為低電平,在前面的命令時(shí)序圖中知道tCLS和tWP最小的時(shí)間參數(shù)都是12us,就表明CLE和nWE這兩個(gè)信號(hào)可以同時(shí)發(fā)出,就表示要命令了,對(duì)于寫(xiě)什么命令,就要看數(shù)據(jù)總線上要發(fā)送的命令了,當(dāng)CLE從高電平變?yōu)榈碗娖胶?,表示上次的?xiě)操作已經(jīng)結(jié)束了。
對(duì)于上面復(fù)雜的時(shí)序,我們可以使用2440上的NAND FLASH控制器簡(jiǎn)化操作,只需要往NFCMMD寄存器寫(xiě)入要傳輸?shù)拿罹涂梢粤耍琋AND FLASH控制器默認(rèn)把上面復(fù)雜的時(shí)序發(fā)出來(lái)。
發(fā)命令后,后面就需要發(fā)送地址了,當(dāng)nWE和ALE有效的時(shí)候,表示寫(xiě)地址,上圖中,要寫(xiě)入的地址是0x00,當(dāng)ALE從高電平變?yōu)榈碗娖降臅r(shí)候,表示寫(xiě)地址結(jié)束,我們可以簡(jiǎn)化為:往NFADDR寄存器中寫(xiě)值就可以了,比如:NFADDR=0x00。
下面寫(xiě)代碼:發(fā)命令的函數(shù),和發(fā)地址的函數(shù)代碼如下:
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCCMD = cmd;
for(i=0; i<10; i++);
}
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
}
接下來(lái)就可以讀取數(shù)據(jù)了,數(shù)據(jù)可以直接通過(guò)讀取NFDATA寄存器里面數(shù)據(jù)來(lái)獲得數(shù)據(jù),根據(jù)時(shí)序圖,是讀5個(gè)字節(jié)的數(shù)據(jù),代碼如下:
unsigned char nand_data(void)
{
return NFDATA;
}
讀芯片ID之前先打開(kāi)片選, 讀取芯片ID函數(shù),代碼如下:
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();
printf("maker id = 0x%x\n\r",buf[0]);
printf("device id = 0x%x\n\r",buf[1]);
printf("3rd byte = 0x%x\n\r",buf[2]);
printf("4th byte = 0x%x\n\r",buf[3]);
printf("page size = %d kb\n\r",1 << (buf[3] & 0x03));
printf("block size = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%x\n\r",buf[4]);
}
下面再寫(xiě)一個(gè)打印菜單的函數(shù),在菜單中調(diào)用讀取芯片ID的函數(shù),代碼如下:
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜單, 供我們選擇測(cè)試內(nèi)容 */
printf("[s] Scan nand flash\n\r");
printf("[e] Erase nand flash\n\r");
printf("[w] Write nand flash\n\r");
printf("[r] Read nand flash\n\r");
printf("[q] quit\n\r");
printf("Enter selection: ");
c = getchar();
printf("%c\n\r", c);
/* 測(cè)試內(nèi)容:
* 1. 識(shí)別nand flash
* 2. 擦除nand flash某個(gè)扇區(qū)
* 3. 編寫(xiě)某個(gè)地址
* 4. 讀某個(gè)地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_id();
break;
case 'e':
case 'E':
break;
case 'w':
case 'W':
break;
case 'r':
case 'R':
break;
default:
break;
}
}
在主函數(shù)中調(diào)用nand flash的初始化函數(shù),和nand flash的測(cè)試函數(shù)。
int main(void)
{
led_init();
//interrupt_init(); /* 初始化中斷控制器 */
key_eint_init(); /* 初始化按鍵, 設(shè)為中斷源 */
//timer_init();
puts("\n\rg_A = ");
printHex(g_A);
puts("\n\r");
//nor_flash_test();
nand_init();
nand_flash_test();
return 0;
}
四、NandFlash的數(shù)據(jù)讀取
在上節(jié) 我們實(shí)現(xiàn)了芯片ID的讀取,可是那個(gè)程序已經(jīng)超過(guò)了4k,我們想把它燒到開(kāi)發(fā)板的話,必需把它燒寫(xiě)到NOR FLASH上去,這節(jié)我們來(lái)講解NAND FLASH數(shù)據(jù)的讀取,并且實(shí)現(xiàn)超過(guò)4k的程序從NAND FLASH啟動(dòng)。
下圖為NAND FLASH內(nèi)部結(jié)構(gòu)圖,從圖中可以可以知道,一個(gè)page含有2k 字節(jié)的頁(yè)數(shù)據(jù),和64字節(jié)的oob區(qū),后面會(huì)介紹頁(yè)數(shù)據(jù)和oob區(qū)有什么關(guān)系。

圖的表格,來(lái)說(shuō)明NAND FLASH內(nèi)部結(jié)構(gòu),前面2K(02047)表示頁(yè)數(shù)據(jù),后邊64字節(jié)(20482111)表示oob。

答:是Page1的第0個(gè)字節(jié)。CPU使用某個(gè)地址訪問(wèn)數(shù)據(jù)的時(shí)候,是在頁(yè)數(shù)據(jù)空間來(lái)尋址的,根本就看不到oob區(qū)。
我們知道NAND FLASH 和 NOR FLASH相比有個(gè)缺點(diǎn),NAND FLASH讀或?qū)懸豁?yè)數(shù)據(jù)的時(shí)候,可能會(huì)發(fā)生位反轉(zhuǎn),里面可能有一位是錯(cuò)誤的,為了解決這個(gè)問(wèn)題,引入oob區(qū),
它寫(xiě)頁(yè)數(shù)據(jù)的時(shí)候,把數(shù)據(jù)寫(xiě)進(jìn)頁(yè)數(shù)據(jù)的同時(shí)會(huì)生成一個(gè)校驗(yàn)碼,把這個(gè)校驗(yàn)碼寫(xiě)進(jìn)oob區(qū)里面,當(dāng)讀數(shù)據(jù)的時(shí)候,讀出1頁(yè)數(shù)據(jù),讀取1數(shù)據(jù)里面有可能有某一位發(fā)生錯(cuò)誤,它繼續(xù)讀出原來(lái)的校驗(yàn)碼,使用oob區(qū)里面的校驗(yàn)碼,來(lái)修正頁(yè)數(shù)據(jù)里面的數(shù)據(jù)。從這里我們可以得出一個(gè)結(jié)論,oob區(qū)的存在是為了解決NAND FLASH的缺陷而存在的。
CPU: 只關(guān)心數(shù)據(jù),不需要看到oob區(qū)的校驗(yàn)碼(把數(shù)據(jù)讀出來(lái),然后進(jìn)行校驗(yàn)再把正確的數(shù)據(jù)返回,就可以了)。CPU想使用某個(gè)addr來(lái)訪問(wèn)數(shù)據(jù)的時(shí)候,addr是在頁(yè)數(shù)據(jù)區(qū)間來(lái)尋址的,addr根本不會(huì)在oob區(qū)里面尋址。
為了形象在下面說(shuō)一個(gè)幽默的對(duì)話來(lái)說(shuō)明一下CPU和NAND FLASH的功能:
CPU大爺: 小nand啊,你的性能比不上小nor啊,聽(tīng)說(shuō)你有位反轉(zhuǎn)的毛病
Nand : 是的,大爺,位反轉(zhuǎn)是我天生的毛病,時(shí)有時(shí)無(wú)
CPU大爺:靠,你說(shuō)你價(jià)格便宜容量大,這不是害我嘛
Nand : 沒(méi)事,我有偏方,用OOB就可以解決這問(wèn)題
CPU大爺:得得得,你那偏方是什么也別告訴我,我只管能讀寫(xiě)正確的數(shù)據(jù)
Nand : 是的,大爺,我這OOB偏方也就我自個(gè)私下使用。您就像使用nor一樣使喚我就可以了。
下圖為讀NAND FLASH的時(shí)序操作:

下面是程序的編寫(xiě):

void wait_ready(void)
{
while (!(NFSTAT & 1));
}
nand_read函數(shù)為NAND FLASH的讀函數(shù),代碼如下:
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
nand_select();
while (i < len)
{
/* 發(fā)出00h命令 */
nand_cmd(00);
/* 發(fā)出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 發(fā)出30h命令 */
nand_cmd(0x30);
/* 等待就緒 */
wait_ready();
/* 讀數(shù)據(jù) */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
在init.c文件中,加上如下代碼,用來(lái)判斷所使用的FLASH是NOR FLASH還是NAND FLASH。代碼如下:
int isBootFromNorFlash(void)
{
volatile unsigned int *p = (volatile unsigned int *)0;
unsigned int val = *p;
*p = 0x12345678;
if (*p == 0x12345678)
{
/* 寫(xiě)成功, 對(duì)應(yīng)nand啟動(dòng) */
*p = val;
return 0;
}
else
{
return 1;
}
}
在init.c文件中的copy2sdram函數(shù)里面加上如下代碼,用來(lái)支持NAND FLASH啟動(dòng),當(dāng)isBootFromNorFlash函數(shù)的返回值為1時(shí),是從NOR FLASH啟動(dòng),當(dāng)isBootFromNorFlash函數(shù)的返回值為0是,是從NAND FLASH啟動(dòng)。
if (isBootFromNorFlash())
{
while (dest < end)
{
*dest++ = *src++;
}
}
else
{
nand_init();
nand_read(src, dest, len);
}
}
五、NandFlash的擦除與燒寫(xiě)
我們本節(jié)需要做的事情:
- 實(shí)現(xiàn)nand_erase
- 實(shí)現(xiàn)nand_write
-
實(shí)現(xiàn)測(cè)試菜單
本節(jié)講的NAND FLASH的燒寫(xiě)和擦除還是比較簡(jiǎn)單的,它只涉及到頁(yè)數(shù)據(jù)區(qū),不涉及到oob區(qū),擦出的時(shí)候是以塊為單位。下圖為擦除的時(shí)序圖:
下面我們逐個(gè)來(lái)實(shí)現(xiàn)他們:
我們就根據(jù)擦除的時(shí)序圖發(fā)出對(duì)應(yīng)的命令和地址,NAND FLASH是以塊為單位進(jìn)行擦除的,假如我們傳入len的值為1,但是它仍然會(huì)擦出一個(gè)塊(128k字節(jié)),我們根據(jù)芯片手冊(cè),來(lái)操作NAND FLASH的擦出操作,函數(shù)功能:從addr地址開(kāi)始,擦除len長(zhǎng)度的數(shù)據(jù)。代碼如下:
int nand_erase(unsigned int addr, unsigned int len)
{
int page = addr / 2048;
if (addr & (0x1FFFF))
{
printf("nand_erase err, addr is not block align\n\r");
return -1;
}
if (len & (0x1FFFF))
{
printf("nand_erase err, len is not block align\n\r");
return -1;
}
nand_select();
while (1)
{
page = addr / 2048;
nand_cmd(0x60);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0xD0);
wait_ready();
len -= (128*1024);
if (len == 0)
break;
addr += (128*1024);
}
nand_deselect();
return 0;
}
操作NAND FLASH之前要,選中芯片,然后就可以根據(jù)芯片手冊(cè)來(lái)操作NAND FLASH的擦除操作了,操作完之后,要取消片選。
往NAND FLASH寫(xiě)數(shù)據(jù)時(shí),只需要把要寫(xiě)的數(shù)據(jù)復(fù)制給NFDATA寄存器即可。代碼如下
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
下圖為燒寫(xiě)的時(shí)序圖:
從上圖中的NAND FLASH燒寫(xiě)時(shí)序圖可以知道對(duì)于NAND FLASH的燒寫(xiě),先發(fā)出0x80命令,再發(fā)出地址周期,然后發(fā)出要燒寫(xiě)的數(shù)據(jù),最后發(fā)出0x10,就開(kāi)始內(nèi)部燒寫(xiě),然后等待燒寫(xiě)成功。(我們寫(xiě)數(shù)據(jù)的時(shí)候是逐頁(yè)寫(xiě)的,開(kāi)始要燒寫(xiě)的數(shù)據(jù)地址可能不是該頁(yè)的起始地址)。操作之前需要選中片選,操作完之后取消片選,代碼如下:
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
int page = addr / 2048;
int col = addr & (2048 - 1);
int i = 0;
nand_select();
while (1)
{
nand_cmd(0x80);
/* 發(fā)出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 發(fā)出數(shù)據(jù) */
for (; (col < 2048) && (i < len); )
{
nand_w_data(buf[i++]);
}
nand_cmd(0x10);
wait_ready();
if (i == len)
break;
else
{
/* 開(kāi)始下一個(gè)循環(huán)page */
col = 0;
page++;
}
}
nand_deselect();
}
我們封裝擦除操作NAND FLASH函數(shù)的時(shí)候,每一次擦除的大小是一個(gè)塊(128*1024)代碼如下:
void do_erase_nand_flash(void)
{
unsigned int addr;
/* 獲得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nand_erase(addr, 128*1024);
}
我們封裝讀取操作NAND FLASH函數(shù),我們實(shí)現(xiàn)NAND FLASH每次的讀取,每次讀取64字節(jié)數(shù)據(jù)。把從地址addr讀取得到的64字節(jié)數(shù)據(jù)存放到buf緩沖區(qū)中,然后通過(guò)串口顯示出來(lái),代碼如下圖所示:
void do_read_nand_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];
/* 獲得地址 */
printf("Enter the address to read: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
printf("Data : \n\r");
/* 長(zhǎng)度固定為64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16個(gè)數(shù)據(jù) */
for (j = 0; j < 16; j++)
{
/* 先打印數(shù)值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可視字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
}
NAND FLASH的燒寫(xiě)封裝函數(shù)代碼如下:
void do_write_nand_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 獲得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...\n\r");
nand_write(addr, str, strlen(str)+1);
}
NAND FLASH的測(cè)試菜單函數(shù)代碼如下:
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜單, 供我們選擇測(cè)試內(nèi)容 */
printf("[s] Scan nand flash\n\r");
printf("[e] Erase nand flash\n\r");
printf("[w] Write nand flash\n\r");
printf("[r] Read nand flash\n\r");
printf("[q] quit\n\r");
printf("Enter selection: ");
c = getchar();
printf("%c\n\r", c);
/* 測(cè)試內(nèi)容:
* 1. 識(shí)別nand flash
* 2. 擦除nand flash某個(gè)扇區(qū)
* 3. 編寫(xiě)某個(gè)地址
* 4. 讀某個(gè)地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_id();
break;
case 'e':
case 'E':
do_erase_nand_flash();
break;
case 'w':
case 'W':
do_write_nand_flash();
break;
case 'r':
case 'R':
do_read_nand_flash();
break;
default:
break;
}
}
}
