嵌入式学习-uboot-lesson12-NandFlash相关
一、NandFlash原理解析1.NandFlash简介根据物理结构上的区别,NandFlash主要分为如下两类: 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的执行顺序,便得到页读的主要流程: 在等待RB信号之前,需要对RB信号做一个初始化的操作,即清除工作,因此需要补充一个,即 然后在操作nandflash之前,需要选中nandflash芯片 综合以上流程,得到页读的总体流程:
2.2编程实现
#define NFCONT (*((volatile unsigned long*)0x70200004))
NFCONT &= ~(1<<1);
在等待RB信号之前,需要对RB信号做一个初始化的操作,即清除工作 #define NFSTAT (*((volatile unsigned char*)0x70200028))
NFSTAT |= (1<<4);
#define NFCMMD (*((volatile unsigned char*)0x70200008))
nand_cmd(0x00);
void nand_cmd(unsigned char cmd)
{ NFCMMD = cmd; }
没有偏移,因此两个列地址都为0 从上图可以看到,两个列地址,三个行地址 #define NFADDR (*((volatile unsigned char*)0x7020000c)) //地址寄存器
nand_addr(0x00);
nand_addr(0x00);
void nand_addr(unsigned char addr)
{ NFADDR = addr; }
一共8个数据IO,因此8位发送一次, #define NFADDR (*((volatile unsigned char*)0x7020000c))
nand_addr(addr&0xff);
nand_addr((addr >>8 ) & (0xff));
nand_addr((addr >>16 ) & (0xff));
nand_cmd(0x30);
如果这一位没有为1,说明没有处于空闲状态,所以要等其为1 while(!(NFSTAT & 0x1));
读一个页的大小,为4KB //读取数据 for(i = 0; i<1024*4; i++) //4KB { buff[i] = NFDATA; }
#define NFCONT (*((volatile unsigned long*)0x70200004))
NFCONT |= (1<<1);
3.初始化操作初始化就是 对上面两个寄存器进行初始化。 因此其流程为:
初始化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);
NFCONT = 1 | (1<<1);
如上图,等待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芯片 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.写入数据 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();
贴上代码,仅供参考: /******************************************** *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,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;
}
菜鸟一枚,如有错误,多多指教。。。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |