Micro2440 Nand Flash存储操作之写

来源:互联网 发布:java main执行 编辑:程序博客网 时间:2024/06/07 23:31

页写操作的大致流程为:在两个写命令周期之间分别写入页地址和数据,当然如果为了保证下次读取该数据时的正确性,还需要把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(); //关闭nandflash片选

//判断状态值的第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(); //关闭nandflash片选

//判断状态值的第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(); //打开nandflash片选
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(); //打开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

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(); //关闭nandflash片选

//判断状态值的第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; //写坏块标注成功
}

关于nandflash的基本操作就讲解到这里,当然nandflash还有一些其他复杂的操作,如逻辑地址与物理地址的转换,坏块的替代等,这些内容本文就不再介绍了