加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

第六站,来到大都市——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,以为理解的很好了,或许当初的确是,但是过了这么久,现在去复习,发现有些地方居然理解不了了,学习啊,还是的温故而知新,今天又花了好长一段时间来各方查证,发现自己还存在诸多问题,那么继续努力吧。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读