1. 程式人生 > >ARM裸機程式設計系列----nandflash

ARM裸機程式設計系列----nandflash

關於NAND Flash

S5PV210的NAND Flash控制器有如下特點:

  •  支援512byte,2k,4k,8k的頁大小
  •  通過各種軟體模式來進行NAND Flash的讀寫擦除等
  •  8bit的匯流排
  •  支援SLC和MCL的NAND Flash
  • 支援1/4/8/12/16bit的ECC
  • 支援以位元組/半字/字為單位訪問資料/ECC暫存器,以字為單位訪問其他暫存器。
注意:在此使用的GEC210的NAND Flash型別為SLC,大小為512MB,型號為K9K8G08U0A。所以本章的內容是針對SLC型別的NAND Flash(包括256M/512M/1GB等),並不適用MLC型別的NAND Flash。


程式例子:
/*nand.c*/
// NAND Flash初始化函式nand_init(),程式碼如下
void nand_init(void) 
{ 
  // 1. 配置NAND Flash 
  NFCONF = (TACLS<<12)|(TWRPH0<<8)|(T    WRPH1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0); 
  NFCONT =(0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x3<<1)|(1<<0); 
  // 2. 配置引腳 
  MP0_1CON = 0x22333322; 
  MP0_2CON = 0x00002222; 
  MP0_3CON = 0x22222222; 
  // 3. 復位 
  nand_reset();
} 

第一步 配置NAND Flash

主要是設定NFCONF和NFCONT兩個暫存器步驟:


NFCONF暫存器

AddrCycle = 1,When page size is 2K or 4K, 1 = 5 address cycle,GEC210的NAND Flash的頁大小為2k,所有是5個地址週期; 
PageSize = 0,When MLCFlash is 0, the value of PageSize is as follows: 0 = 2048 Bytes/page,Mini210S使用的是SLC NAND Flash且每頁大小為2k; 
MLCFlash = 0,在此使用的是SLC NAND Flash; 
TWRPH1/TWRPH0/TACLS是關於訪問時序的設定,需對照NAND Flash晶片手冊設定,這裡不再詳細解釋,分別取TWRPH1=1,TWRPH0=4,TACLS=1; 
ECCType0/MsgLength,我們的裸機程式碼沒有使用到ECC,所有不用設定這兩個標誌。



MODE = 1,使能NAND Flash控制器; 
Reg_nCE0 = 1,取消片選,需要操作NAND Flash時再發片選; 
Reg_nCE1 = 1, 取消片選,需要操作NAND Flash時再發片選; 
InitMECC/InitSECC/SECCLock/MECCLock,我們的裸機程式碼不涉及ECC,這4個標誌位隨便設定即可;
RnB_TransMode = 0,Detect rising edge,RnB是NAND Flash的狀態探測引腳,我們使用上升沿觸發; 
EnbRnBINT = 0 ,禁止RnB中斷; 
EnbIllegalAccINT = 0,禁止Illegal access 中斷 ; 
EnbMLCDecInt/EnbMLCEncInt為MCL相關,不用設定; 
LOCK = 0,我們沒有用到Soft Lock,所以禁止Soft Lock; 
LockTight = 0,我們沒有用到Lock-tight,所有禁止Lock-tight; 
MLCEccDirection,MLC相關,可不用設定

第二步 配置引腳

用於NAND Flash相關功能;

第三步 復位

復位函式nand_reset的相關程式碼如下: 
static void nand_reset(void) 
{ 
    nand_select_chip();
    nand_send_cmd(NAND_CMD_RES); 
    nand_wait_idle(); 
    nand_deselect_chip(); 
} 

NAND Flash的復位操作共4個步驟:

1) 發片選,實質就是NFCONT &= ~(1<<1);往NFCONT的bit[1]寫0;
2) 發命令復位命令NAND_CMD_RES (0xff);實質就是NFCMMD = cmd;將命令寫到NFCMMD暫存器;完整的NAND Flash命令資訊見下圖:


3) 等待NAND Flash 就緒;實質就是while( !(NFSTAT & (BUSY<<4)) ),讀NFSTAT的bit[4]檢查NAND Flash是否就緒;
4) 取消片選,實質就是NFCONT |= (1<<1); 往NFCONT的bit[1]寫1;


<2> NAND Flash讀ID函式nand_read_id(),程式碼如下

void nand_read_id(void) 
{ 
  nand_id_info nand_id; 
  // 1. 發片選 
  nand_select_chip(); 
  // 2. 讀ID 
  nand_send_cmd(NAND_CMD_READ_ID); 
  nand_send_addr(0x00); 
  nand_wait_idle(); 
  nand_id.IDm = nand_read(); 
  nand_id.IDd = nand_read();
  nand_id.ID3rd = nand_read(); 
  nand_id.ID4th = nand_read(); 
  nand_id.ID5th = nand_read(); 
  printf("NANDFlash: makercode = %x,devicec ode = %x\r\n",nand_id.IDm,nand_id.IDd); 
  nand_deselect_chip(); 
}

NAND Flash 讀ID操作




根據上圖,NAND Flash的讀ID操作共4個步驟:
第一步 發片選;
第二步 發讀ID命令NAND_CMD_READ_ID(0x90);
第三步 發地址0x00;呼叫函式nand_send_addr();
第四步 等待NAND Flash 就緒;
第五步 讀ID;呼叫了nand_read()函式,實質就是讀NFDATA暫存器;

/*nand_send_addr()*/
{ 
  // 列地址,即頁內地址 
  col = addr % NAND_PAGE_SIZE; 
  // 行地址,即頁地址 
  row = addr / NAND_PAGE_SIZE; 
  // Column Address A0~A7 
  NFADDR = col & 0xff; 
  for(i=0; i<10; i++);
  // Column Address A8~A11 
  NFADDR = (col >> 8) & 0x0f; 
  for(i=0; i<10; i++); 
  // Row Address A12~A19 
  NFADDR = row & 0xff;
  for(i=0; i<10; i++); 
  // Row Address A20~A27 
  NFADDR = (row >> 8) & 0xff; 
  for(i=0; i<10; i++); 
  // Row Address A28~A30 
  NFADDR = (row >> 16) & 0xff; 
  for(i=0; i<10; i++); 
} 


首先根據頁大小來獲取頁地址和頁內偏移地址,然後通過5個週期將地址傳送出去,實質就是寫NFADDR暫存器,具體每個週期如何傳送,查閱NAND Flash晶片手冊可知,見下圖:


傳送地址後,就可以連續讀出5個ID了,其中第一個是MAKDER CODE, 第二個是DEVICE CODE。

NAND Flash擦除操作

NAND Flash擦除函式nand_erase(),核心程式碼如下:
nand_erase()
{ 
  // 獲得row地址,即頁地址 
  unsigned long row = block_num * NAND_BLOCK_SIZE; 
  // 1. 發出片選訊號 
  nand_select_chip(); 
  // 2. 擦除:第一個週期發命令0x60,第二個週期 發塊地址,第三個週期發命令0xd0 nand_send_c    md(NAND_CMD_BLOCK_ERASE_1st);
  for(i=0; i<10; i++); 
  // Row Address A12~A19 
  NFADDR = row & 0xff; 
  for(i=0; i<10; i++); 
  // Row Address A20~A27 
  NFADDR = (row >> 8) & 0xff; 
  for(i=0; i<10; i++); 
  // Row Address A28~A30 
  NFADDR = (row >> 16) & 0xff; 
  NFSTAT = (NFSTAT)|(1<<4); 
  nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st); 
  for(i=0; i<10; i++); 
  // 3. 等待就緒 
  nand_wait_idle(); 
  // 4. 讀狀態 
  unsigned char status = read_nand_status(); 
}

根據上圖,NAND Flash的擦除操作共6個步驟:
第一步 發片選;
第二步 發擦除命令1 NAND_CMD_BLOCK_ERASE_1(0x60);
第三步 發頁地址,只需發頁地址;
第四步 發擦除命令2 NAND_CMD_BLOCK_ERASE_2st(0xD0);
第五步 等待NAND Flash就緒;
第六步 讀狀態,判斷擦除是否成功。若擦除失敗,則列印是壞塊再取消片選;否則直接直接取消片選即可。讀狀態呼叫了函式read_nand_status(),它的實質就是nand_send_cmd(NAND_CMD_READ_STATUS);ch = nand_read();先發讀狀態命令NAND_CMD_READ_STATUS,然後再讀狀態值。   


