本节来学习裸机下的Nand Flash驱动,本节学完后,再来学习Linux下如何使用Nand Flash驱动
Linux中的Nand Flash驱动,链接如下:
(分析MTD层以及制作Nand Flash驱动链接:http://www.cnblogs.com/lifexy/p/7701181.html)
本节简单制作一个Nand Flash驱动(只需要初始化Flash以及读Flash)
打开2440芯片手册,K9F2G08U0M芯片手册(因为2440中Nand Flash是用的256MB(2Gb)内存,8个数据引脚)
在芯片手册中得到K9F2G08U0M=2048块Block=128K页Pages=256MB=2Gb
1个设备=2048块Block
1块Block=64页Pages
1页=(2K 64)B (因为每个地址里都存放了一个字节,所以用B表示)
其中64B是存放ECC的OOB地址,(ECC:存放判断位反转的校验码)
Nand Flash 缺点:
读数据容易位反转
可以通过ECC编码器值来判断读数据是否位反转,若位反转则重新读数据
写过程:
- 1)写页数据
- 2)然后生成ECC
- 3)将ECC写入到OBB页地址里(写数据是不会出现位反转)
读过程:
- 1)读出页数据,然后生成临时ECC(此时ECC可能有错)
- 2)然后读出OOB页地址里的ECC
- 3)比较两个ECC,判断是否出现位反转
读OOB方法:
读整个Nand Flash时,是读不出页里面的OBB地址,比如读2049这个地址数据时,是读的第二页上的第2个地址:
只有读某一页时,才能读出这个页里面的OOB地址, 比如读第0页的2049这个地址数据时,才是读的第0页OOB的第2个地址:
Nand Flash芯片硬件引脚图:
- 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:读使能,和we类似
- nWP:写保护(protect) (1:不保护,0:只能读不能写),默认接高电平.
1.编写nand_init()函数
1.1设置通信时序
图1(nandflash时序表):
图2(nandflash时序图):
通过图2和图1可以看出:
tCS是等待芯片使能CE的时间, tCS=20nS
tCLS和tALS是等待WE(写信号)结束的时间, tCLS=tALS=15nS
tWP是WE(写信号)维持时间, tWP=15nS
tALH是等待命令写入成功的时间, tALH=5nS
tCLH是等待地址写入成功的时间, tCLH=5nS
图3(2440-nandflash时序图):
首先查看2440芯片手册里nandflash时序图,如上图,可以看出需要设置TACLS,TWRPH0和TWRPH1,这三个参数
TACLS:属于等待WE(写信号)就绪的时间,对比图2得出TACLS= tCLS- tWP=0nS
TWRPH0:属于WE(写信号)的时间, 对比图2得出TWRPH0= tWP=15nS
TWRPH1:属于等待命令写入成功的时间,对比图2得出TWRPH1=tALH=tCLH=5nS
最后,在NFCONF寄存器中设置这三个参数
TACLS[13:12]
表示Duration(持续时间)=HCLK*TACLS,由于Duration=0nS,所以TACLS=0
TWRPH0 [10:8]
表示Duration(持续时间)=HCLK*( TWRPH0 1),由于Duration=15nS,HCLK=10nS(100Mhz),所以TWRPH0 =1.
TWRPH1 [6:4]
表示Duration(持续时间)= HCLK*( TWRPH1 1),由于Duration=5nS,HCLK=10nS(100Mhz),所以TWRPH1 =0
1.2然后定义全局变量,并实现nand_init()初始化:
代码语言:javascript复制/* nand flash 时序 */
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* nand flash 寄存器 */
#define NFCONF *((unsigend int *)0X4E000000); //配置寄存器(用来设置时序)
#define NFCONT *((unsigend int *)0X4E000000); //控制寄存器(用来使能nandflash控制器以及ECC编码器,还有控制芯片使能CE脚)
#define NFCMMD *((unsigend char *)0X4E000000);//发送命令寄存器(命令只有8位)
#define NFADDR *((unsigend char *)0X4E000000);//发送地址寄存器(地址只有8位)
#define NFDATA *((unsigend int *)0X4E000000);//读/写数据寄存器(数据只有8位)
#define NFSTAT *((unsigend int *)0X4E000000);//运行状态寄存器(用于判断RnB脚)
/*因为Nand Flash只有8位I/O脚,所以NFCMMD/ NFADDR/ NFDATA三个寄存器值都是unsigend char型 */
1.3 nand_init()函数初始化
代码语言:javascript复制void nand_init(void)
{
/* 设置时序 */
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* bit4=1:初始化ECC, bit1=1:禁止片选 bit0=1:启动nandflash控制器*/
NFCONT = (1<<4)|(1<<1)|(1<<0);
}
2编写nand_read()函数
2.1编写nand_read()函数需要以下几个子函数:
2.1.1片选使能函数(在读写FLASH之前都要选中片选)
代码语言:javascript复制nand_select() //使能片选
{
int i;
NFCONT&=~(1<<1); // NFCONT控制器位1置0
for(i=0;i<10;i ); //等待芯片使能成功
}
2.1.2取消片选函数(退出读写FLASH时需要取消片选)
代码语言:javascript复制nand_deselect() //取消片选
{
int i;
NFCONT&=~(1<<1); // NFCONT控制器位1置0
for(i=0;i<10;i ); //等待芯片使能成功
}
2.1.3读命令函数
代码语言:javascript复制nand_cmd(unsigned char cmd)
{
int i;
NFCMMD= cmd; // 向NFCMMD寄存器写入命令
for(i=0;i<10;i ); //等待写入命令成功
}
2.1.4判断RnB状态函数(在写入所有命令后都要判断RnB脚是否为高电平就绪)
代码语言:javascript复制nand_waite_idle()
{
int i;
while(!(NFSTAT&0X01)) // 等待NFSTAT寄存器位0置1
for(i=0;i<10;i );
}
2.1.5读数据函数
代码语言:javascript复制nand_read_data()
{
unsigend char p=NFDATA; //读取NFDATA寄存器
return p; //返回
}
2.1.6 编写写入地址函数 (分5个周期)
首先Nand Flash引脚只有8位,然而地址共有2048(块)*64(页)*2KB,为了读出多个地址,如下图,所以需要分5个周期来实现发送地址
如上图,其中 A10~A0对应页大小(列),由于nandflash每页2048B,所以只用到A10~A0
A28~A11对应页目录(行),表示共有2048块*64(每块有64页)个目录
例如,4097 地址就是:
A10~A0=4097 48= 1(A0=1,其余为0)
A28~A11=4097/2048=2(A13=1,其余为0)
所以nand_write_nand()函数如下:
代码语言:javascript复制void nand_read_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
volatile int i;
NFADDR=(col>>0)&0xff; //A7~A0,第1周期
for(i=0;i<10;i );
NFADDR=(col>>8)&0x0f; //A10~A8,第2周期
for(i=0;i<10;i );
NFADDR=(page>>0)&0xff; //A18~A11,第3周期
for(i=0;i<10;i );
NFADDR=(page>>8)&0xff; //A26~A19,第4周期
for(i=0;i<10;i );
NFADDR=(page>>16)&0xff; //A27~A28,第5周期
for(i=0;i<10;i );
}
2.2Nand Flash命令图:
如上图,例如:当要reset复位nand flash时
1) 使能片选nand_select();
2) 发送0XFF复位命令nand_cmd(0xFF);
3) 等待RnB状态是否就绪 nand_wait_idle();
4) 取消片选 nand_deselect();
2.3Nand Flash读数据时序图:
从上图可以看出nand flash 读数据分为了以下几个步骤:
(1) 使能片选CE,将CLE置1,等待发送命令
(2) 将WE置低,将IO置为0X00,然后拉高WE,触发一次上升沿,则将把0x00写入flash中
(3) 将CLE置0,表示发送地址(分为5个周期)
(4) 发送读命令0X30
(5) 等待RnB信号为高电平
(6) 读数据
(在同一页里,数据可以连续读,读下一页时,需要重新发送新的地址才行例如:读1000地址到2050地址时,
1.发出1000地址,到达页0的1000地址上,然后再连续读(2048-1000)次,直到读到页0的2047处.
2.再发出2048地址,到达页1的0地址上,然后连续读(2051-2048)次,直到读到2050为止)
(7) 取消片选nCE
2.4 所以nand_read()函数如下:
代码语言:javascript复制void nand_read(unsigned int src,unsigned char *dest,unsigned int len)
/* src:源地址,为32位地址,所以用unsigend int表示
*dest:目的地址内容,由于这里是将数据读出到目的地址内容中,所以需要用到*指针,因为每个地址里存的是一个字节,所以用unsigend char 型 */
{
int col=src 48; //第一次读,可能不是读的页首地址,所以需要记录当前页的位置
int i=0; //当前读了0次
nand_select(); //1使能片选nCE
while(i<len)
{ nand_cmd(0X00); //2发送读命令0X00
nand_write_addr(src); // 3发送yuan地址(分为5个周期)
nand_cmd(0X30); //4发送读命令0X30
nand_wait_idle(); //5等待RnB信号为高电平
for(;(col<2048)&&(i<len);col ) //连续读页内数据
{dest[i]=nand_read_data(); //6.读数据
i ;
src ;}
col=0;}
nand_deselect(); // 取消片选nCE
}