Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1905902
  • 博文数量: 496
  • 博客积分: 12043
  • 博客等级: 上将
  • 技术积分: 4778
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-27 14:26
文章分类

全部博文(496)

文章存档

2014年(8)

2013年(4)

2012年(181)

2011年(303)

2010年(3)

分类: 嵌入式

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++);

}

阅读(849) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~