nandflash的读写使用,基于OK6410

来源:互联网 发布:可以马赛克视频软件 编辑:程序博客网 时间:2024/05/21 18:46
在嵌入式开发板中,nandflash就相当于电脑中的硬盘。
在nandflash中分两种,一种是单层式存储(SLC),每个单元格只有1位,比较贵,访问速度快(3倍),10w此擦写,功耗低

另外一种是多层式存储(MLC),每个单元格2位,便宜,访问速度慢,只有1w此擦写,功耗高

内存是参与统一编址,而nandflash是独立编址,也就是内存在地址空间分布中占有一席之地,但是nandflash没有,它需要地址线和数据线将nandflash连接到CPU的nandflash控制寄存器中,所以操作方式和一般单片机上的24C16很相似(个人觉得),都是1.传输地址2.传输命令3.数据传输

下面记录一下nandflash的内部分布
每一个nandflash有多个block块,每个block又有多个页,每个页由两个区域组成。
nandflash的读写使用,基于OK6410
由图可知,一个nandflash有4096个block,每个block有128页,每页有4K的数据存储区和218B的校验存储区。要确定某一页的首地址,就要提供2个列地址和3个行地址,这样就可以得到这一页的首地址。

下面开始页读的操作,先在nandflash芯片数据手册里面找到readoperation的流程图
nandflash的读写使用,基于OK6410
由此可知要读,就必须进行如下操作
1.发送命令0x00
2.发送2个列地址
3.发送3个行地址
4.发送命令0x30
5.等待RB信号从0变1
6.读取数据

但是对于一次对nandflash芯片的操作,我们必须还要做一些其他的必须做的事,那就是对于s3c6410来说选中芯片,清除RB信号,在读取完毕后还要取消选中芯片(注意P257)
在NFSTAT里面设置清空RB信号(bit 4),在NFCONT里面设置选中芯片/取消选中芯片
nandflash的读写使用,基于OK6410
void select_chip()
{
NFCONT&=~(1<<1);
}

void disselect_chip()
{
NFCONT|=(1<<1);
}

void clean_RB()
{
NFSTAT|=(1<<4);
}

接下来执行页读流程
对于发送命令,查看NFCMMD寄存器,把命令写入NFCMMD寄存器即可
对于写地址(不管是行地址还是列地址),把地址写入NFADDR

等待RB信号,同样还是在NFSTAT寄存器中判断
void wait_RB()
{
while(!(NFSTAT&0x1));
}

当RB信号空闲的时候就可以开始读取数据了
因为用页读,所以要读4K的内容,用for循环来读取到一个数组里面,,读取数据只需要将NFDATA寄存器里面的值赋给我们一开始定义的数组就可以了
for(i=0;i<4*1024;i++)
buff[i]=NFDATA;

数据读完后记得取消选中芯片

这样页读的操作就完成了。但是要用nandflash之前还得先对nandflash进行初始化,即对NFCONT和NFCONF寄存器进行配置
void nand_init()
{
#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();
}

看到这里有需要有复位函数
nandflash的读写使用,基于OK6410
所以只要对芯片发送ff命令就可以了,但是还是要执行对芯片操作的基本步骤
void nand_reset()
{
select_chip();
clean_RB();
send_cmd(0xff);

wait_RB();

disselect_chip();
}

为了检验nandflash的页读能力,我们手动将nandflash里面的程序复制到内存当中执行,即把nandflash里的程序拷贝到内存程序其实地址(在列及其脚本里面可以查看)_start到bss_end这一部分当中,在start.S里面对nand_to_ram的C语言函数三个参数进行复制(r0,r1,r2),所以copy_to_ram的代码改变为如下
mov r0,#0 //第一个参数
ldr r1,=_start //第二个参数
ldr r2,=bss_end //第三个参数
sub r2,r2,r1
mov ip,lr
bl nand_to_ram
mov lr,ip
mov pc, lr
最后转到nand_to_ram进行程序的复制么这样就即进入了C函数了

void nand_to_ram(unsigned long start_addr,unsigned char*sdram_addr,int size)
{
int i; //i为页号、sdram_addr为内存中的位置、size拷贝数据的大小
for(i=0;i<4;i++,sdram_addr+=1024*2)
{
nf_pageread(i,sdram_addr);
}
size-=1024*8;
for( i=4; size>0;)
{
  nf_pageread(i,sdram_addr);
   size -= 4096;
   sdram_addr += 4096;
   i++;
}
}
这样就可以实现从nandflash里面复制程序到内存里面去执行了





