Nand Flash原理解析
Nand Flash的分类:
根据物理结构上的区别,Nand Flash主要分为如下两类:
SLC (Single Level Cell):单层式存储
MLC (Multi Level Cell):多层式存储
SLC在存储格上只存一位数据,而MLC则存放两位数据
MLC对比SLC:
1、价格:
由于MLC采用了更高密度的存储方式,因此同容量的MLC价格上远低于SLC。
2、访问速度:
SLC的访问速度一般要比MLC快3倍以上。
3、使用寿命:
SLC能进行10万次的擦写,MLC能进行1万次。
4、功耗:
MLC功耗比SLC高15%左右。
编址方式:
内存是统一编址的,有地址线和数据线来访问。
flash不是统一编址的,他是独立编址,CPU内部有nand flash控制寄存器,通过寄存器先传地址,再传命令。
控制类引脚作用:
1. CLE(Command Latch Enable): 命令锁存允许
2. ALE(Address Lactch Enable): 地址锁存允许
3. CE:芯片选择
4. RE:读允许
5. WE:写允许
6. WP:在写或擦除期间,提供写保护
7. R/B:读/忙
Nand Flash读操作
一个设备要使用,一般需要先初始化,nandflash也不例外。
任何一个flash使用,都要先选中,然后再清除选中。
在调用初始化flash的c语言之前,要先把栈设置好。
汇编中调用C语言函数,参数由 r0 r1 r3 寄存器来传递。
代码举例:
1、将代码从flash拷贝到ram中
copy_to_ram:
mov r0,#0 @调用C语言函数来拷贝
ldr r1,=_start
ldr r2,=bss_end
sub r2,r2,r1
mov ip,lr
bl nand_to_ram
mov lr,ip
mov pc,lr
2、nand.c文件
#define NFCONF (*((volatile unsigned long*)0x70200000))
#define NFCONT (*((volatile unsigned long*)0x70200004))
#define NFCMMD (*((volatile unsigned char*)0x70200008))
#define NFSTAT (*((volatile unsigned char*)0x70200028))
#define NFADDR (*((volatile unsigned char*)0x7020000c))
#define NFDATA (*((volatile unsigned char*)0x70200010))
void select_ship(void)
{
NFCONT &= ~(1<<1);
}
void delselect_ship(void)
{
NFCONT |= (1<<1);
}
void clean_RnB()
{
NFSTAT |= (1<<4);
}
void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
}
void wait_RnB(void)
{
while(!(NFSTAT & 0x1));
}
void nand_addr(unsigned char addr)
{
NFADDR = addr;
}
void nand_reset(void)
{
/* 选中 */
select_ship();
/* 清除RnB */
clean_RnB();
/* 发出复位信号 */
nand_cmd(0xff);
/* 等待就绪 */
wait_RnB();
/* 取消选中 */
delselect_ship();
}
void nand_init(void)
{
/*
HCLK的频率为100MHZ,周期就为10ns
TACLS > 0 ns
TWRPH0 > 15ns
TWRPH1 > 5ns
TACLS的值 = HCLK x TACLS > 0ns
TWRPH0的值 = HCLK x (TWRPH0 + 1) > 15ns
TWRPH1的值 = HCLK x (TWRPH1 +1) > 5ns
*/
/* 设置时间参数 */
#define TACLS 1
#define TWRPH0 2
#define TWRPH1 1
NFCONF &= ~((7<<12)|(7<<8)|(7<<4));
NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能 nandflash controller*/
NFCONT = 1 | (1<<1);
/* 复位 */
nand_reset();
}
void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;
/* 选中芯片 */
select_ship();
/* 清除RnB */
clean_RnB();
/* 发出命令0x00 */
nand_cmd(0x00);
/* 发出列地址 */
nand_addr(0x00);
nand_addr(0x00);
/* 发出行地址 */
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
/* 发出命令0x30 */
nand_cmd(0x30);
/* 等待就绪 */
wait_RnB();
/* 读数据 */
for(i = 0; i<1024*4; i++)
{
buff[i] = NFDATA;
}
/* 取消片选 */
delselect_ship();
}
void nand_to_ram(unsigned long start_addr,unsigned char* sdram_addr,int size)
{
/* i为页号、sdram_addr为内存中的位置、size拷贝数据的大小 */
int i;
/*
S3C6410启动时拷贝的8K代码不是存储在Nand flash的第一页上,
而是位于nand flash的前4页上,每页2K,总共8K,
*/
for (i = 0; i < 4; i++, sdram_addr+=2048)
{
NF_PageRead(i,sdram_addr);
}
size -= 1024*8;
for( i=4; size>0;)
{
NF_PageRead(i,sdram_addr);
size -= 4096;
sdram_addr += 4096;
i++;
}
}
Nand Flash写操作
写的操作与读操作类似,最重要的是找到数据手册中的时序图,flash使用的是HCLK。
代码举例:
int NF_Erase(unsigned long addr)
{
int ret;
//选中flash芯片
select_ship();
//清除RnB
clean_RnB();
//发送命令60
nand_cmd(0x60);
//发送行地址(3个周期)
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
//发送命令D0
nand_cmd(0xD0);
//等待RnB
wait_RnB();
//发送命令70
nand_cmd(0x70);
//读取擦除结果
ret = NFDATA;
//取消选中flash芯片
delselect_ship();
return ret;
}
int NF_WritePage(unsigned long addr,unsigned char* buff)
{
int ret,i;
//选中flash芯片
select_ship();
//清除RnB
clean_RnB();
//发送命令80
nand_cmd(0x80);
//发送列地址(2个周期)
nand_addr(0x00);
nand_addr(0x00);
//发送行地址(3个周期)
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
//写入数据
for(i=0;i<1024*4;i++)
{
NFDATA = buff[i];
}
//发送命令10
nand_cmd(0x10);
//等待RnB
wait_RnB();
//发送命令70
nand_cmd(0x70);
//读取写入结果
ret = NFDATA;
//取消选中flash芯片
delselect_ship();
return ret;
}
测试代码:
NF_Erase(128*1+1);
buf[0] = 100;
NF_WritePage(128*1+1,buf);
buf[0] = 10;
NF_PageRead(128*1+1,buf);
if( buf[0] == 100 )
lightLED();