zoukankan      html  css  js  c++  java
  • NAND Flash结构及驱动函数

    目标:以NAND Flash K9F2G08U0M为例介绍其结构及其驱动程序的书写

    1. 结构

    由芯片手册中的图可知:K9F2G08U0M大小为2112Mbits(即 256MB = 2Gb ) 共有2048Block=128K页

    这里: 

    1个device=2048Block

    1块Block=64Pages

    1页Page=(2K+64)B            (每个地址里都存放了一个字节,用B表示)

    其中,由于Nandflash自身的位反转问题,64B用于存放OOB地址,用作ECC校验;

    (注意:每个芯片的块、页的大小可能不同,要根据具体的芯片手册决定)

    2. NAND Flash与S3C2440的硬件连接

    连接引脚分析:

        RnB: 就绪(ready)/忙(busy)输出信号,需要采用上拉电阻;(1:表示写入数据成功    0:表示正在写入)
        CLE: 命令(command)锁存(latch)使能;                          (1:     表示当前传的是命令值 )
        ALE: 地址锁存使能;                                                      ( 1:表示当前传的是地址值, 当CLE=0和ALE=0,表示传的是数据)
        nCE: 芯片使能(低电平使能) (n:表示低电平有效)
        nWE: 写使能 ,比如写命令时,当CLE=1,ALE=0时,当nWE来个上升沿,则会将IO数据写入flash中;
        nRE: 读使能,和nWE类似;
    引脚操作:

    1).  从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令怎么传入命令?

           在DATA0~DATA7上既传输数据,又传输地址,也传输命令:

           a. 当ALE=1为高电平时传输的是地址。

           b. 当ALE=0和CLE=0都为低电平时传输的是数据。

           c. 当CLE=1为高电平时传输的是命令。
    2). 2440的数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等,怎样选中,避免干扰?

      这些设备,要访问之必须"选中",没有选中的芯片不会工作,相当于没接一样。

    3). 假设烧写NAND FLASH,把命令、地址、数据发给它之后,NAND FLASH肯定不可能瞬间完成烧写的,怎么判断烧写完成?

          通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙

    3.  命令及相关操作

    1)NAND Flash操作函数

    a. 初始化函数nand_init()

    先查看s3c2440的 芯片手册,可知:NAND Flash的初始化需要根据时序设置几个时间(寄存器);

    如上图所示:对于 CLE/ALE 上的时序,我们需要设置 TACLS,TWRPH0,TWRPH1,这几个都在 NFCONF 寄存器里。

    由以上2440芯片手册中的图还无法得到具体时间设置,需要参考K9F2G08U0M的芯片手册。

    K9F2G08U0M芯片时间设置表:

    K9F2G08U0M芯片时序图:

    其中,通过对照2440 NAND Flash时序图和K9F2G08U0M芯片时序图可知:

    TACLS:  属于等待WE(写信号)就绪的时间, 

         TACLS的时间为:tCLS- tWP

    由时间表可知,tCLS=15, tWP =15,则TACLS时间0;

    根据寄存器中描述的计算公式:Duration =  HCLK x TACLS =>  0ns = 10ns x TACLS  => TACLS = 0

    TWRPH0:属于WE(写信号)的时间,

        TWRPH0的时间为: tWP =15nS

        根据寄存器中描述的计算公式:Duration =  HCLK x(TWRPH0+1) =>  15=10xTWRPH0+1 => TWRPH0 = 0.5, 由于取值范围为: (0~7) ,并且,时间表中的时间是最小能识别的时间,所以TWRPH0 = 1

    TWRPH1:属于等待命令写入成功的时间,

           TWRPH1的时间为:tCLH=5nS

        根据寄存器中描述的计算公式:Duration =  HCLK x(TWRPH1+1) =>  5=10x(TWRPH1+1) =>  TWRPH1 = 0

    这里我们假设 开发板HCLK 为是 100MHz,,则 HCLK 的周期是 1/100MHz = 10ns


    根据以上分析,初始化函数nand_init()写为:

     1  #define NFCONF (*((volatile unsigned long *)0x4E000000)) 
     2  void nand_init(void)
     3  {
     4      #define TACLS   0
     5      #define TWRPH0  1
     6      #define TWRPH1  0
     7      /* 设置时序 */
     8      NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
     9      /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
    10      NFCONT = (1<<4)|(1<<1)|(1<<0);    
    11  }

    b. 芯片的选择和禁止函数

    (让芯片操作nCE 引脚)

     1  #define NFCONT  (*((volatile unsigned long *)0x4E000004))
     2  void nand_select(void )  //使能片选
     3  {
     4      NFCONT &= ~(1<<1);   // NFCONT控制器位1置0
     5  }
     6  
     7  void nand_deselect(void ) //取消片选
     8  {
     9      NFCONT |= (1<<1);     // NFCONT控制器位1置1
    10  }

    c. 写命令

    1  // 写命令 注意是八位的命令
    2  #define NFCMMD (*((volatile unsigned char *)0x4E000008))
    3  void nand_cmd(unsigned char cmd)
    4 {
    5  volatile int i;
    6  NFCMMD = cmd;
    7  for (i = 0; i<10; i++); // 延时一段时间
    8 }

    d. 写地址

    地址序列分析:

    由于Nand Flash地址引脚LDATA0-LDATA7只有8位,然而K9F2G08U0M的地址共有2048(块)*64(页)*2KB,为了读出多个地址,如下图,所以需要分5个周期来实现发送地址:

    如上图,

    A10~A0对应页大小(列),由于NANDFlash每页2048B,所以只用到A10~A0; (---->>这一页的第几个)

    A28~A11对应页目录(行),表示共有2048块*64(每块有64页)个页目录;          (---->>泛指位于第几页)

    例如,4100 地址就是:

     A10~A0=4100%2048= 4   (A2=1,其余为0)       第4列(个)

     A28~A11=4100/2048= 2 (A12=1,其余为0)  第二行

    写地址函数nand_addr()为:

     1 void nand_addr(unsigned int addr) //发出5个周期的地址,之间需要延时一会
     2 {
     3 
     4    unsigned int col  = addr % 2048;
     5    unsigned int page = addr / 2048;
     6    volatile int i;                    
     7    NFADDR=(col>>0)&0xff;           //A7~A0,第1周期
     8    for(i=0;i<10;i++);
     9    NFADDR=(col>>8)&0x0f;           //A10~A8,第2周期
    10    for(i=0;i<10;i++);
    11    NFADDR=(page>>0)&0xff;          //A18~A11,第3周期
    12    for(i=0;i<10;i++);
    13    NFADDR=(page>>8)&0xff;          //A26~A19,第4周期
    14    for(i=0;i<10;i++);
    15    NFADDR=(page>>16)&0xff;         //A27~A28,第5周期
    16    for(i=0;i<10;i++);  
    17 }

    e. 读数据

     K9F2G08U0M的芯片手册中的命令字:

    读数据时序图:

     

    根据以上时序图和芯片手册中的流程图可知读数据的步骤为:

    (a)      使能片选CE,选中芯片;

    (b)      写入读命令0x00到flash中

    (b)      发送地址(分为5个周期,之间需要延时)

    (c)      发送读命令0X30

    (d)     判断状态,等待RnB信号为高电平

    (e)     读数据

    (f)   取消选中芯片

     读数据函数nand_read()为:
     1 void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
     2 {
     3  int col = addr % 2048; //从该页的第几个开始读取 
     4  int i = 0;
     5   
     6  nand_select();      /* 1. 选中 */
     7  while (i < len)
     8  {
     9   nand_cmd(0x00);    /* 2. 发出读命令00h */
    10   nand_addr(addr);   /* 3. 发出地址(分5步发出) */
    11   nand_cmd(0x30);    /* 4. 发出读命令30h */
    12   nand_wait_ready(); /* 5. 判断状态 */
    13   /* 6. 读数据 */
    14   for (; (col < 2048) && (i < len); col++)  //一行一行的读,第k页读完后,col=0,开始继续读,直到读取指定的长度len
    15   {
    16     buf[i] = nand_data();     
    17     i++;
    18     addr++;
    19   }
    20   col = 0;        
    21  }
    22  /* 7. 取消选中 */  
    23  nand_deselect();
    24 }

    其中,判断状态(状态等待)函数nand_wait_teadynand_data,获取数据函数分别为:

     1 #define NFSTAT (*((volatile unsigned char *)0x4E000020))
     2 #define NFDATA (*((volatile unsigned char *)0x4E000010))
     3 void nand_wait_teady(void)
     4 {
     5     while(!(NFSTAT & 1))
     6          for(i = 0; i < 10; i++);
     7 }
     8 unsigned char nand_data(void)//返回一个字节的数据
     9 {
    10     return NFDATA;
    11 }

    注意:/*addr:源地址,为32位地址,所以用unsigend int表示;因为每个地址里存的是一个字节,所以buf用unsigend char 型 */

     f. 复位 NAND Flash 函数

    根据命令的表格, 复位函数表示为:

    1     void nand_reset(void)
    2     {
    3         nand_select();           /* 1. 选中芯片
    4         nand_cmd(0xff);          /* 2. 发出读命令0xff */
    5         nand_read_ready();       /* 3, 判断状态 */
    6         nand_deselect();         /* 4. 取消选中
    7     }

    参考:

    https://blog.csdn.net/qqliyunpeng/article/details/51180276

    https://www.cnblogs.com/lifexy/p/7097695.html

  • 相关阅读:
    Mybatis 原始dao CRUD方法
    JQuery的焦点事件focus() 与按键事件keydown() 及js判断当前页面是否为顶级页面 子页面刷新将顶级页面刷新 window.top.location
    使用actionerror做失败登录验证
    Java项目中的下载 与 上传
    shiro框架 4种授权方式 说明
    javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式
    序列化表单为json对象,datagrid带额外参提交一次查询 后台用Spring data JPA 实现带条件的分页查询 多表关联查询
    Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法
    orcal 数据库 maven架构 ssh框架 的全注解环境模版 maven中央仓库批量删除lastupdated文件后依然是lastupdated解决方法 mirror aliyun中央仓库
    EasyUI加zTree使用解析 easyui修改操作的表单回显方法 验证框提交表单前验证 datagrid的load方法
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10177814.html
Copyright © 2011-2022 走看看