文章目錄
-
- 1、NAND FLASH和NOR FLASH的簡單比較
- 2、S3C2440中的NAND FLASH控制器
- 2、顯示NAND FLASH的重要資訊
- 3、讀NAND FLASH
- 4、擦除NANDFLASH的塊
- 5、寫NAND FLASH
1、NAND FLASH和NOR FLASH的簡單比較
- | NOR FLASH | NAND FLASH |
---|---|---|
接口 | 引腳多(像RAM一樣) | 引腳少(複用) |
容量 | 小(1M/2M/32M等) | 大(64M/128M/256M等) |
讀 | 簡單(像RAM一樣) | 複雜 |
寫 | 慢 | 快 |
擦除 | 慢 | 快 |
性能 | 好(無位反轉與壞塊) | 不是很好(存在位反轉與壞塊) |
價格 | 貴 | 便宜 |
XIP | 可以 | 不可以 |
關于NOR FLASH的操作可以翻看我上一篇博文JZ2440之NOR FLASH簡單操作篇
2、S3C2440中的NAND FLASH控制器
NAND FLASH不像NOR FLASH一樣可以直接在晶片内執行程式,NAND FLASH隻有通過與NAND FLASH控制器相連後才能與CPU進行資料交換,是以當我們要對NAND FLASH進行操作時必須先使能 NAND FLASH控制器。
在使用S3C2440中的NAND FLASH控制器時我們需要根據NAND FLASH晶片手冊來設定NAND FLASH的時序。下圖中的圖1和圖2是NAND FLASH控制器中需要我們配置的時序圖與寄存器,
其中TACLS表示發出CLE/ALE信号後過多久發送nWE信号,TWRPH0表示nWE信号的脈沖寬度,TWRPH1表示釋放nWE後過多久釋放CLE/ALE
。其具體的值可以參考圖3和圖4;(其中
HCLK=100MHz
,參考我之前的博文S3C2440中時鐘配置的那些事兒)
![]() | |
| |
在設定好時序後我們還得使能NAND FLASH控制器,這樣我們才可以順利地操作我們的NAND FLASH。在使能NAND FLASH控制器時我們先禁止片選(等要操作NAND FLASH時再使能片選),我們隻需配置如圖5所示的3位。
|
到此NAND FLASH的初始化工作就已經做完了(很簡單吧!),下面記錄下代碼。
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* 設定nand flash時序 */
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/* 使能NAND FLASH控制器,初始化ECC,禁止片選 */
NFCONT = (1<<0) | (1<<1) | (1<<4);
}
void nand_select(void)
{
NFCONT &= ~(1<<1); /* 使能片選 */
}
void nand_deselect(void)
{
NFCONT |= (1<<1); /* 禁止片選 */
}
2、顯示NAND FLASH的重要資訊
對NAND FLASH的操作主要分為三步
①發指令 ②發位址 ③進行操作
,S3C2440中的NAND FLASH控制器中與上面三步相關的寄存器有
NFCMMD、NFADDR、NFDATA和NFSTAT
,在對NAND FLASH進行操作時,我們隻需操作上面那行寄存器就可以了。
NAND FLASH沒有像NOR FLASH一樣的CFI接口, 是以NAND FLASH的一些重要參數要通過發送特殊的指令來進入特殊的模式讀取參數資訊,對應的操作如圖6和圖7
| |
具體實作代碼如下
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = 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++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void do_scan_nand_flash(void)
{
unsigned char buf[5] = {0};
nand_select(); /* 使能片選 */
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data(); /* 廠家ID */
buf[1] = nand_data(); /* 裝置ID */
buf[2] = nand_data(); /* 3rd cyc */
buf[3] = nand_data(); /* 4th cyc */
buf[4] = nand_data(); /* 5th cyc */
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]);
}
3、讀NAND FLASH
讀操作可以根據圖8所示進行操作,
注意在發出指令位址後需要讀取NFSTA寄存器第0位的值來判斷讀取是否就緒
。
|
具體代碼如下
void nand_wait_ready(void)
{
while (!(NFSTAT & 1));
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
unsigned int i = 0;
unsigned int page = addr / 2048; /* 一頁可以存放2048個位元組的資料 */
unsigned int col = addr & (2048 - 1); /* 對2048取餘 */
nand_select(); /* 使能片選 */
while (1)
{
nand_cmd(0x00); /* 發出00指令 */
nand_addr_byte(col & 0xff); /* 發出 col addr */
nand_addr_byte((col>>8) & 0xff);
nand_addr_byte(page & 0xff); /* 發出 rol/page addr */
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0x30); /* 發出30指令 */
nand_wait_ready(); /* 等待就緒 */
/* 讀資料 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
{
break;
}
else
{
col = 0;
page++;
}
}
nand_deselect(); /* 禁止片選 */
}
4、擦除NANDFLASH的塊
對NAND FLASH執行擦除操作時一次是擦除一個block的(我們所用的NAND FLASH是
K9F2G08U0C
,
其中一個page為2K個byte,一個block為128K個byte
),塊擦除的操作如圖9。
|
具體操作如下
int nand_erase(unsigned int addr, unsigned int len)
{
unsigned int page;
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); /* 發出60指令 */
nand_addr_byte(page & 0xff); /* 發出 rol/page addr */
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0xD0); /* 發出D0指令 */
nand_wait_ready(); /* 等待就緒 */
len -= (128*1024);
if (len == 0)
{
break;
}
addr += (128*1024);
}
nand_deselect(); /* 禁止片選 */
return 0;
}
這裡我們要求addr必須是一個block的起始位址,擦除長度len也必須是整數個block。
5、寫NAND FLASH
寫操作相較于前幾種操作略有差别,它是在發出80指令和位址後就要發送要寫的資料,最後再發送10指令,如圖10。
|
具體實作如下
unsigned char nand_w_data(unsigned char val)
{
NFDATA = val;
}
void nand_write(unsigned int addr,unsigned char *buf, unsigned int len)
{
unsigned int i = 0;
unsigned int page = addr / 2048;
unsigned int col = addr & (2048 - 1);
nand_select(); /* 使能片選 */
while (1)
{
nand_cmd(0x80); /* 發出80指令 */
nand_addr_byte(col & 0xff); /* 發出 col addr */
nand_addr_byte((col>>8) & 0xff);
nand_addr_byte(page & 0xff); /* 發出 rol/page addr */
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 寫資料 */
for (; (col < 2048) && (i < len); col++)
{
nand_w_data(buf[i++]);
}
nand_cmd(0x10); /* 發出10指令 */
nand_wait_ready(); /* 等待就緒 */
if (i == len)
{
break;
}
else
{
/* 開始下一個page */
col = 0;
page++;
}
}
nand_deselect(); /* 禁止片選 */
}
因為還沒有搞懂如何檢查壞塊以及檢查到壞塊後怎麼進行跳轉,是以這裡就沒有列出了,等以後搞懂了如何檢查壞塊和使用ECC後再來補吧。
注意:
因為此時我們的代碼已經超過4K了,如果依然要進行NAND啟動的話我們就得用前4K的代碼将整個程式複制到SDRAM中執行,具體操作如下
/* 判斷是nor啟動還是nand啟動 */
int isBootFromNorFlash(void)
{
volatile unsigned int *p = (volatile unsigned int *)0;
unsigned int val = *p;
*p = 0x12345678;
if (*p == 0x12345678) /* nor flash不能直接寫,nand flash可以 */
{
/* 寫成功,nand啟動 */
*p = val;
return 0;
}
else
{
return 1;
}
}
void copy2sdram(void)
{
/* 要從lds檔案中獲得__code_start, __bss_start
* 然後從0位址把資料複制到__code_start
*/
extern int __code_start, __bss_start;
volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
int len;
len = ((int)&__bss_start - (int)&__code_start);
if (isBootFromNorFlash())
{
while (dest < end)
{
*dest++ = *src++;
}
}
else
{
nand_init();
nand_read(src, dest, len);
}
}
可翻看我之前的博文如何對代碼重定位。