第六站,来到大都市——nandflash程序完全解析
发布时间:2020-12-15 18:27:18 所属栏目:百科 来源:网络整理
导读:第六站,来到大都市——nandflash程序分析 每一次旅行,都会到一个新的地方,而对于新的环境,我们在一开始,会觉得陌生,路在何方?该往哪里走?我们心中只有一个名字而没有方向,所以我们得学着如何寻址,得需要一个地图,一种方法,才能找到我们要去的地
第六站,来到大都市——nandflash程序分析
每一次旅行,都会到一个新的地方,而对于新的环境,我们在一开始,会觉得陌生,路在何方?该往哪里走?我们心中只有一个名字而没有方向,所以我们得学着如何寻址,得需要一个地图,一种方法,才能找到我们要去的地方,取得我们想要得到的东西。
对于嵌入式,nand就好比一个大都市,里面存放着大部分东西我们需要用到,所以我们一定得要知道如何操作他。
?
?
照样,我们来先给出关于nandflash控制器的各种寄存器的宏定义
regs.h:
/*****************************nand**************************/
#define BASE_NAND 0x70200000 #define NFCONF (*(volatile unsigned int *)(BASE_NAND + 0x00)) ???? //配置寄存器 #define NFCONT (*(volatile unsigned int *)(BASE_NAND + 0x04)) ???? //控制寄存器 #define NFCMMD (*(volatile unsigned int *)(BASE_NAND + 0x08))???? //命令寄存器 #define NFADDR (*(volatile unsigned int *)(BASE_NAND + 0x0c))???? //地址寄存器 #define NFDATA (*(volatile unsigned int *)(BASE_NAND + 0x10))???? //数据寄存器 #define NFSTAT (*(volatile unsigned int *)(BASE_NAND + 0x28))???? //状态寄存器 #define GPOCON (*(volatile unsigned int *)0x7F008140) #define GPPCON (*(volatile unsigned int *)0x7F008160) /****************************lcd*****************************/ 其实尽力过这么多站了,对于如何上下车,早已经不再是什么新鲜事了,新鲜的事,也就是关键的事是我们如何去初始化一个新的硬件呢,
请看nand.c文件,其中包括nandflash的初始化,以及读写nand的函数
nand.c:
#include "common.h"
#include "regs.h" /*对于当前的NAND Flash来说是256M字节的容量 *整个NAND包括2048个块(其他NAND可能会变) *每一个块里包括64个页(其他NAND可能会变) *每一个页包括2048字节的存储空间和64字节 *其他NAND的页大小可能不同,但是6410只支持两种页: *???? 512字节的页和2048字节的页,所以即使是用了其他的NAND *???? 也要把它当作2948字节的页来操作 */ /*定义三个宏,分别使能、禁止片选和检查NAND是否可操作*/ #define EN_NAND???? (NFCONT &= ~(1 << 1))????????? //NFCONT寄存器的第1位就是用来使能片选,禁止片选的,取0使能,取1禁止 #define DS_NAND???? (NFCONT |= 1 << 1) #define BS_NAND???? while(!(NFSTAT & 1))????????? //NFSTAT状态寄存器,通过位[0]判断存储器是否忙,为1,则不忙,为0,则忙,不可被操作。 void nand_init(void) { ???? /*配置GPPCON3-7为NAND专用*/ ???? GPPCON &= ~(0x3ff << 6);???? //这里不用多说,回到第二站去吧 ???? GPPCON |= 0x2aa << 6; ???? /*配置NANDC的控制属性,详情查看S3C6410手册P258*/ ???? NFCONF = (0 << 12) | (2 << 8) | (0 << 4) | (1 << 1);???? //第1位表示地址周期选择,这里赋值1表示选择为5地址周期。 ?????????????????????????????????????????????????????????????????????????? //(2 << 8) | (0 << 4)是设置TACLS的设置相关了,我不多说,请看 ?????????????????????????????????????????????????????????????????????????? //贴子:http://www.100ask.net/forum/showtopic-4247.aspx ?????????????????????????????????????????????????????????????????????????? ???? NFCONT = (1 << 7) | (1 << 6) | (1 << 1) | (1 << 0);????????? //(1 << 0)表示控制器使能,(1 << 1) 强制nGCS[2]为高,禁用片选, ?????????????????????????????????????????????????????????????????????????? //(1 << 6)表示锁存备用区ECC(主要是与读取ECC区有关,具体有啥用,我也没了解),(1 << 7)锁存主区ECC。 ???? /*重新启动NAND*/ ???? EN_NAND; ???? NFCMMD = 0xff;???? //发命令,只要把命令写到命令寄存器即可,同理,发地址和发数据也是一样写对应的寄存器,命令ffh就是用来复位即重启的,更多命令请查看nandflash手册 ???? BS_NAND; ???? DS_NAND; } /*发送要写或者要读的函数 *要分五个周期发过去 *详情查看S3C6410手册P261和NAND手册P9*/ void send_addr(unsigned int addr) { ???? unsigned int col_addr = addr & 0x7ff; //看地址周期图我们知道,列地址是低11位(这里有些重点内容,请看稍后附加说明), ????????????????????????????????????????????????? ? //即这里用我们传入的地址值与上0x7ff,得到低11位值 ???? unsigned int raw_addr = (addr >> 12) & 0x1ffff;???? //同理,这将列地址移出,然后再与上0x1ffff,得到17位行地址。 ???? NFADDR = col_addr & 0xff;?????????????? //列地址,所谓列地址,通俗讲就是页内地址,以下两个连续地址周期发列地址,就是(0~2048+64-1),2048表示页大小,加上的64表示OOB区大小 ???? NFADDR = (col_addr >> 8) & 0x7; ???? NFADDR = raw_addr & 0xff;?????????????? //行地址,也就是说哪一页地址,以下三个地址周期发行地址。 ???? NFADDR = (raw_addr >> 8) & 0xff; ???? NFADDR = (raw_addr >> 16) & 1; } /*发送擦除地址,NAND擦除必须以块为单位 *擦除的时候只需要发送一个能够定位到页 *的地址即可,NAND会把那个页所在的块整 *个都擦除了*/ void send_block_addr(unsigned int addr)??????????????????? //这是发送块地址,在nand_erase()函数中被调用, ???????????????????????????????????????????????????????????????? //nand的擦除是以块为单位,因此这里只需要发送块地址,不需要列地址 { ???? unsigned int raw_addr = (addr >> 12) & 0x1ffff; ???? NFADDR = raw_addr & 0xff; ???? NFADDR = (raw_addr >> 8) & 0xff; ???? NFADDR = (raw_addr >> 16) & 1; } /*以下为对NAND的擦除和读写操作函数 *详情参看S3C6410手册P261和NAND手册P16 */ void nand_read(unsigned int ddr,unsigned int nand,unsigned int len)???? //读函数,其中的参数一表示读出的内容存放地址,参数二表示读的源起始地址,参数三表示读的长度 { ???? unsigned int addr = nand; ???? int i; ???? ???? EN_NAND; ???? for(addr = nand; addr < nand + len; addr += 2048){? //因为读写是以页为单位,所以这里是每读一页(2048),就让add自加2048,指向下一页 ????????? NFCMMD = 0x00;???????????????????????????????????????????? //这里是命令读的第一周期命令 ????????? send_addr(addr);??????????????????????????????????????? //发地址,5个周期地址一发,寻址 ????????? NFCMMD = 0x30;???????????????????????????????????????????? //读的第二周期命令,这样就可以正确读数据了 ????????? BS_NAND;????????????????????????????????????????????????? //为什么要关呢? ????????? for(i = 0; i < 2048; i += 4,ddr += 4){?????????????? //这里是将读到的数据保存在指定地址,这个是保存的地址不是nand上,所以读不是以页为单位,采用4字节的写。 ?????????????? *((volatile unsigned int *)ddr) = NFDATA; ????????? } ???? } ???? DS_NAND; } void nand_erase(unsigned int nand,unsigned int len) { ???? unsigned int addr; ???? EN_NAND; ???? for(addr = nand; addr < nand + len; addr += 64 * 2048){ ????????? NFCMMD = 0x60;???? //命令60h就是 Block Erase,块擦除命令 ????????? send_block_addr(addr);???? //操作,都是先发命令再发地址的,如果是传输数据,最后发数据 ????????? NFCMMD = 0xd0;?????????????? //这是第二周期的命令,有些命令是需要配对使用的,比如60h和d0h配对,表示擦除块。 ??????????????????????????????????????? //又比如00h和30h配对表示Read,具体请看nandflash数据手册,不再赘述。 ????????? BS_NAND; ???? } ???? DS_NAND; } void nand_write(unsigned int ddr,unsigned int len) { ???? unsigned int addr; ???? int i; ???? EN_NAND; ???? for(addr = nand; addr < nand + len; addr += 2048){ ????????? NFCMMD = 0x80; ????????? send_addr(addr); ????????? for(i = 0; i < 2048; i += 4,ddr += 4){ ???????????????????????? NFDATA = *((volatile unsigned int *)ddr); ????????? } ????????? NFCMMD = 0x10; ????????? BS_NAND; ???? } ???? DS_NAND; } 附加说明: ???? 关于NAND的地址问题,在网上有各种争论,这的确也是一个理解上的难点,在此,我给出我的理解吧,也只能说是个人之见,希望对看到此分析的人有所帮助。 ???? 通过查看nandflash数据手册,我们看到地址周期表(不方便贴图啦,自己对着去翻翻吧)在寻找nand中的一个地址时,我们需要分5次发送地址,所以我们 ???? 传入的一个地址值,需要被拆分为5部分。 void send_addr(unsigned int addr) { ???? unsigned int col_addr = addr & 0x7ff; //看地址周期图我们知道,列地址是低11位(这里有些重点内容,请看稍后附加说明), ????????????????????????????????????????????????? ? //即这里用我们传入的地址值与上0x7ff,得到低11位值 ???? unsigned int raw_addr = (addr >> 11) & 0x1ffff;???? //同理,这将列地址移出,然后再与上0x1ffff,得到17位行地址。 ???? NFADDR = col_addr & 0xff;?????????????? //列地址,所谓列地址,通俗讲就是页内地址,以下两个连续地址周期发列地址,就是(0~2048+64-1),2048表示页大小,加上的64表示OOB区大小 ???? NFADDR = (col_addr >> 8) & 0x7; ???? NFADDR = raw_addr & 0xff;?????????????? //行地址,也就是说哪一页地址,以下三个地址周期发行地址。 ???? NFADDR = (raw_addr >> 8) & 0xff; ???? NFADDR = (raw_addr >> 16) & 1; } 列地址就是页内地址,行地址就是页的首地址. 一个nand分很多页,而一页的大小为2K(2048,应该是2048+64-1的,但是我们程序中一般不会去读写OOB区,所以这里说的页,是程序可读写,真正的页不是2K), ???? nand的寻址:就好比一个学校,学校分很多系部教学楼,而系部教学楼又分了很多间教室,我们要定位一个教室,那么首先得要定位是哪个 系部,然后找到了这个系部,再去在系部内寻找我们要定位的那个教室,nand寻址也就是这样一个过程。 例如:给出地址5000,一页是2048,那么5000是不是超过了2页的大小?那么这个地址落在第三页,5000-2*2048=904,904只是相当于这页首地址在本页内的偏移,最终我们找到了正确的地址。 ????????? 由此,我们在发这个地址的时候,列地址,即前两个周期发送的地址值就该是0x388(十进制为904),行地址(即后三个地址周期)发送值为0x2(十进制2,计数从0开始的) ???? 继续理解地址:地址可以这样计算的,继续比如寻址5000,那么5000/2048=2.44140625,那么是在第2页(从0计数),小数点后面的不够成一页,那么在页内,以该页为偏移,地址值为 ???????????????????????? 2048*0.44140625=904,即在页内的偏移,也就是所谓的列地址。 通过看nandflash的数据手册中地址周期表,A0~A10为列地址,那么语句col_addr = addr & 0x7ff即是提取出给定地址的低11位。 raw_addr = (addr >> 11) & 0x1ffff;???? //同理,这将列地址移出,然后再与上0x1ffff,得到17位行地址。 既然已经成功初始化nand,也写好了读写nand函数,那么接下来,就是在main函数中去验证他的时候了。具体请看main.c文件内容
main.c:
#include "common.h"
#include "regs.h" int main(int argc,char **argv) { ???? nand_init(); ???? ???? /*tftp 54000000 zImage*/ ???? /*nand erase 40000 300000*/ ???? nand_erase(0x40000,0x300000); ???? nand_write(0x54000000,0x40000,0x300000); ???? nand_read(0x50008000,0x300000); ???? printf("everything OK...n"); ???? return 0; } 我记得我曾经花了蛮多功夫去理解nand,以为理解的很好了,或许当初的确是,但是过了这么久,现在去复习,发现有些地方居然理解不了了,学习啊,还是的温故而知新,今天又花了好长一段时间来各方查证,发现自己还存在诸多问题,那么继续努力吧。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |