------------------------------------------------------------------------------------------文章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

K9F1G08U0E.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);
}
 
如果最后实在没有办法解决问题再使用备用函数。
【示例程序(推荐):主读取函数执行后若出现ECC校验错误,则调用备用读取函数】
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函数来读取,也能在瞬间完成。

结论】
(Read函数为连续读取,Read2函数为随机读取)

文章转载自:https://blog.csdn.net/ZLK1214/article/details/78208636

(转)Nandflash读写的更多相关文章

  1. NandFlash读写

    1.NandFlash分类 根据物理结构上的区别,NandFlash主要分为如下两类:•SLC (Single Level Cell): 单层式存储•MLC (Multi Level Cell): 多 ...

  2. 【ARM】arm系列知识框架

    [ARM编程模型] 硬件: 电路原理图 软件: 体系结构, 指令集, 寄存器组 [ARM编程技术] 汇编/C语言 编译, 链接, 烧写和调试 windows: MDK linux  : gcc [AR ...

  3. u-boot懂你并不难

    转载:http://blog.chinaunix.net/uid-28236237-id-3865045.html u-boot第一阶段分析(一) u-boot 第一阶段分析(二) u-boot 第二 ...

  4. 04.移植u-boot

    1.读readme获取信息    1.1 由Building the Software可知,需修改顶层makefile,指定架构和编译器    ifeq ($(HOSTARCH),$(ARCH))   ...

  5. 外设:K9F2G08 nandflash 底层读写、控制驱动程序,可随机读写

    /****************************************************************************** Copyright (C), 2001- ...

  6. nandflash的读写(2440)

    说明: 根据物理结构上的区别 , NandFlash主要分为如下两类:1)•SLC (Single Level Cell): 单层式存储2)•MLC (Multi Level Cell): 多层式存储 ...

  7. 使用jlink直接烧norflash或者nandflash不借助uboot的猜想

    由于喜欢折腾,我是在linux下使用jlink的,既然JLinkExe可以进行内存读写操作,loadbin等操作,并且通过指定命令文件支持批量指令输入,那么首先jlink是可以直接访问内部存储器的,包 ...

  8. NorFlash和NandFlash区别

      Flash编程原理都是只能将1写为0,而不能将0写成1.所以在Flash编程之前,必须将对应的块擦除,而擦除的过程就是将所有位都写为1的过程,块内的所有字节变为0xFF.因此可以说,编程是将相应位 ...

  9. uboot在nandflash和norflash是如何运行的

    转自:http://www.aiuxian.com/article/p-2796357.html 电子产品如果没有了电,就跟废品没什么区别,是电赋予了他们生命,然而程序则是他们的灵魂. 小时候一直很好 ...

随机推荐

  1. 01: flask基础

    1.1 flask介绍   参考博客: https://www.cnblogs.com/sss4/p/8097653.html 1.django.tornado.flask比较 1. Django:1 ...

  2. ubuntu----VMware 鼠标自由切换问题及主机虚拟机共享剪切板问题

    VMware 安装了Ubuntu之后,在正常安装了VMware tools后,仍然不能正常的在Ubuntu与物理机之间自由的切换,每次都要按下ctrl+Alt,而且鼠标指针会经常性的离奇的失灵 解决方 ...

  3. topcoder srm list

    300 305 310 315 320 325 330 335 340 350 360 370 380 390 400 410 415 420 425 430 435 440 445 450 455 ...

  4. 百度地图bd map使用方法

    一个经验:(当项目中的方法, 很多的时候, 相互调用的时候) 可以在script中, 先定义, 注意是定义, 一个"入口"函数, function initMap(), 然后, 让 ...

  5. POJ 2226 Muddy Fields(最小点覆盖)题解

    题意:一片r*c的地,有些地方是泥地,需要铺地板.这些地板宽1,长无限,但只能铺在泥地上不能压到其他地方,问你铺满所有泥地最少几块 思路:我们把一行中连续的泥地看成整体,并把所有横的整体里的点编成一个 ...

  6. 集合02_Queue

    Queue集合 模拟队列先进先出(FIFO),不允许随机访问元素 Queue接口定义的方法: //void add(Object e) boolean offer(Object e),加入队列尾部,比 ...

  7. 浅谈FFT、NTT和MTT

    前言 \(\text{FFT}\)(快速傅里叶变换)是 \(O(n\log n)\) 解决多项式乘法的一个算法,\(\text{NTT}\)(快速数论变换)则是在模域下的,而 \(\text{MTT} ...

  8. 中文目录对 sublime text 有什么影响?

    用了这软件好几个月了,一直没出现问题.最近做精简时,发现一个奇怪的问题. 相同的配置,为什么两个程序表现得不一样? 难道是哪里的配置不一样? 难道是插件被我精简得太厉害了? 难道是插件有依赖文件被我删 ...

  9. linux 基础命令(12月25日笔记)

    1.  cp指令指令:cp          (copy,复制)作用:复制文件/文件夹到指定的位置语法:#cp [-r] 被复制的文档路径 文档被复制到的路径选项:         -r:recurs ...

  10. Scala的配置

    Scala基于Java的JVM,所以先检查是否安装JDK. 在官网上下载并安装好了之后,就是配置环境变量了. SCALA_HOME 变量:C:\Program Files (x86)\scala. P ...