NandFlash读操作:
NandFlash的读取分为页读和随机读。页读每次读取一个page,从page的第一个数据开始读。其实也就是列号(偏移地址)为0,只提供页地址。
随机读能读取到一个page里面的某个存储单元,但是需要提供行地址和列地址。
页读和随机读的区别只是在于是否提供列号(偏移地址)。
打开NandFlash的芯片手册,找到读时序图:

根据时序图可以看出,NandFlash的读取先要发送00h的命令,然后发送地址(两个周期的列地址,三个周期的行地址),发送30h命令,发送完0x30后,R/B信号会呈现忙的状态,就是说开始处理之前请求的这些命令去了。所以在R/B信号为低时要等待R/B出现高电平。
然后就可以读取数据了。在以上这些步骤之前呢,还需要先选择片选和对R/B信号做清除,然后在最后读取完数据后还要取消选择NandFlash芯片。
在2440手册中可以看到这个寄存器,它便是控制片选的:
接着是清除R/B信号,当RnB信号也就是R/B信号从低跳变成高的时候,我们就认为前面的指令正确执行可以获取数据了,而当RnB从低跳变到高时,下面这个寄存器中的[2]位会自动置位,而我们的清除工作也是对这一位操作:需要注意这个寄存器是8位的,所以定义地址时要写成char*
然后向下面这个寄存器中写入命令:这也是一个8位寄存器

向下面这个寄存器中发送地址(8位寄存器):

可以在时序图中清晰的看到需要发送两次列地址,如果是页读的访问方式,列地址便无效了,列地址直接写0x00。随机访问方式的话需要写入两次列地址信息。发送地址信息比较容易晕头,举例说明:
写入5次地址,前两次为列地址(页内偏移),后三次为行地址(相当于页号)。
如写入十进制地址8000,则需要写入的页号为:8000/2048(每页大小)=3.9页,所以写入第3页,在第三页业内偏移为:8000-3*2048=1856。
程序表示为:
void nand_addr(unsigned char addr)
{
NFADDR = addr;
}
void nand_send_addr(unsigned int addr)
{
unsigned int page = addr / 2048;
unsigned int colunm = addr & (2048 - 1);//这一句和colunm =addr%2048等效
/* 这两个地址表示从页内哪里开始 */
nand_addr(colunm & 0xff);
nand_addr((colunm >> 8) & 0xff);
/* 下面三个地址表示哪一页 */
nand_addr(page & 0xff);
nand_addr((page >> 8) & 0xff);
nand_addr((page >> 16) & 0xff);
}
或者:
不要以为跟着手册的写入顺序应该这样写:
nand_addr(addr & 0xff); /* a0~a7 */
nand_addr((addr >> 8) & 0x1f); /* 程序的角度: a8~a11*/
nand_addr((addr >> 12) & 0xff); /* 程序的角度:a12~a19 */
nand_addr((addr >> 20) & 0xff); /* 程序的角度:a20~a27*/
nand_addr((addr >> 28) & 0xff); /* 程序的角度:a28 */
其实应该这样写:
nand_addr(addr & 0xff); /* a0~a7 */
nand_addr((addr >> 8) & 0x7); /* 程序的角度: a8~a11 */
a0-a7共8位加上a8-a10三位总共11位,2的11次方就是一页2048的大小!如果1页为4096字节,传送的两次列地址可能就是a0-a7,a8-a11了(没使用过每page4096字节的NandFlash)。
nand_addr((addr >> 11) & 0xff); /* 程序的角度:a12~a19 */
nand_addr((addr >> 19) & 0xff); /* 程序的角度:a20~a27 */
nand_addr((addr >> 27) & 0xff); /* 程序的角度: a28 */
|
从时序图中也可以看出,发送了0x30命令后便需要去等待RnB信号由低向高的跳变。而检测的寄存器也是前面做清除的那个寄存器NFSTAT,也是第[2]位。
等待完成后便可以从NFDATA寄存器中读取到数据了。NFDATA也是一个8位寄存器。

如果想要连续读取某个地址后连续的n个字节,只需要反复读取NFDATA寄存器即可。
连续读取1000个字节:
for(i=0;i<1000;i++)
{
buff[i] = NFDATA;
}
|
取得数据后,取消片选,整个读取功能便实现了
总结一下NandFlash的读操作步骤:
1、 选中nandflash芯片
2、 清除RnB
3、 发送命令0x00
4、 发送列地址
5、 发送行地址
6、 发送命令0x30
7、 等待RnB
8、 读取数据
9、 取消选中nandflash芯片
代码实现(读取一页):
void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;
int col = addr % 2048;
int page = addr / 2048;
//选中nandflash芯片
select_chip();
//清除RnB
clear_RnB();
//发送命令0x00
send_cmd(0x00);
send_addr(col & 0xff);
send_addr((col >> 8) & 0xff);
send_addr(page & 0xff);
send_addr((page >> 8) & 0xff);
send_addr((page >> 16) & 0xff);
//发送命令0x30
send_cmd(0x30);
//等待RnB
wait_RnB();
//读取数据
for(i=0;i<2048;i++)
{
buff[i] = NFDATA;
}
//取消选中nandflash芯片
deselect_chip();
}
|
NandFlash写操作:
NandFlash的写操作之前需要先对NandFlash进行擦除工作,于是手册中找到擦除时序:
可以在NandFlash手册的Features中看到,NandFlash的擦除是按照页所在的块一整块来擦除的。根据时序可以得到擦除的步骤为:
1、 选中flash芯片
2、 清除RnB
3、 发送命令0x60
4、 发送行地址
5、 发送命令D0
6、 等待RnB
7、 发送命令0x70
8、 读取擦除结果
9、 取消选中flash芯片
代码实现:
int NF_Erase(unsigned long addr)
{
int ret;
unsigned int page = addr/2048;
//选中flash芯片
select_chip();
//清除RnB
clear_RnB();
//发送命令0x60
send_cmd(0x60);
//发送行地址
send_addr(page&0xff);
send_addr((page>>8)&0xff);
send_addr((page>>16)&0xff);
//发送命令D0
send_cmd(0xD0);
//等待RnB
wait_RnB();
//发送命令0x70
send_cmd(0x70);
//读取擦除结果
ret = NFDATA;
//取消选中flash芯片
deselect_chip();
return ret;
}
|
擦除后就可以执行写操作,写入的时序为:
根据时序图,我们可以得到写入数据的流程:
1、 选中Flash芯片(NandFlash芯片操作都需要先片选)
2、 清除RnB(既然要查看RnB的跳变自然要先清除)
3、 发送命令0x80
4、 发送2个列地址
5、 发送3个行地址
6、 发送数据(往数据寄存器中写),跟读操作一样,需要连续写的话直接重复往NFDATA写入就行,不需要其他操作。
7、 发送命令0x10
8、 等待RnB
9、 发送命令0x70
10、 读取写入结果(在数据寄存器中读,没错,发送数据也是往数据寄存器中写的)
11、 关闭片选(NandFlash操作都需要先选择Flash在关闭Flash)
代码实现:
int NF_WritePage(unsigned long addr,unsigned char *buff)
{
unsigned int i,ret = 0,page,col;
col = addr % 2048;
page = addr /2048;
//选中nandflash
select_chip();
//清除RnB
clear_RnB();
//发送0x80命令
send_cmd(0x80);
//发送2个列地址
/* 这种写法ok
send_addr(col & 0xff);
send_addr((col >> 8) & 0xff);
send_addr(page & 0xff);
send_addr((page >> 8) & 0xff);
send_addr((page >> 16) & 0xff);
*/
send_addr(addr & 0xff); // a0~a7
send_addr((addr >> 8) & 0x7); // 以程序角度看 a8~a10
send_addr((addr >> 11) & 0xff); // 以程序角度看 a12~a19
send_addr((addr >> 19) & 0xff); // 以程序角度看 a20~a27
send_addr((addr >> 27) & 0xff); // 以程序角度看 a28
/* This’s wrong 移位错误
send_addr(addr & 0xff); // a0~a7
send_addr((addr >> 8) & 0x7); // 以程序角度看 a8~a10
send_addr((addr >> 12) & 0xff); // 以程序角度看 a12~a19
send_addr((addr >> 20) & 0xff); // 以程序角度看 a20~a27
send_addr((addr >> 28) & 0xff); // 以程序角度看 a28
*/
//发送数据
for(i=0;i<2048;i++)
{
NFDATA = buff[i];
}
//发送0x10命令
send_cmd(0x10);
//等待RnB
wait_RnB();
//发送0x70命令
send_cmd(0x70);
//读取写入结果
ret = NFDATA;
//关闭nandflash
deselect_chip();
return ret;
}
|
当然还可以对NandFlash进行读取ID的操作,这个工作根据时序图仿造读写操作就可以实现了,在裸机中用得比较少我就不写了。
下一节NandFlash操作详解(三)