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位有符号数,所以是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

NAND FLASH的相关文章

NAND flash学习所获----(Zac)

Nand Falsh外围电路:peripheral circuit 1.Nand flash里至少有2个state Machine(controller),即2个主控. 一个主控:负责处理前端事情. 一个主控:负责后端任务处理. 2.DDC:Dynamic Data cache(包括SDC,PDC,TDC,DDC1,DDC2),有的解析为Page buffer,或者sense Amps. 镁光的NAND flash:L94C:单die 8G,L95B:单die 16G 大致的物理结构图: 所以体

u-boot分析(九)----nand flash初始化|nand flash读写分析

u-boot分析(九) 上篇博文我们按照210的启动流程,分析到了初始化串口,由于接下来的取消存储保护不是很重要,所以我们今天按照u-boot的启动流程对nand flash初始化进行分析. 今天我们会用到的文档: 1.        2440芯片手册:http://download.csdn.net/detail/wrjvszq/8358949 2.        6410芯片手册:http://download.csdn.net/detail/wrjvszq/8358965 3.      

Nand Flash原理(二)

K9F2G08U0B的存储阵列 图 2-1 K9F2G08U0B的存储阵列 由图2-1,我们可以知道:K9F2G08U0B的一页为(2K+64)字节(2K 表示的是 main 区容量,64表示的是 spare 区容量),它的一块为 64 页,而整个设备包括了2048个块.这样算下来一共有 2112M 位容量,如果只算 main 区容量则有256M 字节(即 256M×8 位). 要实现用 8 个 IO 口来要访问这么大的容量,如图 2-1 所示:K9F2G08U0A 规定了用 5 个周期来实现.

NAND Flash操作技术详解

NAND Flash是构成固态存储的基本存储单元其采用IO的接口方式与控制器相连.控制器对NAND Flash进行操作时需要通过命令交互的方式对NAND Flash进行操作.下面对NAND Flash的操作进行解析可以发现在研发FTL的时候可以充分发掘NAND Flash提供的操作命令来优化读写性能.分析介绍的NAND Flash以Micron提供的MT29F16G08AA为蓝本.该芯片能够提供16Gb 的存储容量具体性能指标如下所示 Organization –     Page size:

uboot 从sd卡加载文件并烧写到nand flash

uboot下可以从用tftp和nfs加载文件. 但是现在有个开发板配套uboot网络功能出现异常,执行ping命令就会导致开发板重启,只能选择先从sd卡加载文件 启动开发板,任意键进入uboot,然后执行下面的命令扫描sd卡设备: mmc rescan 然后获取sd卡信息: hwgw # fatinfo mmc Interface: MMC Device 0: Vendor: Man 035344 Snr 20d2c703 Rev: 8.0 Prod: SD01G Type: Removable

LCD实验学习笔记(七):NAND FLASH

s3c2440 CPU内置NAND FLASH控制器.相关寄存大器起始地址为0x4e000000. 通过设置NFCONF寄存器,设置NAND FLASH 时序. 通过设置NFCONT寄存器,使能NAND FLASH.初始化ECC等. 代码: #define GSTATUS1 (*(volatile unsigned int *)0x560000B0) //读此寄存器可以知道CPU芯片型号#define BUSY 1 #define NAND_SECTOR_SIZE_LP 2048 //大页每页2

NAND FLASH控制器

一.nand flash访问原理 地址空间概念 nand的编址 nand命令  命令,地址,数据 使用S3C2440的nand flash控制器访问nand flash 前几个编译出来的文件都小于4k,读出来放到SDRAM中去 SDRAM.dm9000地址-->2440地址 nand 没有地址总线 片内内存:SDRAM:网卡:寄存器  都是CPU统一编址 写地址,读数据   连续读一页                           只进行了读操作,擦除,oob访问都没有 二.源码分析

怎么看时序图--nand flash的读操作详解(转载)

出处:http://blog.chinaunix.net/uid-28852942-id-3992727.html这篇文章不是介绍 nand flash的物理结构和关于nand flash的一些基本知识的.你需要至少了解 你手上的 nand flash的物理结构和一些诸如读写命令 操作的大概印象,你至少也需要看过 s3c2440中关于nand flash控制寄存器的说明. 由于本人也没有专门学过这方面的知识,下面的介绍也是经验之谈. 这里 我用的 K9F2G08-SCB0 这款nand flas

nand Flash原理

6.1.3) Block 块 一个 Nand Flash (的 chip,芯片) 由很多个块(Block)组成,块的大小一般是 128KB, 256KB, 512KB,此处是 128KB.其他的小于 128KB 的,比如 64KB,一般都是下面将要介绍到的small block的Nand Flash. 块 Block,是Nand Flash的擦除操作的基本/最小单位. 6.1.4) Page 页 每个块里面又包含了很多页(page) .每个页的大小,对于现在常见的Nand Flash多数是2KB

nand flash的实现(摘,参考)

首先明确一下我们的编程步骤. (1).加电在nand_flash加载boot.s中4K以内的程序.这4k将自动拷贝到SRAM(片内RAM)执行. (2).我们需要用这4k的程序实现nand-flash中4K以后的程序的拷贝(当然,拷贝到SDRAM基址为0x30000000处)继续执行(main.o部分的程序).对于SDRAM的初始化和Watchdog的禁用已经在前一个实验中使用到了,这里就不再详细叙述.主要来看一下nand-flash的初始化和使用. 查阅一下s3c2440的空间布局.查看手册图