JZ2440 NAND Flash控制器

本文详细介绍了NAND Flash的工作原理与初始化过程,并通过具体实例讲解了如何在S3C2440平台上实现NAND Flash的读取操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

裸机系列代码地址:链接:http://pan.baidu.com/s/1pLHOd0v 密码:4x5s

NAND Flash在嵌入式系统中的地位与PC上的硬盘类似,用于保存系统运行所必须的操作系统,应用程序,用户数据,

运行过程中产生的各类数据。
NOR Flash:支持XIP,即代码可以直接在NOR Flash上运行,无需复制到内存中,NOR Flash的接口与ARM完全相同
可以随意访问地址的数据。在NOR Flash上常用JFFS2文件系统
NAND Flash:为支持 NAND 启动,系统内配了一个4K的ram(steppingstone),系统上电时,硬件自动将NAND前4k的内容
拷贝到这个4k的片内ram上,然后在ram上执行前4k的程序,前4k的程序需要把NAND Flash中的代码拷贝到SDRAM上,然后在
SDRAM上继续执行。NAND 上常用YAFFS文件系统

NAND Flash与S3C2440的连线
8个IO引脚(IO0-IO8)
5个使能信号: nWE(写信号)
ALE(地址锁存信号)
CLE(命令锁存信号)
nCE(片选信号)
nRE(读信号)
1个状态引脚 RDY/B
一个写保护引脚 nWP

命令字及操作方法
操作NANF Flash时先写命令,再写地址,然后写数据。至于具体的操作流程,s3c2440提供了NAND控制器,即一堆寄存器用来
操作,而相应寄存器中该怎么填写就和具体的NAND Flash硬件相关了。韦东山老师的书上提供的教学NAND Flash为 K9F1208U0M
而我的JZ2440开发板上的NAND Flash为K9F2G08U0C,先来看看两者的不同。


这里不分析具体的命令字怎么用,具体的东西会在代码中体现
现在看看S3C2440 NAND Flash控制器部分的介绍
s3c2440提供了NFCONF NFCONT NFCMMD NFADDR NFDATA NFSTAT和ECC相关的一系列寄存器,这里不介绍ECC相关的寄存器(实际上是我不懂)
(1)s3c2440的 NAND Flash 配置寄存器 NFCONF
此寄存器用来设置NANF Flash的时序参数TACLS、TWRPH0、TWRPH1,设置数据位宽,还有其他只读位位用来标示s3c2440所接的NAND的特性
[13:12] : Duration = Hclk*TACLS
[10:8] : Duration = HCLK*(TWRPH0+1)
[6:4]  : Duration = HCLK*(TWRPH1+1)
[0] : 位宽设置 0=8位,1=16位
以上是NFCONF需要设置的地方,下面三个域是标示NAND硬件特性的地方,为只读
[3]    : AdvFlash,0=512byte/page ,1=2048byte/page,由NCON0引脚决定,不可人为设置
[2]    : pagesize,0=512byte/page ,1=2048byte/page,由GPG13引脚决定,不可人为设置

[1]    : AddrCycle,0=1:4 address cycle , 1=1:5 address cycle,由GPG14引脚决定,不可人为设置

NFCONF的设置参见下面三个图,前两个图分别来自S3C2440 NAND FLASH控制器部分和K9F2G08U0C的数据手册,给出了各量的对应关系,第三个图给出了各

量的取值范围



(2)S3C2440的NFCONT寄存器(注意:在s3c2410中,没有这个寄存器,s3c2410这的功能被集成到了NFCONF寄存器中)

这个寄存器主要是来使能NAND FLASH控制器,使能控制器引脚信号nFCE,初始化ECC
[4]    : 1 = 初始化ECC, 0 = 不初始化ECC
[1]    : 0 = 使能选中芯片, 1 = 禁止选中芯片
[0]    :0 = 禁止NAND Flash控制器, 1 = 使能NAND Flash控制器

(3)NFCMD寄存器: NAND Flash命令寄存器
对于不同的flash,操作命令不一样,在对flash进行操作时,先要把操作类别写入此寄存器

(4)NFADDR寄存器:NAND flash地址寄存器
在对NAND Flash进行操作时,写入操作类型后,之后就要写入操作地址,写入操作地址的格式和具体的NAND Flash硬件相关,因为不同的硬件的
总容量不一样,块大小也不一样,所以行列地址会不一样,具体怎么写需要参考具体硬件的数据手册,如下图说明了地址如何转化成行列地址


(5)NFDATA寄存器:NAND Flash的数据寄存器
在写入需要读写flash单元的地址后,之后就是要写入或读出的数据了,要写入或读出的数据保存在此寄存器中,因为NAND flash芯片的IO引脚
为8位,所以此寄存器只用到了低8位,这也是NFADDR为什么要多次写入地址的原因,因为一次只能写入8位。

(6)NFSTAT寄存器:NAND Flash状态寄存器用到位0,0=busy , 1=ready。每次进行一次操作后都要检查上一步操作是否已经完成

