您的位置:首页 > 其它

嵌入式学习-uboot-lesson12-NandFlash相关

2016-07-06 12:37 441 查看

一、NandFlash原理解析

1.NandFlash简介

根据物理结构上的区别,NandFlash主要分为如下两类:

•SLC (Single Level Cell): 单层式存储

•MLC (Multi Level Cell): 多层式存储

SLC在存储格上只存一位数据,而MLC则存放两位数据。

SLC的访问速度比MLC快3倍,SLC能进行10万次的擦写,MLC能进行1万次,MLC功耗比SLC高15%左右

2.访问方式

NANDFlash在地址空间以外,采用独立编址的方式,不同于内存的非独立编址。

根据其原理图可以得知,



一共有8个数据端口DATA0 ~ DATA7,这8个数据端口,需要传递地址、命令、数据。通过寄存器控制这8个端口,实现不同的功能。



3.构成



从上图可以看到,一共有4096个block,每个block又划分为128pages,每一页的4K存放数据,218B存放校验码。

存储单元的构成:



从上图可以看到,存储单元可以被划分为行地址和列地址。

通过行地址可以找到某个页,通过列地址则是找到偏移,扎到存储单元。

4.信号引脚



1. CLE(Command Latch Enable): 命令锁存允许

2. ALE(Address Lactch Enable): 地址锁存允许

3. CE:芯片选择

4. RE:读允许

5. WE:写允许

6. WP:在写或擦除期间,提供写保护

7. R/B:读/忙

二、NandFlash读操作

1.读取的方式

页读:把整个页都读出来,需要提供页地址

随机读:读取页中某一个单元格的地址,需要提供页地址和列地址

本次是使用页读的方式

其流程如下:



2.页读操作



2.1流程

根据上面的图,看I/Ox的执行顺序,便得到页读的主要流程:

1.发送命令0x00

2.address 发送行地址

3.address 发送列地址

4.发送0x30命令

当发送0x30之后,发现RB信号处于忙的状态,因此需要等待一段时间,使其处于等待的状态。

5.等待RB信号

6读取数据

在等待RB信号之前,需要对RB信号做一个初始化的操作,即清除工作,因此需要补充一个,即

0.清除RB信号

然后在操作nandflash之前,需要选中nandflash芯片

0.选中nandflash芯片

综合以上流程,得到页读的总体流程:

1.选中nandflash芯片

2.清除RB信号

3.发送命令0x00

4.address 发送行地址

5.address 发送列地址

6.发送0x30命令

7.等待RB信号

8.读取数据

9.取消选中nandflash芯片

2.2编程实现

1.选中nandflash芯片

使能nandflash片选信号,根据下面的图可知,将NFCONT 的第一位置0即可





#define NFCONT             (*((volatile unsigned long*)0x70200004))
NFCONT &= ~(1<<1);


2.清除RB信号

在等待RB信号之前,需要对RB信号做一个初始化的操作,即清除工作



#define NFSTAT             (*((volatile unsigned char*)0x70200028))
NFSTAT |= (1<<4);


3.发送命令0x00



#define NFCMMD             (*((volatile unsigned char*)0x70200008))
nand_cmd(0x00);
void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
}


4.address 发送列地址

没有偏移,因此两个列地址都为0



从上图可以看到,两个列地址,三个行地址

#define NFADDR             (*((volatile unsigned char*)0x7020000c))  //地址寄存器
nand_addr(0x00);
nand_addr(0x00);
void nand_addr(unsigned char addr)
{
NFADDR = addr;
}


5.address 发送行地址



一共8个数据IO,因此8位发送一次,

#define NFADDR             (*((volatile unsigned char*)0x7020000c))
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));


6.发送0x30命令

nand_cmd(0x30);


7.等待RB信号



如果这一位没有为1,说明没有处于空闲状态,所以要等其为1

while(!(NFSTAT & 0x1));


8.读取数据

读一个页的大小,为4KB



//读取数据

for(i = 0; i<1024*4; i++) //4KB

{

buff[i] = NFDATA;

}

9.取消选中nandflash芯片

#define NFCONT             (*((volatile unsigned long*)0x70200004))
NFCONT |= (1<<1);


3.初始化操作



初始化就是 对上面两个寄存器进行初始化。

因此其流程为:

1.初始化NFCONF

2.初始化NFCONT

3.复位

1.初始化NFCONF



初始化NFCONF 主要是对上面的三个寄存器进行设置。





根据上面两幅图,可以算出三个寄存器的值

#define TACLS  1
#define TWRPH0 2
#define TWRPH1 1
/*
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
*/

NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);


2.初始化NFCONT



NFCONT = 1 | (1<<1);


3.复位



如上图,等待RB信号从低到高,便是reset成功。

其流程为:

1.选中

select_ship();


2.清除RnB

clean_RnB();


3.发出复位信号

nand_cmd(0xff);


4.等待就绪

wait_RnB();


5.取消选中

diselect_ship();


4.测试

对上面写的代码进行测试,主要 是通过内存拷贝来验证。

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;  //每拷贝一次就减少4KB
sdram_addr += 4096;
i++;
}

}


贴上nand.c全部代码:

/********************************************
*file name: nand.c
*author : stone
*date : 2016.7.3
*function : nandflash相关操作
*********************************************/
#define NFCONT (*((volatile unsigned long*)0x70200004)) //控制寄存器
#define NFSTAT (*((volatile unsigned char*)0x70200028)) //NAND 状态寄存器
#define NFCMMD (*((volatile unsigned char*)0x70200008)) //命令寄存器
#define NFADDR (*((volatile unsigned char*)0x7020000c)) //地址寄存器
#define NFDATA (*((volatile unsigned char*)0x70200010)) //数据寄存器
#define NFCONF (*((volatile unsigned long*)0x70200000)) //配置寄存器

/* 设置时间参数 */
#define TACLS 1
#define TWRPH0 2
#define TWRPH1 1

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

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

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

void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
}

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

void wait_RnB(void)
{
while(!(NFSTAT & (0x1<4)));
}

void nand_reset(void)
{
/* 选中 */
select_ship();

/* 清除RnB */
clean_RnB();

/* 发出复位信号 */
nand_cmd(0xff);

/* 等待就绪 */
wait_RnB();

/* 取消选中 */
diselect_ship();
}

void nand_init(void)
{
//初始化NFCONF

/*
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
*/

//NFCONF &= ~((7<<12)|(7<<8)|(7<<4));
NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

//初始化NFCONT
NFCONT = 1 | (1<<1);

//复位
nand_reset();

}

void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;

//选中nandflash芯片
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);

//等待R/B信号
wait_RnB();

//读取数据
for(i = 0; i<1024*4; i++) //4KB
{
buff[i] = NFDATA;
}

//取消选中nandflash芯片
diselect_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; //每拷贝一次就减少4KB sdram_addr += 4096; i++; } }



三、NandFlash写操作

NandFlah写操作和读操作一样,也分为两种按页写,随机写

本次使用页写的方式

流程如下:



1).使用按页写方式。



看上图,可得出流程:

1.发送命令0x80

2.发送列地址(2个周期)

3.发送行地址(3个周期)

4.写入数据

5.发送命令10

6.等待RnB

7.发送命令70

8.读取写入结果

和读操作一样,也需要选中flash芯片和清除RB,因此其最后流程为:

1.选中flash芯片

2.清除RnB

3.发送命令0x80

4.发送列地址(2个周期)

5.发送行地址(3个周期)

6.写入数据

7.发送命令10

8.等待RnB

9.发送命令70

10.读取写入结果

11.取消选中flash芯片

编程实现:

1.选中flash芯片

select_ship();


2.清除RnB

clean_RnB();


3.发送命令0x80

nand_cmd(0x80);


4.发送列地址(2个周期)

nand_addr(0x00);
nand_addr(0x00);


5.发送行地址(3个周期)

nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));


6.写入数据

for(i=0;i<1024*4;i++)

{

NFDATA = buff[i];

}

7.发送命令10

nand_cmd(0x10);


8.等待RnB

wait_RnB();


9.发送命令70

nand_cmd(0x70);


10.读取写入结果

ret = NFDATA;


11.取消选中flash芯片

diselect_ship();


2).擦除工作

在写入数据之前,需要对其进行擦除工作,擦除的时候会把这个页所在的块全部擦除。



1.选中flash芯片

select_ship();


2.清除RnB

clean_RnB();


3.发送命令60

nand_cmd(0x60);


4.发送行地址(3个周期)

nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));


5.发送命令D0

nand_cmd(0xD0);


6.等待RnB

wait_RnB();


7.发送命令70

nand_cmd(0x70);


8.读取擦除结果

ret = NFDATA;


9.取消选中flash芯片

diselect_ship();


3)测试

测试时采用LED亮灭的方式进行验证。

NF_Erase(128*1+1); //第128+1页
buf[0] = 100;
NF_WritePage(128*1+1,buf);

buf[0] = 10;
NF_PageRead(128*1+1,buf);

if( buf[0] == 100 )
led_off();


贴上代码,仅供参考:

nand.c

/********************************************
*file name: nand.c
*author : stone
*date : 2016.7.3
*function : nandflash相关操作
*********************************************/
#define NFCONT (*((volatile unsigned long*)0x70200004)) //控制寄存器
#define NFSTAT (*((volatile unsigned char*)0x70200028)) //NAND 状态寄存器
#define NFCMMD (*((volatile unsigned char*)0x70200008)) //命令寄存器
#define NFADDR (*((volatile unsigned char*)0x7020000c)) //地址寄存器
#define NFDATA (*((volatile unsigned char*)0x70200010)) //数据寄存器
#define NFCONF (*((volatile unsigned long*)0x70200000)) //配置寄存器

/* 设置时间参数 */
#define TACLS 1
#define TWRPH0 2
#define TWRPH1 1

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

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

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

void nand_cmd(unsigned char cmd)
{
NFCMMD = cmd;
}

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

void wait_RnB(void)
{
while(!(NFSTAT & (0x1<4)));
}

void nand_reset(void)
{
/* 选中 */
select_ship();

/* 清除RnB */
clean_RnB();

/* 发出复位信号 */
nand_cmd(0xff);

/* 等待就绪 */
wait_RnB();

/* 取消选中 */
diselect_ship();
}

void nand_init(void)
{
//初始化NFCONF

/*
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
*/

//NFCONF &= ~((7<<12)|(7<<8)|(7<<4));
NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

//初始化NFCONT
NFCONT = 1 | (1<<1);

//复位
nand_reset();
}

void NF_PageRead(unsigned long addr,unsigned char* buff)
{
int i;

//选中nandflash芯片
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);

//等待R/B信号
wait_RnB();

//读取数据
for(i = 0; i<1024*4; i++) //4KB
{
buff[i] = NFDATA;
}

//取消选中nandflash芯片
diselect_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; //每拷贝一次就减少4KB sdram_addr += 4096; i++; } }

//在写之前必须要擦除
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芯片
diselect_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芯片
diselect_ship();

return ret;
}


main.c

/********************************************
*file name: main.c
*author   : stone
*date     : 2016.7.3
*function : 总程序
*********************************************/

int gboot_main()
{

/*mmu 初始化,暂时不用*/
#ifdef MMU_ON
mmu_init();
#endif

/*led初始化*/
led_init();

/*中断初始化*/
init_irq();

/*按键初始化*/
button_init();

led_on();

NF_Erase(128*1+1); //第128+1页
buf[0] = 100;
NF_WritePage(128*1+1,buf);

buf[0] = 10;
NF_PageRead(128*1+1,buf);

if( buf[0] == 100 )
led_off();

while(1);

return 0;
}


菜鸟一枚,如有错误,多多指教。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: