以前有一篇博文分析过norflash,现在分析下nandflash。
Nand在驱动方面与nor有很大的不同,首先接口就不同,nor用的是emc接口,而nand用的是ecc接口,这样nand就复杂了很多,好在现在大部分的arm芯片都有自己的nand控制器,帮我们完成了许多的工作,下面以s<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />3c2410为例:
1. 读nand的ID
主要方法是先初始化ecc接口,然后写读ID的指令到指令寄存器,然后给ecc读ID的地址(0),然后等待指令响应完成,读出ID
U32 ReadChipId(void)
{
U32 id;
NFChipEn();
//bit11=0 NAND flash nFCE = L (active) 片选选中
WrNFCmd(RdIDCMD);
//rNFCMD = 0x90,Read ID Command 写读ID的指令到指令寄存器
WrNFAddr(0);
//rNFADDR = 0x00, 找到0x0地址
//whether nand flash is busy?
//rNFSTAT&1 = 1 ,busy
//rNFSTAT&1 = 0 ,no busy
NF_WAITRB(); //等到指令完成
id = RdNFDat()<<8;
// Maker code(K9S1208V:0xec) 这时候读数据寄存器,读出信息
id |= RdNFDat();
// Devide code(K9S1208V:0x76)
NFChipDs();
//bit11=1 NAND flash nFCE = H (inactive) 删除NFLASH
return id;
}
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
这里我们回顾一下nor,
int Strata_CheckID(int targetAddr)
{
//Add by HHTech
_WR(targetAddr, 0x0090);
return _RD(targetAddr); }
nor没有寄存器的概念,直接和总线打交道,先直接给总线上的地址0写入指令(指令都是写在地址0的),然后直接在数据总线上读出数据。
2.
一些宏函数定义:
#define EnNandFlash() (rNFCONF |= 0x8000)
//bit15=1 enable NAND flash controller
#define DsNandFlash() (rNFCONF &= ~0x8000)
//bit15=0 disable NAND flash controller
#define InitEcc() (rNFCONF |= 0x1000)
//bit12=1 initialize ECC
#define NoEcc() (rNFCONF &= ~0x1000)
//bit12=0 initialize ECC
#define NFChipEn() (rNFCONF &= ~0x800)
//bit11=0 NAND flash nFCE = L (active)
#define NFChipDs() (rNFCONF |= 0x800)
//bit11=1 NAND flash nFCE = H (inactive)
#define WrNFCmd(cmd) (rNFCMD = (cmd))
//write commond to nand flash
#define WrNFAddr(addr) (rNFADDR = (addr))
//write address to nand flash
#define WrNFDat(dat) (rNFDATA = (dat))
//write data to nand flash
#define RdNFDat() (rNFDATA)
//read data from nand flash
#define RdNFStat() (rNFSTAT)
//read status from nand flash
#define NFIsBusy() (!(rNFSTAT&1))
//whether nand flash is busy?
#define NFIsReady() (rNFSTAT&1)
//whether nand flash is ready?
#define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));}
3.Nand常用指令定义:(具体对照下面程序)
#define READCMD0 0
#define READCMD1 1
#define READCMD2 0x50
#define ERASECMD0 0x60
#define ERASECMD1 0xd0
#define PROGCMD0 0x80
#define PROGCMD1 0x10
#define NFSTATUS 0x70
#define RdIDCMD 0x90
4.以下是nand的其他驱动函数
/****************************************
**** K9F1208U0M nand flash 的状态 ****
****************************************/
static U16 ReadStatus(void) //道理同上
{
U16 stat;
NFChipEn();
WrNFCmd(NFSTATUS);//NFSTATUS = 0x70
stat = RdNFDat();
NFChipDs();
return stat;
}
/**********************************************
**** K9F1208U0M nand flash 的块擦除操作 ****
**********************************************/
U32 EraseBlock(U32 addr)
{
U8 status;
U32 i;
NFChipEn();
WrNFCmd(ERASECMD0);
//ERASECMD0 = 0x60,Erase one block 1st command 删除指令
WrNFAddr(NFBlcokAddr>>9&0xff);
//删除的扇区地址,高9~32位,即块地址,每一个块256个字节
WrNFAddr((NFBlcokAddr>>17)&0xff);
WrNFAddr((NFBlcokAddr>>25)&0xff);
WrNFCmd(ERASECMD1);
//ERASECMD1 = 0xd0,Erase one blcok 2nd command 删除指令
for(i=0;i<10;i++);
//wait tWB(100ns)
NF_WAITRB();
// Wait tBERS max 3ms. 指令响应结束
status = ReadStatus();
// Read status command 读状态
if (status&0x1)
// Erase error 删除失败,把这个扇区当作坏块
{
NFChipDs();
MarkBadBlk(addr);
return 0;
}
else
{
NFChipDs();;
return 1;
}
NFChipDs();
}
/********************************************
**** K9F1208U0M nand flash 的页数据读 ****
********************************************/
U32 ReadPage(U32 addr, U8 *buf)//addr = page address
{
U16 i;
U8 tmp[3],se[16];
InitEcc();
NFChipEn();
WrNFCmd(READCMD0);
//READCMD0 = 0//read the first half page
WrNFAddr(addr&0xff); //32位地址
WrNFAddr((addr>>9)&0xff);
WrNFAddr((addr>>17)&0xff);
WrNFAddr((addr>>25)&0xff);
for(i=0;i<10;i++); //wait tWB(100ns)
NF_WAITRB();
for(i=0; i<256; i++)
buf = RdNFDat();
//读出块数据 256字节
tmp[0] = rNFECC0; //验证信息
tmp[1] = rNFECC1;
tmp[2] = rNFECC2;
for(i=0;i<16;i++)
{
se=RdNFDat(); // Read spare array
}
NFChipDs();
if(tmp[0]==se[0] && tmp[1]==se[1] && tmp[2]==se[2])
//验证成功
{
Uart_Printf("\n[ECC OK:%x,%x,%x]\n",se[0],se[1],se[2]);
return 1;
}
else
//失败,打印出错误
{
Uart_Printf("\n[ECC ERROR(RD):read:%x,%x,%x, reg:%x,%x,%x]\n",
se[0],se[1],se[2],tmp[0],tmp[1],tmp[2]);
return 0;
}
}
/********************************************
**** K9F1208U0M nand flash 的页数据读 ****
********************************************/
U32 WritePage(U32 addr, U8 *buf)
{
U16 i;
U8 status;
U32 tmp[3];
InitEcc();
NFChipEn();
WrNFCmd(0x00);////write the first half page
WrNFCmd(PROGCMD0); //PROGCMD0 = 0x80
WrNFAddr(addr&0xff);
WrNFAddr((addr>>9)&0xff);
WrNFAddr((addr>>17)&0xff);
WrNFAddr((addr>>25)&0xff);
for(i=0; i<256; i++)
WrNFDat(buf);//Write one page to nand flash from buffer
tmp[0] = rNFECC0;
tmp[1] = rNFECC1;
tmp[2] = rNFECC2;
WrNFDat(tmp[0]);
WrNFDat(tmp[1]);
WrNFDat(tmp[2]);
WrNFCmd(PROGCMD1);//PROGCMD1 = 0x10
for(i=0;i<10;i++); //tWB = 100ns.
NF_WAITRB(); //wait tPROG 200~500us;
status = ReadStatus();
for(i=0;i<3;i++); //twhr=60ns
if (status&0x1) // Page write error
{
NFChipDs();
Uart_Printf("[PROGRAM_ERROR:block#=%d]\n",addr);
MarkBadBlk(addr);
return 0;
}
else
{
NFChipDs();
return 1;
}
}
5.坏区
另外,由于nand的工艺,nand非常容易产生坏的block,因此我们要进行坏块处理:
如下:
/*********************************************
**** 屏蔽 K9F1208U0M nand flash 的坏块 ****
*********************************************/
U32 MarkBadBlk(U32 addr) //标记坏块
{
int i;
U16 status;
addr = addr<<5;
NFChipEn();
WrNFCmd(READCMD2); //READCMD2=0x50
WrNFCmd(PROGCMD0); //PROGCMD0=0x80
WrNFAddr(4); //mark offset 4,5,6,7
WrNFAddr(addr);
WrNFAddr(addr>>8);
WrNFAddr(addr>>16);
WrNFDat(0); //mark with 0
WrNFDat(0);
WrNFDat(0); //mark with 0
WrNFDat(0);
WrNFCmd(PROGCMD1); //PROGCMD1=0x10
for(i=0;i<10;i++); //tWB = 100ns.
NF_WAITRB(); //needn't check return status
WrNFCmd(READCMD0); //READCMD0=0
status = ReadStatus();
for(i=0;i<3;i++); //twhr=60ns
if (status&0x1) // Spare arrray write error
{
NFChipDs();
Uart_Printf("[Program error is occurred but ignored]\n");
}
else
{
NFChipDs();
}
Uart_Printf("[block #%d is marked as a bad block]\n",addr);
return 1;
NFChipDs();
}
/*********************************************
**** 检查 K9F1208U0M nand flash 的坏块 ****
*********************************************/
static int CheckBadBlk(U32 addr)
{
U8 data;
int i;
addr = addr<<5;
NFChipEn();
WrNFCmd(READCMD2); //READCMD2 = 0x50
WrNFAddr(5); //mark offset 4,5,6,7
WrNFAddr(addr);
WrNFAddr(addr>>8);
WrNFAddr(addr>>16);
for(i=0;i<10;i++); // wait tWB(100ns) //
NF_WAITRB(); // Wait tR(max 12us)
data = RdNFDat();
NFChipDs();
if(data!=0xff)
{
Uart_Printf("[block %d has been marked as a bad block(%x)]\n",addr,data);
return 1;
}
else
{
return 0;
}
}
文章评论(0条评论)
登录后参与讨论