首页 > 代码库 > Nand Flash介绍和Nand Flash控制器使用

Nand Flash介绍和Nand Flash控制器使用

一、Flash介绍

    常用的flash类型有NOR Flash 和Nand Flash 两种;

    (1)Nor Flash

    1、Nor Flash的接口和RAM完全相同,可以随机访问任意地址的数据,在其上进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小,通常,Nor Flash用于存储程序
    2、Nor Flash的块大小范围为64KB—128KB;
    3、擦写一个Nor Flash块需要4s,
    4、市场上Nor Flash 的容量通常为1MB—4MB

    (2)Nand Flash
    1、Nand Flash的接口仅仅包含几个I/O引脚,需要串行地访问,Nand Flash进行擦除和写操作的效率很高,容量较大,

通常Nand Flash用于存储数据
    2、Nand Flash的块大小范围为8KB—64KB;
    3、擦写一个Nand Flash块需要2ms;
    4、Nand Flash 一般以512字节为单位进行读写
    5、 市场上 Nand Flash 的容量一般为 8M—512M

 

二、Nand  Flash的物理结构

    以三星公司生产的 K9F1208U0M  为例:

    1、容量:64MB,

                      一共4个层

                      每层1024个块(block);

                      1块包含32页

                      1页包含 512 + 16  = 528个字节

 

 

    2、外部接口:8个I/O口,5个使能信号(ALE、CLE、nWE、nRE、nCE),1个状态引脚(RDY/B),1个写保护引脚(nWE);            

    3、命令、地址、数据都通过8个I/O口输入输出;

    4、写入命令、地址、数据时,都需要将nWE、nCE信号同时拉低;数据在WE上升沿被锁存;

    5、CLE、ALE用来区分I/O引脚上传输的是数据还是地址;

    6、64MB的空间需要26位地址,因此以字节为单位访问Flash时需要4个地址序列

    7、读/写页在发出命令后,需要4个地址序列,而擦除块在发出擦除命令后仅需要3个地址序列

 

三、Nand Flash访问方法

 1 特殊功能寄存器定义 


#define rNFCONF  (*(volatile unsigned int *)0x4e000000) 
#define rNFCMD   (*(volatile unsigned char *)0x4e000004) 
#define rNFADDR  (*(volatile unsigned char *)0x4e000008) 


#define rNFDATA  (*(volatile unsigned char *)0x4e00000c) 
#define rNFSTAT  (*(volatile unsigned int *)0x4e000010) 
#define rNFECC   (*(volatile unsigned int *)0x4e000014) 
#define rNFECC0 (*(volatile unsigned char  *)0x4e000014) 
#define rNFECC 1 (*(volatile unsigned char *)0x4e000015) 
#define rNFECC2 (*(volatile unsigned char *)0x4e000016) 

2 操作的函数实现 


1. 发送命令 
#define NF_CMD(cmd)    {rNFCMD=cmd; } 

2. 写入地址 
#define NF_ADDR(addr)  {rNFADDR=addr;} 

3. Nand Flash 芯片选中 
#define NF_nFCE_L()    {rNFCONF&=~(1<<11);} 

4. Nand Flash 芯片不选中 
#define NF_nFCE_H()    {rNFCONF|=(1<<11);} 

5. 初始化 ECC 
#define NF_RSTECC()    {rNFCONF|=(1<<12);}
 
6. 读数据 

#define NF_RDDATA()         (rNFDATA) 

7. 写数据 
#define NF_WRDATA(data) {rNFDATA=http://www.mamicode.com/data;}

8. 获取Nand Flash 芯片状态 
#define NF_WAITRB()         {while(!(rNFSTAT&(1<<0)));} 
0/假: 表示Nand Flash 芯片忙状态 
1/真:表示Nand Flash 已经准备好 

3.NandFlash读写擦具体实现。

      操作Nand  Flash时,先传输命令,然后传输地址,最后读、写数据,期间要检查flash的状态;

      K9F1208U0M  一页大小为528字节,而列地址A0——A7可以寻址的范围是256字节,所以将一页分为A、B、C三个区

                 A区:0—255字节

                 B区:256—511字节

                 C区:512—527字节

 (0)Nand Flash 初始化 


void NF_Init(void) 

    /* 设置 Nand Flash 配置寄存器, 每一位的取值见1.3 节 */ 
    rNFCONF=(1<< 15)|(1<<14)|(1<< 13)|(1<<12)|(1<< 11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); 
    /* 复位外部 Nand Flash 芯片 */ 
    NF_Reset(); 
}

