首页 > 代码库 > NAND FLASH

NAND FLASH

一、

技术分享
@***********************************************************************************************************************************************************************************************************************@ 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位有符号数,所以是32Mhalt_loop:                                  b    halt_loop         @死循环;死循环的作用:在最后加个死循环,也就是让程序停留在这里了,这样是很有用的,一方面可以在这个时候查看各个存储器的值,以便检查自己的程序是否正确,另一方面,比如如果是C函数比如用到printf                                  @这样之类的函数,如果不加死循环,输出会瞬间消失
View Code

 

二、

技术分享
/*****************************************************************************************************************************************************************************************************************************         关看门狗和配置存储控制器*****************************************************************************************************************************************************************************************************************************//*****************初始宏定义*****************/#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字节的地址,刚好指向下一个                                    //寄存器的首地址}
View Code

 

三、

技术分享
/********************************************************************************************************************************************************************************************************************************               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 ;} 
View Code

 

四、

技术分享
#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;}
View Code

 

五、

技术分享
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*/
View Code

 

NAND FLASH