关于nandflash的说明,请参考其他。
现在先贴出来韦东山先生的代码,作我学习之用。
1 @************************************************
2 @ File:head.s
3 @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
4 @************************************************
5
6 .text
7 .global _start
8 _start:
9 @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
10 ldr sp, =4096 @设置堆栈
11 bl disable_watch_dog @关WATCH DOG
12 bl memsetup @初始化SDRAM
13 bl nand_init @初始化NAND Flash
14
15 @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
16 @nand_read_ll函数需要3个参数:
17 ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址
18 mov r1, #4096 @2. 源地址 = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处
19 mov r2, #2048 @3. 复制长度= 2048(bytes),对于本实验的main.c,这是足够了
20 bl nand_read @调用C函数nand_read
21
22 ldr sp, =0x34000000 @设置栈
23 ldr lr, =halt_loop @设置返回地址
24 ldr pc, =main @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
25
26 halt_loop:
27 b halt_loop
28
上面的代码的作用很明显,现在主要说明地址相关的东西;
ldr sp, =4096 @设置堆栈 首先上电后4k代码会自动拷贝到steppingstone里面,disable_watch_dog,memsetup等函数都是在这4k范围里面。
后面的链接脚本如下:
1 SECTIONS {
2 firtst 0x00000000 : { head.o init.o nand.o} @在nand flash里面地址为0x0000_0000初存放head.o,init.o,nand.o等
3 second 0x30000000 : AT(4096) { main.o } @在4096处,注意此时是4k以外,这个代码就是指出当代码大小大于4k时的处理方法。
4 } @可以看到main.o的运行地址是0x3000_0000,说明运行4k以后的代码需要复制到sdram里面执行。
5
上面说明main函数是在刚才提到的4k范围以外。所以head文件里面会有将4096(4k)的main函数编译出来的东西拷贝到3000_0000也就是sdram的地方。
这个在以后程序比较大的时候基本是比较通用的函数,所以一定得花时间搞明白。上面简单的说明来地址映射方面的问题。
再看init.c,这个文件只是关闭看门狗和初始化sdram:
1 /* WOTCH DOG register */
2 #define WTCON (*(volatile unsigned long *)0x53000000)
3
4 /* SDRAM regisers */
5 #define MEM_CTL_BASE 0x48000000 //定义寄存器的方法
6
7 void disable_watch_dog();
8 void memsetup();
9
10 /*上电后,WATCH DOG默认是开着的,要把它关掉 */
11 void disable_watch_dog()
12 {
13 WTCON = 0;
14 }
15
16 /* 设置控制SDRAM的13个寄存器 */
17 void memsetup()
18 {
19 int i = 0;
20 unsigned long *p = (unsigned long *)MEM_CTL_BASE;
21
22 /* SDRAM 13个寄存器的值 */
23 unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
24 0x00000700, //BANKCON0
25 0x00000700, //BANKCON1
26 0x00000700, //BANKCON2
27 0x00000700, //BANKCON3
28 0x00000700, //BANKCON4
29 0x00000700, //BANKCON5
30 0x00018005, //BANKCON6
31 0x00018005, //BANKCON7
32 0x008C07A3, //REFRESH
33 0x000000B1, //BANKSIZE
34 0x00000030, //MRSRB6
35 0x00000030, //MRSRB7
36 };
37
38 for(; i < 13; i++)
39 p[i] = mem_cfg_val[i];
40 }
上面设置sdram寄存器的方法是用c语言,现在贴出汇编的方法来比较;
1 memsetup:
2 @ 设置存储控制器以便使用SDRAM等外设
3
4 mov r1, #MEM_CTL_BASE @ 存储控制器的13个寄存器的开始地址
5 adrl r2, mem_cfg_val @ 这13个值的起始存储地址
6 add r3, r1, #52 @ 13*4 = 54
7 1:
8 ldr r4, [r2], #4 @ 读取设置值,并让r2加4
9 str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4
10 cmp r1, r3 @ 判断是否设置完所有13个寄存器
11 bne 1b @ 若没有写成,继续
12 mov pc, lr @ 返回
13
14
15 .align 4
16 mem_cfg_val:
17 @ 存储控制器13个寄存器的设置值
18 .long 0x22011110 @ BWSCON
19 .long 0x00000700 @ BANKCON0
20 .long 0x00000700 @ BANKCON1
21 .long 0x00000700 @ BANKCON2
22 .long 0x00000700 @ BANKCON3
23 .long 0x00000700 @ BANKCON4
24 .long 0x00000700 @ BANKCON5
25 .long 0x00018005 @ BANKCON6
26 .long 0x00018005 @ BANKCON7
27 .long 0x008C07A3 @ REFRESH
28 .long 0x000000B1 @ BANKSIZE
29 .long 0x00000030 @ MRSRB6
30 .long 0x00000030 @ MRSRB7
同样是往这13个寄存器写值。
而main函数只是led的亮灭,这里就不贴出来了
现在主要看nand.c文件。
先贴出来如下:
//===================================我是分解线=============================
//@nand.c
1 #define LARGER_NAND_PAGE
2
3 #define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
4 #define BUSY 1
5
6 #define NAND_SECTOR_SIZE 512 @小页
7 #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1) 511 0b1_1111_1111
8
9 #define NAND_SECTOR_SIZE_LP 2048 @2k 大页
10 #define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1) @2047 0b111_1111_1111