(1)复位

          命令:FFh

          步骤:发出命令即可复位Nand Flash芯片;

 static void NF_Reset(void) 

    int i; 
     
    NF_nFCE_L();         /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0xFF);        /* 复位命令          */ 
    for(i=0;i< 10;i++); /* 等待tWB = 100ns.       */ 
    NF_WAITRB();        /* wait 200~500us;      */ 
    NF_nFCE_H();        /* 取消Nand Flash 选中*/ 
}

(2)读操作

          命令:

                 00h——读A区

                 01h——读B区

                 50h——读C区

           操作步骤:

              1、发出命令 00h、01h 或50h,  00h将地址位A8设为0,  01h将A8设为1 ;

              2、依次发出4个地址序列;

              3、检测R/nB,待其为高电平时,就可以读取数据了;

参数说明:block :块号 
          page :页号 
          buffer :指向将要读取到内存中的起始位置 
返回值:1:读成功 
        0 :读失败 
static int NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer) 

    int i; 
    unsigned int blockPage; 
    unsigned char ecc0, ecc1, ecc2; 
    unsigned char *bufPt=buffer; 
    unsigned char se[16]; 
     
    page=page&0x1f; 
    blockPage=(block<<5)+page; 
    NF_RSTECC();      /* 初始化 ECC              */ 
    NF_nFCE_L();       /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0x00);      /* 从A 区开始读      */ 
    NF_ADDR(0);       /*  A0~A7 位(Column Address)      */ 
    NF_ADDR(blockPage&0xff);        /* A9-A 16, (Page Address) */ 
    NF_ADDR((blockPage>>8)&0xff);        /* A17-A24, (Page Address) */ 
    NF_ADDR((blockPage>> 16)&0xff);  /* A25,     (Page Address) */ 


    /* 等待Nand Flash 处于再准备状态 */ 
    for(i=0;i< 10;i++); 
    NF_WAITRB();       /*等待 tR(max 12us) */ 
    /* 读整个页, 512 字节             */ 
    for(i=0;i<512;i++) 
    { 
         *bufPt++=NF_RDDATA(); 
    } 
     
    /* 读取 ECC 码 */ 
    ecc0=rNFECC0; 
    ecc 1=rNFECC 1; 
    ecc2=rNFECC2; 

/* 读取该页的OOB 块 */ 
    for(i=0;i< 16;i++) 
    { 
         se[i]=NF_RDDATA(); 
    } 
     
    NF_nFCE_H();        /* 取消Nand Flash 选中*/ 


    /* 校验 ECC 码, 并返回 */ 
    if(ecc0==se[0] && ecc 1==se[1] && ecc2==se[2]) 
           return 1; 
    else 
          return 0; 

(3)flash编程

            命令:

                   80h——10h :写单页;

                   80h——11h :对多个层进行些页操作;    

             操作步骤:

                 1、写单页步骤:

                            【1】发出80h命令后;

                            【2】发送4个地址序列;

                            【3】向flash发送数据;

                            【4】发出命令10h启动写操作,flash内部自动完成写、校验操作;

                            【5】通过命令70h读取状态位,查询写操作是否完成;

                  2、多页写

                            【1】发出80h、4个地址序列、最多528字节的数据;

                            【2】发出11h命令;

                            【3】接着在相邻层执行【1】、【2】两步操作;

                            【4】第四页的最后使用10h代替11h,启动flash内部的写操作;

                            【5】可以通过71h查询写操作是否完成;


以页为单位写入. 
参数说明:block        块号 
          page   页号 
          buffer  指向内存中待写入Nand flash 中的数据起始位置 
返回值:      0 :写错误 
         1:写成功 
