一、Nandflash原理解析
1、角色分析
类似于电脑的硬盘
2、NandFlash分类
根据物理结构上的区别,NandFlash主要分为如下两类:
SLC (Single Level Cell): 单层式存储
MLC (Multi Level Cell): 多层式存储
SLC在存储格上只存一位数据,而MLC则存放两位数据。
2.2 MLC对比SLC
1. 价格:
由于MLC采用了更高密度的存储方式,因此同容量的MLC价格上远低于SLC.
2. 访问速度:
SLC的访问速度一般要比MLC快3倍以上.
3. 使用寿命:
SLC能进行10万次的擦写,MLC能进行1万次
4. 功耗:
MLC功耗比SLC高15%左右
3.访问过程
3.1 对比内存与NandFlash编址区别
内存的存取方式:比如*(0x3008000) = 5; 等号左边走的是地址线,而等号右边走的是数据线,所以读取速度很快。
NandFlash是独立编址的,ARM中有个NandFlash控制器,用8根I/O口传送地址、命令、数据。这些地址、命令、数据当然是对Nand Flash控制器对应的寄存器操作的。
3.2 NandFlash地址结构
Nand Flash中的一个块被分成64个页,其中每一页又有2KB+64B的大小B。当然不同nandflash可能大小有所不同,但是结构都是一致的。
如何访问呢:下面的表格说明了5个周期内的行地址和列地址信息。从行地址获取页的位置,用列地址找出那个页(偏移)。
我们知道K9F2G08U0A的每一页有(2K+64)=2112Byte,2112byte 需要12bit来表示,对于2112byte系列的NAND,这2112byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[11:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,因为以块为单位擦除。64个page需要6bit来表示,占用A[17:12],即该page在块内的相对地址,也就是确定位于哪一页。A11这一位地址被用来设置2048byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A18以上的bit来表示,也就是确定位于哪一块。用A[0:28]这29位,就可以将K9F2G08U0A这256M的数据存储空间全部访问到。
http://blog.csdn.net/feihuxiaozi/article/details/6943124
3.3 信号引脚
1. CLE(Command Latch Enable): 命令锁存允许
2. ALE(Address Lactch Enable): 地址锁存允许
3. CE:芯片选择
4. RE:读允许
5. WE:写允许
6. WP:在写或擦除期间,提供写保护
7. R/B:读/忙
二、Nand Flash读取编写
①页读: (页地址),此处实现页读。
②随机读: (页地址or行地址 +列地址)
2.1 实现按页读取Nand Flash
打开Nandflash手册后,一般会搜索operating,然后去找read时序图。然后I/Ox中可以了解到读取的步骤:
1.发送命令0x00 2.发送5个周期的地址 3.发送命令0x30 4.读取数据
然后有一个r/b信号,发送0x30信号后。它从高电平到低电平了,要等待它恢复到高电平后,把前面的事情做完,才能读取数据。同时在等待r/b信号之前,需要清除r/b信号
最后在操作Nandflash时需要选中芯片,最后要取消选中芯片。
总结:NF_PageRead的具体步骤就出来了。详细步骤见函数注释。
然后要对S3C2440中的具体寄存器操作:
-
#define NFCONF (volatile unsigned long*) 0x4E000000
-
#define NFCONT (volatile unsigned long*) 0x4E000004
-
#define NFSTAT (volatile unsigned char*) 0x4E000020
-
#define NFCMMD (volatile unsigned char*) 0x4E000008
-
#define NFADDR (volatile unsigned char*) 0x4E00000C
-
#define NFDATA (volatile unsigned char*) 0x4E000010
-
-
void select_chip()
-
{
-
*(NFCONT) &= ~(1<<1);
-
}
-
-
void dselect_chip()
-
{
-
*(NFCONT) |= (1<<1);
-
}
-
-
void clear_RnB()
-
{
-
*(NFSTAT) |= (1<<2);
-
}
-
-
void send_cmd(unsigned cmd)
-
{
-
*(NFCMMD) = cmd;
-
}
-
-
void send_addr(unsigned addr)
-
{
-
*(NFADDR) = addr;
-
}
-
-
void wait_RnB()
-
{
-
while(!(*(NFSTAT)&(1<<2)))
-
{
-
;
-
}
-
}
-
-
void NF_PageRead(unsigned long addr, unsigned char* buff)
-
{
-
int i;
-
//选中Nand flash芯片
-
select_chip();
-
-
//清除R/B信号
-
clear_RnB();
-
-
//发送命令0x00
-
send_cmd(0x00);
-
-
//发送列地址
-
send_addr(0x00);
-
send_addr(0x00);
-
-
//发送行地址
-
send_addr(addr&0xff);
-
send_addr((addr>>8)&0xff);
-
send_addr((addr>>16)&0xff);
-
-
//发送命令0x30
-
send_cmd(0x30);
-
-
//等待R/B信号
-
wait_RnB();
-
-
//读取数据
-
for(i = 0; i<2048; i++)
-
{
-
buff[i] = *(NFDATA);
-
}
-
-
//取消选中nand flash芯片
-
dselect_chip();
-
}
2.2 然后首先需要对Nand Flash初始化
在NFCONF中有TACLS、TWRPH0、TWRPH1三个属性。TACLS对应twp,TWRPH0对应tcls-twp,TWRPH1对应tclh的值。
其中twp、tcls、tclh最小值在nandflash中有对应的值。初始化是保证这个值大于最小值。
表格中有对应的公式,同时又涉及到了HCLK-->100MHz-->10ns。
-
#define TACLS 1
-
#define TWRPH0 2
-
#define TWRPH1 1
-
-
//初始化后必须复位
-
void nand_reset()
-
{
-
//选中flash
-
select_chip();
-
//清除Rnb信号
-
clear_RnB();
-
//发送0xff
-
send_cmd(0xff);
-
//等待RnB信号
-
wait_RnB();
-
//取消选中
-
dselect_chip();
-
}
-
-
void nand_init()
-
{
-
//初始化NFCONF
-
*(NFCONF) = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); //10ns 30ns 20ns
-
//初始化NFCONT,使能nandflash,使能芯片选中
-
*(NFCONT) = (1<<0)|(1<<1);
-
//初始化后复位
-
nand_reset();
-
}
2.4 启动时数据复制
2440中的启动IRAM只有4k,之前是把4k大小复制到内存中去运行的。但是这样做有一个问题,就是当我的bootload大于4k时就会出问题了。
这样首先要清楚程序启动流程,2440把nandflash中的启动代码复制4k到IRAM中,IRAM再把剩下的从Nandflash复制进内存中。最后从内存中运行剩下的bootload。
旧的复制程序:
-
copy_to_ram:
-
ldr r0, =0x0
-
ldr r1, =0x30008000
-
add r3, r0, #1024*4
-
copy_loop:
-
ldr r2, [r0], #4
-
str r2, [r1], #4
-
cmp r0, r3
-
bne copy_loop
-
mov pc, lr
新的复制程序:
-
copy_to_ram:
-
mov r0, #0
-
ldr r1, =_start //从0地址开始拷贝
-
ldr r2, =bss_end //拷贝的结束地址
-
sub r2, r2, r1 //size是地址相减
-
-
mov ip, lr //保存lr指针,后面要跳转bl指令,
-
bl nand_to_ram //调用nand_to_ram,参数用r0,r1,r2中传递
-
mov lr, ip
-
mov pc, lr
-
void nand_to_ram(unsigned long start_addr, unsigned char* sdram_addr, int size)
-
{
-
int i;
-
for(i = (start_addr>>11);size >0;) //左移11位页地址,只用了第一排的页
-
{
-
NF_PageRead(i, sdram_addr); //存放到sdram_addr
-
size -= 2048; //size减少2048
-
sdram_addr += 2048; //sdram_addr要加上2k
-
i++;
-
}
-
}
注意:之前的MPLLCON的宏设置地址不对。改回0x4c000004。这里我没错。。。。
错误记录:
Nand flash错误总结:
#define NFCMMD (volatile unsigned long*) 0x4E000008
#define NFADDR (volatile unsigned long*) 0x4E00000C
#define NFDATA (volatile unsigned long*) 0x4E000010
应改成,地址长度是不一样的。
#define NFCMMD (volatile unsigned char*) 0x4E000008
#define NFADDR (volatile unsigned char*) 0x4E00000C
#define NFDATA (volatile unsigned char*) 0x4E000010
#define NFCONF (volatile unsigned long*) 0x4E00C000
地址写错
#define NFCONF (volatile unsigned long*) 0x4E000000
NF_PageRead(1, sdram_addr);
1应该写成i,初始地址要不断增加
NF_PageRead(i, sdram_addr);
三、Nand Flash写入编写
3.1 同样找到page program operation
通过时序图,分析出操作步骤:
-
int NF_WritePage(unsigned long addr, unsigned char* buff)
-
{
-
int ret,i;
-
//选中芯片
-
select_chip();
-
//清除r/b
-
clear_RnB();
-
//发送命令0x80
-
send_cmd(0x80);
-
//发送列地址
-
send_addr(0x00);
-
send_addr(0x00);
-
//发送行地址
-
send_addr(addr&0xff);
-
send_addr((addr>>8)&0xff);
-
send_addr((addr>>16)&0xff);
-
//写入数据
-
for(i = 0; i<2048; i++)
-
{
-
*(NFDATA) = buff[i];
-
}
-
-
//发送命令0x10
-
send_cmd(0x10);
-
//等待R/B
-
wait_RnB();
-
//发送命令0x70
-
send_cmd(0x70);
-
//读取是否成功
-
ret = *(NFDATA);
-
//取消选中
-
dselect_chip();
-
-
return ret;
-
}
3.2 写入之前需要擦除步骤
通过时序图,分析出操作步骤:
-
int NF_Erase(unsigned long addr)
-
{
-
int ret;
-
//选中芯片
-
select_chip();
-
//清除R/B
-
clear_RnB();
-
//发送命令0x60
-
send_cmd(0x60);
-
//发送行地址
-
send_addr(addr&0xff);
-
send_addr((addr>>8)&0xff);
-
send_addr((addr>>16)&0xff);
-
//发送擦除命令0xd0
-
send_cmd(0xD0);
-
//等待R/B
-
wait_RnB();
-
//发送命令0x70
-
send_cmd(0x70);
-
//读取擦除结果
-
ret = *(NFDATA);
-
//取消片选
-
dselect_chip();
-
-
return ret;
-
}
3.3 在main.c函数中增加验证
-
NF_Erase(64*5 +1); //擦除
-
buf[0] =100;
-
NF_WritePage(64*5+1, buf); //写入
-
buf[0] = 10;
-
NF_PageRead(64*5+1, buf); //读取
-
-
if(buf[0] == 100)
-
{
-
led_off();
-
}
-
else
-
{
-
led_on();
-
}
阅读(1235) | 评论(0) | 转发(0) |