您的位置:首页 > 其它

JZ2440 NAND Flash控制器

2017-06-10 21:52 295 查看
裸机系列代码地址:链接: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: