uboot-step 13 NandFlash 做硬盤(pán)
- NandFlash 簡(jiǎn)介
- 內(nèi)部結(jié)構(gòu)
- 編址方式
- 信號(hào)引腳
- 命令設(shè)置
- S3c6410 NCONF
- 代碼實(shí)現(xiàn)
- 參考鏈接
NandFlash 簡(jiǎn)介
對(duì)于嵌入式設(shè)備來(lái)說(shuō)袍榆,NandFlash就相當(dāng)于電腦中硬盤(pán)的作用古瓤,我們的代碼和數(shù)據(jù)都存儲(chǔ)在上面葱轩。
分類
SLC (Single level cell): 單層式存儲(chǔ) 每個(gè)存儲(chǔ)單元存一位數(shù)據(jù)
-
MLC (multi level cell): 多層式存儲(chǔ) 每個(gè)存儲(chǔ)單元存兩位數(shù)據(jù)
|分類|價(jià)格|壽命|速度|功耗|
|---|---|---|---|
|SLC|高|10萬(wàn)|快3倍|低|
|MLC|低|1萬(wàn)|慢|高15%|
內(nèi)部結(jié)構(gòu)
NandFlash分為塊(Block)和頁(yè)(Pages)宴树,如下圖:
上圖是開(kāi)發(fā)板所采用的Flash芯片的datasheet里面的框圖,總共大小為256M
每頁(yè)分為main區(qū)和spare區(qū)额各,main去用來(lái)存保存的數(shù)據(jù)国觉,spare存儲(chǔ)一些附加信息,比如壞塊的標(biāo)記臊泰,ECC校驗(yàn)和等蛉加,此處所用的Nandflash的main區(qū)有2K大小蚜枢,還有64個(gè)Bytes的spare區(qū)缸逃,每塊有64頁(yè),共有128KPages厂抽,總共有256MByte的存儲(chǔ)空間
編址方式
與內(nèi)存的編址方式不同需频,NandFlash的編址方式為獨(dú)立編址,內(nèi)存屬于統(tǒng)一編址筷凤,占據(jù)了cpu的地址空間昭殉,而NandFlash有自己的地址空間苞七,并不占用CPU的地址空間。NandFlash的地址分為行地址與列地址挪丢,行地址即為頁(yè)地址蹂风,列地址為頁(yè)內(nèi)偏移。下面是尋址所用的周期數(shù)乾蓬,前兩個(gè)周期發(fā)送列地址惠啄,后三個(gè)周期發(fā)送行地址,總共需要5個(gè)周期發(fā)送地址任内。
信號(hào)引腳
引腳如上圖所示撵渡,NandFlash總共有IO0~IO7八個(gè)IO口,數(shù)據(jù)死嗦,命令趋距,和地址都需要這8個(gè)IO口來(lái)進(jìn)行傳輸
- CLE,ALE :命令越除,地址使能引腳节腐,代表當(dāng)前傳輸?shù)臄?shù)據(jù)是地址還是命令
- CE,RE摘盆,WE :片選铜跑,讀寫(xiě)使能引腳,低有效
- WP骡澈,R/B: 保護(hù)使能引腳锅纺,準(zhǔn)備、忙狀態(tài)引腳肋殴,可以得知NandFlash的工作狀態(tài)
命令設(shè)置
NandFlash使用流程 讀囤锉,寫(xiě),擦除
S3C6410 NandFlash Controler
如上圖护锤,為S3C6410的Nand Flash Controller 與NandFlash的接口框圖官地,將對(duì)應(yīng)的引腳與NandFlash進(jìn)行連接
使用之前,首先要對(duì)相關(guān)的寄存器進(jìn)行配置烙懦,圈出來(lái)的是常用的驱入,剩下的是與ECC校驗(yàn)相關(guān)的寄存器相關(guān)的寄存器如下:
配置寄存器: 這里主要設(shè)置TACLS,TWRPH0氯析,TWRPH1 時(shí)序參數(shù)和ECC配置
控制寄存器: 使能,片選等控制信號(hào)
地址寄存器亏较,數(shù)據(jù)寄存器,命令寄存器
狀態(tài)寄存器:主要是看NANDFlash狀態(tài)是繁忙還是已準(zhǔn)備好
代碼實(shí)現(xiàn)
前面我們代碼搬移的時(shí)候是從steppingstone中搬移到內(nèi)存中的掩缓,實(shí)際上正確的應(yīng)該是從NandFlash 中搬移到內(nèi)存中雪情,因此需要重寫(xiě)代碼搬移的函數(shù),在代碼搬移之前你辣,需要先進(jìn)行NandFlash的初始化巡通。
下面是NandFlash簡(jiǎn)單驅(qū)動(dòng)文件nand.c,沒(méi)有進(jìn)行ECC校驗(yàn)尘执,無(wú)法保證數(shù)據(jù)正確
/*
tiny6410用的nandflash為 一頁(yè)2K
*/
#define NFCONF (*((volatile unsigned long*)0x70200000))
#define NFCONT (*((volatile unsigned long*)0x70200004))
#define NFCMMD (*((volatile unsigned char*)0x70200008))
#define NFSTAT (*((volatile unsigned char*)0x70200028))
#define NFADDR (*((volatile unsigned char*)0x7020000c))
#define NFDATA (*((volatile unsigned char*)0x70200010))
void select_ship(void)
{
NFCONT &= ~(1<<1); //CE引腳接到了ncs[2] 這個(gè)引腳上
}
void delselect_ship(void)
{
NFCONT |= (1<<1); //CE
}
void clean_RnB()
{
NFSTAT |= (1<<4);
}
void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
}
void wait_RnB(void)
{
while(!(NFSTAT & 0x1));
}
void nand_addr(unsigned char addr)
{
NFADDR = addr;
}
void nand_reset(void)
{
/* 選中 */
select_ship();
/* 清除RnB */
clean_RnB();
/* 發(fā)出復(fù)位信號(hào) */
nand_cmd(0xff);
/* 等待就緒 */
wait_RnB();
/* 取消選中 */
delselect_ship();
}
void nand_init(void)
{
/* 設(shè)置時(shí)間參數(shù) */
#define TACLS 7
#define TWRPH0 7
#define TWRPH1 7
NFCONF &= ~((7<<12)|(7<<8)|(7<<4));
NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能 nandflash controller*/
NFCONT = 1 | (1<<1);
/* 復(fù)位 */
nand_reset();
}
void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;
/* 選中芯片 */
select_ship();
/* 清除RnB */
clean_RnB();
/* 發(fā)出命令0x00 */
nand_cmd(0x00);
/* 發(fā)出列地址 */
nand_addr(0x00);
nand_addr(0x00);
/* 發(fā)出行地址 */
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
/* 發(fā)出命令0x30 */
nand_cmd(0x30);
/* 等待就緒 */
wait_RnB();
/* 讀數(shù)據(jù) */
for(i = 0; i<1024*2; i++)
{
*buff++ = NFDATA;
}
/* 取消片選 */
delselect_ship();
}
void nand_to_ram(unsigned long start_addr,unsigned char* sdram_addr,int size)
{
int i;
for( i=(start_addr >>11); size>0;)
{
NF_PageRead(i,sdram_addr);
size -= 2048;
sdram_addr += 2048;
i++;
}
}
int NF_Erase(unsigned long addr)
{
int ret;
//選中flash芯片
select_ship();
//清除RnB
clean_RnB();
//發(fā)送命令60
nand_cmd(0x60);
//發(fā)送行地址(3個(gè)周期)
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
//發(fā)送命令D0
nand_cmd(0xD0);
//等待RnB
wait_RnB();
//發(fā)送命令70
nand_cmd(0x70);
//讀取擦除結(jié)果
ret = NFDATA;
//取消選中flash芯片
delselect_ship();
return ret;
}
int NF_WritePage(unsigned long addr,unsigned char* buff)
{
int ret,i;
//選中flash芯片
select_ship();
//清除RnB
clean_RnB();
//發(fā)送命令80
nand_cmd(0x80);
//發(fā)送列地址(2個(gè)周期)
nand_addr(0x00);
nand_addr(0x00);
//發(fā)送行地址(3個(gè)周期)
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
//寫(xiě)入數(shù)據(jù)
for(i=0;i<1024*2;i++)
{
NFDATA = buff[i];
}
//發(fā)送命令10
nand_cmd(0x10);
//等待RnB
wait_RnB();
//發(fā)送命令70
nand_cmd(0x70);
//讀取寫(xiě)入結(jié)果
ret = NFDATA;
//取消選中flash芯片
delselect_ship();
return ret;
}
由于以前是從steppingstone中搬移代碼到內(nèi)存,現(xiàn)在更改為從flash中宴凉,新改的start.s文件如下誊锭,只有改動(dòng)部分
reset:
bl set_svc
bl set_peri_port
bl disable_watchdog
bl disable_interrupt
bl disable_mmu
bl init_clock
bl mem_init
bl init_stack //由于要跳轉(zhuǎn)到c語(yǔ)言中運(yùn)行,先要設(shè)置堆棧弥锄,進(jìn)行堆棧初始化
bl nand_init //copy 到內(nèi)存之前炉旷,要先進(jìn)行flash的初始化
bl copy_to_ram //跳轉(zhuǎn)到去拷貝代碼
bl clean_bss
ldr pc, =gboot_main
copy_to_ram:
mov r0,#0
ldr r1,=_start
ldr r2,=bss_end
sub r2,r2,r1
mov ip,lr //調(diào)用之前先保存當(dāng)前的lr指針,后面要恢復(fù)的 ip寄存器是r12寄存器
bl nand_to_ram //這里牽扯到匯編調(diào)用c函數(shù)的參數(shù)傳遞叉讥,r0窘行,r1,r2,分別為函數(shù)的第1,2,3個(gè)參數(shù),正好對(duì)應(yīng)讀flash函數(shù)的flash開(kāi)始地址r0=0图仓,目標(biāo)地址r1=0x50008000和復(fù)制大小r2=bss_end - _start=整個(gè)代碼的大小
mov lr,ip
mov pc,lr
init_stack:
msr cpsr_c, #0xd2
ldr sp, =0x53000000 //初始化r13_irq
msr cpsr_c, #0xd3
ldr sp, =0x54000000 //初始化R13_svc
mov pc ,lr
下面是主程序罐盔,main.c 首先寫(xiě)入了一些數(shù)據(jù),然后讀出來(lái)判斷數(shù)據(jù)是否正確
unsigned char buf[1024*2];
#ifdef MMU_ON
mmu_init();
#endif
led_init();
button_init();
init_irq();
led_off();
NF_Erase(128*1+1);
buf[0] = 100;
NF_WritePage(128*1+1,buf);
buf[0] = 10;
NF_PageRead(128*1+1,buf);
if( buf[0] == 100 )
led_on();
while(1);
return 0;
此去經(jīng)年
zhaiyk@sina.cn
August 11, 2016