TQ2440nand flashi浅谈

关于nand flash的地址 A8,寻址

在NAND Flash中有8个I/O引脚(IO0—IO7)、5个全能信号(nWE ALE CLE nCE nRE)、一个引脚,1个写保护引脚。操作NAND Flash时,先传输命令,然后传输地址,最后读写数据。对于64MB的NAND Flash,需要一个26位的地址。只能8个I/O引脚充当地址、数据、命令的复用端口,所以每次传地址只能传8位。这样就需要4个地址序列。因此读写一次nand flash需要传送4次(A[7:0] A[16:9] A[24:17] A[25])。64M的NAND Flash的地址范围为0x00000000—0x03FFFFFF。128M的NAND Flash的地址范围为0x00000000---0x07FFFFFF。1KB = 0x000-0x3FF.128字节=0x00H--7FH。

一页有528个字节,而在前512B中存放着用户的数据。在后面的16字节中(OOB)中存放着执行命令后的状态信息。主要是ECC校验的标识。列地址A0-A7可以寻址的范围是256个字节,要寻址528字节的话,将一页分为了A.(1 half array)B(2 half array) C(spare array)。A区0—255字节,B区 256-511 字节C区512—527字节。访问某页时必须选定特定的区。这可以使地址指针指向特定的区实现。

在NAND Flash 中存在三类地址,分别为Block Address 、Column Address Page Address.。(实际就是块地址和页地址)

Column Address 用来选择是在上半页寻址还是在下半页寻址A[0]—A[7].也就相当于页内的偏移地址。在进行擦除时不需要列地址,因为擦除是以块为单位擦除。32个Page需要5bit来表示。也就是A[13:9];也就是页在块内的相对地址。A8这一位用来设置512字节的上半页,还是下半页,1表示是在上半页,而2表示是在下半页。Block的地址有A[25:14]组成.

一个容量为64M(512Mbit)的NAND Flash,分为131072页,528列。(实际中由于存在spare area,故都大于这个值),有4096块,需要12bit来表示即A[25:14].如果是128M(1Gbit)的话,blodk Address为A[26:14].由于地址只能在IO0—IO7上传送。编程时通常通过移位来实现地址的传送。传送过程如下:

第1个地址序列:传递column address,也就是NAND Flash[7:0],这一周期不需要移位即可传递到I/O[7:0]上,而half page pointer 即A8是由操作指令决定,00h,在A区,01h在B区,指令决定在哪个half page上进行读写,而真正A8的值是不需要程序员关心的;

第2个地址序列:就是将NAND_ADDR 右移9位,而不是8位,将NAND_ADDR[16:9]传递到I/O[7:0]上;

第3个地址序列:将NAND_ADDR[24:17] 传递到I/O[7:0]上;

第4个地址序列:将NAND_ADDR[25]传送到I/O上。

整个地址的传送过程需要4步才能完成。如果NAND Flash 的大小是32MB的以下的话,那么block address 最高位只到bit24,因此寻址只需要3步,就可以完成。

在进行擦除操作时由于是以块进行擦除,所以只需要3个地址序列,也就是只传递块的地址,即A[14:25]。

NAND Flash地址的计算:

Column Address 翻译过来是列地址,也就是在一页里的偏移地址。其实是指定Page上的某个Byte,指定这个Byte,其实也就是指定此页的读写起始地址。

Page Address:页地址。页的地址总是以512Bytes对齐的,所以它的低9位问题0,确定读写操作在NAND Flash中的哪个页进行。

当我们得到一个Nand Flash地址addr时,我们可以这样分解出Column Address和Page Address。

Columnaddr = addr % 512 // column address

Pageaddr = addr>>9 // page address

实际上A0~A7是页内地址,比如从第2个开始读起。不过一般都从0开始读起,呵呵。

也就是一个Nand Flash地址的A0-A7是它的column address ,A9—A25是它的Page Address,地址A8被忽略。

现在假设我要从Nand Flash中的第5000字节处开始读取1024个字节到内存的0x30000000处,我们这样调用read函数

NF_Read(5000, 0x30000000,1024);

我们来分析5000这个src_addr.

根据:

column_addr=src_addrQ2;

page_address=(src_addr>>9);

我们可得出column_addr=5000Q2=392

page_address=(5000>>9)=9

于是我们可以知道5000这个地址是在第9页的第392个字节处,于是我们的NF_read函数将这样发送命令和参数

column_addr=5000Q2;

page_address=(5000>>9);

NF_CMD=0x01; //要从2nd half开始读取 所以要发送命令0x01

NF_ADDR= column_addr &0xff; //1st Cycle A[7:0]发送从第几个字节开始读

NF_ADDR=page_address& 0xff//这是读第几页的意思

NF_ADDR=(page_address>>8)&0xff; //3rd.Cycle A[24:17]

NF_ADDR=(page_address>>16)&0xff; //4th.Cycle A[25]

还有一种方法其实跟上面的一样,不过比较好理解一点:

NF_ADDR= addr & 0xff

NF_ADDR= (addr>>9) & 0xff  (右移9位不是8为和上面的page_address一样的嘿嘿)

NF_ADDR= (addr>>17) & 0xff (和上面的page_address>>8对应9+8==17不是吗)

NF_ADDR= (addr >> 25) & 0xff

addr指的是地址执行addr & 0xff时他只会读8位。

下面以上面的0x5000这个地址说好了

0x5000换成二进制为0101,0000,0000,0000首先第一个地址序列读八位(因为我们nand flash是八位八位这样读的),也就是把0000,0000读进去了,接下来第二个地址序列也是读八位(其体读多少位要看nand flash这里是八位),这样就把0000,0101读进去了,也就是0xa0,后面那些都是0了(这个是nand flash k9F1208U0M的情况)

向NandFlash的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从rNFDATA寄存器(NandFlash数据寄存器)读取数据了.

我用下面的代码进行数据的读取.

for(i=column_addr;i<512;i++)

*buf++=NF_RDDATA();

每当读取完一个Page之后,数据指针会落在下一个Page的0号Column(0号Byte).

下面是一些核心代码:

 

 


#include <string.h>

#include "def.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "Nand.h"

 extern void RdNF2SDRAM();
//
//  Reset the chip
//
static void rNF_Reset()
{
 NF_CE_L();               //这些不是函数来的坑爹的发出片选信号
 NF_CLEAR_RB();            //一些检查用来后面检查是否就
 NF_CMD(CMD_RESET);       //#define CMD_RESET  0xff复位
 NF_DETECT_RB();  //#define NF_DETECT_RB() {while(!(rNFSTAT&(1<<2)))};RNFSTAT第三位等于0时
 NF_CE_H();      //关片选
}


static void rNF_Init(void)
{
 rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);//一些时序设置
 rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);//刚开始关片选
 rNFSTAT = 0;//是能禁止
 rNF_Reset();//设置吼了复位一下
}

static char rNF_ReadID()
{
 char pMID;
 char pDID;
 char nBuff;
 char n4thcycle;
 int i;


 NF_nFCE_L();// 发出片选信号  
 NF_CLEAR_RB();//一些检查用来后面检查是否就
 NF_CMD(CMD_READID); // read id command#define CMD_READID  0x90发出读信号
 NF_ADDR(0x0);//发出0就可以读到ID了
 for ( i = 0; i < 10; i++ );

 pMID = NF_RDDATA8();//读NFDATA这个寄存起就可以读到数据
 pDID = NF_RDDATA8();

 nBuff     = NF_RDDATA8();
 n4thcycle = NF_RDDATA8();
 NF_nFCE_H();//关片选


 return (pDID);
}

static void rSB_ReadPage(U32 addr, unsigned char * to)
{
 U32 i;

 rNF_Reset();

 //  Enable the chipr复位一下
 NF_nFCE_L();// 发出片选信号
 NF_CLEAR_RB();//一些检查用来后面检查是否就 //清除RnB信号

 // Issue Read command
 NF_CMD(CMD_READ);//#define CMD_READ 0x00

 //  Set up address发出读信号
 NF_ADDR(0x00);//发出地址从那个字节开始读
 NF_ADDR((addr) & 0xff);//从那一页开始这里是第0叶
 NF_ADDR((addr >> 8) & 0xff);
 NF_ADDR((addr >> 16) & 0xff);


 NF_DETECT_RB();  // wait tR(max 12us)是否就随了等待RnB信号变高,即不忙 


 for (i = 0; i < 512; i++)
 {
  to[i] =  NF_RDDATA8();// 终以开始读数据了嘿嘿
 }

 NF_nFCE_H();//关片选

}

static void rLB_ReadPage(U32 addr, unsigned char * to)
{
 U32 i;

 rNF_Reset();

 //  Enable the chip
 NF_nFCE_L();  
 NF_CLEAR_RB();

 // Issue Read command
 NF_CMD(CMD_READ);

 //  Set up address
 NF_ADDR(0x00);
 NF_ADDR(0x00);
 NF_ADDR((addr) & 0xff);
 NF_ADDR((addr >> 8) & 0xff);
 NF_ADDR((addr >> 16) & 0xff);

 NF_CMD(CMD_READ3);//#define CMD_READ3 0x30  Read3 发送读命令

 NF_DETECT_RB();  // wait tR(max 12us)

 for (i = 0; i < 2048; i++)
 {
  to[i] =  NF_RDDATA8();// 终以开始读数据了嘿嘿
 }

 NF_nFCE_H();

}


void RdNF2SDRAM( )//代码是从这里开始的
{
 U32 i;
 U32 start_addr = 0x0;
 unsigned char * to = (unsigned char *)0x30000000;
 U32 size = 0x100000;
 rNF_Init();
 switch(rNF_ReadID())//获得设备代码
 {
  case 0x76:
   for(i = (start_addr >> 9); size > 0; )//start_addr >> 9计算出在第几页相当于i/512,从这里不难看出他是先右移了9位的,然后才是8位,对应于上面的17位(第三的地址序列)
   {
    rSB_ReadPage(i, to);
    size -= 512;//因为每次是读一叶的所以每次减512
    to += 512;
    i ++;
   }
   break;
  case 0xf1:
  case 0xda:
  case 0xdc:
  case 0xd3:
   for(i = (start_addr >> 11); size > 0; )
   {
    rLB_ReadPage(i, to);
    size -= 2048;
    to += 2048;
    i ++;
   }
   break;
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值