一、
@*********************************************************************************************************************************************************************************************************************** @ File: head.s @功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 @*********************************************************************************************************************************************************************************************************************** .text @表示下面的语句都属于代码段 .global _start @.global是将文本中的某个程序定义为全局的 _start: @这些作为启动代码,nand flash中的前4K自动复制到SDRAM中,这4k在sdram中存储和运行,主要包括:关看门狗,存储控制器寄存器的设置,NNAD FLASH读数据前的预处理三个部分 ldr sp, =4096 @执行C语言前需要设置栈,这里是关看门狗、设置存储控制器、NAND FLASH读数据前预处理,这些在stepping中运行,stepping大小4k,栈指针设置指向栈顶 @C语言中编译出来的程序,运行时,内存的分配:1、栈(stack):由编译器自动分配、翻译,存放函数的参数值和局部变量,操作方式类似于数据结构的栈;2、堆:由程序员分配、释放。如果程序员不释放, @程序结束时有可能由操作系统释放。请注意它和数据结构中堆是两回事,操作方式类似于链表。3、BSS段,存放未初始化的全局变量和静态变量。4、数据段DATA,存放初始化之后的全局变量和静态变量 @代码段TEST:程序代码主体,函数主体等,注意为二进制格式。 @栈和堆栈:1、栈(heap):由系统自动分配;只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出;对于申请大小,在编译程序时,由编译器确定;扩展方式是向低地址扩展; @对于申请效率,由系统自动分配,速度快,但是程序员无法控制;存储的内容,函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数, @在大多数C编译器中,参数是由右往左入栈的。2、堆(stack):申请方式是程序员分配,并且需指明大小;申请之后,操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第 @一个空间大于所申请空间的堆结点,然后将该节点从空闲节点链表中删除,并将该结点的空间分配给程序,对于大多数系统,会在这块内存空间的首地址处记录本次分配的大小,这样,代码中的delete语句才能 @正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好的呢个语申请的大小,系统会自动的将多余的那部分重新放入空闲链表中;申请大小的限制,堆的大小受限于计算机系统中有效的虚拟内存; @扩展方向,向高地址扩展;申请效率,由程序员分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便;存放内容,一般是在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排 bl disable_watch_dog @关看门狗 bl memsetup @初始化SDRAM,设置存储控制器 bl nand_init @初始化NAND Flash, 主要包括:1、设置时序,2、使能NAND Flash控制器、禁止控制引脚信号nFCE、初始化ECC,3、使用前复位; 同时里面包括了初始化所有子函数的定义,以及读数据的子函数 @这部分运行代码放在4K之后,当程序启动后将它们读出来,然后存入SDRAM中;将NAND FLASH中地址4096开始的2048字节代码(main.c编译得到,代码较少,取1k足够)复制到SDRAM中 ldr r0, =0x30000000 @SDRAM的起始地址,目标地址 mov r1, #4096 @nand flash中4k之后的起始地址,源地址,main.c中的代码都存在于NAND Flash地址4096开始处 mov r2, #2048 @复制代码的长度,取2048bytes,对于本实验的main.c, 这是足够了 bl nand_read @跳转到读数据,把NAND FLASH 地址4096(4k)开始的代码,取长度2048(2k),读取到SDRAM上以0x30000000开始的地址上 ldr sp, =0x34000000 @第二部分代码运行的堆栈设置,栈指针指向栈顶,SDRAM的尾地址0x34000000,栈的扩展方向是向低地址扩展,所以栈指针指向栈顶 ldr lr, =halt_loop @设置返回地址;lr也就是r14连接寄存器,用来保存子程序的返回地址;在汇编中调用c函数时,如果C函数中有返回值,那么调用前就一定要先配置lr的值,也就是执行完C函数之后pc指针指向的地址; @ldr lr, =halt_loop,这里的意思就是说,执行C函数之前,先把死循环那里的地址放到lr中,执行完C之后,PC指针就指向了Lr的值,也就死循环的地址,也就是说,执行完C之后,就接着运行死循环啦; @当然,C函数中没有返回值时,也就不必先设置lr的值了,它没有返回,就自然执行完了,就执行下一条指令,当然也就没必要设置了 ldr pc, =main @也就相当于跳转到main函数,b指令和bl指令只能跳转32M的范围,所以这里使用向pc赋值的方法进行跳转;b和bl指令只能跳转32M的范围,因为寄存器是32位的,此时的值是24位有符号数,所以是32M halt_loop: b halt_loop @死循环;死循环的作用:在最后加个死循环,也就是让程序停留在这里了,这样是很有用的,一方面可以在这个时候查看各个存储器的值,以便检查自己的程序是否正确,另一方面,比如如果是C函数比如用到printf @这样之类的函数,如果不加死循环,输出会瞬间消失
二、
/***************************************************************************************************************************************************************************************************************************** 关看门狗和配置存储控制器 *****************************************************************************************************************************************************************************************************************************/ /***************** 初始宏定义 *****************/ #define WTCON (*(volatile unsigned long *)0x53000000) //看门狗控制寄存器WTCON;整句的意思就是:取地址0x53000000的值为WTCON; 解释:1、0x53000000是看门狗看控制寄存器WTCON的地址;2、volatile unsigned long * //代表一个无符号长整形的指针,volatile是一个类型限定词,表示这个值是不稳定的,编译器不好把这个值放入寄存器缓存中进行优化; //3、(volatile unsigned long *)0x53000000意思就是说把0x53000000当作一个去优化的无符号长整型指针; //4、*(volatile unsigned long *)0x53000000意思就是取地址0x53000000所指向的无符号长整型的数值; #define MEM_CTL_BASE 0X48000000 //存储控制寄存器的首地址;跟上面宏定义的区别:定义了一个变量表示那个地址对应的值,这里相当于定义一个变量,为那个地址 /**************** 关看门狗 ****************/ void disable_watch_dog(); { WTCON=0; //往寄存器中直接写0,就可以关闭 } /********************* 设置SDRAM的13个寄存器 *********************/ void memsetup() { int i; unsigned long *p = (unsigned long *)MEM_CTL_BASE; //这句的意思就是定义一个指针变量P指向地址MEM_CTL_BASE; //指针赋值一般三种:1、int *p1=&a; 2、int *p; p=&a; 3、p1=p2 //为什么不直接Unisgned long *p = MEME_CTL_BASE呢,因为前面定义的是无符号长整形指针变量,后面(unsigned long *)就是一个强制类型转换,把这个地址的类型 //转换成跟前面指针类型匹配的指针,然后赋值,如果*(unsigned long *)MEM_CTL_BASE也就是取这个地址指针的值 //简单的说,这里相当于unsigned long *p1=p2; 在这里 (unsigned long *)MEM_CTL_BASE相当于就是p2,是指向MEM_CTL_BASE的一个变量 unsigned long const mem_cfg_val[]={ //13个寄存器需要配置的值 0x22011110, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x00018005, //BANKCON6 0x00018005, //BANKCON7 0x008C07A3, //REFRESH 0x000000B1, //BANKSIZE 0x00000030, //MRSRB6 0x00000030, //MRSRB7 } for(i=0;i<13;i++) p[i]=mem_cfg_val[i]; //给地址赋值, 一个指针通常是分配4个字节; p[i]和*(p+i)相同意思就是说从地址p开始向后偏移i个单位后的地址空间内的值,而一个指针单位通常分配4个字节,所以每次偏移4字节的地址,刚好指向下一个 //寄存器的首地址 }
三、
/******************************************************************************************************************************************************************************************************************************** NAND Flash初始化 ********************************************************************************************************************************************************************************************************************************/ /********************* 初始定义部分 *********************/ #define LARGER_NAND_PAGE //宏定义大页 #define GSTATUS1 (*(volatile unsigned int *)0x56000000) // #define BUSY 1 //宏定义BUSY为1,用于检查NFSTAT位0,循环查询NFSTAT位0,直到它等于1 #define NAND_SETOR_SIZE 512 //宏定义小页的大小;NAND Flash的型号是K9F1208U0M,结构就是小页,内部4096块,每块32页,每页512+16(512的主区main区一般用来存放主要数据, //16字节的备用域spare区一般用来存放ECC校验码) #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1) #define NAND_SETOR_SIZE_LP 2048 //宏定义大页;如果NNAD Flash的型号是K9F2G8U0A,结构就是大页,内部2048块,每块64页,每页2k+64(2k的主区main区一般用来存放主要数据, //64字节的备用域spare区一般用来存放ECC校验码) #define NAND_SETOR_SIZE_LP (NAND_SECTOR_SIZE_LP - 1) /*结构体说明: 1、结构体是一种构造数据类型,将不同类型的数据组合成一个整体 2、结构体定义: struct 结构体名 { 类型标识符 成员名; }; 3、struct是关键字,不能省略;结构体名可以省略,无名结构体;成员类型可以是基本型或构造型; 4、结构体变量的定义: (1)先定义结构体类型,在定义结构体变量: struct 结构体名 {类型标识符 成员名;}; struct 结构体名 结构体变量名; (2)定义结构体类型的同时定义结构体变量: struct 结构体名 {类型标识符 成员名;} 结构体变量名; (3)直接定义结构体变量(没有结构体名的无名结构体):struct {类型标识符 成员名;} 结构体变量名; 这样没有结构体名的,只能这样定义一次,也就是说不能再向(1)那样在后面随意定义了 5、结构体变量的引用: (1)结构体变量不能整体引用,只能引用变量成员 (2)结构体变量引用方式:结构体变量名.成员名 (3)例如 struct 结构体名 {类型标识符 成员1;} 结构体变量1,结构体变量2;这样,可以把一个变量赋值给另一个变量,如 结构体名.变量1=结构体名.变量1; 6、结构体变量的初始化: (1)形式一:struct 结构体名 { 类型标识符 成员名;} ; struct 结构体名 结构体变量={初始数据}; (2)形式二:struct 结构体名 { 类型标识符 成员名;} 结构体变量={初始数据}; 7、结构体数组: (1)结构体数组就是,用一个数组来包括所有的结构体变量,数组的每个元素,就是一个结构体变量; (2)形式一:struct 结构体名 { 类型标识符 成员名;} ; struct 结构体名 结构体数组 (3)形式二:struct 结构体名 { 类型标识符 成员名;} 结构体数组; (4)形式三:struct { 类型标识符 成员名;} 结构体数组; 8、结构体数组初始化:在前面的基础上加上初始变量组的各个成员 (1)形式一:用大括号将各个变量的一组成员括起来:结构体名 结构体数组名[]={ {成员1值1,成员2值1},{成员1值2,成员2值2}; (2)形式二:如果不加括号分开,那么按顺序排:结构体名 结构体数组名[]={成员1值1,成员2值1,成员1值2,成员2值2}; 9、结构体和指针 (1)指向结构体变量的指针 a.定义形式:struct 结构体名 *结构体指针名; b.使用结构体指针变量引用成员形式:(*结构体指针名).成员名 或者 结构体指针名->成员名 或者 结构体变量名.成员名 (2)指向结构体数组的指针 (3)用指向结构体的指针作函数参数 a.用结构体变量的成员作参数----值传递 b.用指向结构体变量或数组的指针作参数----地址传递 c.用结构体变量作为参数------多值传递,效率低 */ /* typedef说明: 1、typedef是C语言的关键字,作用是为一个数据类型定义一个新的数据类型,它并没有新建一种新的数据类型,只是把原有数据类型换了一个名字而已 2、用法一:为现有的数据类型定义同义词,便于记住和归类使用等,格式:typedef 原有数据类型 定义新的名字;定义之后,可以在任何需要使用原数据类型处,用新的名字替换;例如:typedef int size; 以后就都可以用size来代替int; 3、用法二:用来掩饰复合类型: a.数组,格式:typedef 原数据类型名 数组; 例如:typedef char line[81];以后line a, b; 就等价于 char a[88],b[88]; b.指针,格式:typedef const 原数据类型名 指针 ;例如:typedef char *pstr; 以后使用pstr就相当于char *;(只要是指针生命typedef,不管什么时候都需要在typedef中加一个const,以使得该指针本身是常量,而不是对象。 4、用法三:在链表(struct)中的应用:格式:typedef struct {类型标识符,成员名 }结构名别名;例如:typedef struct { int a; int b;} MY_TYPE; 这样声明之后,以后如果需要定义结构就直接MY_TYPE tmp;tmp是满足这个结构的一个变量; 如果没有家typedef的话,后面直接定义肯定不能省略结构体名,定义的时候struct 结构体名 变量名,这样的形式来定义了 */ typedef unsigned int S3C24X0_REG32; //自定义类型名S3C24X0_REG32为unsigned int,以后用S3C24X0_REG32就相当于是unsigned int ,可以直接来定义结构变量,如果没加typedef,定义就比较麻烦 /********************************** 定义2410的需要用到的寄存器的结构体 **********************************/ typedef struct{ S3C24X0_REG32 NFCONF; S3C24X0_REG32 NFCMD; S3C24X0_REG32 NFADDR; S3C24X0_REG32 NFDATA; S3C24X0_REG32 NFSTAT; S3C24X0_REG32 NFECC; } S3C2410_NAND; //这里的S3C5410_NAND不是结构变量,相当于是结构体别名,后面可以直接用它来定义结构变量,如:S3C2410_NAND() 结构变量名; /**************************************** 定义2440的寄存器需要用到的寄存器的结构体 ****************************************/ typedef struct { S3C24X0_REG32 NFCONF; S3C24X0_REG32 NFCONT; S3C24X0_REG32 NFCMD; S3C24X0_REG32 NFADDR; S3C24X0_REG32 NFDATA; S3C24X0_REG32 NFMECCD0; S3C24X0_REG32 NFMECCD1; S3C24X0_REG32 NFSECCD; S3C24X0_REG32 NFSTAT; S3C24X0_REG32 NFESTAT0; S3C24X0_REG32 NFESTAT1; S3C24X0_REG32 NFMECC0; S3C24X0_REG32 NFMECC1; S3C24X0_REG32 NFSECC; S3C24X0_REG32 NFSBLK; S3C24X0_REG32 NFEBLK; } S3C2440_NAND; //同样S3C2440_NAND也不是结构变量,相当于结构体别名,后面用它来直接定义结构变量 /**************************** 定义每个子函数指针 ****************************/ typedef struct { void (*nand_reset)(void); //复位子函数,定义了一个指针变量,后面会指向复位的子函数 void (*wait_idle)(void); //查询NFSTAT位函数,定义了一个指针变量,后面会指向查询状态的子函数 void (*nand_select_chip)(void); //发出片选,定义了一个指针变量,后面会指向发出片选的子函数 void (*nand_deselect_chip)(void); //禁止片选,定义了一个指针变量,后面会指向发出命令的子函数 void (*write_cmd)(int cmd); //发出命令,发出0xff是reset命令,发出0是读命令 void (*write_addr)(unsigned int addr); //写地址指针,开始指向addr,定义了一个指针变量,它用来指向发送数据的子函数,所以指向读的地址addr(先取一个变量) unsigned char (*read_data)(void); //读数据命令,定义了一个读数据的指针变量,后面用它指向读取数据的子函数 }t_nand_chip; //t_nand_chip为结构体别名,后面可以用它来直接定义这个结构的结构体变量 /************************** 定义结构体指针的地址 **************************/ /* static说明: 1、全局静态变量:在全局变量之前加上static,全局变量就被定义成为一个全局静态变量 a.好处一,不会被其它文件所访问,修改 b.好处二,其他文件中可以使用相同名字的变量,不会发生重复 2、局部静态变量:在局部变量之前加上static,局部变量就被定义为一个局部静态变量,好处应该跟上一个相同,不会被同一文件内的其它函数访问、修改,也不用担心同一文件内重名等 3、静态函数:在函数的返回类型前加上static,函数就被定义为了静态函数 a.好处一,其他文件中可以定义相同名字的函数,不发生冲突 b.好处二,静态函数不能被其他文件所用 */ static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000; //0x4e000000是NAND FLASH寄存器的首地址;S3C2410_NAND是NAND FLASH所有控制寄存器组成的结构的结构体名,这里s3c2410nand也就是一个结构体的指针变量名, //跟上面一样,S3C2410_NAND*代表一个这种结构的指针,(S3C2410_NAND *)0x4e000000意思就是说把0x4e000000强制转换成一个S3C2410_NAND结构体指针 static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000; //0x4e000000是NAND FLASH的起始地址; static t_nand_chip nand_chip; //定义了一个子函数指针结构体变量nand_chip /*************************** 需要供外部调用的函数 ***************************/ void nand_init(void); //这个函数是用来对NAND FLASH的初始化处理,在汇编中调用 void nand_read(unsigned char *buf, unsigned long start_addr, int size); //这个函数是用来对NAND FLASH的读操作,同样在汇编的第一部分代码中调用 /****************************************************** NAND FLASH操作中需要用到的子函数进行声明(不区分型号) *******************************************************/ static void nand_reset(void); //复位 static void wait_idle(void); //查询状态 static void nand_select_chip(void); //发出片选 static void nand_deselect_chip(void); //禁止片选 static void write_cmd(int cmd); //发出命令 static void write_addr(unsigned int addr); //发出地址 static unsigned char read_data(void); //读数据 /****************************************************** NAND FLASH操作中需要用到的子函数进行声明(2410) *******************************************************/ static void s3c2410_nand_reset(void); static void s3c2410_wait_idle(void); static void s3c2410_nand_select_chip(void); static void s3c2410_nand_deselect_chip(void); static void s3c2410_write_cmd(int cmd); static void s3c2410_write_addr(unsigned int addr); static unsigned char s3c2410_read_data(); /****************************************************** NAND FLASH操作中需要用到的子函数进行声明(2440) *******************************************************/ static void s3c2440_nand_reset(void); static void s3c2440_wait_idle(void); static void s3c2440_nand_select_chip(void); static void s3c2440_nand_deselect_chip(void); static void s3c2440_write_cmd(int cmd); static void s3c2440_write_addr(unsigned int addr); static unsigned char s3c2440_read_data(void); /***************************************************** S3C2410的操作子函数 ******************************************************/ /*复位子函数(由4个子函数构成)*/ static void s3c2410_nand_reset(void) { s3c2410_nand_select_chip(); s3c2410_write_cmd(0xff); // 复位命令 s3c2410_wait_idle(); s3c2410_nand_deselect_chip(); } /*查询等待*/ static void s3c2410_wait_idle(void) { int i; volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT; while(!(*p & BUSY)) for(i=0; i<10; i++); } /* 发出片选信号 */ static void s3c2410_nand_select_chip(void) { int i; s3c2410nand->NFCONF &= ~(1<<11); for(i=0; i<10; i++); } /* 取消片选信号 */ static void s3c2410_nand_deselect_chip(void) { s3c2410nand->NFCONF |= (1<<11); } /* 发出命令 */ static void s3c2410_write_cmd(int cmd) { volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD; *p = cmd; } /* 发出地址 */ static void s3c2410_write_addr(unsigned int addr) { int i; volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->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++); } /* 读取数据 */ static unsigned char s3c2410_read_data(void) { volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA; return *p; } /******************************************************* S3C2440的操作子函数 ********************************************************/ /* 复位 */ static void s3c2440_nand_reset(void) { s3c2440_nand_select_chip(); s3c2440_write_cmd(0xff); // 复位命令 s3c2440_wait_idle(); s3c2440_nand_deselect_chip(); } /* 查询状态 */ static void s3c2440_wait_idle(void) //查询NFSTAT位0,直到它等于它等于1,NFSTAT地址;NFSTAT是NFCON的状态寄存器;第0位为RnB[0]:0 NAND FLASH存储器忙,1 NAND FLASH存储器运行就绪,只读。 { int i; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT; //s3c2440nand是前面定义的一个指针,指向那些寄存器组成的结构体;也就是定义一个指针变量p,指针指向NFSTAT寄存器的地址,此时*p就是NFSTAT的值 while(!(*p & BUSY)) //对于指针*p而言,p就是指针变量,变量的值就是地址,*p是地址指向的值;if和while循环的区别在于,if只循环一次,而while在里面无限次循环*p=1时, //*p为0时,也就是while(1),然后进入for循环延时,等待,一直到*p为1时,也就是while(0),就直接跳出了,不执行下面的for循环延时了,这里因为 //for(i=0;i<10;i++)只有一行,所以没有用括号括起来。 for(i=0; i<10; i++); //这里加个for循环延时语句,就是在NFSTAT还没为1时,等待 } /* 发出片选信号 */ static void s3c2440_nand_select_chip(void) { int i; s3c2440nand->NFCONT &= ~(1<<1); //前面s3c2440nand->NFCONF就是相当于一个指向NFCONF寄存器的指针;发出片选信号时,Reg_nCE[1]: 0强制nFCE为低(使能片选),1强制nFCE为高(禁止片选), //因此,需要把reg_nCE[1]为0,第MODE[0]不变就行;a&=b,也就是a=a&b; for(i=0; i<10; i++); } /* 取消片选信号 */ static void s3c2440_nand_deselect_chip(void) { s3c2440nand->NFCONT |= (1<<1); //同样是操作寄存器NFCON,MODE[0]不改变,Reg_nCE[1]的位设置为1禁止片选;Reg_nCE[1]必须设置为1,其它可以设置不变 } /* 发出命令 */ static void s3c2440_write_cmd(int cmd) { volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD; //指定一个指针变量,指向NFCMD的地址 *p = cmd; //设置指针的值,也就是设置这个地址的值 } /* 发出地址(小页) */ static void s3c2440_write_addr(unsigned int addr) //NAND FLASH上的地址,感觉发送地址,就是把地址放到地址寄存器NFADDR { int i; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; //设置一个指针变量,指向NFADDR的地址寄存器;对于地址寄存器NFADDR:[15:8]保留,[7:0]NAND Flash存储器地址值 *p = addr & 0xff; //取地址A0-A7位,放到地址寄存器 (列地址) for(i=0; i<10; i++); *p = (addr >> 9) & 0xff; //取地址A9-A16位,放到地址寄存器 (行地址,也就是页地址) for(i=0; i<10; i++); *p = (addr >> 17) & 0xff; //取地址A17-A24位,放到地址寄存器 (行地址,也就是页地址) for(i=0; i<10; i++); *p = (addr >> 25) & 0xff; //取地址A25- 位,放到地址寄存器 (列地址,也就是页地址) for(i=0; i<10; i++); } /* 发出地址(大页) */ static void s3c2440_write_addr_lp(unsigned int addr) { int i; volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR; int col, page; col = addr & NAND_BLOCK_MASK_LP; page = addr / NAND_SECTOR_SIZE_LP; *p = col & 0xff; /* Column Address A0~A7 */ for(i=0; i<10; i++); *p = (col >> 8) & 0x0f; /* Column Address A8~A11 */ for(i=0; i<10; i++); *p = page & 0xff; /* Row Address A12~A19 */ for(i=0; i<10; i++); *p = (page >> 8) & 0xff; /* Row Address A20~A27 */ for(i=0; i<10; i++); *p = (page >> 16) & 0x03; /* Row Address A28~A29 */ for(i=0; i<10; i++); } /* 读取数据 */ static unsigned char s3c2440_read_data(void) { volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA; //定义一个指针变量,指针变量指向NFDATA的地址,也就是*p就是NFDATA的值 return *p; //返回*p,也就是这个函数的值就是*p,也就是NFDATA的值 } /********************************************************** 把结构体和子函数连接起来 ***********************************************************/ static void nand_reset(void) //也就是说,后面用nand_reset()就是复位子函数 { nand_chip.nand_reset(); } static void wait_idle(void) //也就是说,后面用wait_idle()就是查询状态 { nand_chip.wait_idle(); } static void nand_select_chip(void) //也就是说,后面用nand_select_chip就是发出片选 { int i; nand_chip.nand_select_chip(); for(i=0; i<10; i++); } static void nand_deselect_chip(void) //也就是说,后面用nand_deselect_chip()就是禁止片选 { nand_chip.nand_deselect_chip(); } static void write_cmd(int cmd) //也就是说,后面用write_cmd()就是发出命令 { nand_chip.write_cmd(cmd); } static void write_addr(unsigned int addr) //也就是说,后面用write_addr()就是发送地址 { nand_chip.write_addr(addr); } static unsigned char read_data(void) //也就是说,后面用read_data()就是读数据 { return nand_chip.read_data(); } /********************************************************************************************************************************************************************************************************************************* 初始化NAND Flash *********************************************************************************************************************************************************************************************************************************/ void nand_init(void) { /************************************************************ 宏定义时序 配置时序;设置NFCONF寄存器 (NFCONF是nand flash 配置寄存器) *************************************************************/ #define TACLS 0 //TACLS[13:12]持续时间:HCLK*TACLS, 这里取0(取值范围0-3); #define TWRPH0 3 //TWRPH0[10:8]持续时间:HCLK*(TWRPH0+1),这里取3,(取值范围0-7); #define TWRPH1 0 //TWRPH1[6:4]持续时间:HCLK*(TWRPH1+1),这里取0(取值范围0-7); /****************************************************************************************************** 先判断是2410还是2440,然后给总结构体的子函数赋值对应的函数,赋值完成后,就初始设置,然后使用前的复位 ******************************************************************************************************/ if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002)) //判断如果是2410 { /*定义结构体的子函数对应的函数*/ nand_chip.nand_reset = s3c2410_nand_reset; nand_chip.wait_idle = s3c2410_wait_idle; nand_chip.nand_select_chip = s3c2410_nand_select_chip; nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip; nand_chip.write_cmd = s3c2410_write_cmd; nand_chip.write_addr = s3c2410_write_addr; nand_chip.read_data = s3c2410_read_data; /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */ s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); } else //如果判断是2440 { /*定义结构体的子函数对应的函数*/ nand_chip.nand_reset = s3c2440_nand_reset; //结构体内复位的子函数选择2440的 nand_chip.wait_idle = s3c2440_wait_idle; //结构体内查询状态的子函数选择2440的 nand_chip.nand_select_chip = s3c2440_nand_select_chip; //结构体内发出片选的子函数选择2440的 nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip; //结构体内禁止片选的子函数选择2440的 nand_chip.write_cmd = s3c2440_write_cmd; //结构体内发送命令的子函数选择2440的 /* #ifdef 语句1 程序2 #endif 意思就是,如果宏定义了语句1,那么就执行程序2*/ #ifdef LARGER_NAND_PAGE //如果宏定义了大页,结构体内发送地址的子函数就选2440大页的子函数 nand_chip.write_addr = s3c2440_write_addr_lp; #else //如果没宏定义大页,结构体内发送地址的子函数就选2440小页子函数 nand_chip.write_addr = s3c2440_write_addr; #endif nand_chip.read_data = s3c2440_read_data; //结构体内读取数据的子函数选择2440的 /* 设置时序 */ //从这里才是正式的开始,前面只是一些函数的定义;步骤:一、设置时序;二、使能NAND,初始化ECC,禁止片选;三、复位(发出片选、发出reset命令、查询状态、禁止片选); s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); //四、正式开始:1、发出片选;2、发出读命令;3、发出地址信号;4、查询状态;5、读数据;(1-4步循环512次,512字节)6、禁止片选 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ //时序:TACLS[13:12]取0, TWRPH0[10:8]取3,TWRPH1[6:4]取0; s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0); //设置寄存器NFCONT,MODE[0]:0禁止NAND FLASH 控制器,1使能NAND FLASH控制器,设置为1(1<<0);Reg_nCE[1]: 0强制nFCE为低(使能片选),1强制nFCE为高(禁止片选),设置1(1<<1); } //InitECC[4]: 1初始化ECC编译器/译码器(只写),设置1; nand_reset(); //使用前复位 } /********************************************************************************************************************************************************************************************************************************* 读函数子函数 *********************************************************************************************************************************************************************************************************************************/ void nand_read(unsigned char *buf, unsigned long start_addr, int size) { int i, j; /* return语法说明: 1、return表示从被调函数返回到主调函数继续执行,返回时可附带一个返回值,返回值可以是一个常量、变量、或是表达式。 2、return作用:结束正在运行的函数,并返回函数值 3、return后的返回类型: a.不需要返回值时,主函数加上void声明,然后后面不需要加return返回,加上void后,即是写了return也不会有返回值; b.在一些void的函数中,你需要跳出它,可以直接return,后面不加任何量 c.函数用数据类型定义,那么返回就是数,例如:int cc(){ return x ;} ,那么返回的就是x的值,也就是cc()就为那个数; d.函数用string定义,那么返回就是一个字符串,跟数一样; 4、跳出函数 a.返回一个函数值,并跳出函数,例如: function cd(n) {if(n==1) return 1;}}, 这就是说n=1时,cd(1)=1,并且跳出,接着执行后面的; b.function cd(n) {if(n==1) return;}}, 如果n=1,直接跳出if里面的,执行后面的 5、主函数的问题: a.一个普通子函数遇到return,就会跳出函数执行,但是如果是主函数遇到return,那么整个程序就会停止,退出程序的执行;对于主函数,void main()是错误的,如果希望程序具有好的可移植性,一定要用int main(),然后加返回值 b.main函数的返回值用于说明程序的退出状态,如果返回0,则代表程序正常退出,否则代表程序异常退出。 */ /*读写前,先检查地址或长度是否对其*/ #ifdef LARGER_NAND_PAGE //如果是大页 if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) { return ; /* 地址或长度不对齐 */ } #else //如果是小页 if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { //start_addr是起始地址(4096),NAND_BLOCK_MASK是每页的地址长度(512-1),size是所取地址长度(2048) return ; /* 地址或长度不对齐 */ //这里就是检查上面是不是为1,如果是为1,那么return就跳出这个if函数,继续执行下面的函数 } #endif /* 发出片选 */ nand_select_chip(); //现在是正式开始步骤了:1、发出片选;2、读命令;3、发送地址;4、查询状态;5、读数据;6、禁止片选; //这里先发出片选信号,然后后面跟着循环地址的长度来进行读取 for(i=start_addr; i < (start_addr + size);) { //这里加个循环,是循环读取数据,从起始地址(4096)开始读长度size为2048的地址,循环读取2048次 /* 发出READ0命令 */ write_cmd(0); //发送读命令;也就是命令寄存器NFCMD=0; /* Write Address */ write_addr(i); //i现在是发送的地址的值,把这个值带入发送地址的子函数,在子函数中运行的过程就是把此时的地址的数字依次取它的位A0-A7、A9-A16、A17-A24、A25-,这些依次送入地址寄存器NFADDR即可 #ifdef LARGER_NAND_PAGE write_cmd(0x30); #endif wait_idle(); //循环查询NFSTAT位0,直到它等于1,就可以读取数据了 #ifdef LARGER_NAND_PAGE for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) { #else for(j=0; j < NAND_SECTOR_SIZE; j++, i++) { #endif *buf = read_data(); //读取数据的那个子函数就是取数据寄存器NFADDR的值(也就是现在读取的地址),也就是说,read_data()=NFADDR;这里就是定义了一个指针,指向那个地址 buf++; //读这个地址的数据,每次读一页 } } /* 取消片选信号 */ nand_deselect_chip(); return ; }
四、
#define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define GPF4_out (1<<(4*2)) #define GPF5_out (1<<(5*2)) #define GPF6_out (1<<(6*2)) void wait(volatile unsigned long dly) { for(; dly > 0; dly--); } int main(void) { unsigned long i = 0; GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1-3对应的GPF4/5/6三个引脚设为输出 while(1){ wait(30000); GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1-3 if(++i == 8) i = 0; } return 0; }
五、
SECTIONS { /*lds文件注释时,可以跟C语言一样用/*注释 */*/ firtst 0x00000000 : { head.o init.o nand.o} /*这几个函数在NAND FLASH前4K内,所以,存储和运行都在stepping中,地址是0开头的4k内,首地址为0 */ second 0x30000000 : AT(4096) { main.o } /*操作LED灯的函数在NAND flash的后4K内存放,所以存放首地址是4096(4k),而它被读出到SDRAM中运行,所以运行地址是SDRAM的首地址0x30000000*/ } /* lds的基本结构: SECTIONS{ secname start BLOCK(align) (NOLOAD) : AT(ldadr) { contents} >region : phdr = fill } 1、secname和contents是必须的,其它都可选 2、secname, 是段名 3、contens, 放在本段的内容,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等); 4、start, 本段链接(运行)的地址 5、AT(ldadr), ldadr是本段储存的地址,如果没有AT(ldadr),那么储存的地址和运行的地址一样,也是start */
时间: 2024-10-12 16:46:14