分类: 嵌入式
2012-06-16 15:59:24
3c2440 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++);
}