硬件原理
上面是我使用的NAND FLASH的硬件原理图,面对这些引脚,很难明白他们是什么含义,下面直接引用韦东山老师的课程中的提问:
NAND FLASH是一个存储芯片
那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"
问1. 原理图上NAND FLASH和S3C2440之间只有数据线,
怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址,
问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令
怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等
那么怎么避免干扰?
答3. 这些设备,要访问之必须"选中",
没有选中的芯片不会工作,相当于没接一样
问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,
怎么判断烧写完成?
答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
读操作步骤
由上图的读操作时序图可以得到这么一个读操作的流程:
(1)发出命令
(2)发出地址
(3)读数据
再结合我们的S3C2440芯片,具体操作读NAND FLASH的步骤如下(芯片是通过NAND FLASH控制器进行读写操作的):
NAND FLASH S3C2440
发命令 选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址 选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 选中芯片 NFDATA=数据值
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据 选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
要注意的是,每次操作之前都要写选中NAND FLASH。
接下来我们先了解一下NAND FLASH的内部结构:
由上图可以看到,每一页的大小为2KB,而且每一页都有64B的OOB(out of bank,主要用于坏块检测)。如果CPU读取NAND FLASH上的第2048个数据,那么将会访问到的是page 1的第一个字节(从page 0开始数),这一点很重要,OOB对于读写操作来说是透明的。另外,发出的地址给NAND FLASH是分为五个周期进行的:
有了这些基础知识后,下面马上开始写代码(下面的代码是用来重定位的作用,也就是从NAND FLASH中读取数据,然后拷贝到目的地址)
void delay_some_time(void)
{
volatile int i;
for(i=0; i<10; i++);
}
void nand_select(void)
{
NFCONT &= ~(1<<1);
}
void nand_deselect(void)
{
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
delay_some_time(); //经验判断,发出命令后需要等待一些时间
}
void nand_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
//puts("in nand_addr\n\r");
/*芯片手册规定要在5个周期之内分别按下面的规则发出地址*/
NFADDR = col & 0xff;
delay_some_time();
NFADDR = (col >> 8) & 0xff;
delay_some_time();
NFADDR = page & 0xff;
delay_some_time();
NFADDR = (page >> 8) & 0xff;
delay_some_time();
NFADDR = (page >> 16) & 0xff;
delay_some_time();
//puts("leave nand_addr\n\r");
}
void nand_wait_ready(void)
{
/*NFSTAT的最低位为0的时候代表NAND flash正忙*/
while (!(NFSTAT & 1));
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int col = addr % 2048; //有可能是从每页的中间开始读
int i = 0;
//puts("in nand_read function\n\r");
/*1. 选中:因为NAND FLASH的数据线是复用的,所以必须先选中*/
nand_select();
//puts("after nand_select\n\r");
while (i<len)
{
/*2. 发出读命令00h*/
nand_cmd(0x00);
/*3. 发出地址(分5步发出)*/
nand_addr(addr);
/*4. 发出读命令30h*/
nand_cmd(0x30);
/*5. 判断状态,确定是否已经读完*/
nand_wait_ready();
/*6. 读数据*/
for(; (col < 2048) && (i < len); col++)
{
buf[i] = nand_data();
i++;
addr++;
}
col = 0;
}
/*7. 取消片选*/
nand_deselect();
}
(1)每次读操作开始前都要先选中芯片
(2)读操作前都要先发出00命令
(3)按照NAND FLASH芯片手册的要求,发出的地址,要分为五个周期,分别发出:列地址的低8位、列地址的高8位、行(页)地址的0~7位、行(页)地址的8~15位和行(页)地址的15~23位。其中,列地址求余后的余数,页地址则是除法得到的商。
(4)之后是发出的30命令(就是这么规定,没有解释)
(5)NAND FLASH读数据也很简单,直接读寄存器NFDATA
(6)读完后,取消片选,整个读操作的流程就算结束