• 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

  • 相关阅读:
    如何从程序集中加载及卸载插件(下)
    Castle AOP 系列(四):实现一个简单的基于上下文调用的权限校验机制
    Castle AOP 系列(一):对类方法调用的拦截
    Mussel使用系列(六):分布式调用的牛刀小试
    新发现XmlNode中变态的地方
    我们发现你在XX邮箱的账号密码与其他网站被盗账号密码一致 请立即更改密码。
    html5 css3 新特性一览表
    [android] Http Post 请求
    [vs 使用技巧] VS2013显示行数 行号
    ORACLE数据库存储使用情况查询命令
  • 原文地址:https://www.cnblogs.com/lxl-lennie/p/10177814.html
Copyright © 2020-2023  润新知