static int NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer) 

    int i; 
    unsigned int blockPage = (block<<5)+page; 
    unsigned char *bufPt = buffer; 


    NF_RSTECC();      /* 初始化 ECC            */ 
    NF_nFCE_L();     /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0x0);      /* 从A 区开始写      */ 
    NF_CMD(0x80);  /* 写第一条命令      */ 
    NF_ADDR(0);      /* A0~A7 位(Column Address)     */ 
    NF_ADDR(blockPage&0xff);      /* A9-A 16, (Page Address) */ 
    NF_ADDR((blockPage>>8)&0xff);      /* A17-A24, (Page Address) */ 
    NF_ADDR((blockPage>> 16)&0xff);  /* A25,    (Page Address) */ 


    for(i=0;i<512;i++) 
    { 
        NF_WRDATA(*bufPt++);            /* 写一个页512 字节到 Nand Flash 芯片 */ 
    } 
     
    /* 
    * OOB 一共16 Bytes, 每一个字节存放什么由程序员自己定义, 通常, 
    * 我们在 Byte0-Byte2 存 ECC 检验码. Byte6 存放坏块标志. 
    */ 
    seBuf[0]=rNFECC0;  /* 读取 ECC 检验码 0 */ 
    seBuf[1]=rNFECC 1;  /* 读取 ECC 检验码 1 */ 
    seBuf[2]=rNFECC2;  /* 读取 ECC 检验码 2 */ 
    seBuf[5]=0xff;       /* 非坏块标志       */ 
     
    for(i=0;i< 16;i++) 
    { 
        NF_WRDATA(seBuf [i]); /* 写该页的OOB 数据块  */ 
    } 


    NF_CMD(0x10);     /* 结束写命令 */ 


    /* 等待Nand Flash 处于准备状态 */ 
    for(i=0;i< 10;i++); 
    NF_WAITRB(); 
  
    /* 发送读状态命令            Nand Flash */ 
    NF_CMD(0x70); 
    for(i=0;i<3;i++); 

  if (NF_RDDATA()&0x1) 
    {   /*如果写有错, 则标示为坏块    */ 
        NF_nFCE_H();  /* 取消Nand Flash 选中*/ 
        NF_MarkBadBlock(block); 
        return 0; 
    } else { /* 正常退出 */ 
        NF_nFCE_H(); /* 取消Nand Flash 选中*/ 
        return 1; 
    } 

(4)复制

               命令:

                     00h——8Ah——10h  :单层页内复制

                     03h——8Ah——11h  :多层页内复制

               操作步骤:

                   1、单层页内复制步骤:

                            【1】发出命令00h;

                            【2】4个源地址序列;

                            【2】接着发出8Ah;

                            【4】发出4个目的地址序列;

                            【5】发出10h命令,启动写操作;

                            【6】通过70h命令读取状态查询操作是否完成;

                   2、多层页内复制步骤:

                             【1】发出命令00h(第一层)、4个源页地址序列;

                             【2】以后各层依次发出命令03h、4个源页地址序列;

                             【3】发出命令8Ah、目的地址、命令11h;

                             【4】各层依次执行【3】,在最后一页的地址后,用10h代替11h,启动写操作;

                             【5】通过71h命令读取状态查询操作是否完成;

(5)擦除

               命令:

                     60h——D0h  :单层内块擦除

                     60h-60h ——D0h  :多层内块擦除

               操作步骤:

                  1、单层内块擦除:

                              【1】发出命令字60h;

                              【2】发出块(block)地址,仅需3个地址序列;

                              【3】发出D0h,启动擦除操作;

                              【4】发出70h命令查询状态,是否完成擦除;

                   2、多层内块擦除:

                              【1】发出命令字60h,3个块地址序列;

                              【2】对每个层执行【1】;

                              【3】发出命令D0h,启动擦除操作;

                              【4】发出71h命令查询状态,检查是否完成擦除;

(6)读取芯片ID

                 命令:90h

                 操作步骤:

                     1、发出命令90h;

                     2、发出4个地址序列(均设为0);

                     3、连续读入5个数据,分别表示:厂商代码、设备代码、保留字节、多层操作代码;

返回值为Nand flash 芯片的 ID 号 


unsigned short  NF_CheckId(void) 

    int i; 
    unsigned short id; 
     
    NF_nFCE_L();            /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0x90);           /* 发送读ID 命令到 Nand Flash 芯片 */ 
    NF_ADDR(0x0);           /* 指定地址 0x0 ,芯片手册要求    */ 
    for(i=0;i< 10;i++);      /* 等待tWB = 100ns.           */ 
    id=NF_RDDATA()<<8;  /* 厂商ID(K9S 1208V:0xec)          */ 
    id|=NF_RDDATA();         /* 设备 ID(K9S 1208V:0x76)  */ 
    NF_nFCE_H();             /* 取消Nand Flash 选中*/ 
    return id; 

(7)读状态

                 命令:

                       70h——单层状态

                       71h——多层状态

                 操作步骤:写入命令字之后,然后启动读操作即可读入此寄存器。

(8)Nand flash 标记坏块 


如果是坏块, 通过写 OOB 块的Byte6 把该块标记为坏块。 
参数说明:block 块号 
返回值:1:ok ,成功完成标记。 
        0 :表示写OOB 块正确. 
