天天看點

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=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) 

    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(); 

    /* 校驗 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) 

    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) */ 

        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;       /* 非壞塊标志       */ 

        nf_wrdata(sebuf [i]); /* 寫該頁的oob 資料塊  */ 

    nf_cmd(0x10);     /* 結束寫指令 */ 

    /* 等待nand flash 處于準備狀态 */ 

    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) 

    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) 

    unsigned int blockpage=(block<<5); 

    sebuf[0]=0xff; 

    sebuf[1]=0xff; 

    sebuf[2]=0xff; 

    sebuf[5]=0x44;      /* 設定壞塊标記 */ 

    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 資料塊 */ 

         nf_wrdata(sebuf [i]); 

    nf_cmd(0x10);         /* 結束寫指令 */ 

    /* 等待nandflash 準備好 */ 

    for(i=0;i< 10;i++);  /* twb = 100ns.     */ 

    nf_waitrb();

/*讀nandflash 的寫狀态 */ 

    for(i=0;i<3;i++);  /* twhr=60ns  */ 

    if (nf_rddata()&0x1) 

                nf_nfce_h(); /* 取消nand flash 選中*/ 

                return 0; 

     } else { 

    return 1; 

(9)nand flash 檢查壞塊 

檢查指定塊是否是壞塊. 

傳回值:1:指定塊是壞塊 

        0 :指定塊不是壞塊。 

static int nf_isbadblock(u32 block) 

    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) */ 

    for(i=0;i< 10;i++); /* wait twb(100ns)  */ 

    /* 讀取讀出值 */ 

    data=nf_rddata(); 

    nf_nfce_h();     /* 取消nand flash 選中*/ 

    /* 如果data 不為0xff 時, 表示該塊是壞塊 */ 

    if(data != 0xff) 

(10)擦除指定塊中資料 

傳回值: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  { 

                 return 1; 

     } 

 }