JZ2440的NandFlash控制器

it2022-05-05  140

片的型号:K9F2G08U0C

摘要: 本文以S3C2440为例来讲解NAND FLASH控制器的使用方法. 例程中故意将一部分代码放置到 nand 的4k 字节之后, 因无法自动拷贝到steppingstone, 所以需要读取nand中的内容到sdram, 本例程重点关注nand的初始化和读取.

1. 硬件部分:

1.1 简介:

芯片大小:256M Byte记忆单元阵列:(256M + 8,192K)bit x 8bit擦写次数比较少:10 万次 数据保留时间:10 年8个IO 口进行数据和地址的复用,因此,读写的时候要用到多个周期

 引脚定义及接法

1.2 内部存储单元的组织结构:

K9F2G08U0C共有2048个Block(块), 每个Block含有64 Page(页), 每个Page含有2k byte的正常存储空间以及64 byte的校验空间 .

总空间 = 2048 * 64 * (2 * 1024 + 64)  byte = 264MB

实际存储空间 = 2048 * 64 * 2 * 1024 byte = 256MB

对 nandflash 的结构的几点说明:

 一页中 1k 表示的是main 区(用于存储用户数据)容量,32 表示的是 spare 区(用于在读写操作的时候存放校验码)容量块的大小一般是 128kb、256kb、512kb,貌似这里更小,是64kb每个块里边包含很多页,老的 nandflash ,页大小是 256 Bytes、512Bytes,这类被称作 small block,地址周期只有 4 个。常见的nandflash,页大小多数是 2k Bytes,被称作 big block,地址周期 5 个,更新的 nandflash 页大小是 4k Bytes,这里的这个芯片,页大小是 2k Bytes,属于 big block。这个芯片的写操作是以页为单位的,擦除是以块为单位的。在一个块中,对每一页的编程必须是顺序的,比如,一个块中有128个页,那么你只能先对 Page0 编程,再对 Page1 编程 ...为了能让 nandflash 作为启动介质,s3c2440 内部集成了4k 的 sram ,当从 nand 启动的时候,nandflash 代码的前 4k 空间会被赋值到 s3c2440 内部,然后从内部的 sram 开始启动。s3c2440 硬件产生 ECC 校验码。

1.3寻址方式:

   NAND的读写的最小单位为Page:

小页:

大页:(K9F2G08U0C共有2048个Block(块), 每个Block含有 64 Page(页),所以我们这款芯片为大页读写)

列地址: 进行 Block 和 Page 寻址

行地址: 进行 Page 内寻址

2. 软件部分:

由于 s3c2440 内部有nand 的控制器,去查看2440的数据手册,自己编写时序操作程序不是明智的选择,正确的方式是一步一步配置好 s3c2440 的 nandflash 控制器,然后对应查看K9F2G08U0C的芯片手册看是否符合要求! 在此以读Nandflash为例子来配置寄存器。

2.1 初始化部分:

设置好时序中的几个间隔时间: 从 s3c2440 芯片手册上可以知道:     图1: 对于 CLE/ALE 上的时序,我们需要设置 TACLS,TWRPH0,TWRPH1,这几个都在 NFCONF 寄存器里。

寄存器

地址

R/W

描述

复位值

NFCONF

0x4E000000

R/W

Nandflash 的配置寄存器

0x0000100X

NFCONFBit描述初始值

Reserved

[15:14]

保留

-

TACLS

 

[13:12]

CLE & ALE duration setting value (0~3)

Duration =  HCLK x TACLS

01

...

.........

TWRPH0

[10:8]

TWRPH0 duration setting value (0~7)

Duration =  HCLK x ( TWRPH0 + 1 )

000

...

.........

TWRPH1

[6:4]

TWRPH1 duration setting value (0~7)

Duration =  HCLK x ( TWRPH1 + 1 )

000

从这里之后的几个都是由硬件决定(就是上下拉)的不需要软件来管。

 原理图中的这个地方设置的是上表中 TWRPH1 之后的位:

CCON = 1; // 支持 1k 字节或 2k 字字节每页的NAND flash存储器 GPG13 = 1; // 每页 2k 字节 GPG14 = 1; // 5个地址周期 GPG15 = 0; // 8位总线

要 求上边的 TACLS、TWRPH0、TWRPH1 的值:

我们假设 HCLK 为 最大值 136MHz ,稍作分析可得,我们实际的HCLK肯定要小很多,但是如果最大值满足那么小一点的HCLK肯定满足条件!则 HCLK 的周期是 1/136MHz = 7.4ns<flash时钟来自于HCLK后面讲时钟会提到>查看 nandflash 的数据手册,找到跟时间相关的时序图和时间:

        时间表:

    时序:

找一个跟上边红色数字 图1 中都有的一张时序图:

我们把图一也拿过来

    图1:

则  TACLS 时间是 tCLS -tWP,查 时间表 得到 12ns -12ns = 0ns     根据寄存器中描述的计算公式:Duration =  HCLK x TACLS =>  0ns = 7.4ns x TACLS  => TACLS = 0

TWRPH0 的时间是 tWP ,查 时间表 得到 12ns      根据寄存器中描述的计算公式:Duration =  HCLK x ( TWRPH0 + 1 )  =>  12ns = 7.4ns x (TWRPH0 + 1)  => TWRPH0 = 1.6 ,由于他的取值范围是 (0~7) ,并且,时间表中的时间是最小能识别的时间,那我们取TWRPH0 =3

TWRPH1 的时间是 tclh = 5ns     根据寄存器中描述的计算公式:Duration =  HCLK x ( TWRPH1 + 1 ) => 5ns = 7.4ns x (TWRPH1 + 1) => TWRPH1 = 0 即定能满足

#define NFCONF (*((volatile unsigned long *)0x4E000000)) void nand_init(void ) { #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 /* 设置时序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能 nandflash 控制器,初始化ECC,关片选 */ NFCONT = (1<<4)|(1<<1)|(1<<0); }

2.2 芯片的选择和禁止(让芯片操作 CE 引脚):

#define NFCONT (*((volatile unsigned long *)0x4E000004)) void nand_select(void ) { NFCONT &= ~(1<<1); } void nand_deselect(void ) { NFCONT |= (1<<1); }

2.3 写命令和写地址:

//写命令 注意是八位的命令   #define NFCMMD (*((volatile unsigned char *)0x4E000008))      void nand_cmd(unsigned char cmd)   {   volatile int i;     NFCMMD = cmd;   for (i = 0; i<10; i++); // 延时一段时间   }   // 写地址   (大页) #define NFADDR (*((volatile unsigned char *)0x4E00000C))   #define NAND_SECTOR_SIZE    2048   #define NAND_BLOCK_MASK     (NAND_SECTOR_SIZE_LP - 1)      void nand_addr(unsigned int addr)   {   // unsigned int col = addr % 2048;   // unsigned int page = addr / 2048;    col = addr & NAND_BLOCK_MASK;    page = addr / NAND_SECTOR_SIZE;    volatile int i;    NFADDR = col & 0xff;                /* Column Address A0~A7 */    for (i = 0; i<10; i++);    NFADDR = (col>>8) & 0x0f;       /* Column Address A8~A11 */    for (i = 0; i<10; i++);    NFADDR = page & 0xff;            /* Row Address A12~A19 */    for (i = 0; i<10; i++);    NFADDR = (page>>8) & 0xff;   /* Row Address A20~A27 */    for (i = 0; i<10; i++);    NFADDR = (page>>16) & 0x03;  /* Row Address A28~A29 */    for (i = 0; i<10; i++);   }  

对程序的解释:

 flash芯片的手册上有对于大页 flash 的访问 各个周期传递的位:

因此上边的命令 NFADDR = ... 就不难理解了。

至于,各个周期间的延时:

从flash芯片的数据手册上知道:

对于 I/O 引脚上的数据进行采集是在 WE 的上升沿进行的,因此, 每两个周期的间隔至少应该大于一个 tDS = 12ns,而对 flash 他的时钟来自 HCLK =136MHz,即使是单周期指令,也要 1/136MHz=7.4ns,因此,要延时一段时间。

2.4 读数据:

flash 芯片上:

#define NFSTAT (*((volatile unsigned char *)0x4E000020))   #define NFDATA (*((volatile unsigned char *)0x4E000010))   void nand_wait_teady(void)   {       while(!(NFSTAT & 1))            for(i = 0; i < 10; i++);   }      unsigned char nand_data(void)   {       return NFDATA;   }   /*  * 参数的含义: 从NAND Flash位置start_addr开始,将数据复制到SDRAM地址buf处,共复制size字节 */   void nand_read(unsignedchar* buf, unsignedlong start_addr, int size) {       int i,j;   if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { return; }         nand_select_chip(); //选中芯片          for(i=start_addr; i < (start_addr + size);)      {           write_cmd(0x00); // 发出读命令 00h           write_addr(i); // 发送读的地址           write_cmd(0x30); // 发出读命令 30h           nand_wait_ready(); // 等待不忙              for(j=0; j < NAND_SECTOR_SIZE; j++,i++)           {               *buf = read_data(); // 读数据                 buf++;           }         }       nand_deselect_ship(); // 取消片选     return ;   }  

总结一下:

(1)选中芯片(2)发送00h(3)发出地址(4)发30h(5)等待就绪(6)读一页数据

2.5 复位 flash 芯片:

知道了上边的命令的表格,那 复位的实现也就简单了:

void nand_reset(void)   {       nand_select_chip(); // 选中芯片       write_cmd(0xff);       nand_wait_ready();       nand_deselect_chip();   }  

 


最新回复(0)