static int NF_MarkBadBlock(unsigned int block) 

    int i; 
    unsigned int blockPage=(block<<5); 
  
    seBuf[0]=0xff; 
    seBuf[1]=0xff; 
    seBuf[2]=0xff; 
    seBuf[5]=0x44;      /* 设置坏块标记 */ 
     
    NF_nFCE_L();       /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0x50);         /* 从C 区开始写    */ 
    NF_CMD(0x80);         /* 发送编程命令, 让Nand Flash 处理写状态 */ 
    NF_ADDR(0x0);  /*        A0~A7 位(Column Address)         */ 
    NF_ADDR(blockPage&0xff);           /* A9-A 16, (Page Address) */ 
    NF_ADDR((blockPage>>8)&0xff);            /* A17-A24, (Page Address) */ 
    NF_ADDR((blockPage>> 16)&0xff);  /* A25,           (Page Address) */ 
     
    /* 写OOB 数据块 */ 
    for(i=0;i< 16;i++) 
    { 
         NF_WRDATA(seBuf [i]); 
    } 


    NF_CMD(0x10);         /* 结束写命令 */ 
     
    /* 等待NandFlash 准备好 */ 
    for(i=0;i< 10;i++);  /* tWB = 100ns.     */ 
    NF_WAITRB();

/*读NandFlash 的写状态 */ 
    NF_CMD(0x70); 
    for(i=0;i<3;i++);  /* twhr=60ns  */ 
    if (NF_RDDATA()&0x1) 
    { 
                NF_nFCE_H(); /* 取消Nand Flash 选中*/ 
                return 0; 
     } else { 
                NF_nFCE_H(); /* 取消Nand Flash 选中*/ 
    } 
    return 1; 

(9)Nand Flash 检查坏块 


检查指定块是否是坏块. 
参数说明:block :块号 
返回值:1:指定块是坏块 
        0 :指定块不是坏块。 
static int NF_IsBadBlock(U32 block) 

    int i; 
    unsigned int blockPage; 
    U8 data; 
         
    blockPage=(block<<5); 
    NF_nFCE_L();        /* 片选 Nand Flash 芯片*/ 
    NF_CMD(0x50);       /* Read OOB 数据块   */ 
    NF_ADDR(517&0xf);  /* A0~A7 位(Column Address)         */ 
    NF_ADDR(blockPage&0xff); /* A9-A 16, (Page Address) */ 
    NF_ADDR((blockPage>>8)&0xff);       /* A17-A24, (Page Address) */ 
    NF_ADDR((blockPage>> 16)&0xff);  /* A25,    (Page Address) */ 


    /* 等待NandFlash 准备好 */ 
    for(i=0;i< 10;i++); /* wait tWB(100ns)  */ 
    NF_WAITRB(); 


    /* 读取读出值 */ 
    data=http://www.mamicode.com/NF_RDDATA();
    NF_nFCE_H();     /* 取消Nand Flash 选中*/ 
    /* 如果data 不为0xff 时, 表示该块是坏块 */ 
    if(data != 0xff) 
        return 1; 
    else 
        return 0; 

(10)擦除指定块中数据 


参数说明:block 块号 
返回值:0 :擦除错误。(若是坏块直接返回0 ;若擦除出现错误则标记为坏块然后返回0) 

1 :成功擦除。 
 static int NF_EraseBlock(unsigned int block) 
 { 
     unsigned int blockPage=(block<<5); 
     int i; 


     /* 如果该块是坏块, 则返回 */ 
     if(NF_IsBadBlock(block)) 
         return 0; 


     NF_nFCE_L();      /* 片选 Nand Flash 芯片*/ 
     NF_CMD(0x60);     /* 设置擦写模式      */ 
     NF_ADDR(blockPage&0xff);       /* A9-A 16, (Page Address) , 是基于块擦*/ 
     NF_ADDR((blockPage>>8)&0xff);        /* A17-A24, (Page Address) */ 
     NF_ADDR((blockPage>> 16)&0xff);  /* A25, (Page Address) */ 
     NF_CMD(0xd0);     /* 发送擦写命令, 开始擦写 */ 


     /* 等待NandFlash 准备好 */ 
     for(i=0;i< 10;i++); /* tWB(100ns) */ 
     NF_WAITRB(); 


     /* 读取操作状态         */ 
     NF_CMD(0x70); 
     if (NF_RDDATA()&0x1) 
     { 
                 NF_nFCE_H(); /* 取消Nand Flash 选中*/ 
                 NF_MarkBadBlock(block); /* 标记为坏块 */ 
                 return 0; 
     }  else  { 
                 NF_nFCE_H(); /* 取消Nand Flash 选中*/ 
                 return 1; 
     } 
 } 

Nand Flash介绍和Nand Flash控制器使用