目标:以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=10x(TWRPH0+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) 取消选中芯片
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