NandFlash操作详解(二)

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操作详解(三)





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值