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

Nand Flash介绍和Nand Flash控制器使用

发布时间:2020-12-15 17:38:23 所属栏目:百科 来源:网络整理
导读:一、Flash介绍 ??? 常用的flash类型有NOR Flash 和Nand Flash 两种; ??? (1)Nor Flash ????1、Nor Flash的接口和RAM完全相同,可以随机访问任意地址的数据,在其上进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小

一、Flash介绍

??? 常用的flash类型有NOR Flash 和Nand Flash 两种;

??? (1)Nor Flash

????1、Nor Flash的接口和RAM完全相同,可以随机访问任意地址的数据,在其上进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小,通常,Nor Flash用于存储程序
????2、Nor Flash的块大小范围为64KB—128KB;
????3、擦写一个Nor Flash块需要4s,
??? 4、市场上Nor Flash 的容量通常为1MB—4MB

??? (2)Nand Flash
?? ?1、Nand Flash的接口仅仅包含几个I/O引脚,需要串行地访问,Nand Flash进行擦除和写操作的效率很高,容量较大,

通常Nand Flash用于存储数据
??? 2、Nand Flash的块大小范围为8KB—64KB;
????3、擦写一个Nand Flash块需要2ms;
??? 4、Nand Flash 一般以512字节为单位进行读写
??? 5、?市场上 Nand Flash 的容量一般为 8M—512M

?

二、Nand? Flash的物理结构

??? 以三星公司生产的 K9F1208U0M? 为例:

????1、容量:64MB,

????????????????????? 一共4个层

??????????????????????每层1024个块(block);

??????????????????????1块包含32页

??????????????????????1页包含 512 + 16? = 528个字节

?

?

????2、外部接口:8个I/O口,5个使能信号(ALE、CLE、nWE、nRE、nCE),1个状态引脚(RDY/B),1个写保护引脚(nWE);????????????

????3、命令、地址、数据都通过8个I/O口输入输出;

??? 4、写入命令、地址、数据时,都需要将nWE、nCE信号同时拉低;数据在WE上升沿被锁存;

??? 5、CLE、ALE用来区分I/O引脚上传输的是数据还是地址;

????6、64MB的空间需要26位地址,因此以字节为单位访问Flash时需要4个地址序列

??? 7、读/写页在发出命令后,需要4个地址序列,而擦除块在发出擦除命令后仅需要3个地址序列

?

三、Nand Flash访问方法

?1 特殊功能寄存器定义?


#define rNFCONF ?(*(volatile unsigned int *)0x4e000000)?
#define rNFCMD ? (*(volatile unsigned char *)0x4e000004)?
#define rNFADDR ?(*(volatile unsigned char *)0x4e000008)?


#define rNFDATA ?(*(volatile unsigned char *)0x4e00000c)?
#define rNFSTAT ?(*(volatile unsigned int *)0x4e000010)?
#define rNFECC ? (*(volatile unsigned int *)0x4e000014)?
#define rNFECC0 (*(volatile unsigned char ?*)0x4e000014)?
#define rNFECC 1 (*(volatile unsigned char *)0x4e000015)?
#define rNFECC2 (*(volatile unsigned char *)0x4e000016)?

2 操作的函数实现?


1. 发送命令?
#define NF_CMD(cmd) ? ?{rNFCMD=cmd; }?

2. 写入地址?
#define NF_ADDR(addr) ?{rNFADDR=addr;}?

3. Nand Flash 芯片选中?
#define NF_nFCE_L() ? ?{rNFCONF&=~(1<<11);}?

4. Nand Flash 芯片不选中?
#define NF_nFCE_H() ? ?{rNFCONF|=(1<<11);}?

5. 初始化 ECC?
#define NF_RSTECC() ? ?{rNFCONF|=(1<<12);}
?
6. 读数据?

#define NF_RDDATA() ? ? ? ? (rNFDATA)?

7. 写数据?
#define NF_WRDATA(data) {rNFDATA=data;}?

8. 获取Nand Flash 芯片状态?
#define NF_WAITRB() ? ? ? ? {while(!(rNFSTAT&(1<<0)));}?
0/假: 表示Nand Flash 芯片忙状态?
1/真:表示Nand Flash 已经准备好?

3.NandFlash读写擦具体实现。

????? 操作Nand? Flash时,先传输命令,然后传输地址,最后读、写数据,期间要检查flash的状态;

????? K9F1208U0M? 一页大小为528字节,而列地址A0——A7可以寻址的范围是256字节,所以将一页分为A、B、C三个区

?????????????????A区:0—255字节

???????????????? B区:256—511字节

???????????????? C区:512—527字节

?(0)Nand Flash 初始化?


void NF_Init(void)?
{?
? ? /* 设置 Nand Flash 配置寄存器,每一位的取值见1.3 节 */?
? ? rNFCONF=(1<< 15)|(1<<14)|(1<< 13)|(1<<12)|(1<< 11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);?
? ? /* 复位外部 Nand Flash 芯片 */?
? ? NF_Reset();?
}

(1)复位

????????? 命令:FFh

??????????步骤:发出命令即可复位Nand Flash芯片;

?static void NF_Reset(void)?
{?
? ? int i;?
? ? ?
? ? NF_nFCE_L(); ? ? ? ? /* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0xFF); ? ? ? ?/* 复位命令 ? ? ? ? ?*/?
? ? for(i=0;i< 10;i++); /* 等待tWB = 100ns. ? ? ? */?
? ? NF_WAITRB(); ? ? ? ?/* wait 200~500us; ? ? ?*/?
? ? NF_nFCE_H(); ? ? ? ?/* 取消Nand Flash 选中*/?
}

(2)读操作

????????? 命令:

???????????????? 00h——读A区

???????????????? 01h——读B区

???????????????? 50h——读C区

???????????操作步骤:

????????????? 1、发出命令 00h、01h 或50h,? 00h将地址位A8设为0,? 01h将A8设为1?;

??????????????2、依次发出4个地址序列;

????????????? 3、检测R/nB,待其为高电平时,就可以读取数据了;

参数说明:block :块号?
? ? ? ? ? page :页号?
? ? ? ? ? buffer :指向将要读取到内存中的起始位置?
返回值:1:读成功?
? ? ? ? 0 :读失败?
static int NF_ReadPage(unsigned int block,unsigned int page,unsigned char *buffer)?
{?
? ? int i;?
? ? unsigned int blockPage;?
? ? unsigned char ecc0,ecc1,ecc2;?
? ? unsigned char *bufPt=buffer;?
? ? unsigned char se[16];?
? ? ?
? ? page=page&0x1f;?
? ? blockPage=(block<<5)+page;?
? ? NF_RSTECC(); ? ? ?/* 初始化 ECC ? ? ? ? ? ? ?*/?
? ? NF_nFCE_L(); ? ? ? /* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0x00); ? ? ?/* 从A 区开始读 ? ? ?*/?
? ? NF_ADDR(0); ? ? ? /* ?A0~A7 位(Column Address) ? ? ?*/?
? ? NF_ADDR(blockPage&0xff); ? ? ? ?/* A9-A 16,(Page Address) */?
? ? NF_ADDR((blockPage>>8)&0xff); ? ? ? ?/* A17-A24,(Page Address) */?
? ? NF_ADDR((blockPage>> 16)&0xff); ?/* A25,? ? (Page Address) */?


? ? /* 等待Nand Flash 处于再准备状态 */?
? ? for(i=0;i< 10;i++);?
? ? NF_WAITRB(); ? ? ? /*等待 tR(max 12us) */?
? ? /* 读整个页,512 字节 ? ? ? ? ? ? */?
? ? for(i=0;i<512;i++)?
? ? {?
? ? ? ? ?*bufPt++=NF_RDDATA();?
? ? }?
? ? ?
? ? /* 读取 ECC 码 */?
? ? ecc0=rNFECC0;?
? ? ecc 1=rNFECC 1;?
? ? ecc2=rNFECC2;?

/* 读取该页的OOB 块 */?
? ? for(i=0;i< 16;i++)?
? ? {?
? ? ? ? ?se[i]=NF_RDDATA();?
? ? }?
? ? ?
? ? NF_nFCE_H(); ? ? ? ?/* 取消Nand Flash 选中*/?


? ? /* 校验 ECC 码,并返回 */?
? ? if(ecc0==se[0] && ecc 1==se[1] && ecc2==se[2])?
? ? ? ? ? ?return 1;?
? ? else?
? ? ? ? ? return 0;?
}?

(3)flash编程

????????? ? 命令:

?????????????????? 80h——10h?:写单页;

???????????????????80h——11h?:对多个层进行些页操作;????

?????????????操作步骤:

?????????????????1、写单页步骤:

??????????????????????????? 【1】发出80h命令后;

????????????????????????????【2】发送4个地址序列;

????????????????????????????【3】向flash发送数据;

????????????????????????????【4】发出命令10h启动写操作,flash内部自动完成写、校验操作;

????????????????????????????【5】通过命令70h读取状态位,查询写操作是否完成;

??????????????????2、多页写

????????????????????????????【1】发出80h、4个地址序列、最多528字节的数据;

????????????????????????????【2】发出11h命令;

????????????????????????????【3】接着在相邻层执行【1】、【2】两步操作;

????????????????????????????【4】第四页的最后使用10h代替11h,启动flash内部的写操作;

????????????????????????????【5】可以通过71h查询写操作是否完成;


以页为单位写入.?
参数说明:block ? ? ? ?块号?
? ? ? ? ? page ? 页号?
? ? ? ? ? buffer ?指向内存中待写入Nand flash 中的数据起始位置?
返回值: ? ? ?0 :写错误?
? ? ? ? ?1:写成功?
static int NF_WritePage(unsigned int block,unsigned char *buffer)?
{?
? ? int i;?
? ? unsigned int blockPage = (block<<5)+page;?
? ? unsigned char *bufPt = buffer;?


? ? NF_RSTECC(); ? ? ?/* 初始化 ECC ? ? ? ? ? ?*/?
? ? NF_nFCE_L(); ? ? /* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0x0); ? ? ?/* 从A 区开始写 ? ? ?*/?
? ? NF_CMD(0x80); ?/* 写第一条命令 ? ? ?*/?
? ? NF_ADDR(0); ? ? ?/* A0~A7 位(Column Address) ? ? */?
? ? NF_ADDR(blockPage&0xff); ? ? ?/* A9-A 16,(Page Address) */?
? ? NF_ADDR((blockPage>>8)&0xff); ? ? ?/* A17-A24,? ?(Page Address) */?


? ? for(i=0;i<512;i++)?
? ? {?
? ? ? ? NF_WRDATA(*bufPt++); ? ? ? ? ? ?/* 写一个页512 字节到 Nand Flash 芯片 */?
? ? }?
? ? ?
? ? /*?
? ? * OOB 一共16 Bytes,每一个字节存放什么由程序员自己定义,通常,?
? ? * 我们在 Byte0-Byte2 存 ECC 检验码. Byte6 存放坏块标志.?
? ? */?
? ? seBuf[0]=rNFECC0; ?/* 读取 ECC 检验码 0 */?
? ? seBuf[1]=rNFECC 1; ?/* 读取 ECC 检验码 1 */?
? ? seBuf[2]=rNFECC2; ?/* 读取 ECC 检验码 2 */?
? ? seBuf[5]=0xff; ? ? ? /* 非坏块标志 ? ? ? */?
? ? ?
? ? for(i=0;i< 16;i++)?
? ? {?
? ? ? ? NF_WRDATA(seBuf [i]); /* 写该页的OOB 数据块 ?*/?
? ? }?


? ? NF_CMD(0x10); ? ? /* 结束写命令 */?


? ? /* 等待Nand Flash 处于准备状态 */?
? ? for(i=0;i< 10;i++);?
? ? NF_WAITRB();?
??
? ? /* 发送读状态命令 ? ? ? ? ? ?Nand Flash */?
? ? NF_CMD(0x70);?
? ? for(i=0;i<3;i++);?

? if (NF_RDDATA()&0x1)?
? ? { ? /*如果写有错,则标示为坏块 ? ?*/?
? ? ? ? NF_nFCE_H(); ?/* 取消Nand Flash 选中*/?
? ? ? ? NF_MarkBadBlock(block);?
? ? ? ? return 0;?
? ? } else { /* 正常退出 */?
? ? ? ? NF_nFCE_H(); /* 取消Nand Flash 选中*/?
? ? ? ? return 1;?
? ? }?
}?

(4)复制

???????????????命令:

???????????????????? 00h——8Ah——10h? :单层页内复制

???????????????????? 03h——8Ah——11h? :多层页内复制

???????????????操作步骤:

?????????????????? 1、单层页内复制步骤:

????????????????????????????【1】发出命令00h;

??????????????????????????? 【2】4个源地址序列;

????????????????????????????【2】接着发出8Ah;

????????????????????????????【4】发出4个目的地址序列;

??????????????????????????? 【5】发出10h命令,启动写操作;

????????????????????????????【6】通过70h命令读取状态查询操作是否完成;

