三十五.Nandflash的操作
发布时间:2020-12-15 17:29:22 所属栏目:百科 来源:网络整理
导读:1.nandflash就是嵌入式系统的硬盘 2.分类 (1)MLC:存储单元格存储两位,慢,偏移,寿命短,容量大 (2)SLC:存储一位。快,寿命长,容量小,昂贵 3访问; (1)独立编址,有专用的控制器,控制器里有相应的寄存器,先送地址,然后命令,最后数据 (2)地
1.nandflash就是嵌入式系统的硬盘
2.分类
(1)MLC:存储单元格存储两位,慢,偏移,寿命短,容量大
(2)SLC:存储一位。快,寿命长,容量小,昂贵
3访问;
(1)独立编址,有专用的控制器,控制器里有相应的寄存器,先送地址,然后命令,最后数据
(2)地址组成:行地址(页编号),列地址(在页中的偏移),一个nand可以有很多块,每一块可以很多页,每一页的两个部分可以不同规格
(3)信号引脚:
4.初始化nand控制器,(搜索关键字operation)
(1)初始化NFCONF,配置三个重要的与HCLK有关的时间参数
(2)初始化NGCONT,一开始要disable chip select 以及使能控制器
(3)复位:选中nandflash,清除RB标志,发送0xff命令,等待RB信号,取消选中RB信号
5.按页读
(1)选中nandflash
(2)清除RB标志
(3)写入0x00命令
(4)发送列地址,两次(但是由于是页读的方式,所以列地址,即页中的偏移量就是0)
(5)发送页编号,即行地址,分三次完成
(6)发送0x30命令
(7)等待RB信号变为ready
(8)读出数据,每次都取出数据寄存器的值,他会自动更新自己的值
(9)取消选中nandflash
6.修改之前的代码拷贝启动代码(之前只从iram复制到内存)
(1)时钟初始化有问题,错把UPLL当MPLL
(2)汇编和C语言的参数传递,不超过4个的时候,直接用r0--r3传递,且顺序和从函数的形参一致
(3)注意保存lr,否则连续两次跳转,会破坏lr,导致在执行完复制函数以后,不能返回到应执行的初始化后续代码部分
(4)注意在调用c函数之前进行栈的初始化
7.拷贝函数的三个参数,页的起始地址r0,(ulong,但是由于是页读方式,所以要去掉低12位,低12位是列地址,这里我们要去页编号,即行地址),内存起始地址r1(目标起始地址,uchar*),数据大小r3,每一页对应2048个字节(2K,视具体nandflash而言,每一个循环,大小减小2048)
8.一旦要去读取(等待)RB信号,之前就要对其进行清除;
一旦有选中操作,在结束之后要取消选中。
9.写入之前要擦除,参数是要擦除的页地址,擦除的是指定的页所在的块:
(1)选中nandflash
(2)清除RB标志
(3)写入0x60命令
(4)发送页编号,即行地址,分三次完成
(5)发送0xD0命令
(6)等待RB信号变为ready
(7)发送命令0x70
(8)读取擦出结果
(9)取消选中nandflash
(10)返回擦除结果
10.按页写
(1)选中nandflash
(2)清除RB标志
(3)写入0x80命令
(4)发送列地址,两次(但是由于是页读的方式,所以列地址,即页中的偏移量就是0)
(5)发送页编号,即行地址,分三次完成
(6)写入数据,用循环
(7)发送0x10命令
(8)等待RB信号变为ready
(9)发送命令0x70,
(10)读取写入结果,从数据寄存器读
(11)取消选中nandflash
(12)返回写入结果
11.代码实现
(1)寄存器宏定义
/*nandflash相关寄存器定义*/ #define NFCONF 0x70200000 #define NFCONT 0x70200004 #define NFCMMD 0x70200008 #define NFADDR 0x7020000C #define NFDATA 0x70200010 #define NFDATA8 (*(volatile unsigned char *)0x70200010) #define NFSTAT 0x70200028 int nand_erase(unsigned int block_addr); int Nand_PageWrite(unsigned long start_addr,char *buf); (2)nand驱动文件 /**************************** @File:nand.c @ @Tiny6410裸机上学期代码 @nandflash变硬盘 @Author:小君君 @****************************/ #include "common.h" #define NF_SetCommand(cmd) {(vi NFCMMD) = (cmd);} #define NF_SetAddr(addr) {(vi NFADDR) = (addr);} #define NF_SetData(data) {NFDATA8 = (data);} #define Chip_Select() {(vi NFCONT) &= ~(1 << 1);} #define Chip_DisSelect() {(vi NFCONT) |= (1 << 1);} #define NF_EnableRB() {(vi NFSTAT) |= (1 <<2);} #define NF_ChekBusy() {while(!((vi NFSTAT) & (1 <<0)));} /*复位函数*/ void nand_reset() { /*1.选中nandflash*/ Chip_Select(); /*2.清除RB标志位,开启忙信号检测功能*/ NF_EnableRB(); /*3.发送0xff复位命令*/ NF_SetCommand(0xFF); /*4.等待RnB信号,检测忙信号*/ NF_ChekBusy(); /*5.取消选中nandflash,为了省电*/ Chip_DisSelect(); } /*初始化nandflash控制器*/ void nand_init() { /*0.引脚功能配置*/ /*1.初始化NFCONF,设置时序参数*/ (vi NFCONF) = ( (0x2<<12)|(0xf<<8)|(0x7<<4) ); /*2.初始化NFCONT,取消片选,使能nandflash控制器*/ (vi NFCONT) = (0x3<<0); /*3.清除状态寄存器*/ (vi NFSTAT) = 0; /*4.复位操作*/ nand_reset(); } /*按页读取函数*/ void Nand_PageRead(unsigned long start_addr,char *buf) { int i; int page_size = 2048; /*(1)选中nandflash*/ Chip_Select(); /*(2)清除RB标志*/ NF_EnableRB(); /*(3)写入0x00读数据命令*/ NF_SetCommand(0x00); /*(4)发送列地址,两次(但是由于是页读的方式,所以列地址,即页中的偏移量就是0)*/ NF_SetAddr(0x00); NF_SetAddr(0x00); /*(5)发送页编号,即行地址,分三次完成*/ NF_SetAddr((start_addr) & 0xFF); NF_SetAddr((start_addr >> 8) & 0xFF); NF_SetAddr((start_addr >> 16) & 0xFF); /*(6)发送0x30命令*/ NF_SetCommand(0x30); /*(7)等待RB信号变为ready*/ NF_ChekBusy(); /*(8)读出数据,每次都取出数据寄存器的值,他会自动更新自己的值*/ for(i=0; i < page_size; i++) { *buf++ = NFDATA8; } /*(9)取消选中nandflash*/ Chip_DisSelect(); } /* 从NAND中拷贝代码到DRAM*/ int copy2ddr(unsigned int nand_start,unsigned long ddr_start,unsigned int len) { char *buf = (char *)ddr_start; unsigned int i; unsigned int page_shift = 11; // 1.发片选 Chip_Select(); // 2.使len为2048的整数倍 len = (len/2048+1)*2048; // 3.循环拷贝,每次拷贝一页数据 for (i = nand_start; i < nand_start + (len>>page_shift); i++,buf+=(1<<page_shift)) { // 读一页,即2048byte Nand_PageRead(i,buf); } return 0; } /*写入之前要擦除,参数是要擦除的页地址,擦除的是指定的页所在的块*/ int nand_erase(unsigned int block_addr) { unsigned int start_addr; int ret; start_addr = block_addr << 6; /*(1)选中nandflash*/ Chip_Select(); /*(2)清除RB标志*/ NF_EnableRB(); /*(3)写入0x60命令*/ NF_SetCommand(0x60); /*(4)发送页编号,即行地址,分三次完成*/ NF_SetAddr((start_addr) & 0xFF); NF_SetAddr((start_addr >> 8) & 0xFF); NF_SetAddr((start_addr >> 16) & 0xFF); /*(5)发送0xD0命令*/ NF_SetCommand(0xD0); /*(6)等待RB信号变为ready*/ NF_ChekBusy(); /*(7)发送命令0x70*/ NF_SetCommand(0x70); /*(8)读取擦出结果*/ ret = (NFDATA8); /*(9)取消选中nandflash*/ Chip_DisSelect(); /*(10)返回擦除结果*/ return ret; } int Nand_PageWrite(unsigned long start_addr,char *buf) { int i = 0,ret; /*(1)选中nandflash*/ Chip_Select(); /*(2)清除RB标志*/ NF_EnableRB(); /*(3)写入0x80命令*/ NF_SetCommand(0x80); /*(4)发送列地址,两次(但是由于是页读的方式,所以列地址,即页中的偏移量就是0)*/ NF_SetAddr(0x00); NF_SetAddr(0x00); /*(5)发送页编号,即行地址,分三次完成*/ NF_SetAddr((start_addr) & 0xFF); NF_SetAddr((start_addr >> 8) & 0xFF); NF_SetAddr((start_addr >> 16) & 0xFF); /*(6)写入数据,用循环*/ for(i = 0; i < 2048;i++) NF_SetData(*buf++); /*(7)发送0x10命令*/ NF_SetCommand(0x10); /*(8)等待RB信号变为ready*/ NF_ChekBusy(); /*(9)发送命令0x70,*/ NF_SetCommand(0x70); /*(10)读取写入结果,从数据寄存器读*/ ret = (NFDATA8); /*(11)取消选中nandflash*/ Chip_DisSelect(); /*(12)返回写入结果*/ return ret; }
12.相关时序图以及关键表格
13.测试文件 /**************************** @File:main.c @ @Tiny6410裸机上学期代码 @Nandflash测试文件 @Author:小君君 @****************************/ #include "common.h" int main(void) { unsigned int i = 500000; char buf[2048]; //mmu_init();//MMU初始化,这里不使用MMU led_init();//LED的GPIO初始化 button_init();//按键初始化 irq_init();//中断初始化 led_on();//点亮4颗LED while(i--); buf[1] = 100; nand_erase(5);//擦除第五块 Nand_PageWrite(64*5+1,buf);//每一块有64页,这里我们给的是页地址,所以要乘64,+1与否都是可以的。+1以后是第五块的第二页,否则是第五块的第一页 buf[1] = 10; Nand_PageRead(64*5+1,buf);//验证读取出来的数据是不是之前写进去的100,是的话就闪烁LED if(100 == buf[1]){ for(i = 0; i < 1000;i++){ led_off(); i = 500000; while(i--); led_on(); i = 500000; while(i--); } } while(1) ; return 0; } 14.在start.s调用代码搬移子函数 @将bin文件从_start开始到bss_start结束的数据搬移到_start指定的链接地址(0x50008000) copy_to_ddr: mov r0,#0x00 ldr r1,=_start ldr r2,=bss_start sub r2,r2,r1 mov ip,lr bl copy2ddr mov lr,ip mov pc,lr (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |