NAND操作原理
硬件连接
NAND与2440电路连接图
1.NAND FLASH和S3C2440之间只有数据线,在DATA0~DATA7上既传输数据,又传输地址,当ALE为高电平时传输的是地址。
2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
3.通过状态引脚RnB来判断:它为高电平表示就绪(烧写完成),它为低电平表示正忙
4.操作步骤: 发出命令,发出地址,发出数据/读数据
NAND 控制器
2440内部有NAND 控制器。在 NAND Flash 中执行引导代码,在 SDRAM 中执行主代码。可以通过寄存器控制,向NANDflash发送指令或读取数据。其中包括NFCMMD命令寄存器,NFADDR地址寄存器,NFDATA数据寄存器。读取数据时可以从NFDATA中读取。
所以,对于NANDFLASH的操作,可以总结为:CPU只需操作nandflash控制器的寄存器,nandflash控制器会自动与NANDFLASH进行数据通信。
2440--> NAND FLASH CPU--> S3C2440
发命令 选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址 选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 选中芯片 NFDATA=数据值
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据 选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
主要寄存器:
UBOOT实际操作
可以使用UBOOT实际操作Nandflash
1. 读ID
CPU对S3C2440的操作 u-boot 中实现代码
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1//写入1
发出命令0x90 NFCMMD=0x90 mw.b 0x4E000008 0x90
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
读数据得到0xEC val=NFDATA md.b 0x4E000010 1
读数据得到device code val=NFDATA md.b 0x4E000010 1
0xda
退出读ID的状态 NFCMMD=0xff mw.b 0x4E000008 0xff
2. 读内容: 读0地址的数据
使用UBOOT命令查看地址0的数据:
nand dump 0
Page 00000000 dump:
17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5
CPU对S3C2440的操作 u-boot 实现代码
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30
读数据得到0x17 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0xea val=NFDATA md.b 0x4E000010 1
退出读状态 NFCMMD=0xff mw.b 0x4E000008 0xff
NandFlash编程
编程原理
nandflash的编程主要分别几步:
1.初始化 ,初始化主控芯片的nandflash控制器
2.识别芯片,读取ID
3.读数据 page单位
4.写数据 page单位
5.擦除 块block单位
Nand控制器具有时序输出,nandflash芯片也具有时序输入,所以要根据芯片的时序配置nandflash控制器的时序输出
Nandflash的CLE与ALE时序图与寄存器:
NandFlash在HCLK下,HCLK是100M,那么一个时钟周期10ns
flash芯片初始化时序以及时序周期
可以看出,在芯片时序图中,操作要求先上拉CLE/下拉CE/下拉ALE---下拉WE。
首先tCLS时间最小12ns,而tWP的最小时间也是12ns,所以CLE与WE可以同时上拉/下拉。对应2440的时序图,TACLS可以为0,所以NFCONF的[13:12]可以设置为0
再来看TWRPH0,表示的是WE下拉时间长度,对应flash时序图的tWP,最小12ns,根据计算公式10*(TWRPH0+1)>=12,TWRPH0>=1,取1即可
TWRPH1表示WE上拉后CLE/ALE延时下拉时间长度,对应flash时序图中tALH,最小5.根据计算公式得TWRPH1>=0.所以flash初始化:
void nand_init(void)
{
/*设置时序*/
NFCONF=(0<<12)|(1<<8)|(0<<4);
/*始能,禁止片选,初始化ECC编码器*/
NFCONT=(1<<0)|(1<<1)|(1<<4);
}
读取设备ID
接下来读取设备ID,内存大小
在nandflash芯片数据手册中有读取id时序图:
在时序图中,发送90h指令与00h地址后,芯片会返回厂家地址ECH,设备地址DAH,3rd字节,4rd字节,在第4字节中,[5:4]表示block大小,[1:0]表示page大小
由时序图可知,首先下拉片选CE,然后上拉CLE与WE,这时输入命令,再上拉WE,下拉CLE,命令输入结束
然后上拉ALE,下拉WE,输入地址,再上拉WE,下拉ALE。
2440中的NANDFLASH控制器会帮助我们进行时序的控制,只需将值写入NANDFLASH控制器寄存器即可。
添加读取id代码,并且编写测试函数:
#include "my_printf.h"
#include "s3c2440_soc.h"
void nand_init(void)
{
/*设置时序*/
NFCONF=(0<<12)|(1<<8)|(0<<4);
/*始能,禁止片选,初始化ECC编码器*/
NFCONT=(1<<0)|(1<<1)|(1<<4);
}
void nand_select(void)
{
NFCONT&=~(1<<1);//始能片选
}
void nand_deselect(void)
{
NFCONT|=(1<<1);
}
void nand_cmd(unsigned int cmd)
{
volatile int i;
NFCMD=cmd;
for(i=0;i<10;i++);
}
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR=addr;
for(i=0;i<10;i++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0]=nand_data();
buf[1]=nand_data();
buf[2]=nand_data();
buf[3]=nand_data();
buf[4]=nand_data();
nand_deselect();
printf("maker id=0x%x\n\r",buf[0]);
printf("device id=0x%x\n\r",buf[1]);
printf("3rd byte=0x%x\n\r",buf[2]);
printf("4rd byte=0x%x\n\r",buf[3]);
printf("page size=%d kb\r\n",1<<(buf[3]&0x03));
printf("block size=%d kb\r\n",(1<<((buf[3]>>4)&0x03))*64);
printf("5rd byte=0x%x\n\r",buf[4]);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
printf("[s] Scan nand flash\n\r");
printf("[e] Erase nand flash\n\r");
printf("[w] Write nand flash\n\r");
printf("[r] Read nand flash\n\r");
printf("[q] quit\n\r");
printf("Enter selection: ");
c = getchar();
printf("%c\n\r", c);
/* 测试内容:
* 1. 识别nand flash
* 2. 擦除nand flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_id();
break;
case 'e':
case 'E':
break;
case 'w':
case 'W':
break;
case 'r':
case 'R':
break;
default:
break;
}
}
}
输出结果: