S3C2440内部集成了一个Nand flash控制器

来源:互联网 发布:bim软件分类 编辑:程序博客网 时间:2024/06/01 13:13

S3C2440内部集成了一个Nand flash控制器。S3C2440的Nand flash控制器包含了如下的特性:

l        一个引导启动单元

l        Nand Flash存储器接口,支持8位或16位的每页大小为256字,512字节,1K字和2K字节的Nand flash

l        软件模式:用户可以直接访问Nand Flash存储器,此特性可以用于Nand Flash存储器的读、擦除和编程。

l        S3C2440支持8/16位的Nand Flash存储器接口总线

l        硬件ECC生成,检测和指示(软件纠错)。

l        Steppingstone接口,支持大/小端模式的按字节/半字/字访问。

我用的开发板是天嵌的TQ2440,板子用到的Nand Flash是Samsung公司的K9F2G08U0A,它是8位的Nand flash。本文只介绍Nand Flash的电路原理和Nand Flash的读、写、擦除等基本操作,暂不涉及Nand Flash启动程序的问题。

Nand Flash的电路连接如图 1所示:

 

图 1 Nand Flash电路原理

    上图的左边为K9F2G08U0A与2440的连接图,原理方面就不多介绍,去看看datasheet估计就懂得了,右边的部分是S3C2440的Nand控制器的配置。配置引脚NCON,GPG13,GPG14和GPG15用来设置Nand Flash的基本信息,Nand控制器通过读取配置引脚的状态获取外接的Nand Flash的配置信息,图 2是这四个配置引脚的定义:

 

图 2 Nand控制配置引脚信息

       由于K9F2G08U0A的总线宽度为8位,页大小为2048字节,需要5个寻址命令,所以NCON、GPG13和GPG14应该接高电平,GPG15应该接低电平。

K9F2G08U0A没有地址或数据总线,只有8个IO口,这8个IO口用于传输命令、地址和数据。K9F2G08U0A主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,main区用于正常数据的存储,spare区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。K9F2G08U0A的存储阵列如图 3所示:

 

图 3 K9F2G08U0A内部存储阵列

由上图,我们可以知道:K9F2G08U0A的一页为(2K+64)字节(2K表示的是main区容量, 64表示的是spare区容量),它的一块为64页,而整个设备包括了2048个块。这样算下来一共有2112M位容量,如果只算main区容量则有256M字节(即256M×8位)。

 

图 4 K9F2G08U0A地址序列

要实现用8个IO口来要访问这么大的容量,如图 4所示:K9F2G08U0A规定了用5个周期来实现。第一个周期访问的地址为A0~A7;第二个周期访问的地址为A8~A11,它作用在IO0~IO3上,而此时IO4~IO7必须为低电平;第三个周期访问的地址为A12~A19;第四个周期访问的地址为A20~A27;第五个周期访问的地址为A28,它作用在IO0上,而此时IO1~IO7必须为低电平。前两个周期传输的是列地址,后三个周期传输的是行地址。通过分析可知,列地址是用于寻址页内空间,行地址用于寻址页,如果要直接访问块,则需要从地址A18开始。由于所有的命令、地址和数据全部从8位IO口传输,所以Nand flash定义了一个命令集来完成各种操作。有的操作只需要一个命令(即一个周期)即可,而有的操作则需要两个命令(即两个周期)来实现。K9F2G08U0A的命令说明如图 5所示:

 

图 5 K9F2G08U0A命令表

为了方便使用,我们宏定义了K9F2G08U0A的常用命令

#define CMD_READ1                0x00              //页读命令周期1

#define CMD_READ2                0x30              //页读命令周期2

#define CMD_READID               0x90              //读ID命令

#define CMD_WRITE1               0x80              //页写命令周期1

#define CMD_WRITE2               0x10              //页写命令周期2

#define CMD_ERASE1               0x60              //块擦除命令周期1

#define CMD_ERASE2               0xd0              //块擦除命令周期2

#define CMD_STATUS               0x70              //读状态命令

#define CMD_RESET                0xff               //复位

#define CMD_RANDOMREAD1          0x05       //随意读命令周期1

#define CMD_RANDOMREAD2          0xE0       //随意读命令周期2

#define CMD_RANDOMWRITE          0x85       //随意写命令

接下来介绍几个Nand Flash控制器的寄存器。Nand Flash控制器的寄存器主要有NFCONF(Nand Flash配置寄存器),NFCONT(Nand Flash控制寄存器),NFCMMD(Nand Flash命令集寄存器),NFADDR(Nand Flash地址集寄存器),NFDATA(Nand Flash数据寄存器),NFMECCD0/1(Nand Flash的main区ECC寄存器),NFSECCD(Nand Flash的spare区ECC寄存器),NFSTAT(Nand Flash操作状态寄存器),NFESTAT0/1(Nand Flash的ECC状态寄存器),NFMECC0/1(Nand Flash用于数据的ECC寄存器),以及NFSECC(Nand Flash用于IO的ECC寄存器)。

       (1)NFCONF:2440的NFCONF寄存器是用来设置NAND Flash的时序参数TACLS、TWRPH0、TWRPH1。配置寄存器的[3:0]是只读位,用来指示外部所接的Nand Flash的配置信息,它们是由配置引脚NCON,GPG13,GPG14和GPG15所决定的(比如说K9F2G08U0A的配置为NCON、GPG13和GPG14接高电平,GPG15接低电平,所以[3:0]位状态应该是1110)。

(2)NFCONT:用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定NAND Flash。

(3)NFCMMD:对于不同型号的Flash,操作命令一般不一样。参考前面介绍的K9F2G08U0A命令序列。

(4)NFADDR:当写这个寄存器时,它将对Flash发出地址信号。只用到低8位来传输,所以需要分次来写入一个完整的32位地址,K9F2G08U0A的地址序列在图4已经做了详细说明。

(5)NFDATA:只用到低8位,读、写此寄存器将启动对NAND Flash的读数据、写数据操作。

(6)NFSTAT:只用到位0,用来检测NAND是否准备好。0:busy,1:ready。

NFCONF寄存器使用TACLS、TWRPH0、TWRPH1这3个参数来控制NAND Flash信号线CLE/ALE与写控制信号nWE的时序关系,它们之间的关系如图6和图7所示:

 

图6 CLE/ALE时序图

 

图7 nWE和nRE时序图

 TACLS为CLE/ALE有效到nWE有效之间的持续时间,TWRPH0为nWE的有效持续时间,TWRPH1为nWE无效到CLE/ALE无效之间的持续时间,这些时间都是以HCLK为单位的。通过查阅K9F2G08U0A的数据手册,我们可以找到并计算与S3C2440相对应的时序:K9F2G08U0A中的Twp与TWRPH0相对应,Tclh与TWRPH1相对应, TACLS应该是与Tcls相对应。K9F2G08U0A给出的都是最小时间, 2440只要满足它的最小时间即可。TACLS、TWRPH0、TWRPH1这三个变量取值大一些会更保险,在这里,这三个值分别取1,2和0。

下面就开始详细介绍K9F2G08U0A的基本操作,包括复位,读ID,页读、写数据,随意读、写数据,块擦除等。

为了更好地应用ECC和使能Nand Flash片选,我们还需要一些宏定义:

#define NF_nFCE_L()        {rNFCONT &= ~(1<<1); }

#define NF_CE_L()          NF_nFCE_L()    //打开nandflash片选

#define NF_nFCE_H()        {rNFCONT |= (1<<1); }

#define NF_CE_H()          NF_nFCE_H()        //关闭nandflash片选

#define NF_RSTECC()        {rNFCONT |= (1<<4); }   //复位ECC

#define NF_MECC_UnLock()   {rNFCONT &= ~(1<<5); }  //解锁main区ECC

#define NF_MECC_Lock()     {rNFCONT |= (1<<5); }   //锁定main区ECC

#define NF_SECC_UnLock()   {rNFCONT &= ~(1<<6); }   //解锁spare区ECC

#define NF_SECC_Lock()     {rNFCONT |= (1<<6); }   //锁定spare区ECC

NFSTAT是另一个比较重要的寄存器,它的第0位可以用于判断nandflash是否在忙,第2位用于检测RnB引脚信号:

#define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));}  //等待Nand Flash不忙

#define NF_CLEAR_RB()  {rNFSTAT |= (1<<2); }   //清除RnB信号

#define NF_DETECT_RB()   {while(!(rNFSTAT&(1<<2)));}  

//等待RnB信号变高,即不忙

NFCMMD,NFADDR和NFDATA分别用于传输命令,地址和数据,为了方便起见,我们可以定义一些宏定义用于完成上述操作: 

#define NF_CMD(data)       {rNFCMD  = (data); }       //传输命令

#define NF_ADDR(addr)      {rNFADDR = (addr); }       //传输地址

#define NF_RDDATA()         rNFDATA)                  //读32位数据

#define NF_RDDATA8()       (rNFDATA8)                 //读8位数据

#define NF_WRDATA(data)    {rNFDATA = (data); }       //写32位数据

#define NF_WRDATA8(data)    {rNFDATA8 = (data); }     //写8位数据

首先,是初始化操作

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); //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器

rNF_Reset();//复位芯片

}

复位操作,写入复位命令

static void rNF_Reset()

{

NF_CE_L();                   //打开nandflash片选

NF_CLEAR_RB();               //清除RnB信号

NF_CMD(CMD_RESET);           //写入复位命令

NF_DETECT_RB();              //等待RnB信号变高,即不忙

NF_CE_H();                   //关闭nandflash片选

}

读取K9F2G08U0A芯片ID的操作如下:时序图在datasheet的figure18。首先需要写入读ID命令(0x90),然后再写入0x00地址,并等待芯片就绪,就可以读取到一共五个周期的芯片ID,第一个周期为厂商ID,第二个周期为设备ID,第三个周期至第五个周期包括了一些具体的该芯片信息,函数如下

static char rNF_ReadID()

{

       char pMID;

       char pDID;

       char cyc3, cyc4, cyc5;

 

       NF_nFCE_L();              //打开nandflash片选

       NF_CLEAR_RB();            //清RnB信号

       NF_CMD(CMD_READID);       //读ID命令

       NF_ADDR(0x0);             //写0x00地址

       for ( i = 0; i < 100; i++ );等一段时间

       //读五个周期的ID

       pMID = NF_RDDATA8();      //厂商ID:0xEC

       pDID = NF_RDDATA8();      //设备ID:0xDA

       cyc3 = NF_RDDATA8();      //0x10

       cyc4 = NF_RDDATA8();      //0x95

       cyc5 = NF_RDDATA8();      //0x44

       NF_nFCE_H();              //关闭nandflash片选

       return (pDID);

}

下面介绍Nand Flash读操作,读操作是以页为单位进行的。如果在读取数据的过程中不进行ECC校验判断,则读操作比较简单,在写入读命令的两个周期之间写入要读取的页地址,然后读取数据即可。如果为了更准确地读取数据,则在读取完数据之后还要进行ECC校验判断,以确定所读取的数据是否正确。

在上文中已经介绍过,Nand Flash的每一页有两区:main区和spare区,main区用于存储正常的数据,spare区用于存储其他附加信息,其中就包括ECC校验码。当我们在写入数据的时候,我们就计算这一页数据的ECC校验码,然后把校验码存储到spare区的特定位置中,在下次读取这一页数据的时候,同样我们也计算ECC校验码,然后与spare区中的ECC校验码比较,如果一致则说明读取的数据正确,如果不一致则不正确。ECC的算法较为复杂,好在S3C2440能够硬件产生ECC校验码,这样就省去了不少的麻烦事。S3C2440既可以产生main区的ECC校验码,也可以产生spare区的ECC校验码。因为K9F2G08U0A是8位IO口,因此S3C2440共产生4个字节的main区ECC码和2个字节的spare区ECC码。在这里我们规定,在每一页的spare区的第0个地址到第3个地址存储main区ECC,第4个地址和第5个地址存储spare区ECC。

产生ECC校验码的过程为:在读取或写入哪个区的数据之前,先解锁该区的ECC,以便产生该区的ECC。在读取或写入完数据之后,再锁定该区的ECC,这样系统就会把产生的ECC码保存到相应的寄存器中。main区的ECC保存到NFMECC0/1中(因为K9F2G08U0A是8位IO口,因此这里只用到了NFMECC0),spare区的ECC保存到NFSECC中。对于读操作来说,我们还要继续读取spare区的相应地址内容,以得到上次写操作时所存储的main区和spare区的ECC,并把这些数据分别放入NFMECCD0/1和NFSECCD的相应位置中。最后我们就可以通过读取NFESTAT0/1(因为K9F2G08U0A是8位IO口,因此这里只用到了NFESTAT0)中的低4位来判断读取的数据是否正确,其中第0位和第1位为main区指示错误,第2位和第3位为spare区指示错误。

下面是一段具体的页读操作程序:

U8 rNF_ReadPage( U32 page_number )

{

    U32 i, mecc0, secc;

 

NF_RSTECC();               //复位ECC

NF_MECC_UnLock();          //解锁main区ECC

 

    NF_nFCE_L();//使能芯片 

    NF_CLEAR_RB();//清除RnB

 

NF_CMD(CMD_READ1);          //页读命令周期1,0x00

    //写入5个地址周期

    NF_ADDR(0x00);                  //列地址A0-A7

    NF_ADDR(0x00);                  //列地址A8-A11

    NF_ADDR((addr) & 0xff);           //行地址A12-A19

    NF_ADDR((addr >> 8) & 0xff);    //行地址A20-A27

    NF_ADDR((addr >> 16) & 0xff);   //行地址A28

 

NF_CMD(CMD_READ2);        //页读命令周期2,0x30

 

    NF_DETECT_RB();       ////等待RnB信号变高,即不忙

 

    for (i = 0; i < 2048; i++)

    {

       buf[i] =  NF_RDDATA8();//读取一页数据内容

    }

 

NF_MECC_Lock();          //锁定main区ECC值

NF_SECC_UnLock();        //解锁spare区ECC

 

mecc0=NF_RDDATA();   //读spare区的前4个地址内容,即第2048~2051地址,这4个字节为main区的ECC

   //把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内

rNFMECCD0=((mecc0&0xff00)<<8)|(mecc0&0xff);

rNFMECCD1=((mecc0&0xff000000)>>8)|((mecc0&0xff0000)>>16);

 

NF_SECC_Lock();       //锁定spare区的ECC值

 

secc=NF_RDDATA();   //继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值

       //把读取到的spare区的ECC校验码放入NFSECCD的相应位置内

rNFSECCD=((secc&0xff00)<<8)|(secc&0xff);

    NF_nFCE_H();   //关闭nandflash片选

 

    //判断所读取到的数据是否正确

if ((rNFESTAT0&0xf) == 0x0)

return 0x66;                  //正确

else

return 0x44;                  //错误

}

这段程序是把某一页的内容读取到全局变量数组buffer中。该程序的输入参数直接就为K9F2G08U0A的第几页,例如我们要读取第128064页中的内容,可以调用该程序为:rNF_ReadPage(128064)。由于第128064页是第2001块中的第0页(128064=2001×64+0),所以为了更清楚地表示页与块之间的关系,也可以写为:rNF_ReadPage(2001*64)。

页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据,当然如果为了保证下次读取该数据时的正确性,还需要把main区的ECC值和spare区的ECC值写入到该页的spare区内。然后我们还需要读取状态寄存器,以判断这次写操作是否正确。下面就给出一段具体的页写操作程序,其中输入参数也是要写入数据到第几页:

U8 rNF_WritePage(U32 page_number)

{

U32 i, mecc0, secc;

U8 stat, temp;

temp = rNF_IsBadBlock(page_number>>6);     //判断该块是否为坏块

if(temp == 0x33)

return 0x42;       //是坏块,返回

NF_RSTECC();          //复位ECC

NF_MECC_UnLock();     //解锁main区的ECC

NF_nFCE_L();          //打开nandflash片选

NF_CLEAR_RB();        //清RnB信号

 

NF_CMD(CMD_WRITE1);           //页写命令周期1

 //写入5个地址周期

NF_ADDR(0x00);           //列地址A0~A7

NF_ADDR(0x00);           //列地址A8~A11

NF_ADDR((page_number) & 0xff);         //行地址A12~A19

NF_ADDR((page_number >> 8) & 0xff);    //行地址A20~A27

NF_ADDR((page_number >> 16) & 0xff);  //行地址A28

 

for (i = 0; i < 2048; i++)//写入一页数据

{

NF_WRDATA8((char)(i+6));

}

NF_MECC_Lock();    //锁定main区的ECC值

mecc0=rNFMECC0;    //读取main区的ECC校验码

//把ECC校验码由字型转换为字节型,并保存到全局变量数组ECCBuf中

ECCBuf[0]=(U8)(mecc0&0xff);

ECCBuf[1]=(U8)((mecc0>>8) & 0xff);

ECCBuf[2]=(U8)((mecc0>>16) & 0xff);

ECCBuf[3]=(U8)((mecc0>>24) & 0xff);

NF_SECC_UnLock();                  //解锁spare区的ECC

//把main区的ECC值写入到spare区的前4个字节地址内,即第2048~2051地址

for(i=0;i<4;i++)

{

NF_WRDATA8(ECCBuf[i]);

}

NF_SECC_Lock();                      //锁定spare区的ECC值

secc=rNFSECC;                   //读取spare区的ECC校验码

//把ECC校验码保存到全局变量数组ECCBuf中

ECCBuf[4]=(U8)(secc&0xff);

ECCBuf[5]=(U8)((secc>>8) & 0xff);

//把spare区的ECC值继续写入到spare区的第2052~2053地址内

for(i=4;i<6;i++)

{

NF_WRDATA8(ECCBuf[i]);

}

    NF_CMD(CMD_WRITE2);          //页写命令周期2

    delay(1000);                //延时一段时间,以等待写操作完成

NF_CMD(CMD_STATUS);          //读状态命令

//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

do{

stat = NF_RDDATA8();

}while(!(stat&0x40));

NF_nFCE_H();                    //关闭Nand Flash片选

//判断状态值的第0位是否为0,为0则写操作正确,否则错误

if (stat & 0x1)

{

temp = rNF_MarkBadBlock(page_number>>6);//标注该页所在的块为坏块

if (temp == 0x21)

return 0x43            //标注坏块失败

else

return 0x44;           //写操作失败

}

else

return 0x66;                  //写操作成功

}

