對Nand Flash的操作(S3C2440)
Nand Flash的概述 先看下電路原理圖 從原理圖可以看出: 在DATA0~DATA7上既傳輸資料,又傳輸地址,也傳輸命令: 當ALE為高電平時傳輸的是地址。 當CLE為高電平時傳輸的是命令。 當ALE和CLE都為低電平時傳輸的是資料。
假設燒寫NAND FLASH,把命令、地址、資料發給它之後,NAND FLASH肯定不可能瞬間完成燒寫的,怎麼判斷燒寫完成? 通過狀態引腳RnB來判斷:它為高電平表示就緒,它為低電平表示正忙。
看一下NAND的引腳:
根據NAND FLASH的晶片手冊,一般的過程是:
- 發出命令
- 發出地址
- 發出資料/讀資料 相應的命令字如圖: s3c2440來說,內部集成了一個NAND FLASH控制器,CPU只需操作NAND FLASH控制器,就實現對NAND FLASH的操作。
NAND Flash控制器提供了NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT和其他一些暫存器。
NAND FLASH控制器的時序,是為了讓NAND FLASH外設工作起來,假如外接不同的 NAND FLASH外設,那麼它的操作時序可能就會不同,所以對Nand Flash控制器的設定也不同。 需要根據不同的nand flash,設定nand flash控制器。
看一下nand flash控制器時序: 由圖可知,需要設定nand flash控制器的TACLS、TWRPH0、TWRPH1。 TACLS、TWRPH0、TWRPH1分別是NFCONF暫存器的某幾位。
看一下nand flash晶片的時序: 分析: 依次看這3張圖 圖1 ,TACLS是CLE / ALE使能之後,過多久nWE使能。就對應圖2中nand flash晶片的時序中的tCLS和tALS減去tWP。再看圖3,tCLS、tALS、tWP最小都為12ns;也就是TACLS可以為12-12=0。
圖1,TWRPH0是nWE的使能時間。對應圖2中的tWP。再看圖3,為12ns。
圖1,TWRPH1是nWE失能之後,過多久CLE / ALE失能。就對應圖2中nand flash晶片的時序中的tCLH、tALH。再看圖3,都為5ns。
然後根據時序,設定暫存器。 HCLK為100MHz,也就是10ns。 所以,可設定TACLS即[13:12] = 0;TWRPH0即[10:8] = 1;TWRPH1即[6:4] = 0; 再根據NFCONT暫存器,初始化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<<4) | (1<<1) | (1<<0);
}
再實現一個函式,讀取nand的ID
void nand_select(void)
{
/*使能片選*/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/*禁止片選*/
NFCONT |= (1<<1);
}
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]);
}
在之前的博文中,重定位程式碼到SDRAM,只能實現從NOR Flash複製程式碼到SDRAM。現在實現 讀NAND 函式,然後實現從nand到SDRA的重定位。
具體程式碼如下:
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)
{
/* 發出00h命令 */
nand_cmd(00);
/* 發出地址 */
/* 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);
/* 發出30h命令 */
nand_cmd(0x30);
/* 等待就緒 */
wait_ready();
/* 讀資料 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
//然後在重定位函式中,新增程式碼
void nand_init(void);
void nand_read_relocate(unsigned int addr, unsigned char *buf, unsigned int len);
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()) //判斷是nor還是nand啟動
{//nor啟動
while (dest < end)
{
*dest++ = *src++;
}
}
else
{//nand啟動
nand_init();
nand_read(src, dest, len);//從nand複製程式碼到SDRAM
}
}
最後實現,讀、寫、擦除函式。
#include "s3c2440_soc.h"
#include "printf.h"
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<<4) | (1<<1) | (1<<0);
}
void nand_select(void)
{
/*使能片選*/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/*禁止片選*/
NFCONT |= (1<<1);
}
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++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
void wait_ready(void)
{
while (!(NFSTAT & 1));
}
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]);
}
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)
{
/* 發出00h命令 */
nand_cmd(00);
/* 發出地址 */
/* 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);
/* 發出30h命令 */
nand_cmd(0x30);
/* 等待就緒 */
wait_ready();
/* 讀資料 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
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;
}
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);
/* 發出地址 */
/* 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);
/* 發出資料 */
for (; (col < 2048) && (i < len); )
{
nand_w_data(buf[i++]);
}
nand_cmd(0x10);
wait_ready();
if (i == len)
break;
else
{
/* 開始下一個迴圈page */
col = 0;
page++;
}
}
nand_deselect();
}
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");
/* 長度固定為64 */
for (i = 0; i < 4; i++)
{
/* 每行列印16個數據 */
for (j = 0; j < 16; j++)
{
/* 先列印數值 */
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");
}
}
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);
}
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);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 列印選單, 供我們選擇測試內容 */
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);
/* 測試內容:
* 1. 識別nand flash
* 2. 擦除nand flash某個扇區
* 3. 編寫某個地址
* 4. 讀某個地址
*/
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;
}
}
}