nand flash按照我的了解,在開發闆上就類似我們所用的電腦中的硬碟,用來儲存系統運作的作業系統,應用程式,資料等,掉電之後還可以永久得儲存資料(不包括臨時資料)。通過控制或配置NAND Flash的控制器寄存器,即可完成對nand的操作:包括讀、寫、擦除等。
而控制、配置這些寄存器是根據闆子的原理圖、使用者手冊以及nand flash晶片手冊上的說明來配置的。
通過原理圖,可見nand flash與cpu之間的資料傳輸主要是通過LDDATA0~7這8根引腳線,其中傳輸的“資料”可以是位址、資料,也可以是指令,這就要靠CLE、ALE引腳的狀态進行選擇。 對于指令的傳輸,主要是對NFCMD寄存器寫以上相對應的指令值,但這個指令是分兩個周期進行的。而對于資料的傳輸,在資料傳輸模式下,傳輸資料也是要根據以上的格式去進行傳輸,分5個周期,每一個周期傳輸的資料都十分講究,這就要求要有一個符合這種格式的巧妙算法(在接下來的代碼中展現)。
了解了這些指令、資料的傳輸格式之後,就是要對具體的寄存器進行相應的配置了。首先要介紹這些寄存器的作用(資料手冊上對各寄存器都有說明):
(1)NFCONF:用來設定時序參數,設定位寬。
(2)NFCMD:指令寄存器。
(3)NFADDR:位址寄存器。
(4)NFDATA:資料寄存器,用于讀寫資料。
(5)NFSTAT:狀态寄存器,隻用到最低1位,表示是否忙碌。
程式設計舉例:
實作從nand flash中拷貝程式到sdram中運作,其中涉及到啟動代碼的程式設計,初始化記憶體控制器,讀取并拷貝nand flash上的資料。(隻展示部分核心代碼)
head.S
.text
.global _start
_start:
ldr sp,=4096 @設定堆棧
bl disable_watch_dog @關閉看門狗
bl set_mem @設定記憶體控制器
bl nand_init @nand初始化
ldr r0,=0x30000000
ldr r1,=4096
ldr r2,=4096 @傳遞參數
bl nand_read @從nand中拷貝
ldr sp,=0x38000000
ldr lr, =halt @設定傳回位址
ldr pc, =main
halt:
b halt
nand.c
typedef struct s3c2440_nand
{
unsigned int NFCONF;
unsigned int NFCONT;
unsigned int NFCMMD;
unsigned int NFADDR;
unsigned int NFDATA;
unsigned int NFMECCD0;
unsigned int NFMECCD1;
unsigned int NFSECCD;
unsigned int NFSTAT;
unsigned int NFESTAT0;
unsigned int NFESTAT1;
unsigned int NFMECC0;
unsigned int NFMECC1;
unsigned int NFSECC;
unsigned int NFSBLK;
unsigned int NFEBLK;
}s3c2440_nand;
//定義nandflash控制器的起始位址
static s3c2440_nand* nand_base = (s3c2440_nand*)0x4E000000;
//片選
void select_chip_or_not(int flag)//0不片選,1片選
{
if(flag == 1)
{
nand_base->NFCONT |= (0x1<<1);
}
if(flag == 0)
{
nand_base->NFCONT &= ~(0x1<<1);
}
}
void write_command(unsigned char cmd)
{
volatile unsigned char *p = (volatile unsigned char *)&nand_base->NFCMMD;
*p = cmd;
}
void write_addr(unsigned int addr)
{
int i;
volatile unsigned char* p = (volatile unsigned char *)nand_base->NFADDR;
*p = addr & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 9) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 17) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 25) & 0xff;
for(i=0; i<10; i++);
}
//等待nand flash就緒
void wait_ldle(void)
{
volatile unsigned char* p = (volatile unsigned char*)nand_base->NFSTAT;
int flag = *p & 1;
while(!flag)
{
int i;
for(i=0;i<20;i++);
}
}
unsigned char read_data(void)
{
volatile unsigned char *p= (volatile unsigned char*)nand_base->NFDATA;
return *p;
}
void nand_read(unsigned char* buf,unsigned char base_addr,unsigned int size)
{
int i,j;
//片選
select_chip_or_not(1);
//複制資料
for(i=base_addr;i<base_addr+size;)
{
write_command(0);//發送讀指令
write_addr(i); //發送位址
write_command(0x30);
wait_ldle();
for(j=0;j<512;j++,i++)
{
*buf = read_data(); //讀取資料,一次讀取一頁(512個位元組)
buf++;
}
}
select_chip_or_not(0);//取消片選
}
void nand_reset(void)
{
select_chip_or_not(1);//片選
write_command(0xff);
wait_ldle();
select_chip_or_not(0);
}
void nand_init(void)
{
nand_base->NFCONF = (0<<12)|(3<<8)|(0<<4);
nand_base->NFCONT = (1<<4)|(1<<1)|(1<<0);
nand_reset();
}
還要說下Makefile
objs := head.o init.o nand.o main.o
nand.bin : $(objs)
arm-linux-ld -Tnand.lds $^ -o nand_elf
arm-linux-objcopy -O binary -S nand_elf $@
%.o : %.S
arm-linux-gcc -c $< -o $@
%.o : %.c
arm-linux-gcc -c $< -o $@
clean :
rm -f nand.bin *.o nand_elf
平時寫的Makefile不一樣,這裡使用了一個名為nand.lds的連結腳本,這樣做主要是為了試驗讓編譯好的程式在連結時存放的位址和理論運作位址都不同且不在同一個儲存設備上,友善看試驗效果。
nand.lds
SECTIONS {
first 0x00000000 : {head.o init.o nand.o}
second 0x30000000 : AT(4096) {main.o}
}
連結腳本上的第一段是放head.S init.c nand.c編譯出的内容,從0位址開始存放和執行,而main.o則是需要從nand flash手動複制到sdram中去執行。