S3C2410/S3C2440 NAND Flash

    NAND Flash 在嵌入式系统中的地位与PC上的硬盘类似,用于保存系统运行所必需的操作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存掉电数据丢失不同, NAND Flash 中的数据在掉电后仍可永久保存。 

1、操作方法概述
    访问 NAND Flash 时需要先发出命令,然后发出地址序列,最后读/写数据;需要使用各个使能信号来分辨是命令、地址还是数据。 S3C2410 的 NAND Flash 控制器提供了 NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC 等 6 个寄存器来简化这些操作。 S3C2440 的 NAND Flash 控制器则提供了 NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT 和其他与ECC有关的寄存器。对 NAND Flash 控制器的操作, S3C2410 和 S3C2440 有一点小差别:有些寄存器的地址不一样,有些寄存器的内容不一样。
NAND Flash 的读写操作次序如下:

<strong><span style="font-family:KaiTi_GB2312;font-size:18px;"> 设置NFCONF(对于 S3C2440 , 还要设置 NFCONT)寄存器,配置 NAND Flash。
 向 NFCMD 寄存器写入命令。
 向 NFADDR 寄存器写入地址。
 读/写数据:通过寄存器 NFSTAT 检测 NAND Flash 的状态,在启动某个操作后,应该检测 R/nB 信号以确定该操作是否完成、是否成功。</span></strong>

2、寄存器介绍

NFCONF :NAND Flash 配置寄存器。
S3C2410 的 NFCONF 寄存器。
被用来使能/禁止 NAND Flash 控制器、 使能/禁止控制引脚信号 nFCE、初始化ECC、设置NAND Flash的时序参数等。
S3C2440 的 NFCONF 寄存器。
被用来设置 NAND Flash 的时序参数 TACLS、TWRPH0、TWRPH1,设置数据位宽;还有一些只读位,用来指示是否支持其他大小的页(比如一页大小为 256/512/1024/2048 字节)。
它没有实现 S3C2410 的 NFCONF 寄存器的控制功能,这些功能在 S3C2440 的NFCONT 寄存器里实现。

NFCONT:NAND Flash 控制寄存器, S3C2410 没有这个寄存器。
被用来使能/禁止 NAND Flash 控制器、使能/禁止控制引脚信号 nFCE、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定 NAND Flash。

NFCMD:NAND Flash 命令寄存器。(同 S3C2440 的 NFCMMD 寄存器)
对于不同型号的 Flash,操作命令一般不一样。参考相关数据手册。

NFADDR:NAND Flash 地址寄存器。
当写这个寄存器时,它将对 Flash 发出地址信号。

NFDATA:NAND Flash 数据寄存器。
只用到低8位,读、写此寄存器将启动对 NAND Flash 的读数据、写数据操作。

NFSTAT:NAND Flash 状态寄存器
只用到位0, 0:busy ,1:ready

代码实例:

@******************************************************************************
@ File:head.s
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************  

.text
.global _start
_start:
				ldr			sp, =4096										@ 设置堆栈 
																						@	因为函数 disable_watch_dog、memsetup、nand_init、nand_read 
																						@ 都是在 C 程序中定义 
																						
				bl			disable_watch_dog						@ 关闭开门狗
				bl			memsetup										@ 初始化 SDRAM
				bl			nand_init										@	初始化 NAND Flash
				
																						@ 将 NAND Flash 中地址4096开始的1024字节代码(main.c编译得到)复制到 SDRAM 中
																						@ nand_read 函数需要3个参数,分别在 r0 , r1 , r2
				ldr			r0,	=0x30000000							@ 目标地址,这是 SDRAM 的起始地址
				mov			r1, #4096										@ 源地址,连接的时候,main.c中的的代码都存在 NAND Flash 地址 4096 开始处
				mov			r2, #2048										@ 复制长度。(单位 bytes)
				bl			nand_read										@ 调用 C 函数 nand_read
				
				ldr			sp, =0x34000000							@ 设置栈
				ldr			lr, =halt_loop							@ 设置返回地址
				ldr			pc, =main										@ b指令和bl指令只能前后跳转32M的返回,所以这里使用向PC赋值的方法进行跳转
halt_loop:
				b				halt_loop
				

/*
	init.c   关闭开门狗 初始化SDRAM
*/

/*WATCH DOG 寄存器*/
#define			WTCON									(*(volatile unsigned long *)0x53000000)