该段程序先判断该页所在的坏是否为坏块,如果是则退出。在最后写操作失败后,还要标注该页所在的块为坏块,其中所用到的函数rNF_IsBadBlock和rNF_MarkBadBlock,我们在后面介绍。我们再总结一下该程序所返回数值的含义,0x42:表示该页所在的块为坏块;0x43:表示写操作失败,并且在标注该页所在的块为坏块时也失败;0x44:表示写操作失败,但是标注坏块成功;0x66:写操作成功。

擦除是以块为单位进行的,因此在写地址周期是,只需写三个行周期,并且要从A18开始写起。与写操作一样,在擦除结束前还要判断是否擦除操作成功,另外同样也存在需要判断是否为坏块以及要标注坏块的问题。下面就给出一段具体的块擦除操作程序:

U8 rNF_EraseBlock(U32 block_number)

{

char stat, temp;

 

temp = rNF_IsBadBlock(block_number);     //判断该块是否为坏块

if(temp == 0x33)

return 0x42;           //是坏块,返回

NF_nFCE_L();             //打开片选

NF_CLEAR_RB();        //清RnB信号

 

NF_CMD(CMD_ERASE1);    //擦除命令周期1

    //写入3个地址周期,从A18开始写起

NF_ADDR((block_number << 6) & 0xff);         //行地址A18~A19

NF_ADDR((block_number >> 2) & 0xff);         //行地址A20~A27

NF_ADDR((block_number >> 10) & 0xff);        //行地址A28

    NF_CMD(CMD_ERASE2);         //擦除命令周期2

    delay(1000);          //延时一段时间

    NF_CMD(CMD_STATUS);          //读状态命令

   //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

do{

       stat = NF_RDDATA8();

}while(!(stat&0x40));

NF_nFCE_H();             //关闭Nand Flash片选

 

//判断状态值的第0位是否为0,为0则擦除操作正确,否则错误

if (stat & 0x1)

{

       temp = rNF_MarkBadBlock(page_number>>6);    //标注该块为坏块

       if (temp == 0x21)

              return 0x43            //标注坏块失败

       else

              return 0x44;           //擦除操作失败

}

else

return 0x66;                  //擦除操作成功

}

该程序的输入参数为K9F2G08U0A的第几块,例如我们要擦除第2001块,则调用该函数为:rNF_EraseBlock(2001)。

K9F2G08U0A除了提供了页读和页写功能外,还提供了页内地址随意读、写功能。页读和页写是从页的首地址开始读、写,而随意读、写实现了在一页范围内任意地址的读、写。随意读操作是在页读操作后输入随意读命令和页内列地址,这样就可以读取到列地址所指定地址的数据。随意写操作是在页写操作的第二个页写命令周期前,输入随意写命令和页内列地址,以及要写入的数据,这样就可以把数据写入到列地址所指定的地址内。下面两段程序实现了随意读和随意写功能,其中随意读程序的输入参数分别为页地址和页内地址,输出参数为所读取到的数据,随意写程序的输入参数分别为页地址,页内地址,以及要写入的数据。

U8 rNF_RamdomRead(U32 page_number, U32 add)

{

NF_nFCE_L();                    //打开Nand Flash片选

NF_CLEAR_RB();               //清RnB信号

NF_CMD(CMD_READ1);           //页读命令周期1

//写入5个地址周期

NF_ADDR(0x00);           //列地址A0~A7

NF_ADDR(0x00);       //列地址A8~A11

NF_ADDR((page_number) & 0xff);     //行地址A12~A19

NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27

NF_ADDR((page_number >> 16) & 0xff);         //行地址A28

 

NF_CMD(CMD_READ2);          //页读命令周期2

NF_DETECT_RB();                    //等待RnB信号变高,即不忙

NF_CMD(CMD_RANDOMREAD1);   //随意读命令周期1

//页内地址

NF_ADDR((char)(add&0xff));                  //列地址A0~A7

NF_ADDR((char)((add>>8)&0x0f));           //列地址A8~A11

NF_CMD(CMD_RANDOMREAD2);            //随意读命令周期2

return NF_RDDATA8();               //读取数据

}

U8 rNF_RamdomWrite(U32 page_number, U32 add, U8 dat)

{

U8 temp,stat;

NF_nFCE_L();                    //打开Nand Flash片选

NF_CLEAR_RB();               //清RnB信号

NF_CMD(CMD_WRITE1);                //页写命令周期1

//写入5个地址周期

NF_ADDR(0x00);                                     //列地址A0~A7

NF_ADDR(0x00);                                     //列地址A8~A11

NF_ADDR((page_number) & 0xff);           //行地址A12~A19

NF_ADDR((page_number >> 8) & 0xff);    //行地址A20~A27

NF_ADDR((page_number >> 16) & 0xff);  //行地址A28

NF_CMD(CMD_RANDOMWRITE);                 //随意写命令

//页内地址

NF_ADDR((char)(add&0xff));                   //列地址A0~A7

NF_ADDR((char)((add>>8)&0x0f));          //列地址A8~A11

 

NF_WRDATA8(dat);                          //写入数据

NF_CMD(CMD_WRITE2);                //页写命令周期2 

delay(1000);                 //延时一段时间 

NF_CMD(CMD_STATUS);      //读状态命令

//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

do{

       stat =  NF_RDDATA8();

}while(!(stat&0x40));

NF_nFCE_H();                    //关闭Nand Flash片选

 //判断状态值的第0位是否为0,为0则写操作正确,否则错误

if (stat & 0x1)

return 0x44;                  //失败

else

return 0x66;                  //成功

}

下面介绍上文中提到的判断坏块以及标注坏块的那两个程序:rNF_IsBadBlock和rNF_MarkBadBlock。在这里,我们定义在spare区的第6个地址(即每页的第2054地址)用来标注坏块,0x44表示该块为坏块。要判断坏块时,利用随意读命令来读取2054地址的内容是否为0x44,要标注坏块时,利用随意写命令来向2054地址写0x33。下面就给出这两个程序,它们的输入参数都为块地址,也就是即使仅仅一页出现问题,我们也标注整个块为坏块。

U8 rNF_IsBadBlock(U32 block)

{

       return rNF_RamdomRead(block*64, 2054);

}

U8 rNF_MarkBadBlock(U32 block)

{

U8 result;

result = rNF_RamdomWrite(block*64, 2054, 0x33);

    if(result == 0x44)

return 0x21;                  //写坏块标注失败

else

return 0x60;                  //写坏块标注成功

}

关于Nand Flash的基本操作就介绍到这吧

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ipad永久停用了怎么办 ipad停用1小时怎么办 ipad停用无法关机怎么办 citizen手表坏了怎么办 服务器磁盘满了怎么办 百度地图定位不准怎么办 宝宝准生证丢了怎么办 fgo刷不到材料怎么办 行者轨迹未上传怎么办? 房贷少了几块钱怎么办 房贷少了2块怎么办 天天在我们家蹭饭怎么办? 亲戚天天来吃饭怎么办 cad找不到图了怎么办 新建没有工作表怎么办 Html打开是乱码怎么办 xp只读取消不了怎么办 手机地图信号弱怎么办 苹果定位不准确怎么办 苹果导航卡死怎么办 没有上传的发票怎么办 发票显示未上传怎么办 发票未上传怎么办 影响 公务员国考补录不合格的怎么办 车辆转入退档怎么办 校园网总是断网怎么办 校园网经常断网怎么办 身份证号姓名电话号码泄露怎么办 中国邮政银行短信通知怎么办 中通递送不到怎么办 智齿碎了一半怎么办 驾照扣50分怎么办 办不了租赁合同怎么办 收到佰仟律师函怎么办 佰仟律师打电话怎么办 右转车道右方路禁行怎么办 收费站没带现金怎么办 过收费站没有现金怎么办 高速收费站没有现金怎么办 过收费站没现金怎么办 查社保忘记密码怎么办