R/nB为低电平这段时间就是 在处理这些命令(实际上是根据命令将你定位的那一页数据读到内部寄存器中),
等 R/nB变成高电平了,就指示命令处理完毕,现在数据也就可以读出来了。
综上我们从手册中我们就知道了读操作的具体步骤,
1 首先nand flash 也是一个外设,要访问他就需要片选它,所以在执行时序图上的步骤之前需要片选nand flash.
2 看后面就是安装时序图来了,看时序图! 第一步先是发送一个命令 0x00.
3 看时序图! 然后发送五个地址序列(先发送两个列地址,在发送三个行地址(即页地址))
4 看时序图! 接着再是一个命令 0x30.
5 看时序图! R/nB 引脚为低表示现在正忙,正在处理这些命令,那就要等待 R/nB 引脚变为高电平
6 看时序图! 这个时候就可以读数据了
7 一次读操作结束了 nand flash 暂时是不需要使用了,那么别忘了应该 取消片选信号。
至于这每一个步骤中具体的时序,cpu中的nand flash控制器会帮我们完成。我们要做就是设置几个时间参数
这里我们反复强调了要看时序图。其实学嵌入式前期对数据手册一定要多看,看多了你就回知道,什么东西的你重点要看的,什么是和你的编程操作无关的你不需要关心。这样后面你才能,拿到一个外设 就能写出他的操作。而不用跟着书背步骤。只要手册在就行了。
上面的步骤,是一个具体的读操作的步骤,不过在使用一个器件之前我们需要初始化一下它。至于初始化也就是设置我们上面多次提到的
我们说过 s3c2440已经帮我做了很多底层的单元操作,我们只需设置几个时间参数 片内的 nand flash就会自动发出相应操作的时序操作
那么到底设置那几个时间呢。
s3c2440 手册上给出了 需要我们设置的几个参数。
从中我们可以看出 第一幅时序图是 命令和地址锁存的时序,第二幅是 数据读取和写入的时序。
可以看出,他们要设置的时间都是一样的。前面分析了那么多,这里应该不难 看出
1 TACLS 很明表示的是 CLE/ALE 的建立时间(这里并不准确,其实是 CLE/ALE有效到 WE 变成低电平之间的时间,但 WE 却是在上升沿才锁存命令/地址)
2 TWRPH0 代表的是 WE 的脉冲宽度,即有效时间
3 TWRPH1 代表的是 CLE/ALE 的保持时间
那到底设置成什么数字呢。既然 上图中读/写/命令/地址 操作需要的时间参数都是一样的
那么我们从 nand flash中随便找一个 命令时序来对照不就行了
就拿上面我们说过的 命令锁存时序来对比
那么我们就能得到下面的关系
TWRPH0 = tWP
TWRPH1 = tCLH
TACLS = tCLS - tWP
然后设置成多少呢? 看手册啊,手册中对 tWP tCLH tCLS 都会至少给出 需要的最小时间
这款芯片nand flash手册中这三个参数要求是
所以 TWRPH0 = tWP >=12ns
TWRPH1 = tCLH >=5ns
TACLS = tCLS - tWP >=0;
而这三个参数在s3c2440的数据手册中说明为
当然这里的时间都是以 HCLK 为单位的,这几个参数 是在 nand flash控制寄存器中的 NFCONF中设置的
这里我没用使用MPLL 所以HCLK是 12MHZ
所以 TWRPH0 = 0 TWRPH1 =0 TACLS =0; (如果你的时钟频率比较高,那就要设别的数了。当然有是有你真不知道,设置大点总没错,只不过速度可能会慢点。)
所以 NFCONF =0;(NOFCONF其他位数据手册中有说明,这里只是简单读操作,其他位可以不设置)
然后是初始化一下 ECC 使能nand flash控制器(我们只是设置了几个时间参数,时序的具体操作就是靠他来完成的,所以要使能他)
然后先 取消片选nand flash 因为我们现在还没有操作它啊,只是初始化一下。所以还是应该先取消片选,等真正读的时候再使能片选信号
NFCONT = (1<<4) | (1<<1) (1<<0);
(数据手册中有对应位的说明)
最后,第一次使用nand flash 我们需要复位操作一下。
综上,nand flash 的初始化代码如下
void nand_init(void){
NFCONF =0;
NFCONT = (1<<4) | (1<<1) | (1<<0);
nand_reset(); //nand reset代码在后面
}
下面代码一些地址的写法是与 nand flash 型号有关的。具体需要参考芯片手册
void select_chip(void){
NFCONT &= (~(1<<1)) ;
int i;
for(i=10;i>0;i--);
}
void deselect_chip(void){
NFCONT |= (1<<1);
int i;
for(i=10;i>0;i--);
}
void write_command(unsigned char command){
NFCMMD = command;
int i;
for(i=10;i>0;i--);
}
/*
这款nand flash 的页大小是 2K
五个地址周期 (2个列地址 和3和行地址(页地址))
*/
void write_address(unsigned int address){
unsigned int page = address/2048;
unsigned int col = address&2048;
int i;
NFADDR = col & 0xff;
for(i=5;i>0;i--);
NFADDR = (col >>8) & 0x0f;
for(i=5;i>0;i--);
NFADDR = page & 0xff;
for(i=5;i>0;i--);
NFADDR = (page >>8)& 0xff;
for(i=5;i>0;i--);
NFADDR = (page >>16)&0x01;
for(i=5;i>0;i--);
}
unsigned char read_one_data(void){
return NFDATA;
int i;
for(i=10;i>0;i--);
}
void wait_ready(void){
while(!(NFSTAT & 1));
int i;
for(i=10;i>0;i--);
}
static void nand_reset(void){
select_chip();
write_command(0xff);
wait_ready();
deselect_chip();
}
void nand_init(void){
NFCONF =0;
NFCONT = (1<<4) | (1<<1) | (1<<0);
nand_reset();
}
/*
nand flash 的读操作是以页为单位的。
des: nand flash中读出的数据放到哪
start_addr: 从哪里开始读
size: 读多大
*/
void nand_read(unsigned char *des,unsigned int start_addr,unsigned int size){
unsigned int col = start_addr & 2048;
select_chip();
unsigned int start = start_addr;
unsigned int end = start_addr + size;
while(start < end){ //每读一页需要发一次命令
write_command(0x00);
write_address(start);
write_command(0x30);
wait_ready();
while((col<2048) && (start<end)){ //在一页中读,我用的型号一页大小为2K
*des = read_one_data();
des++;
col++;
start++;
}