1. 程式人生 > >對Nand Flash的操作(S3C2440)

對Nand Flash的操作(S3C2440)

Nand Flash的概述 先看下電路原理圖 在這裡插入圖片描述 從原理圖可以看出: 在DATA0~DATA7上既傳輸資料,又傳輸地址,也傳輸命令: 當ALE為高電平時傳輸的是地址。 當CLE為高電平時傳輸的是命令。 當ALE和CLE都為低電平時傳輸的是資料。

假設燒寫NAND FLASH,把命令、地址、資料發給它之後,NAND FLASH肯定不可能瞬間完成燒寫的,怎麼判斷燒寫完成? 通過狀態引腳RnB來判斷:它為高電平表示就緒,它為低電平表示正忙。

看一下NAND的引腳: 在這裡插入圖片描述

根據NAND FLASH的晶片手冊,一般的過程是:

  1. 發出命令
  2. 發出地址
  3. 發出資料/讀資料 相應的命令字如圖: 在這裡插入圖片描述 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晶片的時序: 圖2 在這裡插入圖片描述 分析: 依次看這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;
		}
	}
}