JZ2440的NandFlash控制器
芯片的型号: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寻址方式:
大页:(K9F2G08U0C共有2048个Block(块), 每个Block含有 64 Page(页),所以我们这款芯片为大页读写)
列地址: 进行 Block 和 Page 寻址
行地址: 进行 Page 内寻址
2. 软件部分:
2.1 初始化部分:
寄存器
地址
R/W
描述
复位值
NFCONF
0x4E000000
R/W
Nandflash 的配置寄存器
0x0000100X
NFCONF
Bit
描述
初始值
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 之后的位:
![]()
- 我们假设 HCLK 为 最大值 136MHz ,稍作分析可得,我们实际的HCLK肯定要小很多,但是如果最大值满足那么小一点的HCLK肯定满足条件!则 HCLK 的周期是 1/136MHz =
7.4ns<flash时钟来自于HCLK后面讲时钟会提到>
- 查看 nandflash 的数据手册,找到跟时间相关的时序图和时间:
- #define NFCONF (*((volatile unsigned long *)0x4E000000))
- void nand_init(void )
- {
- #define TACLS 0
- #define TWRPH0 3
- #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++);
- }
对程序的解释:
2.4 读数据:
- #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();
- }