zoukankan      html  css  js  c++  java
  • 嵌入式Linux学习笔记 NAND Flash控制器

    一、NAND Flash介绍和NAND Flash控制器的使用

    NAND Flash在嵌入式系统中的作用,相当于PC上的硬盘

    常见的Flash有NOR Flash和NAND Flash,NOR Flash上进行读取的效率非常高,但是擦除和写操作的效率很低,容量一般比较小;NAND Flash进行擦除和写操作的效率更高,并且容量更大。一般NOR Flash用于存储程序,NAND Flash用于存储数据。

    1)NAND Flash的物理结构

    笔者用的开发板上NAND Flash型号是K9F1G08,大小为128M,下图为它的封装和外部引脚

    I/O0-I/O7        数据输入/输出

    CLE        命令锁存使能

    ALE        地址锁存使能

    CE        芯片使能

    RE        读使能

    WE        写使能

    WP        写保护

    R/B        就绪/忙输出信号

    Vcc        电源

    Vss        地

    N.C        不接

     

    K9F1G08功能结构图如下

     

    K9F1G08内部结构有下面一些功能部件

    ①X-Buffers Latches & Decoders:用于行地址

    ②X-Buffers Latches & Decoders:用于列地址

    ③Command Register:用于命令字

    ④Control Logic & High Voltage Generator:控制逻辑及产生Flash所需高压

    ⑤Nand Flash Array:存储部件

    ⑥Data Register & S/A:数据寄存器,读、写页时,数据存放此寄存器

    ⑦Y-Gating

    ⑧I/O Buffers & Latches

    ⑨Global Buffers

    ⑩Output Driver

    NAND Flash 存储单元组织结构图如下:

     

    K9F1G08容量为1056Mbit,分为65536行(页)、2112列,每一页大小为2kb,外加64字节的额外空间,这64字节的额外空间的列地址为2048-2111

    命令、地址、数据都通过IO0-IO7输入/输出,写入命令、地址或数据时,需要将WE、CE信号同时拉低,数据在WE信号的上升沿被NAND FLash锁存;命令锁存信号CLE、地址锁存信号ALE用来分辨、锁存命令或地址。

    K9F1G08有128MB的存储空间,需要27位地址,以字节为单位访问Flash时,需要4个地址序列

     

    2)NAND Flash访问方法

    NAND Flash硬件连接如下图:

     

    NAND Flash和S3C2440的连线包括,8个IO引脚,5个使能信号(nWE、ALE、CLE、nCE、nRE)、1个状态引脚(R/B)、1个写保护引脚(nWP)。地址、数据和命令都是在这些使能信号的配合下,通过8个IO引脚传输。写地址、数据、命令时,nCE、nWE信号必须为低电平,它们在nWE信号的上升沿被锁存。命令锁存使能信号CLE和地址锁存使能信号ALE用来区别IO引脚上传输的是命令还是地址。

     

    命令字及操作方法

            操作NAND Flash时,先传输命令,然后传输地址,最后读写数据,这个期间要检查Flash的状态。K9F1G08容量为128MB,需要一个27位的地址,发出命令后,后面要紧跟着4个地址序列。

    下图为K9F1G08的命令字

    下图为K9F1G08的地址序列

    K9F1G08有2112列,所以必须使用A0-A11共12位来寻址,有65535行,所以必须使用A12-A27共16位来寻址。

     

    3)S3C2440 NAND Flash控制器介绍

    NAND Flash的读写操作次序如下:

    ①设置NFCONF配置NAND Flash

    ②向NFCMD寄存器写入命令

    ③向NFADDR寄存器写入地址

    ④读写数据:通过寄存器NFSTAT检测NAND Flash的状态,在启动某个操作后,应该检测R/nB信号以确定该操作是否完成、是否成功。

     

    下面介绍这些寄存器:

    ①NFCONF:配置寄存器

            用来设置NAND Flash的时序参数,设置数据位宽,设置是否支持其他大小的页等。

    ②NFCONT:控制寄存器

            用来使能NAND Flash控制器、使能控制引脚信号nFCE、初始化ECC,锁定NAND Flash等功能

    ③NFCMD:命令寄存器

            用来发送Flash操作命令

    ④NFADDR:地址寄存器

            用来向Flash发送地址信号

    ⑤NFDATA:数据寄存器

            读写此寄存器启动对NAND Flash的读写数据操作

    ⑥NFSTAT:状态寄存器

            0:busy,1:ready

     

    二、NAND Flash控制器操作实例:读Flash

    1)读NAND Flash的步骤

    ①设置NFCONF

            在HCLK=100Mhz的情况下,TACLS=0,TWRPH0=3,TWRPH1=0,则

            NFCONF = 0x300

            使能NAND Flash控制器、禁止控制引脚信号nFCE,初始化ECC

            NFCONT = (1<<4) | (1<<1) | (1<<0)

    ②操作NAND Flash前,复位

            NFCONT &= ~(1<<1)        发出片选信号

            NFCMD = 0xff        reset命令

            然后循环查询NFSTAT位0,直到等于1,处于就绪态

            最后禁止片选信号,在实际使用时再使能

            NFCONT |= 0x2        禁止NAND Flash

    ③发出读命令

            NFCONT &= ~(1<<1)        发出片选信号

            NFCMD = 0        读命令

    ④发出地址信号

    ⑤循环查询NFSTAT,直到等于1

    ⑥连续读NFDATA寄存器,得到一页数据

    ⑦最后禁止NAND Flash片选信号

            NFCONT |= (1<<1)

     

     2)代码详解

    本实例的目的是把一部分代码存放在NAND Flash地址4096之后,当程序启动后通过NAND Flash控制器读出代码,执行。

    连接脚本 nand.lds

     

    SECTIONS { 

      firtst   0x00000000 : { head.o init.o nand.o}

      second  0x30000000 : AT(4096) { main.o }

    head.o init.o nand.o三个文件运行地址为0,生成的镜像文件偏移地址也为0

    main.0的运行地址为0x30000000,生成的镜像文件偏移地址为4096

     

     

    @******************************************************************************

    @ File:head.s

    @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行

    @******************************************************************************       

      

    .text

    .global _start

    _start:

                                                @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义

                ldr     sp, =4096               @设置堆栈 

                bl      disable_watch_dog       @关WATCH DOG

                bl      memsetup                @初始化SDRAM

                bl      nand_init               @初始化NAND Flash


                                                @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中

                                                @nand_read_ll函数需要3个参数:

                ldr     r0,     =0x30000000     @1. 目标地址=0x30000000,这是SDRAM的起始地址

                mov     r1,     #4096           @2.  源地址   = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处

                mov     r2,     #2048           @3.  复���长度= 2048(bytes),对于本实验的main.c,这是足够了

                bl      nand_read               @调用C函数nand_read


                ldr     sp, =0x34000000         @设置栈

                ldr     lr, =halt_loop          @设置返回地址

                ldr     pc, =main               @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转

    halt_loop:

                b       halt_loop

     

    init.c 用于初始化操作
    /* WOTCH DOG register */
    #define  WTCON (*(volatile unsigned long *)0x53000000)
     
    /* SDRAM regisers */
    #define  MEM_CTL_BASE 0x48000000
     
    void disable_watch_dog();
    void memsetup();
     
    /*上电后,WATCH DOG默认是开着的,要把它关掉 */
    void disable_watch_dog()
    {
    WTCON = 0;
    }
     
    /* 设置控制SDRAM的13个寄存器 */
    void memsetup()
    {
    int  i = 0;
    unsigned long *p = (unsigned long *)MEM_CTL_BASE;
     
        /* SDRAM 13个寄存器的值 */
        unsigned long  const    mem_cfg_val[]={ 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 < 13; i++)
    p[i] = mem_cfg_val[i];
    }
     
    nand.c 用于操作nand flash
     
    #define BUSY            1
     
    #define NAND_SECTOR_SIZE_LP    2048        //K9F1G08使用2048+64列
    #define NAND_BLOCK_MASK_LP     (NAND_SECTOR_SIZE_LP - 1)
     
    typedef unsigned int S3C24X0_REG32;
     
    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;        //此结构体存储操作NAND Flash相关寄存器
     
     
    typedef struct {
        void (*nand_reset)(void);
        void (*wait_idle)(void);
        void (*nand_select_chip)(void);
        void (*nand_deselect_chip)(void);
        void (*write_cmd)(int cmd);
        void (*write_addr)(unsigned int addr);
        unsigned char (*read_data)(void);
    }t_nand_chip;        //存储nand相关操作的函数地址
     
    static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;        //s2c2440nand控制器地址
     
    static t_nand_chip nand_chip;
     
    /* 供外部调用的函数 */
    void nand_init(void);
    void nand_read(unsigned char *buf, unsigned long start_addr, int size);
     
    /* NAND Flash操作的总入口, 它们将调用S3C2440的相应函数 */
    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);
     
    /* S3C2440的NAND Flash处理函数 */
    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);
     
    /* S3C2440的NAND Flash操作函数 */
     
    /* 复位 */
    static void s3c2440_nand_reset(void)
    {
        s3c2440_nand_select_chip();
        s3c2440_write_cmd(0xff);  // 复位命令
        s3c2440_wait_idle();
        s3c2440_nand_deselect_chip();
    }
     
    /* 等待NAND Flash就绪 */
    static void s3c2440_wait_idle(void)
    {
        int i;
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
        while(!(*p & BUSY))        //*p=1表示就绪,跳出循环
            for(i=0; i<10; i++);
    }
     
    /* 发出片选信号 */
    static void s3c2440_nand_select_chip(void)
    {
        int i;
        s3c2440nand->NFCONT &= ~(1<<1);
        for(i=0; i<10; i++);    
    }
     
    /* 取消片选信号 */
    static void s3c2440_nand_deselect_chip(void)
    {
        s3c2440nand->NFCONT |= (1<<1);
    }
     
    /* 发出命令 */
    static void s3c2440_write_cmd(int cmd)
    {
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
        *p = cmd;
    }
     
    /* 发出地址 */
    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; /* 列地址 A0~A7 */
    for(i=0; i<10; i++);
    *p = (col >> 8) & 0x0f; /* 列地址 A8~A11 */
    for(i=0; i<10; i++);
    *p = page & 0xff; /* 行地址 A12~A19 */
    for(i=0; i<10; i++);
    *p = (page >> 8) & 0xff; /* 行地址 A20~A27 */
    for(i=0; i<10; i++);
    *p = (page >> 16) & 0x03; /* 行地址 A28~A29 */
    for(i=0; i<10; i++);
    }
     
     
    /* 读取数据 */
    static unsigned char s3c2440_read_data(void)
    {
        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
        return *p;
    }
     
     
    /* 在第一次使用NAND Flash前,复位一下NAND Flash */
    static void nand_reset(void)
    {
        nand_chip.nand_reset();
    }
     
    static void wait_idle(void)
    {
        nand_chip.wait_idle();
    }
     
    static void nand_select_chip(void)
    {
        int i;
        nand_chip.nand_select_chip();
        for(i=0; i<10; i++);
    }
     
    static void nand_deselect_chip(void)
    {
        nand_chip.nand_deselect_chip();
    }
     
    static void write_cmd(int cmd)
    {
        nand_chip.write_cmd(cmd);
    }
    static void write_addr(unsigned int addr)
    {
        nand_chip.write_addr(addr);
    }
     
    static unsigned char read_data(void)
    {
        return nand_chip.read_data();
    }
     
     
    /* 初始化NAND Flash */
    void nand_init(void)
    {
    #define TACLS   0
    #define TWRPH0  3
    #define TWRPH1  0
            nand_chip.nand_reset         = s3c2440_nand_reset;
            nand_chip.wait_idle          = s3c2440_wait_idle;
            nand_chip.nand_select_chip   = s3c2440_nand_select_chip;
            nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
            nand_chip.write_cmd          = s3c2440_write_cmd;
            nand_chip.write_addr         = s3c2440_write_addr_lp;
            nand_chip.read_data          = s3c2440_read_data;
     
    /* 设置时序 */
            s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
            /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
            s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
        }
        
        /* 复位NAND Flash */
        nand_reset();
    }
     
     
    /* 读函数 用于把nand flash中代码复制到sdram中*/
    void nand_read(unsigned char *buf, unsigned long start_addr, int size)
    {
        int i, j;
     
        if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
            return ;    /* 地址或长度不对齐 */
        }
     
     
        /* 选中芯片 */
        nand_select_chip();
     
        for(i=start_addr; i < (start_addr + size);) {
          /* 发出READ命令 */
          write_cmd(0);
     
          /* 写地址 */
          write_addr(i);
          write_cmd(0x30);
          wait_idle();
     
     
          for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {
              *buf = read_data();
              buf++;
          }
        }
     
        /* 取消片选信号 */
        nand_deselect_chip();
        
        return ;
    }
     
    main.c 很简单,点灯
     
    #define GPBCON (*(volatile unsigned long *)0x56000010)
    #define GPBDAT (*(volatile unsigned long *)0x56000014)

    #define GPB5_out (1<<(5*2))
    #define GPB6_out (1<<(6*2))
    #define GPB7_out (1<<(7*2))
    #define GPB8_out (1<<(8*2))

    void  wait(unsigned long dly)
    {
    for(; dly > 0; dly--);
    }

    int main(void)
    {
    unsigned long i = 0;
    GPBCON = GPB5_out|GPB6_out|GPB7_out|GPB8_out; // 将LED1-4对应的GPB5/6/7/8四个引脚设为输出

    GPBDAT = ~(1<<5) | ~(1<<7) | ~(1<<8);
    while(1){
    wait(30000);
    GPBDAT = (~(i<<5)); // 根据i的值,点亮LED1-4
    if(++i == 16)
    i = 0;
    }

    return 0;
    }
     
    最后是Makefile
    objs := head.o init.o nand.o main.o

    nand.bin : $(objs)
    arm-linux-ld -Tnand.lds -o nand_elf $^
    arm-linux-objcopy -O binary -S nand_elf $@
    arm-linux-objdump -D -m arm  nand_elf > nand.dis

    %.o:%.c
    arm-linux-gcc -Wall -c -O2 -o $@ $<

    %.o:%.S
    arm-linux-gcc -Wall -c -O2 -o $@ $<

    clean:
    rm -f  nand.dis nand.bin nand_elf *.o

    转自Lunix 公社
  • 相关阅读:
    JavaSE 基础 第51节 定义自己的异常
    JavaSE 基础 第50节 Java中的异常链
    JavaSE 基础 第49节 手动抛出异常
    JavaSE 基础 第48节 Java中的异常声明
    JavaSE 基础 第47节 获取异常信息
    JavaSE 基础 第46节 异常的分类
    JavaSE 基础 第45节Java异常快速入门
    JavaSE 基础 第44节 引用外部类的对象
    JavaSE 基础 第43节 静态内部类
    通用爬虫
  • 原文地址:https://www.cnblogs.com/Zyf2016/p/6337829.html
Copyright © 2011-2022 走看看