现在来看看代码来分析,代码中主要是实现了NAND FLASH的读函数,下图为读的时序图


首先是Makefile文件

objs=head.o init.o nand.o led.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 -c -o2 -o $@ $<

%.o:%.S
	arm-linux-gcc -Wall -c -o2 -o $@ $<

clean:
	rm -f nand_elf nand.dis nand.bin *.o 
接下来看看链接文件

SECTIONS{
	first  0x00000000  : {head.o init.o nand.o}
	second 0x30000000  : AT(4096) {led.o}
}
整个程序被分成了两个部分存放在NAND FLASH中,注意led.c中的代码放在了NAND的4096地址之后,这个地址之后的代码不会自动被复制到Steppingstone到自动执行

所以需要我们手动将这部分代码复制到SDRAM中执行,而对NAND的读写不像Steppingstone那样简单,这需要我们自己写读写函数来支持NAND FLASH的读操作

head.S文件

.text
.global _start
_start:
	ldr sp,=4096            /*设置给C函数运行的堆栈*/
	bl disable_watchdog     /*关中断*/
	bl sdram_init           /*初始化存储控制器*/
	bl nand_init            /*初始化NAND Flash,设置NFCONF和NFCONT*/
	ldr r0,=0x30000000      /*nand_read的第一个参数,目的地址*/
	mov r1,#4096            /*nand_read的第二个参数,起始地址*/
	mov r2,#2048            /*nand_read的第三个参数,读取长度*/
	bl nand_read            /*调用nand_read将NAND FLASH中的代码复制到SDRAM中*/

	ldr sp,=0x34000000      /*设置第二部分代码的堆栈*/
	ldr lr,=halt_loop
	ldr pc,=main            /*调到main函数中执行按键控制led的程序*/
	
halt_loop:
	b halt_loop
init.c文件

void sdram_init(void)
{
	#define MEM_CTL_BASE ((unsigned long *)0x48000000)  /*存储控制系列寄存器起始地址*/
	unsigned long VAL[13]=                              /*需要被填写到存储控制寄存器中的值*/
		{
			0x22011110,
			0x00000700,
			0x00000700,
			0x00000700,
			0x00000700,
			0x00000700,
			0x00000700,
			0x00018005,
			0x00018005,
			0x008c07A3,
			0x000000b1,
			0x00000030,
			0x00000030,
		};
	int i=0;
	volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
	for(;i<13;i++)
		{
			p[i] = VAL[i];
		}
}

void disable_watchdog(void)
{
	#define WATCH_DOG (*(volatile unsigned long *)0x53000000)
	WATCH_DOG = 0X00;
}
nand.c文件,这个文件中实现了对NAND FLASH的读
#define NFCONF (*(volatile unsigned long *)0x4E000000)
#define NFCONT (*(volatile unsigned long *)0x4E000004)
#define NFCMD  (*(volatile unsigned long *)0x4E000008)
#define NFADDR (*(volatile unsigned long *)0x4E00000C)
#define NFDATA (*(volatile unsigned long *)0x4E000010)
#define NFSTAT (*(volatile unsigned long *)0x4E000020)

#define TACLS   0
#define TWRPH0  3
#define TWRPH1  0

#define BUSY    1

/*初始化NAND FLASH:初始化ECC,使能NAND控制器,禁止片选,设置时序*/
void nand_init(void)
{
	NFCONF = ((TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4));
	NFCONT = ((1<<4)|(1<<1)|(1<<0));
}

void wait_idle(void)
{
	int i;
	/*注意此处的p必须为volatile,即*p的值和NFSTAT的值保持同步,仿真编译器优化*/
	volatile unsigned char *p=(volatile unsigned char *)&NFSTAT;
	while(!(*p & BUSY))
		for(i=0;i<10;i++);
}

/*选中NANF FLASH芯片*/
void nand_select_chip(void)
{
	int i;
	NFCONT &=~(1<<1);     /*使能片选*/
	for(i=0;i<10;i++);
}

/*写命令*/
void write_cmd(int cmd)
{
	/*因为NAND FLASH 只有8个IO线,所以一次只能写char型大小*/
	volatile unsigned char *p = (volatile unsigned char *)&NFCMD;
	*p=cmd;
}

/*写地址,此处需要主要,写地址分为写行地址和列地址,列地址为页内寻址,行地址为寻址页*/
void write_addr(unsigned int addr)
{
	int i;
	int col;
	int page;
	volatile unsigned char *p = (volatile unsigned char *)&NFADDR;
	col=addr & (2048-1);    //页内所在的单元,计算列地址
	page = addr / 2048;     //所在页,计算行地址

	*p = col &0xff;         //写列地址
	for(i=0;i<10;i++);
	*p = (col >>8 ) & 0xff;
	for(i=0;i<10;i++);

	
	*p = page & 0xff;        //写行地址
	for(i=0;i<10;i++);
	*p = (page >> 8) & 0xff;
	for(i=0;i<10;i++);
	*p = (page >> 16) & 0xff;
	for(i=0;i<10;i++);
}

/*读取数据,*/
unsigned char read_data(void)
{
	volatile unsigned char *p = (volatile unsigned char *)&NFDATA;
	return *p;
}

/*取消片选*/
void nand_deselect_chip(void)
{
	int i;
	NFCONT |=(1<<1);     /*使能片选*/
	for(i=0;i<10;i++);
}

/*复位NAND FLASH,往往在第一次使用时会复位*/
void nand_reset(void)
{
	nand_select_chip();
	write_cmd(0xff);
	wait_idle();
	nand_deselect_chip();
}

void nand_read(unsigned char *buf,unsigned long start_addr,int size)
{
	int i,j;
	
    /*地址长度不对齐直接返回,其实不对齐也可以读,不过稍微麻烦些,因为NAND一次为读一个页,如果地址
	和读取的总长度不为页的整数倍,则要放弃第一个读取页和最后一个读取页的某些部分,这里采用最为简单
	的情况为例,所在在移动到SDRAM中代码在NAND的加载位置必须块对齐*/
	if( (start_addr & (2048-1)) || (size & (2048-1)) )    /*地址长度不对齐直接返回*/
		return ;

	nand_select_chip();
	for(i=start_addr;i<(start_addr +size);)
		{
			write_cmd(0);
			write_addr(i);
			write_cmd(0x30);
			wait_idle();

			for(j=0;j<2048;i++,j++)   /*一次读取一个页的数据*/
				{
					*buf = read_data();
					buf++;
				}
		}
	nand_deselect_chip();
	
}
led.c

#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)

#define GPF4_5_6_OUT  ((0b01<<12)|(0b01<<10)|(0b01<<8))
#define GPF0_2_IN     (~((0x3<<4)&(0x3)))
#define GPG3_IN       (~(0X3<<6))
#define GPF4_5_6_OFF   ((1<<4)|(1<<5)|(1<<6))
int main()
{
	GPFCON|=GPF4_5_6_OUT;
	GPFCON&=GPF0_2_IN;

	GPGCON&=GPG3_IN;
	GPFDAT|=GPF4_5_6_OFF;
	unsigned long gpfval;
	unsigned long gpgval;

	while(1)
	{
		gpfval=GPFDAT;
		if(gpfval&1)
			GPFDAT|=(1<<4);
		else
			GPFDAT&=(~(1<<4));

		if((gpfval>>2)&1)
			GPFDAT|=(1<<5);
		else
			GPFDAT&=(~(1<<5));

		gpgval=GPGDAT;
		if((gpgval>>3)&1)
			GPFDAT|=(1<<6);
		else
			GPFDAT&=(~(1<<6));
			
	}

	return 0;
}







2440 硬件手册 INTRODUCTION This user’s manual describes SAMSUNG&#39;s S3C2440A 16/32-bit RISC microprocessor. SAMSUNG’s S3C2440A is designed to provide hand-held devices and general applications with low-power, and high-performance microcontroller solution in small die size. To reduce total system cost, the S3C2440A includes the following components. The S3C2440A is developed with ARM920T core, 0.13um CMOS standard cells and a memory complier. Its lowpower, simple, elegant and fully static design is particularly suitable for cost- and power-sensitive applications. It adopts a new bus architecture known as Advanced Micro controller Bus Architecture (AMBA). The S3C2440A offers outstanding features with its CPU core, a 16/32-bit ARM920T RISC processor designed by Advanced RISC Machines, Ltd. The ARM920T implements MMU, AMBA BUS, and Harvard cache architecture with separate 16KB instruction and 16KB data caches, each with an 8-word line length. By providing a complete set of common system peripherals, the S3C2440A minimizes overall system costs and eliminates the need to configure additional components. The integrated on-chip functions that are described in this document include: · Around 1.2V internal, 1.8V/2.5V/3.3V memory, 3.3V external I/O microprocessor with 16KB I-Cache/16KB DCache/ MMU · External memory controller (SDRAM Control and Chip Select logic) · LCD controller (up to 4K color STN and 256K color TFT) with LCD-dedicated DMA · 4-ch DMA controllers with external request pins · 3-ch UARTs (IrDA1.0, 64-Byte Tx FIFO, and 64-Byte Rx FIFO) · 2-ch SPls · IIC bus interface (multi-master support) · IIS Audio CODEC interface · AC’97 CODEC interface · SD Host interface version 1.0 & MMC Protocol version 2.11 compatible · 2-ch USB Host controller / 1-ch USB Device controller (ver 1.1) · 4-ch PWM timers / 1-ch Internal timer / Watch Dog Timer · 8-ch 10-bit ADC and Touch screen interface · RTC with calendar function · Camera interface (Max. 4096 x 4096 pixels input support. 2048 x 2048 pixel input support for scaling) · 130 General Purpose I/O ports / 24-ch external interrupt source · Power control: Normal, Slow, Idle and Sleep mode · On-chip clock generator with PLL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值