一。環境和編譯器
開發板:jz2440
系統:Ubuntu12.04
編譯器:gcc
二。驗證代碼
1.head.S
1 @****************************************************************************** 2 @ File:head.s 3 @ 功能:設置SDRAM,將程序復制到SDRAM,然后跳到SDRAM繼續執行 4 @auther: 5 @****************************************************************************** 6 7 .text 8 .global _start 9 _start:10 @函數disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定義11 ldr sp, =4096 @設置堆棧 12 bl disable_watch_dog @關WATCH DOG13 bl memsetup @初始化SDRAM14 bl nand_init @初始化NAND Flash15 16 @將NAND Flash中地址4096開始的1024字節代碼(main.c編譯得到)復制到SDRAM中17 @nand_read_ll函數需要3個參數:18 ldr r0, =0x30000000 @1. 目標地址=0x30000000,這是SDRAM的起始地址19 mov r1, #4096 @2. 源地址 = 4096處20 mov r2, #2048 @3. 復制長度= 2048(bytes)21 bl nand_read @調用C函數nand_read22 23 ldr sp, =0x34000000 @設置棧24 ldr lr, =halt_loop @設置返回地址25 ldr pc, =main @b指令和bl指令只跳轉32M的范圍,所以這里使用向pc賦值的方法進行跳轉26 halt_loop:27 b halt_loop
2.init.c
1 #define WTCON (*(volatile unsigned long *)0x53000000) 2 #define MEM_CTL_BASE 0x48000000 3 4 void disable_watch_dog(); 5 void memsetup(); 6 7 /*關掉看門狗 */ 8 void disable_watch_dog() 9 {10 WTCON = 0;11 }12 13 /* 設置SDRAM */14 void memsetup()15 {16 int i = 0;17 unsigned long *p = (unsigned long *)MEM_CTL_BASE;18 19 /* SDRAM 13個寄存器的值 */20 unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON21 0x00000700, //BANKCON022 0x00000700, //BANKCON123 0x00000700, //BANKCON224 0x00000700, //BANKCON3 25 0x00000700, //BANKCON426 0x00000700, //BANKCON527 0x00018005, //BANKCON628 0x00018005, //BANKCON729 0x008C07A3, //REFRESH30 0x000000B1, //BANKSIZE31 0x00000030, //MRSRB632 0x00000030, //MRSRB733 };34 35 for(; i < 13; i++)36 p[i] = mem_cfg_val[i];37 }
3.nand.c 以下代碼不支持2410 也不支持小頁讀寫,是裁剪源碼的結果。
1 #define GSTATUS1 (*(volatile unsigned int *)0x560000B0) 2 #define BUSY 1 3 4 #define NAND_SECTOR_SIZE_LP 2048 5 #define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1) 6 7 typedef unsigned int S3C24X0_REG32; 8 9 10 typedef struct { 11 S3C24X0_REG32 NFCONF; 12 S3C24X0_REG32 NFCONT; 13 S3C24X0_REG32 NFCMD; 14 S3C24X0_REG32 NFADDR; 15 S3C24X0_REG32 NFDATA; 16 S3C24X0_REG32 NFMECCD0; 17 S3C24X0_REG32 NFMECCD1; 18 S3C24X0_REG32 NFSECCD; 19 S3C24X0_REG32 NFSTAT; 20 S3C24X0_REG32 NFESTAT0; 21 S3C24X0_REG32 NFESTAT1; 22 S3C24X0_REG32 NFMECC0; 23 S3C24X0_REG32 NFMECC1; 24 S3C24X0_REG32 NFSECC; 25 S3C24X0_REG32 NFSBLK; 26 S3C24X0_REG32 NFEBLK; 27 } S3C2440_NAND; 28 29 static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; 30 31 32 33 /* 供外部調用的函數 */ 34 void nand_init(void); 35 void nand_read(unsigned char *buf, unsigned long start_addr, int size); 36 37 38 /* S3C2440的NAND Flash處理函數 */ 39 static void s3c2440_nand_reset(void); 40 static void s3c2440_wait_idle(void); 41 static void s3c2440_nand_select_chip(void); 42 static void s3c2440_nand_deselect_chip(void); 43 static void s3c2440_write_cmd(int cmd); 44 static void s3c2440_write_addr_lp(unsigned int addr); 45 static unsigned char s3c2440_read_data(void); 46 47 48 49 /* 復位 */ 50 static void s3c2440_nand_reset(void) 51 { 52 s3c2440_nand_select_chip(); 53 s3c2440_write_cmd(0xff); // 復位命令 54 s3c2440_wait_idle(); 55 s3c2440_nand_deselect_chip(); 56 } 57 58 /* 等待NAND Flash就緒 */ 59 static void s3c2440_wait_idle(void) 60 { 61 int i; 62 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT; 63 while(!(*p & BUSY)) 64 for(i=0; i<10; i++); 65 } 66 67 /* 發出片選信號 */ 68 static void s3c2440_nand_select_chip(void) 69 { 70 int i; 71 s3c2440nand->NFCONT &= ~(1<<1); 72 for(i=0; i<10; i++); 73 } 74 75 /* 取消片選信號 */ 76 static void s3c2440_nand_deselect_chip(void) 77 { 78 s3c2440nand->NFCONT |= (1<<1); 79 } 80 81 /* 發出命令 */ 82 static void s3c2440_write_cmd(int cmd) 83 { 84 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD; 85 *p = cmd; 86 } 87 88 89 90 91 static void s3c2440_write_addr_lp(unsigned int addr) 92 { 93 int i; 94 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; 95 int col, page; 96 97 col = addr & NAND_BLOCK_MASK_LP; 98 page = addr / NAND_SECTOR_SIZE_LP; 99 100 *p = col & 0xff; /* Column Address A0~A7 */101 for(i=0; i<10; i++); 102 *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */103 for(i=0; i<10; i++);104 *p = page & 0xff; /* Row Address A12~A19 */105 for(i=0; i<10; i++);106 *p = (page >> 8) & 0xff; /* Row Address A20~A27 */107 for(i=0; i<10; i++);108 *p = (page >> 16) & 0x03; /* Row Address A28~A29 */109 for(i=0; i<10; i++);110 }111 112 113 /* 讀取數據 */114 static unsigned char s3c2440_read_data(void)115 {116 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;117 return *p;118 }119 120 121 /* 初始化NAND Flash */122 void nand_init(void)123 {124 #define TACLS 0125 #define TWRPH0 3126 #define TWRPH1 0127 128 129 /* 設置時序 */130 s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);131 /* 使能NAND Flash控制器, 初始化ECC, 禁止片選 */132 s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);133 134 135 /* 復位NAND Flash */136 s3c2440_nand_reset();137 }138 139 140 /* 讀函數 */141 void nand_read(unsigned char *buf, unsigned long start_addr, int size)142 {143 int i, j;144 145 146 if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {147 return ; /* 地址或長度不對齊 */148 } //NAND_BLOCK_MASK_LP=2047 111_1111_1111 SIZE與之相與,若等于0,說明SIZE低11都是0,那么地址對齊;否則不對齊149 150 151 /* 選中芯片 */152 s3c2440_nand_select_chip();153 154 for(i=start_addr; i < (start_addr + size);) {155 /* 發出READ0命令 */156 s3c2440_write_cmd(0);157 158 /* Write Address */159 s3c2440_write_addr_lp(i);160 161 s3c2440_write_cmd(0x30); 162 163 s3c2440_wait_idle();164 165 166 for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {167 168 *buf = s3c2440_read_data();169 buf++;170 } //i的值等于0,512,1024,1536這四個值,也就可以讀寫0到2047這2k之內連續的地址的值171 }172 173 /* 取消片選信號 */174 s3c2440_nand_deselect_chip();175 176 return ;177 }
4.led.c
就是led閃爍之類的,免了。
5.Makefile
1 objs := head.o init.o nand.o led.o 2 3 nand.bin : $(objs) 4 arm-linux-ld -Tnand.lds -o nand_elf $^ 5 arm-linux-objcopy -O binary -S nand_elf $@ 6 arm-linux-objdump -D -m arm nand_elf > nand.dis 7 8 %.o:%.c 9 arm-linux-gcc -Wall -c -O2 -o $@ $<10 11 %.o:%.S12 arm-linux-gcc -Wall -c -O2 -o $@ $<13 14 clean:15 rm -f nand.dis nand.bin nand_elf *.o
以上makefile與之前不同,下面是編譯細節,請用心比較
6.鏈接腳本
1 SECTIONS { 2 firtst 0x00000000 : { head.o init.o nand.o}3 second 0x30000000 : AT(4096) { led.o }4 } 5
三。實驗結果
經驗證,可實現。
本代碼參考韋東山先生的源碼,作來相應的裁剪。
源碼上支持2410和2440,以及大頁,小頁的讀寫,這里都分別作了裁剪到只支持2440和大頁讀。
nand flash理論性的東西改天附上。
四。根據
我們這里選用芯片的存儲結構,這里選用大頁讀寫,單位是2k,小頁是512字節。其中64BYTES是OBB,這里沒有用到。
而大頁的寫地址定義如下:
需要連續5次,正如代碼中:
1 static void s3c2440_write_addr_lp(unsigned int addr) 2 { 3 int i; 4 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; 5 int col, page; 6 7 col = addr & NAND_BLOCK_MASK_LP; 8 page = addr / NAND_SECTOR_SIZE_LP; 9 10 *p = col & 0xff; /* Column Address A0~A7 */11 for(i=0; i<10; i++); 12 *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */13 for(i=0; i<10; i++);14 *p = page & 0xff; /* Row Address A12~A19 */15 for(i=0; i<10; i++);16 *p = (page >> 8) & 0xff; /* Row Address A20~A27 */17 for(i=0; i<10; i++);18 *p = (page >> 16) & 0x03; /* Row Address A28~A29 */19 for(i=0; i<10; i++);20 }
明顯區別與小頁的,可以參看原理圖中小頁的寫地址定義。
此外,讀寫命令如下:
復位命令是ff,代碼是
1 /* 復位 */2 static void s3c2440_nand_reset(void)3 {4 s3c2440_nand_select_chip();5 s3c2440_write_cmd(0xff); // 復位命令6 s3c2440_wait_idle();7 s3c2440_nand_deselect_chip();8 }
而2440中相關控制寄存器如下截圖:
使能信號是NFCONT的最低位 置1 ;
片選是NFCONT【1】位 寫0;
NFCONT[4]是ECC功能用的。
片選代碼:
1 /* 發出片選信號 */ 2 static void s3c2440_nand_select_chip(void) 3 { 4 int i; 5 s3c2440nand->NFCONT &= ~(1<<1); 6 for(i=0; i<10; i++); 7 } 8 9 /* 取消片選信號 */10 static void s3c2440_nand_deselect_chip(void)11 {12 s3c2440nand->NFCONT |= (1<<1);13 }
此外等待信號由NFSTAT決定:
代碼是:
1 /* 等待NAND Flash就緒 */2 static void s3c2440_wait_idle(void)3 {4 int i;5 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;6 while(!(*p & BUSY))7 for(i=0; i<10; i++);8 }
發出命令即直接向NFCMD寫地址就好:
1 /* 發出命令 */2 static void s3c2440_write_cmd(int cmd)3 {4 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;5 *p = cmd;6 }
讀數據:
1 /* 讀取數據 */2 static unsigned char s3c2440_read_data(void)3 {4 volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;5 return *p;6 }
連續讀寫一頁的數據:
1 /* 讀函數 */ 2 void nand_read(unsigned char *buf, unsigned long start_addr, int size) 3 { 4 int i, j; 5 6 7 if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) { 8 return ; /* 地址或長度不對齊 */ 9 }10 11 12 /* 選中芯片 */13 s3c2440_nand_select_chip();14 15 for(i=start_addr; i < (start_addr + size);) {16 /* 發出READ0命令 */17 s3c2440_write_cmd(0);18 19 /* Write Address */20 s3c2440_write_addr_lp(i);21 22 s3c2440_write_cmd(0x30); 23 24 s3c2440_wait_idle();25 26 27 for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {28 29 *buf = s3c2440_read_data();30 buf++;31 }32 }33 34 /* 取消片選信號 */35 s3c2440_nand_deselect_chip();36 37 return ;38 }
首先判斷地址對齊,如不對齊,讀數據取消,因為結果是未知的。 上面解釋就是SIZE與上2047(111_1111_1111),若SIZE的低11位都是0,那么相與后,結果是0,則說明地址對齊;否則不對齊。
i的值是0、2048這個值,可以在0到2047這2k區間連續讀。
/* 發出READ0命令 */
s3c2440_write_cmd(0); /* Write Address */ s3c2440_write_addr_lp(i); s3c2440_write_cmd(0x30); s3c2440_wait_idle();
先發出命令00,表示從開始讀,也就是順序讀寫。
然后寫如地址。
新聞熱點
疑難解答