(转)Nandflash读写
------------------------------------------------------------------------------------------文章1------------------------------------------------------------------------------------------
摘要 以三星公司K9F2808UOB为例,设计了NAND Flash与S3C2410的接口电路,介绍了NAND Flash在ARM嵌入式系统中的设计与实现方法,并在UBoot上进行了验证。所设计的驱动易于移植,可简化嵌入式系统开发。
引言
当前各类嵌入式系统开发设计中,存储模块设计是不可或缺的重要方面。NOR和 NAND是目前市场上两种主要的非易失闪存技术。NOR Flash存储器的容量较小、写入速度较慢,但因其随机读取速度快,因此在嵌入式系统中,常用于程序代码的存储。与NOR相比,NAND闪存的优点是容量大,但其速度较慢,因为它的I/O端口只有8或16个,要完成地址和数据的传输就必须让这些信号轮流传送。NAND型Flash具有极高的单元密度,容量可以比较大,价格相对便宜。
本文以三星公司的 K9F2808UOB芯片为例,介绍了NAND Flash的接口电路与驱动的设计方法。文中介绍了开发NAND Flash驱动基本原理,意在简化嵌入式系统开发过程。
1 NAND Flash工作原理
S3C2410板的NAND Flash支持由两部分组成:集成在S3C2410 CPU上的NAND Flash控制器
和NAND Flash存储芯片。要访问NAND Flash中的数据,必须通过NAND Flash控制器发送命令才能完成。所以, NAND Flash相当于S3C2410的一个外设,并不位于它的内存地址区。
1.1 芯片内部存储布局及存储操作特点
一片NAND Flash为一个设备, 其数据存储分层为:1设备=4 096块;1块=32页;1页=528字节=数据块大小(512字节)+OOB块大小(16字节)。在每一页中,最后16字节(又称OOB,Out?of?Band)用于NAND Flash命令执行完后设置状态用,剩余512字节又分为前半部分和后半部分。可以通过NAND Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位,通过NAND Flash内置的指针指向各自的首地址。
存储操作特点有: 擦除操作的最小单位是块;NAND Flash芯片每一位只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前一定要将相应块擦除(擦除即是将相应块的位全部变为1);OOB部分的第6字节(即517字节)标志是否是坏块,值为FF时不是坏块,否则为坏块。除OOB第6字节外,通常至少把OOB的前3字节用来存放NAND Flash硬件ECC码。
1.2 NAND Flash接口电路
首先介绍开发板的硬件设计,图1为NAND Flash接口电路。其中开关SW的1、2连接时R/B表示准备好/忙,2、3连接时nWAIT可用于增加读/写访问的额外等待周期。在S3C2410处理器中已经集成了NAND Flash控制器,图2为微控制器与NAND Flash连接的方式。
图1 NAND Flash接口电路
1.3 控制器工作原理
NAND Flash控制器在其专用寄存器区(SFR)地址空间中映射有属于自己的特殊功能寄存器,就是通过将NAND Flash芯片的内设命令写到其特殊功能寄存器中,从而实现对NAND Flash芯片读、检验和编程控制。特殊功能寄存器有:NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT、NFECC。
图2 NAND Flash与S3C2410连接电路
2 Flash烧写程序原理及结构
基本原理:将在SDRAM中的一段存储区域中的数据写到NAND Flash存储空间中。烧写程序在纵向上分三层完成。第一层: 主烧写函数,将SDRAM中一段存储区域的数据写到NAND Flash存储空间中。第二层: 该层提供对NAND Flash进行操作的页读、写及块擦除等函数。第三层:为第二层提供具体NAND Flash控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正将数据在SDRAM和NAND Flash之间实现传送的函数。其中第二层为驱动程序的设计关键所在,下面对该层的读、写(又称编程)、擦除功能编码进行详细介绍。
2.1 NAND Flash Read
功能:读数据操作以页为单位,读数据时首先写入读数据命令00H,然后输入要读取页的地址,接着从数据寄存器中读取数据,最后进行ECC校验。
参数说明:block,块号;page,页号;buffer,指向将要读取到内存中的起始位置;返回值1,读成功,返回值0:读失败。
static int NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer){
NF_RSTECC(); /* 初始化 ECC */
NF_nFCE_L(); /* 片选NAND Flash芯片*/
NF_CMD(0x00); /* 从A区开始读 *//* A0~A7(列地址) */
NF_ADDR(0); /* A9A16(页地址) */
NF_ADDR(blockPage&0xff); /* A17A24,(页地址) */
NF_ADDR((blockPage>>8)&0xff);/* A25, (页地址) */
NF_ADDR((blockPage>>16)&0xff);/* 等待NAND Flash处于再准备状态 */
ReadPage();/* 读整个页, 512字节 */
ReadECC();/* 读取ECC码 */
ReadOOB();/* 读取该页的OOB块 *//* 取消NAND Flash 选中*/
NF_nFCE_H();/* 校验ECC码, 并返回 */
Return (checkEcc())}
2.2 NAND Flash Program
功能:对页进行编程命令, 用于写操作。
命令代码:首先写入00h(A区)/01h(B区)/05h(C区), 表示写入那个区; 再写入80h开始编程模式(写入模式),接下来写入地址和数据; 最后写入10h表示编程结束。图3为程序流程图。
图3 写程序流程
参数说明:block,块号;page,页号;buffer,指向内存中待写入NAND Flash中的数据起始位置;返回值0,写错误,返回值1,写成功。
static int NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer){
NF_RSTECC(); /* 初始化 ECC */
NF_nFCE_L(); /* 片选NAND Flash芯片*/
NF_CMD(0x0); /* 从A区开始写 */
NF_CMD(0x80); /* 写第一条命令 *//* A0~A7(列地址) */
NF_ADDR(0);/* A9A16(页地址) */
NF_ADDR(blockPage&0xff);/* A17A24(页地址) */
NF_ADDR((blockPage>>8)&0xff); /* A25(页地址) */
NF_ADDR((blockPage>>16)&0xff);/* 写页为512B到NAND Flash芯片 */
WRDATA(); /*OOB一共16字节,每一个字节存放什么由程序员自己定义, 在Byte0 Byte2存ECC检验码,Byte6 存放坏块标志*/
WRDATA(); /* 写该页的OOB数据块 */
CMD(0x10); /* 结束写命令 */
WAITRB();/* 等待NAND Flash处于准备状态 *//* 发送读状态命令给NAND Flash */
CMD(0x70);
if (RDDATA()&0x1) { /*如果写有错, 则标示为坏块,取消NAND Flash 选中*/
MarkBadBlock(block);
return 0;
} else { /* 正常退出, 取消NAND Flash 选中*/
return 1;}
2.3 NAND Flash Erase
功能:块擦除命令。
命令代码:首先写入60h进入擦写模式,然后输入块地址,接下来写入D0h, 表示擦写结束。
参数说明:block,块号;返回值0,擦除错误(若是坏块直接返回0;若擦除出现错误则标记为坏块然后返回0),返回值1,成功擦除。
static int NF_EraseBlock(unsigned int block){/* 如果该块是坏块, 则返回 */
if(NF_IsBadBlock(block)) return 0;
NF_nFCE_L(); /* 片选NAND Flash芯片*/
NF_CMD(0x60); /* 设置擦写模式 *//* A9A16(Page Address) , 是基于块擦除*/
NF_ADDR(blockPage&0xff);
NF_ADDR((blockPage>>8)&0xff); /* A25(Page Address) */
NF_ADDR((blockPage>>16)&0xff); NF_CMD(0xd0); WAITRB();CMD(0x70);
if(RDDATA()&0x1){/*如有错,标为坏块,取消Flash选中*/
MarkBadBlock(block);
return 0;
} else { /* 退出, 取消Flash 选中*/
return 1;}
3 ECC校检原理与实现
由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能可靠,因此在NAND的生产及使用过程中会产生坏块。为了检测数据的可靠性,在应用NAND Flash的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比较可靠地进行坏区检测。如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个块或是页不能读取或全部出错,而是整个页(例如512字节)中只有一位或几位出错。对数据的校验常用的有奇偶校验、CRC校验等,而在NAND Flash处理中,一般使用一种专用的校验——ECC。ECC能纠正单位错误和检测双位错误,而且计算速度很快,但对1位以上的错误无法纠正,对2位以上的错误不保证能检测。ECC一般每256字节原始数据生成3字节ECC校验数据,这3字节共24位分成两部分:6位的列校验和16位的行校验,多余的2位置1,如表1所列。
表1 校检数据组成
首先介绍ECC的列校检。ECC的列校验和生成规则如图4所示,“^”表示“位异或”操作。由于篇幅关系,行校检不作介绍,感兴趣的读者可以参考芯片datasheet,在三星公司网站可以免费下载。
图4 列校验和生成规则
数学表达式为:
当向NAND Flash的页中写入数据时,每256字节生成一个ECC校验和,称之为原ECC校验和,保存到页的OOB数据区中。当从NAND Flash中读取数据时,每256字节生成一个ECC校验和,称之为新ECC校验和。校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和与新ECC校验和按位异或,若结果为0,则表示无错(或者出现了 ECC无法检测的错误);若3字节异或结果中存在11位为1,表示存在一个位错误,且可纠正;若3个字节异或结果中只存在1位为1,表示 OOB区出错;其他情况均表示出现了无法纠正的错误。
4 UBOOT下功能验证
实现UBOOT对NAND Flash的支持主要是在命令行下实现对NAND Flash的操作。对NAND Flash实现的命令为:nand info、nand device、nand read、nand write、nand erease、nand bad。用到的主要数据结构有:struct nand_flash_dev和struct nand_chip,前者包括主要的芯片型号、存储容量、设备ID、I/O总线宽度等信息,后者是对NAND Flash进行具体操作时用到的信息。由于将驱动移植到UBoot的方法不是本文重点,故不作详细介绍。
验证方式:通过TFTP将数据下载到SDRAM中,利用nand read、nand write、nand erease三个命令对NAND Flash进行读、编程、擦写测试。测试结果如表2所列。和datasheet中数据对比,可以得出结论,驱动在系统中运行良好。
表2 测试结果
结语
现在嵌入式系统应用越来越广泛,而存储器件又是嵌入式系统必不可少的一部分,NAND Flash在不超过4 GB容量的需求下,较其他存储器件优势明显。本文所设计的驱动并未基于任何操作系统,可以方便地移植到多种操作系统和Boot Loader下,对于简化嵌入式系统开发有一定的实际意义。
文章转载自:https://blog.csdn.net/demetered/article/details/11853859
------------------------------------------------------------------------------------------文章2------------------------------------------------------------------------------------------
Nandflash.c与Nandflash.h样例
Nandflash.h
K9F1G08U0E.h:
#define NANDFLASH2 (*(volatile uint8_t *)0x70000000)
#define NANDFLASH2_32 (*(volatile uint32_t *)0x70000000)
#define NANDFLASH2C (*(volatile uint8_t *)0x70010000)
#define NANDFLASH2A (*(volatile uint8_t *)0x70020000)
#define NANDFLASH2A16 (*(volatile uint16_t *)0x70020000)
#define NANDFLASH2A32 (*(volatile uint32_t *)0x70020000)
#define NANDFLASH2S_FAIL 0x01 #define NANDFLASH2S_READY 0x40
#define NANDFLASH2S_NOPROTECTION 0x80
uint8_t K9F1G08U0E_EraseBlock(uint16_t block);
uint8_t K9F1G08U0E_ProgramPage(uint16_t page, const void *data);
void K9F1G08U0E_ReadID(uint8_t data[5]);
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data);
void K9F1G08U0E_Reset(void);
Nandflash.c
#include <stdio.h>
#include <stm32f10x.h>
#include "K9F1G08U0E.h"
/* See [Figure 2] K9F1G08U0E Array Organization */
/* Column (byte) address byte 1 and 2: A7~0, A11~8; Range: 0x0000~0x083f (or 0x07ff) */
/* Row (page) address byte 1 and 2: A19~12, A27~20 Range: 0x00000000~0xffff0000 */
static void K9F1G08U0E_Wait(void);
/* Block count: 1024, each block has 64 pages and each page is 2KB */
uint8_t K9F1G08U0E_EraseBlock(uint16_t block)
{
NANDFLASH2C = 0x60;
NANDFLASH2A16 = block << 6;
NANDFLASH2C = 0xd0;
K9F1G08U0E_Wait();
return (NANDFLASH2 & NANDFLASH2S_FAIL) == 0;
}
/* Page count: 65536 */
uint8_t K9F1G08U0E_ProgramPage(uint16_t page, const void *data)
{
uint16_t i;
NANDFLASH2C = 0x80;
NANDFLASH2A32 = page << 16;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
for (i = 0; i < 2048; i++)
NANDFLASH2 = *((const uint8_t *)data + i);
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET); // 获取ECC码前必须等待FIFO变空
NANDFLASH2_32 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
NANDFLASH2C = 0x10;
K9F1G08U0E_Wait();
return (NANDFLASH2 & NANDFLASH2S_FAIL) == 0;
}
void K9F1G08U0E_ReadID(uint8_t data[5])
{
uint8_t i;
NANDFLASH2C = 0x90;
NANDFLASH2A = 0x00;
for (i = 0; i < 5; i++)
data[i] = NANDFLASH2;
}
// 板上两个VCC和GND都必须接到电源上才可以使用此函数!
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint32_t ecc[2];
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
*((uint8_t *)data + i) = NANDFLASH2;
ecc[0] = NANDFLASH2_32;
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc[1] = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return ecc[0] == ecc[1];
}
/*
// 备用函数, 速度大约比上面的慢4倍
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint8_t ecc1[4];
uint32_t ecc2;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
{
*((uint8_t *)data + i) = NANDFLASH2;
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 1;
NANDFLASH2C = 0xe0;
}
for (i = 0; i < 4; i++)
{
ecc1[i] = NANDFLASH2;
if (i != 3)
{
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 2049;
NANDFLASH2C = 0xe0;
}
}
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc2 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return *(uint32_t *)ecc1 == ecc2;
}
*/
void K9F1G08U0E_Reset(void)
{
NANDFLASH2C = 0xff;
}
static void K9F1G08U0E_Wait(void)
{
NANDFLASH2C = 0x70;
while ((NANDFLASH2 & NANDFLASH2S_READY) == 0);
}
static uint8_t K9F1G08U0E_ReadPage2(uint16_t page, void *data);
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint32_t ecc[2];
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
*((uint8_t *)data + i) = NANDFLASH2;
ecc[0] = NANDFLASH2_32;
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc[1] = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
if (ecc[0] == ecc[1])
return 1;
else
return K9F1G08U0E_ReadPage2(page, data);
}
static uint8_t K9F1G08U0E_ReadPage2(uint16_t page, void *data)
{
uint16_t i;
uint8_t ecc1[4];
uint32_t ecc2;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
{
*((uint8_t *)data + i) = NANDFLASH2;
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 1;
NANDFLASH2C = 0xe0;
}
for (i = 0; i < 4; i++)
{
ecc1[i] = NANDFLASH2;
if (i != 3)
{
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 2049;
NANDFLASH2C = 0xe0;
}
}
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc2 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return *(uint32_t *)ecc1 == ecc2;
}
通过调整FSMC时序的延时时间可以大幅度提高读取速度,添加如下代码:
fsmc_timing.FSMC_HiZSetupTime = 0;
fsmc_timing.FSMC_HoldSetupTime = 1;
fsmc_timing.FSMC_SetupTime = 0;
fsmc_timing.FSMC_WaitSetupTime = 2;
可以发现,即便是使用K9F1G08U0E_ReadPage2函数来读取,也能在瞬间完成。
文章转载自:https://blog.csdn.net/ZLK1214/article/details/78208636
(转)Nandflash读写的更多相关文章
- NandFlash读写
1.NandFlash分类 根据物理结构上的区别,NandFlash主要分为如下两类:•SLC (Single Level Cell): 单层式存储•MLC (Multi Level Cell): 多 ...
- 【ARM】arm系列知识框架
[ARM编程模型] 硬件: 电路原理图 软件: 体系结构, 指令集, 寄存器组 [ARM编程技术] 汇编/C语言 编译, 链接, 烧写和调试 windows: MDK linux : gcc [AR ...
- u-boot懂你并不难
转载:http://blog.chinaunix.net/uid-28236237-id-3865045.html u-boot第一阶段分析(一) u-boot 第一阶段分析(二) u-boot 第二 ...
- 04.移植u-boot
1.读readme获取信息 1.1 由Building the Software可知,需修改顶层makefile,指定架构和编译器 ifeq ($(HOSTARCH),$(ARCH)) ...
- 外设:K9F2G08 nandflash 底层读写、控制驱动程序,可随机读写
/****************************************************************************** Copyright (C), 2001- ...
- nandflash的读写(2440)
说明: 根据物理结构上的区别 , NandFlash主要分为如下两类:1)•SLC (Single Level Cell): 单层式存储2)•MLC (Multi Level Cell): 多层式存储 ...
- 使用jlink直接烧norflash或者nandflash不借助uboot的猜想
由于喜欢折腾,我是在linux下使用jlink的,既然JLinkExe可以进行内存读写操作,loadbin等操作,并且通过指定命令文件支持批量指令输入,那么首先jlink是可以直接访问内部存储器的,包 ...
- NorFlash和NandFlash区别
Flash编程原理都是只能将1写为0,而不能将0写成1.所以在Flash编程之前,必须将对应的块擦除,而擦除的过程就是将所有位都写为1的过程,块内的所有字节变为0xFF.因此可以说,编程是将相应位 ...
- uboot在nandflash和norflash是如何运行的
转自:http://www.aiuxian.com/article/p-2796357.html 电子产品如果没有了电,就跟废品没什么区别,是电赋予了他们生命,然而程序则是他们的灵魂. 小时候一直很好 ...
随机推荐
- Python 3 实现色情图片识别
Python 3 实现色情图片识别 项目简介 项目内容 本实验将使用 Python3 去识别图片是否为色情图片,我们会使用到 PIL 这个图片处理库,会编写算法来划分图像的皮肤区域. 项目知识点 Py ...
- Android 4.4 根据uri获取路径的方法
当我们选择图片以后,返回的是Uri,此时我们要把路径存储到数据库,必须将其转换成String类型. URI: //content://com.android.providers.media.docu ...
- VSCode 预览 .md 文件
VSCode安装md插件 选择Extensions, 输入Markdown Theme Kit,下面会出现相应插件,点击install(截图中因为我已经安装,所以是设置) 或者安装插件Markdown ...
- java常用类-String类
* 字符串:就是由多个字符组成的一串数据.也可以看成是一个字符数组. * 通过查看API,我们可以知道 * A:字符串字面值"abc"也可以看成是一个字符串对象. * B:字符串是 ...
- Python3 tkinter基础 Listbox Scrollbar 创建垂直滚动条
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- noip模拟【ping】
70:很容易想到的是枚举每一个可能的答案来判断是否可行,取最优即可,贪心选择. 100:满足题目条件的这个距离是满足单调性的.如果x不行,那么大于x的距离都不行,二分答案. 学会运用二分,by ws_ ...
- [Sdoi2017]序列计数 矩阵优化dp
题目 https://www.lydsy.com/JudgeOnline/problem.php?id=4818 思路 先考虑没有质数限制 dp是在同余系下的,所以\(f[i][j]\)表示前i个点, ...
- 题解——洛谷 P2680 NOIP提高组 2015 运输计划
树上差分加上二分答案 详细题解待填坑 #include <cstdio> #include <algorithm> #include <cstring> using ...
- 【sql server】索引详解
索引可以理解为一种特殊的目录结构. sql server提供两种索引形式: 聚集索引和非聚集索引. 怎么理解这两种形式. 拿我们常用的字典举例来说, 一个字典好比数据库中的一个表.那么当我们想从字典中 ...
- c# 重试机制
protected async Task<T> TryOperation<T>(int maxRetryCount,Func<Task<T>> func ...