/*SDRAM 寄存器*/
#define			MEM_CTL_BASE					0x48000000

void disable_watch_dog();
void memsetup();

//上电后,WATCH DOG默认是开着的,要把它关掉
void disable_watch_dog(){
	WTCON = 0;
}

//设置控制SDRAM的13个寄存器
void memsetup(){
	int i = 0;
	unsigned long *p = (unsigned long *)MEM_CTL_BASE;
	
	unsigned long const mem_cfg_val[] = {
								0x22011110,					// BWSCON
								0x00000700,					// BANKCON0
								0x00000700,					// BANKCON1
								0x00000700,					// BANKCON2
								0x00000700,					// BANKCON3
								0x00000700,					// BANKCON4
								0x00000700,					// BANKCON5
								0x00018005,					// BANKCON6
								0x00018005,					// BANKCON7
								0x008C07A3,				  //REFRESH
								0x000000B1,					//BANKSIZE
								0x00000030,				  //MRSRB6 
								0x00000030,					//MRSRB7
	};
	
	for(;i < 13; ++i)
		p[i] = mem_cfg_val[i];
}

/*
	nand.c
*/

#define LARGER_NAND_PAGE

#define GSTATUS1				(*(volatile unsigned int *)0x560000B0)
#define BUSY						1

#define NAND_SECTOR_SIZE		512
#define NAND_BLOCK_MASK			(NAND_SECTOR_SIZE - 1)

#define NAND_SECTOR_SIZE_LP		2048
#define NAND_BLOCK_MASK_LP		(NAND_SECTOR_SIZE_LP - 1)

typedef unsigned int S3C24X0_REG32;

/*NAND FLASH (see S3C2410 manual chapter 6)*/
typedef struct{
	S3C24X0_REG32			NFCONF;
	S3C24X0_REG32			NFCMD;
	S3C24X0_REG32			NFADDR;
	S3C24X0_REG32			NFDATA;
	S3C24X0_REG32			NFSTAT;
	S3C24X0_REG32			NFECC;
} S3C2410_NAND;


/*NAND FLASH (see S3C2440 manual chapter 6)*/
typedef struct {
	S3C24X0_REG32			NFCONF;
	S3C24X0_REG32			NFCONT;
	S3C24X0_REG32			NFCMMD;
	S3C24X0_REG32			NFADDR;
	S3C24X0_REG32			NFDATA;
	S3C24X0_REG32			NFMECCD0;
	S3C24X0_REG32			NFMECCD1;
	S3C24X0_REG32			NFSECCD;
	S3C24X0_REG32			NFSTAT;
	S3C24X0_REG32			NFESTAT0;
	S3C24X0_REG32			NFESTAT1;
	S3C24X0_REG32			NFMECC0;
	S3C24X0_REG32			NFMECC1;
	S3C24X0_REG32			NFSECC;
	S3C24X0_REG32			NFSBLK;
	S3C24X0_REG32			NFEBLK;
} S3C2440_NAND;

typedef struct {
	void (*nand_reset)(void);
	void (*wait_idle)(void);
	void (*nand_select_chip)(void);
	void (*nand_deselect_chip)(void);
	void (*write_cmd)(int cmd);
	void (*write_addr)(unsigned int addr);
	unsigned char (*read_date)(void);
} t_nand_chip;

static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;

static t_nand_chip nand_chip;

/* 供外部调用的函数 */
void nand_init(void);
void nand_read(unsigned char *buf, unsigned long start_addr, int size);

/* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_date(void);

/* S3C2410的NAND Flash处理函数 */
static void s3c2410_nand_reset(void);
static void s3c2410_wait_idle(void);
static void s3c2410_nand_select_chip(void);
static void s3c2410_nand_deselect_chip(void);
static void s3c2410_write_cmd(int cmd);
static void s3c2410_write_addr(unsigned int addr);
static unsigned char s3c2410_read_data(void);

/* S3C2440的NAND Flash处理函数 */
static void s3c2440_nand_reset(void);
static void s3c2440_wait_idle(void);
static void s3c2440_nand_select_chip(void);
static void s3c2440_nand_deselect_chip(void);
static void s3c2440_write_cmd(int cmd);
static void s3c2440_write_addr(unsigned int addr);
static unsigned char s3c2440_read_data(void);


/*S3C2410的NAND Flash操作函数*/

//复位
static void s3c2410_nand_reset(void){
	s3c2410_nand_select_chip(); //发出片选信号
	s3c2410_write_cmd(0xff);	//reset命令
	s3c2410_wait_idle();			// 循环查询 NFSTAT位0, 直到它等于1
	s3c2440_nand_deselect_chip(); //进制 NAND Flash
}

//等待NAND Flash就绪 (循环查询 NFSTAT位0, 直到它等于1)
static void s3c2410_wait_idle(void){
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT;
	while(!(*p & BUSY))
		for(i = 0; i < 10; i++);
}

// 发出片选信号 (NFCONF 位 11 写入0)
static void s3c2410_nand_select_chip(void){
	int i;
	s3c2410nand->NFCONF &= ~(1 << 11);
	for(i = 0; i < 10; i++);
}

//取消片选信号(禁止 NAND Flash)
static void s3c2410_nand_deselect_chip(void){
	s3c2410nand->NFCONF |= (1 << 11);
}

//发出命令(将命令对应的数字 写入寄存器 NFCMD 中)
static void s3c2410_write_cmd(int cmd){
	volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD;
	*p = cmd;
}

// 发出地址信号
static void s3c2410_write_addr(unsigned int addr){
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFADDR;
	
	*p = addr & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 9) & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 17) & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 25) & 0xff;
	for(i = 0; i < 10; i++);
}

// 读取数据(取出 NFDATA寄存器中的数据)
static unsigned char s3c2410_read_data(void){
	volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA;
	return *p;
}

/* S3C2410的NAND Flash操作函数 */
// 复位
static void s3c2440_nand_reset(void){
	s3c2440_nand_select_chip();		//发出片选信号
	s3c2440_write_cmd(0xff);			//reset命令
	s3c2440_wait_idle();					// 循环查询 NFSTAT位0, 直到它等于1
	s3c2440_nand_deselect_chip();	//进制 NAND Flash
}

//等待NAND Flash就绪 (循环查询 NFSTAT位0, 直到它等于1)
static void s3c2440_wait_idle(void){
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
	while(!(*p & BUSY))
		for(i = 0; i < 10; i++);
}

// 发出片选信号 (NFCONT 位 1 写入0)
static void s3c2440_nand_select_chip(void){
	int i;
	s3c2440nand->NFCONT &= ~(1 << 1);
	for(i = 0; i < 10; i++);
}

//取消片选信号(禁止 NAND Flash)
static void s3c2440_nand_deselect_chip(void){
	s3c2440nand->NFCONT |= (1 << 1);
}

//发出命令(将命令对应的数字 写入寄存器 NFCMMD 中)
static void s3c2440_write_cmd(int cmd){
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMMD;
	*p = cmd;
}

// 发出地址信号
static void s3c2440_write_addr(unsigned int addr){
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
	
	*p = addr & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 9) & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 17) & 0xff;
	for(i = 0; i < 10; i++);
	*p = (addr >> 25) & 0xff;
	for(i = 0; i < 10; i++);
}

static void s3c2440_write_addr_lp(unsigned int addr){
	int i;
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
	int col, page;
	
	col = addr & NAND_BLOCK_MASK_LP;
	page = addr / NAND_SECTOR_SIZE_LP;
	
	*p = col & 0xff;								/* Column Address A0~A7 */
	for(i = 0; i < 10; i++);
	*p = (col >> 8) & 0x0f;					/* Column Address A8~A11 */
	for(i = 0; i < 10; i++);
	*p = page & 0xff;								/* Row Address A12~A19 */
	for(i = 0; i < 10; i++);
	*p = (page >> 8) & 0xff;				/* Row Address A20~A27 */
	for(i = 0; i < 10; i++);
	*p = (page >> 16) & 0x03;				/* Row Address A28~A29 */
	for(i = 0; i < 10; i++);
}
// 读取数据(取出 NFDATA寄存器中的数据)
static unsigned char s3c2440_read_data(void){
	volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
	return *p;
}


static void nand_reset(void){
	nand_chip.nand_reset();
}

static void wait_idle(void){
	nand_chip.wait_idle();
}

static void nand_select_chip(void){
	int i;
	nand_chip.nand_select_chip();
	for(i = 0; i < 10; i++);
}

static void nand_deselect_chip(void){
	nand_chip.nand_deselect_chip();
}

static void write_cmd(int cmd){
	nand_chip.write_cmd(cmd);
}

static void write_addr(unsigned int addr){
	nand_chip.write_addr(addr);
}

static unsigned char read_date(void){
	return nand_chip.read_date();
}

/* 初始化NAND Flash */
void nand_init(void){
#define		TACLS			0
#define		TWRPH0		3
#define		TWRPH1		0

	/* 判断是S3C2410还是S3C2440 */
	if((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002)){
		nand_chip.nand_reset = s3c2410_nand_reset;
		nand_chip.wait_idle = s3c2410_wait_idle;
		nand_chip.nand_select_chip =  s3c2410_nand_select_chip;
		nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
		nand_chip.write_cmd = s3c2410_write_cmd;
		nand_chip.write_addr = s3c2410_write_addr;
		nand_chip.read_date = s3c2410_read_data;
		/* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
		s3c2410nand->NFCONF = (1 << 15) | (1 << 12) | (1 << 11) | (TACLS << 8) | (TWRPH0 << 4) | (TWRPH1 << 0);
	}
	else{
		nand_chip.nand_reset = s3c2440_nand_reset;
		nand_chip.wait_idle = s3c2440_wait_idle;
		nand_chip.nand_select_chip = s3c2440_nand_select_chip;
		nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
		nand_chip.write_cmd = s3c2440_write_cmd;
#ifdef LARGER_NAND_PAGE
		nand_chip.write_addr = s3c2440_write_addr_lp;
#else
		nand_chip.write_addr = s3c2440_write_addr
#endif
		nand_chip.read_date = s3c2440_read_data;
		/* 设置时序 */
		s3c2440nand->NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);
		/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
		s3c2440nand->NFCONT = (1 << 4) | (1 << 1) | (1 << 0);
	}
	/* 在第一次使用NAND Flash前,复位一下NAND Flash */
	nand_reset();
}


/*
	读函数
	从 NAND Flash 位置 start_addr 开始,将数据复制到 SDRAM 地址 buf 处,共复制 size 字节
*/
void nand_read(unsigned char *buf, unsigned long start_addr, int size){
	int i, j;
	
#ifdef LARGER_NAND_PAGE
	if((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)){
		return ;	 /* 地址或长度不对齐 */
	}
#else
	if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)){
		return ;	 /* 地址或长度不对齐 */
	}
#endif

	 /* 选中芯片 */
	nand_select_chip();
	
	for(i = start_addr; i < (start_addr + size); ){
		 /* 发出READ0命令 */
		write_cmd(0);
		/* Write Address */
		write_addr(i);
		
#ifdef LARGER_NAND_PAGE
		write_cmd(0x30);
#endif
		wait_idle();


#ifdef LARGER_NAND_PAGE
		for(j = 0; j < NAND_SECTOR_SIZE_LP; j++,i++){
#else
		for(j = 0; j < NAND_SECTOR_SIZE; j++,i++){
#endif		
			*buf = read_date();
			buf++;
		
		}
	}
	/* 取消片选信号 */
	nand_deselect_chip();
	
	return ;
}

/*
	main.c
*/

#define	GPFCON		(*(volatile unsigned long *)0x56000050)
#define	GPFDAT		(*(volatile unsigned long *)0x56000054)

#define	GPF4_out	(1<<(4*2))
#define	GPF5_out	(1<<(5*2))
#define	GPF6_out	(1<<(6*2))

void  wait(volatile unsigned long dly)
{
	for(; dly > 0; dly--);
}

int main(void)
{
	unsigned long i = 0;

	GPFCON = GPF4_out|GPF5_out|GPF6_out;		// 将LED1-3对应的GPF4/5/6三个引脚设为输出

	while(1){
		wait(30000);
		GPFDAT = (~(i<<4));	 	// 根据i的值,点亮LED1-3
		if(++i == 8)
			i = 0;
	}

	return 0;
}

/*
	nand.lds
*/
SECTIONS {
	firtst 		0x00000000 :  {head.o init.o nand.o }
	second		0x30000000 : AT(4096) { main.o }
}

# Makefile

objs := nand.o head.o init.o main.o 

nand.bin : $(objs)
	arm-linux-ld	-Tnand.lds -o nand_elf $^
	arm-linux-objcopy -O binary -S nand_elf $@
	arm-linux-objdump -D -m arm nand_elf > nand.dis
	
%.o:%.c
	arm-linux-gcc -Wall -O2 -c -o $@ $<
	
%.o:%.S
	arm-linux-gcc -Wall -O2 -c -o $@ $<
	
clean:
	rm -f nand.dis nand_elf nand.bin *.o


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值