一、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;
}
}