???????????????????2、多层页内复制步骤:

?????????????????????????????【1】发出命令00h(第一层)、4个源页地址序列;

???????????????????????????? 【2】以后各层依次发出命令03h、4个源页地址序列;

???????????????????????????? 【3】发出命令8Ah、目的地址、命令11h;

?????????????????????????????【4】各层依次执行【3】,在最后一页的地址后,用10h代替11h,启动写操作;

???????????????????????????? 【5】通过71h命令读取状态查询操作是否完成;

(5)擦除

?????????????? 命令:

???????????????????? 60h——D0h? :单层内块擦除

?????????????????????60h-60h ——D0h? :多层内块擦除

?????????????? 操作步骤:

??????????????????1、单层内块擦除:

??????????????????????????????【1】发出命令字60h;

??????????????????????????????【2】发出块(block)地址,仅需3个地址序列;

??????????????????????????????【3】发出D0h,启动擦除操作;

????????????????????????????? 【4】发出70h命令查询状态,是否完成擦除;

???????????????????2、多层内块擦除:

??????????????????????????????【1】发出命令字60h,3个块地址序列;

??????????????????????????????【2】对每个层执行【1】;

????????????????????????????? 【3】发出命令D0h,启动擦除操作;

??????????????????????????????【4】发出71h命令查询状态,检查是否完成擦除;

(6)读取芯片ID

???????????????? 命令:90h

???????????????? 操作步骤:

???????????????????? 1、发出命令90h;

?????????????????????2、发出4个地址序列(均设为0);

?????????????????????3、连续读入5个数据,分别表示:厂商代码、设备代码、保留字节、多层操作代码;

返回值为Nand flash 芯片的 ID 号?


unsigned short ?NF_CheckId(void)?
{?
? ? int i;?
? ? unsigned short id;?
? ? ?
? ? NF_nFCE_L(); ? ? ? ? ? ?/* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0x90); ? ? ? ? ? /* 发送读ID 命令到 Nand Flash 芯片 */?
? ? NF_ADDR(0x0); ? ? ? ? ? /* 指定地址 0x0 ,芯片手册要求 ? ?*/?
? ? for(i=0;i< 10;i++); ? ? ?/* 等待tWB = 100ns. ? ? ? ? ? */?
? ? id=NF_RDDATA()<<8; ?/* 厂商ID(K9S 1208V:0xec) ? ? ? ? ?*/?
? ? id|=NF_RDDATA(); ? ? ? ? /* 设备 ID(K9S 1208V:0x76) ?*/?
? ? NF_nFCE_H(); ? ? ? ? ? ? /* 取消Nand Flash 选中*/?
? ? return id;?
}?

(7)读状态

?????????????????命令:

???????????????????????70h——单层状态

?????????????????????? 71h——多层状态

???????????????? 操作步骤:写入命令字之后,然后启动读操作即可读入此寄存器。

(8)Nand flash 标记坏块?


如果是坏块,通过写 OOB 块的Byte6 把该块标记为坏块。?
参数说明:block 块号?
返回值:1:ok ,成功完成标记。?
? ? ? ? 0 :表示写OOB 块正确.?
static int NF_MarkBadBlock(unsigned int block)?
{?
? ? int i;?
? ? unsigned int blockPage=(block<<5);?
??
? ? seBuf[0]=0xff;?
? ? seBuf[1]=0xff;?
? ? seBuf[2]=0xff;?
? ? seBuf[5]=0x44; ? ? ?/* 设置坏块标记 */?
? ? ?
? ? NF_nFCE_L(); ? ? ? /* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0x50); ? ? ? ? /* 从C 区开始写 ? ?*/?
? ? NF_CMD(0x80); ? ? ? ? /* 发送编程命令,让Nand Flash 处理写状态 */?
? ? NF_ADDR(0x0); ?/* ? ? ? ?A0~A7 位(Column Address) ? ? ? ? */?
? ? NF_ADDR(blockPage&0xff); ? ? ? ? ? /* A9-A 16,(Page Address) */?
? ? NF_ADDR((blockPage>>8)&0xff); ? ? ? ? ? ?/* A17-A24,? ? ? ? ? (Page Address) */?
? ? ?
? ? /* 写OOB 数据块 */?
? ? for(i=0;i< 16;i++)?
? ? {?
? ? ? ? ?NF_WRDATA(seBuf [i]);?
? ? }?


? ? NF_CMD(0x10); ? ? ? ? /* 结束写命令 */?
? ? ?
? ? /* 等待NandFlash 准备好 */?
? ? for(i=0;i< 10;i++); ?/* tWB = 100ns. ? ? */?
? ? NF_WAITRB();

/*读NandFlash 的写状态 */?
? ? NF_CMD(0x70);?
? ? for(i=0;i<3;i++); ?/* twhr=60ns ?*/?
? ? if (NF_RDDATA()&0x1)?
? ? {?
? ? ? ? ? ? ? ? NF_nFCE_H(); /* 取消Nand Flash 选中*/?
? ? ? ? ? ? ? ? return 0;?
? ? ?} else {?
? ? ? ? ? ? ? ? NF_nFCE_H(); /* 取消Nand Flash 选中*/?
? ? }?
? ? return 1;?
}?

(9)Nand Flash 检查坏块?


检查指定块是否是坏块.?
参数说明:block :块号?
返回值:1:指定块是坏块?
? ? ? ? 0 :指定块不是坏块。?
static int NF_IsBadBlock(U32 block)?
{?
? ? int i;?
? ? unsigned int blockPage;?
? ? U8 data;?
? ? ? ? ?
? ? blockPage=(block<<5);?
? ? NF_nFCE_L(); ? ? ? ?/* 片选 Nand Flash 芯片*/?
? ? NF_CMD(0x50); ? ? ? /* Read OOB 数据块 ? */?
? ? NF_ADDR(517&0xf); ?/* A0~A7 位(Column Address) ? ? ? ? */?
? ? NF_ADDR(blockPage&0xff); /* A9-A 16,(Page Address) */?
? ? NF_ADDR((blockPage>>8)&0xff); ? ? ? /* A17-A24,? ?(Page Address) */?


? ? /* 等待NandFlash 准备好 */?
? ? for(i=0;i< 10;i++); /* wait tWB(100ns) ?*/?
? ? NF_WAITRB();?


? ? /* 读取读出值 */?
? ? data=NF_RDDATA();?
? ? NF_nFCE_H(); ? ? /* 取消Nand Flash 选中*/?
? ? /* 如果data 不为0xff 时,表示该块是坏块 */?
? ? if(data != 0xff)?
? ? ? ? return 1;?
? ? else?
? ? ? ? return 0;?
}?

(10)擦除指定块中数据?


参数说明:block 块号?
返回值:0 :擦除错误。(若是坏块直接返回0 ;若擦除出现错误则标记为坏块然后返回0)?

1 :成功擦除。? ?static int NF_EraseBlock(unsigned int block)? ?{? ? ? ?unsigned int blockPage=(block<<5);? ? ? ?int i;? ? ? ?/* 如果该块是坏块,则返回 */? ? ? ?if(NF_IsBadBlock(block))? ? ? ? ? ?return 0;? ? ? ?NF_nFCE_L(); ? ? ?/* 片选 Nand Flash 芯片*/? ? ? ?NF_CMD(0x60); ? ? /* 设置擦写模式 ? ? ?*/? ? ? ?NF_ADDR(blockPage&0xff); ? ? ? /* A9-A 16,(Page Address),是基于块擦*/? ? ? ?NF_ADDR((blockPage>>8)&0xff); ? ? ? ?/* A17-A24,(Page Address) */? ? ? ?NF_ADDR((blockPage>> 16)&0xff); ?/* A25,(Page Address) */? ? ? ?NF_CMD(0xd0); ? ? /* 发送擦写命令,开始擦写 */? ? ? ?/* 等待NandFlash 准备好 */? ? ? ?for(i=0;i< 10;i++); /* tWB(100ns) */? ? ? ?NF_WAITRB();? ? ? ?/* 读取操作状态 ? ? ? ? */? ? ? ?NF_CMD(0x70);? ? ? ?if (NF_RDDATA()&0x1)? ? ? ?{? ? ? ? ? ? ? ? ? ?NF_nFCE_H(); /* 取消Nand Flash 选中*/? ? ? ? ? ? ? ? ? ?NF_MarkBadBlock(block); /* 标记为坏块 */? ? ? ? ? ? ? ? ? ?return 0;? ? ? ?} ?else ?{? ? ? ? ? ? ? ? ? ?NF_nFCE_H(); /* 取消Nand Flash 选中*/? ? ? ? ? ? ? ? ? ?return 1;? ? ? ?}? ?}?

(编辑:李大同)

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

    推荐文章
      热点阅读