说说把数据写到nandflash里面
形式和读很相似(在初始化nandflash之后)
nandflash的读写使用,基于OK6410
在对芯片的基础操作之后,
1.发送命令80h
2.发送2个列地址
3.发送3个行地址
4.往数据寄存器写数据
5.发送命令10h
6.等待RB
7.发送命令70h
8.读取写入结果
与上面类似,不在赘述

考虑到往一个页里面写数据,不知道里面原来是否有数据,所以在写入之前还要对那个部分进行擦除,对于一般nandflash来说,都只能对整个block进行擦除
nandflash的读写使用,基于OK6410
如上

贴上nandflash的全部代码(对start.S的操作见上)
#define NFCONT (*((volatile unsigned long*)0x70200004))
#define NFSTAT (*((volatile unsigned char*)0x70200028))
#define NFCMMD (*((volatile unsigned char*)0x70200008))
#define NFADDR (*((volatile unsigned char*)0x7020000C))
#define NFDATA (*((volatile unsigned char*)0x70200010))

#define NFCONF (*((volatile unsigned long*)0x70200000))

void select_chip()
{
NFCONT&=~(1<<1);
}

void disselect_chip()
{
NFCONT|=(1<<1);
}

void clean_RB()
{
NFSTAT|=(1<<4);
}

void send_cmd(unsigned char cmd)
{
NFCMMD=cmd;
}
void wait_RB()
{
while(!(NFSTAT&0x1));
}

void send_addr(unsigned char addr)
{
NFADDR=addr;
}

void nand_reset()
{
//选中芯片
select_chip();
//清除RB
clean_RB();
//发送复位信号0xff
send_cmd(0xff);
//等待RB从0变1
wait_RB();
//取消选中芯片
disselect_chip();
}

void nand_init()
{
#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_chip();
//清除RB
clean_RB();
//发送命令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);
//等待RB信号从0变成1(空闲)
wait_RB();
//读数据
for(i=0;i<4*1024;i++)
buff[i]=NFDATA;
//取消选中芯片
disselect_chip();
}

void nand_to_ram(unsigned long start_addr,unsigned char*sdram_addr,int size)
{
int i; //i为页号、sdram_addr为内存中的位置、size拷贝数据的大小
for(i=0;i<4;i++,sdram_addr+=1024*2)
{
nf_pageread(i,sdram_addr);
}
size-=1024*8;
for( i=4; size>0;)
{
  nf_pageread(i,sdram_addr);
   size -= 4096;
   sdram_addr += 4096;
   i++;
}
}

int nf_erase(unsigned long addr)
{
int dat;
//选中芯片
select_chip();
//清除RB
clean_RB();
//发送命令60h
send_cmd(0x60);
//发送3个行地址
send_addr(addr&0xff);
send_addr((addr>>8)&0xff);
send_addr((addr>>16)&0xff);
//发送命令d0h
send_cmd(0xd0);
//等待RB
wait_RB();
//发送命令70h
send_cmd(0x70);
//读取擦除结果
dat=NFDATA;
//取消选择芯片
disselect_chip();
return dat;
}

int nf_pagewrite(unsigned long addr,unsigned char* buff)
{
int i,dat;
//选中芯片
select_chip();
//清除RB
clean_RB();
//发送命令80h
send_cmd(0x80);
//发送2个列地址
send_addr(0x00);
send_addr(0x00);
//发送3个行地址
send_addr(addr&0xff);
send_addr((addr>>8)&0xff);
send_addr((addr>>16)&0xff);
//往数据寄存器写入数据
for(i=0;i<4*1024;i++)
NFDATA=buff[i];
//发送命令10h
send_cmd(0x10);
//等待RB
wait_RB();
//发送命令70h
send_cmd(0x70);
//读取写入数据
dat=NFDATA;
//取消选中芯片
disselect_chip();
return dat;
}


为了检验,写入的能力,进行一次写入再读取,然后比较,若值相同,则点亮LED的操作。所以在main.c对其操作
void gboot_main()
{
unsigned char temp[4*1024];
#ifdef MMU_ON
mmu_init();
#endif
led_off();
button_init();
irq_init();
nf_erase(128*1+1);
temp[0]=123;
nf_pagewrite(128*1+1,temp);
temp[0]=11;
nf_pageread(128*1+1,temp);
if(temp[0]==123)
led_on();
while(1);

}

实验成功,则LED会点亮