JZ2440:nandflash
本篇文章链接:http://www.voidcn.com/article/p-xxxzdhct-mb.html
芯片的型号:K9F2G08U0C
1. 硬件部分:1.1 简介:
几个特殊的引脚:
? ??
1.2 内部存储单元的组织结构:对 nandflash 的结构的几点说明:
2. 软件部分:
由于 s3c2440 内部有nand 的控制器,去查看芯片的原理图,自己编写时序操作程序不是明智的选择,正确的方式是配置好 s3c2440 的 nandflash 控制器:
2.1 初始化部分:
设置好时序中的几个间隔时间:
从 s3c2440 芯片手册上可以知道:
? ??
图1:
对于 CLE/ALE 上的时序,我们需要设置 TACLS,TWRPH0,TWRPH1,这几个都在 NFCONF 寄存器里。
?
CCON = 1; // 支持 1k 字节或 2k 字字节每页的NAND flash存储器
GPG13 = 1; // 每页 2k 字节
GPG14 = 1; // 5个地址周期
GPG15 = 0; // 8位总线
要 求上边的 TACLS、TWRPH0、TWRPH1 的值:
? ? ? ??时间表:
? ? ? ? 时序:
找一个跟上边 红字 图1 中都有的一张时序图:
我们把图一也拿过来
? ?
?
图1:
则?
TACLS 时间是 tcls -twp,查 时间表 得到 15ns -15ns = 0ns
? ? 根据寄存器中描述的计算公式:
Duration = ?HCLK x TACLS => ?0ns = 10ns x TACLS ?=> TACLS = 0
TWRPH0 的时间是 twp ,查 时间表 得到 15ns?
? ??根据寄存器中描述的计算公式:
Duration = ?HCLK x ( TWRPH0 + 1 ) ?=> ?15ns = 10ns x (TWRPH0 + 1) ?=> TWRPH0 = 0.5,由于他的取值范围是 (0~7),并且,时间表中的时间是最小能识别的时间,那我们取TWRPH0 = 1
TWRPH1 的时间是 tclh = 5ns
? ??根据寄存器中描述的计算公式:
Duration = ?HCLK x ( TWRPH1 + 1 ) => 5ns = 10ns 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 = 15ns,而对 flash 他的时钟来自 HCLK = 100MHz(这里如果不懂,可以查看我之前的文章(
JZ2440:时钟设置)),即使是单周期指令,也要 10ns,因此,要延时一段时间。
2.4 读数据:
flash 芯片上:
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; } /* * 参数的含义: addr 要读的地址,buf 读出来的数据存放的缓存,len 要读的长度 */ void nand_read(unsigned int addr,unsigned char *buf,unsigned int len) { int col = addr % 2048; int i = 0 ; nand_select(); // 选中芯片 while(i < len) { nand_cmd(0x00); // 发出读命令 00h nand_addr(addr); // 发送读的地址 nand_cmd(0x30); // 发出读命令 30h nand_wait_ready(); // 等待不忙 for(;(col < 2048) && (i < len);col++) { buf[i] = nand_data(); // 读数据 i++; addr++; } col = 0; } nand_deselect(); // 取消片选 } 2.5 复位 flash 芯片:
知道了上边的命令的表格,那 复位的实现也就简单了:
void nand_reset(void) { nand_select(); // 选中芯片 nand_cmd(0xff); nand_read_ready(); nand_deselect(); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |