1、nandflash原理解析
1)角色:相當於pc的硬盤
2)從物理結構上區分:
SLC:單層式存儲 (在存儲格上只存儲一位數據,而MLC則存放兩位數據)(比MLC訪問速度快近3倍、10萬次擦寫)
MLC:多層式存儲(密度高,同容量價格要低很多,1萬次擦寫,MLC功耗比SLC高15%)
3)訪問方試:nandflah屬於獨立編址,實際上就是去操作cpu上面的nandflash控制器的相應寄存器(地址、命令、數據)
2、nandflash的驅動設計~讀(以k9f2g08型號爲例)
1)頁讀(本節選擇頁讀的方式將nandflash中的值拷貝到內存中去)
2)隨機讀
按照對應nandflash的datasheet,查看讀操作時的時序圖:
只要看IO口的時序,由時序圖可知,一次發送命令0x00、5個週期的地址(兩個列地址、三個行地址)、在發送0x30。發送完相應命令後,RnB信號線進入低電平狀態(忙狀態),要等待RnB變爲空閒狀態時就可以讀取nandflash中的數據了(事先要選中芯片並對RnB進行清除)。
讀nandflash的代碼如下:
#define NFCONF (*(volatile unsigned long*)0x4E000000)
#define NFCONT (*(volatile unsigned long*)0x4E000004)
#define NFSTAT (*(volatile unsigned char*)0x4E000020)
#define NFCMD (*(volatile unsigned char*)0x4E000008)
#define NFADDR (*(volatile unsigned char*)0x4E00000C)
#define NFDATA (*(volatile unsigned char*)0x4E000010)
void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;
//選中nandflash芯片
select_chip();
//清除RnB(通過操作nandflash的狀態寄存器的第2位(寫1清除))
clear_RnB();
//發送命令0x00
send_cmd(0x00);
//發送列地址
send_addr(0x00);
send_addr(0x00);
//發送行地址
send_addr(addr&0xff);
send_addr((addr>>8)&0xff);
send_addr((addr>>16)&0xff);
//發送命令0x30
send_cmd(0x30);
//等待RnB (在發送相關命令後,RnB信號進入忙的狀態(低電平),要等待RnB由低電平變爲高電平(空閒狀態))
wait_RnB();
//讀取數據
for(i=0;i<2048;i++)
{
//buff[i] = NFDATA;
buff[i] = NFDATA&0xff;
buff[i++] = (NFDATA>>8)&0xff;
buff[i++] = (NFDATA>>16)&0xff;
buff[i++] = (NFDATA>>24)&0xff;
}
//取消選中nandflash芯片
deselect_chip();
}
對應的子函數如下(發送命令和地址都是寫到相應的寄存器中):
void select_chip()
{
NFCONT &= ~(1<<1);
}
void deselect_chip()
{
NFCONT |= (1<<1);
}
void clear_RnB()
{
NFSTAT |= (1<<2);
}
void send_cmd(unsigned cmd)
{
NFCMD = cmd;
}
void send_addr(unsigned addr)
{
NFADDR = addr;
}
void wait_RnB()
{
while (!(NFSTAT&(1<<2)))
{
;
}
}
在對nandfalsh進行讀寫之前,要對其進行初始化,初始化主要是通過操作相應寄存器控制其他信號線上的的高低電平時間,保證時序的準確性和有效性:
//HCLK的時鐘頻率是100HZ,每一個頻率週期的10ns,查看nandfash的控制寄存器,合理設置相應時間。
#define TACLS 1
#define TWRPH0 2
#define TWRPH1 1
void nand_init()
{
//初始化NFCONF
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
//初始化NFCONT
NFCONT = (1<<0) | (1<<1);
//復位
nand_reset();
}
芯片的初始化中,要對芯片進行復位(發送命令0xff,並等待RnB變爲空閒狀態):
代碼:
void nand_reset()
{
//選中flash
select_chip();
//清除RnB
clear_RnB();
//發送0xff命令
send_cmd(0xff);
//等待RnB
wait_RnB();
//取消選中flash
deselect_chip();
}
將nandflash中的內容拷貝到內存中(頁讀的方式,每次拷貝2k字節):
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++;
}
}
上面的函數是在stast.s中調用的,就涉及到了在彙編中調用c語言並傳遞參數的問題:
@如果形參個數少於或等於4,則形參由R0,R1,R2,R3四個寄存器進行傳遞;若形參個數大於4,大於4的部分必須通過堆棧進行傳遞。
bl nand_init
bl copy_to_ram
copy_to_ram:
mov r0,#0x00 @nand頁的起始地址爲0
ldr r1,=_start @內存的起始地址(參看鏈接器腳本)
ldr r2,=bss_end
sub r2,r2,r1
mov ip,lr
bl nand_to_ram
mov lr,ip
mov pc,lr
3、nandflash驅動設計~寫
1)按頁寫(本節按這種方式來寫)
2)隨機寫
int NF_WritePage(unsigned long addr,unsigned char *buff)
{
unsigned int i,ret = 0;
//選中nandflash
select_chip();
//清除RnB
clear_RnB();
//發送0x80命令
send_cmd(0x80);
//發送2個列地址
send_addr(0x00);
send_addr(0x00);
//發送3個行地址
send_addr(addr&0xff);
send_addr((addr>>8)&0xff);
send_addr((addr>>16)&0xff);
//發送數據
for(i=0;i<2048;i++)
{
NFDATA = buff[i];
}
//發送0x10命令
send_cmd(0x10);
//等待RnB
wait_RnB();
//發送0x70命令
send_cmd(0x70);
//讀取寫入結果
ret = NFDATA;
//關閉nandflash
deselect_chip();
return ret;
}
在寫入數據前,必須擦除存儲區:
int NF_Erase(unsigned long addr)
{
int ret;
//選中flash芯片
select_chip();
//清除RnB
clear_RnB();
//發送命令0x60
send_cmd(0x60);
//發送行地址
send_addr(addr&0xff);
send_addr((addr>>8)&0xff);
send_addr((addr>>16)&0xff);
//發送命令D0
send_cmd(0xD0);
//等待RnB
wait_RnB();
//發送命令0x70
send_cmd(0x70);
//讀取擦除結果
ret = NFDATA;
//取消選中flash芯片
deselect_chip();
return ret;
}
爲了驗證以上的驅動程序寫的是否正確,可以進行試驗來驗證(通過判斷led的狀態):
int gboot_main()
{
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;
}