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

posted @ 2018-12-26 17:35  程序猿爱吃鸡  阅读(1239)  评论(0编辑  收藏  举报