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中去執行。
實現的效果如圖: