2010-01-08 22:10:48| 分类: arm|字号 订阅
s3c2440 nand 控制器(以对K9F2G08U0A 256M读操作为例)
(1)NFCONF:2440和2410不同,它的NFCONF寄存器是用来设置NAND Flash的时序参数TACLS、TWRPH0、TWRPH1,设置数据位宽(K9F2G08U0A的位宽为8-bit bus,因此[0]设为0);还有一些只读位,用来指示是否支持其他大小的页(比如一页大小为256/512/1024/2048字节)。NFCONF没有实现对引脚的控制功能,这些功能在NFCONT里实现。
(2)NFCONT:用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定NAND Flash。
(3)NFCMD:对于不同型号的Flash,操作命令一般不一样。参考K9F2G08U0A手册。
(4)NFADDR:当写这个寄存器时,它将对Flash发出地址信号。只用到低8位来传输,所以需要分次来写入一个完整的32位地址,写地址后面会有详细说明。
(5)NFDATA:只用到低8位,读、写此寄存器将启动对NAND Flash的读数据、写数据操作。
(6)NFSTAT:只用到位0,用来检测NAND是否准备好。0:busy,1:ready。
注意:TACLS、TWRPH0、TWRPH1这3个参数控制的是NAND Flash信号线CLE/ALE与写控制信号nWE的时序关系,如图
在设置NFCONF中的TACLS、TWRPH0、TWRPH1时,先查看K9F2G08U0A手册:
由表可知:
CLE Setup Time=12ns,CLE Hold Time=5ns
ALE Setup Time=12ns,ALE Setup Time=5ns,
nWE Pulse Width=12ns
为了满足K9F2G08U0A的时序要求,需要TACLS+TWRPH0+TWRPH1>=46ns,这里设TACLS=0,TWRPH0=3,TWRPH1=0,TACLS+TWRPH0+TWRPH1=50ns,满足要求。
Nand flash的读操作
K9F2G08U0A总共有2048块,每块有64页,总共131072页,每页有(2048+64)个字节(列),故我们可以知道这块flash的容量为2048 *(64 *2112)= 276824064 Bytes = 264 MB。1个Page总共由2112 Bytes组成,这2112个字节按顺序由上而下以列为单位进行排列(1列代表一个Byte。第0列为第0 Byte ,第1列为第1 Byte,以此类推,每个列又由8个位组成,每个位表示1个Byte里面的1bit),但事实上每个Page上的最后64Bytes(Spare Field)是用于存贮检验码和其他信息用的,并不能存放实际的数据,剩下的2048Bytes便是我们用于存放数据用的Data Field。所以实际上我们可以操作的芯片容量为2048 *(64*2048) = 268435456 Bytes = 256MB,。对K9F2G08U0A的读写操作要以页为单位,擦除操作要以块为单位。
在nand.c中包含3个主要函数:
void nand_init(void)
static void nand_reset(void)
void nand_read(unsigned char *buf, unsigned long start_addr, int size)
nand_init()是nand flash的初始化函数,在对nand flash进行任何操作之前,nand_init()必须被调用,其中实现的功能是NFCONF设置TACLS、TWRPH0、TWRPH1和NAND数据位宽,NFCONT使能控制器、禁止片选nFCE(用的时候再选上),初始化ECC,然后调用nand_reset(每次使用前先复位下)。
nand_reset实现的功能是使能片选(NFCONT)、发出复位命令(NFCMD),循环查询NFSTAT位0,直至它为1,最后禁止片选,用的时候再选中。
nand_read函数如下:
/* 从nand flash 位置start_addr开始,将数据复制到SDRAM地址处 */
void nand_read(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
if ((start_addr & (2048-1)) || (size &(2048-1)))
{
return ; /* 地址或长度不对齐 (需要考虑地址或长度对齐,按页对齐)*/
}
/* 选中芯片 */
nand_select_chip();
for(i=start_addr; i < (start_addr + size);)
{
/* 发出1st cycle READ命令 */
write_cmd(0);
/* Write Address */
write_addr(i);
/*发出2nd cycle read命令*/
write_cmd(0x30);
/*查询NFSTAT,nand是否准备好*/
wait_idle();
/*一个地址对应2048个字节数据。由于8bit位宽的限制,
每次读取8位(1个字节),共读2048次得到1页2048Byte数据*/
for(j=0; j < 2048; j++, i++)
{
*buf = read_data();//寄存器NFDATA
buf++;
}
}
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
Nand flash的寻址
其中需要注意的是发送地址函数write_addr(),K9F2G08U0A的地址分为5cycle发送到NFADDR寄存器,前两个cycle是列地址(每个列是1个字节,8位数据,通过I/O0~7),也就是要从页的第几个列(字节)开始操作,后3个cycle发送的是页地址。K9F2G08U0A总共131072页,每页有(2048+64)个字节(列),因此对页的寻址需要17根地址线,对列的寻址需要12根地址线。如下图所示:
也可以这么认为,当我们得到一个对nand操作的地址,A0~A11是它的列地址(一个页里的第几个字节),A12~A28是页地址,为了分解出列地址和页地址,可以这样操作:
col=addr&2047; //2047的到A0~A10得到列地址
page=addr/2048; // 2048为每页的字节数,不包括附加的64字节,得到页地址
或:
col=addr&4095; // 4095得到A0~A11的地址,得到列地址
page=addr/2112; // 2112为每页的字节数,包括附加的64字节,得到页地址
上面第一个操作表明,如果我们忽略每页附加的64字节,那么对列的寻址也不能寻到这64字节,因此只需11根地址线对每页的0~2047列寻址。
假如我们得到一个nand操作的地址是0x0a3e0000,若通过第一种方法分解后,得到列地址是0,得到的页地址是83904,这表明,将从nand的第83904页的第0个字节开始读取数据。
完整的write_addr()函数如下:
static void write_addr(unsigned int addr)
{
int i;
//volatile unsigned char *p = (volatile unsigned char *)&rNFADDR;
/*NFADDR寄存器也只用到低八位来传输,所以需要分4次来写入一个完整的32位地址,需要注意每次的移位操作*/
int col,page;
col=addr&NAND_BLOCK_MASK;// NAND_BLOCK_MASK=2048-1,得到列地址
page=addr/NAND_PAGE_SIZE; // NAND_PAGE_SIZE=2048(1页的字节数),得到
//页地址
rNFADDR = col & 0xff;//先传列地址的0~7位
for(i=0; i<10; i++);
rNFADDR = (col >> 8) & 0x0f;//再传列地址的8~11位
for(i=0; i<10; i++);
rNFADDR=page&0xff;//传页地址的低8位(对应上图的A12~A19)
for(i=0; i<10; i++);
rNFADDR = (page >> 8) & 0xff;//页地址(对应上图的A20~A27)
for(i=0; i<10; i++);
rNFADDR = (page >> 16) & 0x01;//页地址(对应上图的A28)
for(i=0; i<10; i++);
}