NAND Flash 讀操作

<4> NAND Flash讀函式copy_nand_to_sdram(),從NAND Flash中讀資料到DRAM,核心程式碼如下:
copy_nand_to_sdram()
{ 
  // 1. 發出片選訊號 nand_select_chip();
  // 2. 從nand讀資料到sdram,第一週期發命令0x00,第二週期發地址nand_addr,第三個週期發命令0x30,可讀一頁(2k)的資料
  while(length) 
 { 
    nand_send_cmd(NAND_CMD_READ_1st); 
    nand_send_addr(nand_addr); 
    NFSTAT = (NFSTAT)|(1<<4); 
    nand_send_cmd(NAND_CMD_READ_2st); 
    nand_wait_idle(); 
    // 列地址,即頁內地址 
    unsigned long col = nand_addr % NAND_PAGE_SIZE; 
    i = col; 
    // 讀一頁資料,每次拷1byte,共拷2048次(2k),直到長度為length的資料拷貝完畢 
    for(; i<NAND_PAGE_SIZE && length!=0; i++,length--) 
    { 
      *sdram_addr = nand_read(); 
      sdram_addr++; nand_addr++; 
    } 
  } 
  // 3. 讀狀態 
  unsigned char status = read_nand_status(); 
}


NAND Flash 讀操作


根據上圖,NAND Flash的讀操作共7個步驟:
第一步 發片選;
第二步 發讀命令1 NAND_CMD_READ_1st(0x00);
第三步 發地址,呼叫函式nand_send_cmd(),發5個地址週期;
第四步 發讀命令2 NAND_CMD_READ_2st(0xD0);
第五步 等待NAND Flash就緒;
第六步 從頁內偏移地址開始讀,讀到頁結尾即結束,每次讀1byte;
第七步 讀狀態,判斷是否讀成功。

NAND Flash寫操作

 NAND Flash寫函式copy_sdram_to_nand (),從DRAM寫資料到NAND Flash,核心程式碼如下:
copy_sdram_to_nand ()
{ 
  // 1. 發出片選訊號 
  nand_select_chip(); 
  // 2. 從sdram讀資料到nand,第一週期發命令0x80,第二週期發地址nand_addr,第三個週期寫一頁(2k)資料,第四周期發0x10 
  while(length) 
  { 
    nand_send_cmd(NAND_CMD_WRITE_PAGE_1st); 
    nand_send_addr(nand_addr); 
    // 列地址,即頁內地址 
    unsigned long col = nand_addr % NAND_PAGE_SIZE; 
    i = col; 
    // 寫一頁資料,每次拷1byte,共拷2048次(2k),直到長度為length的資料拷貝完畢 
    for(; i<NAND_PAGE_SIZE && length!=0; i++,length--) 
    { 
      nand_write(*sdram_addr); 
      sdram_addr++; 
      nand_addr++; 
    } 
    NFSTAT = (NFSTAT)|(1<<4); 
    nand_send_cmd(NAND_CMD_WRITE_PAGE_2st); 
    nand_wait_idle(); 
  }  
  // 3. 讀狀態 
  unsigned char status = read_nand_status();
}

根據上圖,NAND Flash的寫操作共7個步驟:
第一步 發片選;
第二步 發寫命令1 NAND_CMD_WRITE_PAGE_1st (0x80);
第三步 發地址地址,呼叫函式nand_send_cmd(),發5個地址週期;
第四步 發讀命令2 NAND_CMD_WRITE_PAGE_2st (0x10);
第五步 等待NAND Flash就緒;
第六步 從頁內偏移地址開始寫,讀到頁結尾即結束,每次寫1byte;
第七步 讀狀態,判斷是否讀成功。


2. main.c
在main.c中,首先會呼叫nand_init()來初始化NAND Flash,然後列印一個選單,提供4種選擇測試NAND Flash: 
讀ID功能(nand_read_id()); 
擦除功能(nand_erase()); 
讀功能(copy_nand_to_sdram()); 
寫功能(copy_sdram_to_nand());