Chinaunix首页 | 论坛 | 博客
  • 博客访问: 229438
  • 博文数量: 18
  • 博客积分: 3295
  • 博客等级: 少校
  • 技术积分: 431
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-26 19:11
文章分类
文章存档

2010年(18)

分类: LINUX

2010-11-16 21:24:54

 

   

本章我们将学习嵌入式系统中比较重要的存储器件-----Nand Flash,它相当于PC机的硬盘,用于保存系统运行所必需的操作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存(Sdram)不同,Nand Flash掉电后,数据仍可以永久保存。 

 

 FLASH介绍

Nand Flashflash存储器的一种,其内部采用非线性宏单元模式,为固态大容量内存的实现提供了廉价有效的解决方案。Nand Flash存储器具有容量较大,改写速度快等优点,适用于大量数据的存储,因而在业界得到了越来越广泛的应用,如嵌入式产品中包括数码相机、MP3随身听记忆卡、体积小巧的U盘等。NAND型闪存以块为单位进行擦除操作。闪存的写入操作必须在空白区域进行,如果目标区域已经有数据,必须先擦除后写入,因此擦除操作是闪存的基本操作。

我的TQ2440是三星生产的K9F2G08,容量为256MB。下观就以它为例进行介绍。

该芯片为

48引脚,有三种封装,其外围引脚较少,这样就方便和S3C2440的硬件连接,即方便设计电路,又可以减少占用总线资源。具体的引脚见手册的第8页,在此我就不列出了。

下面是芯片的功能结构图,

l  X-Buffers Latches & Decoders:用于产生行地址;

l  Y-Buffers Latches & Decoders:用于产生列地址;

l  Command Register:用于命令字的操作;

l  Control Logic & High Voltage Generator:控制逻辑及产生Flash所需的高压;

l  Nand Flash Array:存储部件;

l  Page Register & S/A :页寄存器,当读、写页时,会将数据先读入或写入此寄存器,大小为2048+64 B

下图为NAND FLASH的存储单元组织结构图:

K9F2G08

容量为256MB,分为131072行(页),2112 x 8列。每页大小为2048字节,另外有64字节的额外空间,这64字节的空间的列地址为2048—2111

命令、地址、数据都通过8I/O口输入/输出,这种形式减少了芯片的引脚个数,并使得系统很容易升级到更大的容量,写入命令、地址或数据时,都需要将WE#CE#信号同时拉低。数据在WE#信号的上升沿被锁存,其中CLE为命令锁存信号、ALE为地址锁存信号。整个芯片为(256+8MB,因此,需要29根地址线来寻址,这样,如果我们以字节为单位发出寻址信号,总共需要5个周期,其中2周期列地址信号,3周期的行地址信号。见下图。

操作NAND FLASH时,先传输命令,接着输出地址,最后读/写数据,期间还要检查FLASH的状态。具体的命令字见下表。

  当对NAND FLASH操作时,先发送命令,然后再发送5个字节的地址序列。下面介绍一些常用指令,其它指令,详见手册,上面写的很清楚。

命令字为00h30h

如上图,当发出

00h命令后,接着5个字节的地址序列,然后再发出30h命令,接着,就可以读出数据了,当发送完30h后,可以检测R/B引脚看是否准备好了,如果准备好,就可以读出数据。

Reset

命令字:FFh

当向芯片发送FFh命令时,可以复位芯片,如果芯片正在处于读、写、擦除状态,复位命令会终止这些命令。

Page Program

命令字:80h10h.

NAND FLASH的写操作一般是以页为单位进行的,因此才会叫Page Program,但是也支持一个字以上的(包括一个字)连续写操作。开始发出80h命令,接着发送5个字节的地址序列,然后向FLASH发送数据,最大为一页大小,最后发送10h命令启动烧写,此时FLASH内部会自动完成写、校验操作。发送10h后,可以通过读状态命令70h(见下文)获知写操作是否完成,是否成功。

Copy-Back Program

命令字:00h8Ah10h.

此命令用于将一页复制到同一层的另外一页,它省去了读出数据、将数据重新载入FLASH的过程,使得效率大大提高。

关于层:NAND FLASH有层的概念,K9F2G08分为两层,每层包括了1024个块,如下图,因此上面的命令表中才会有two-plane的命令,这些命令可以使得在两个层间完成操作,这样程序员就很方便的使用一个命令完成更多的操作,其命令格式和单层操作的类似,详见手册。但K9F2G08R0A不支持两层命令。

 

上图清晰的告诉我们,开始发送

00h和源地址,当发送35h之后,芯片将一页大小的数据读入到内部寄存器,接着,我们发送85h和目的地址序列,最后发送10h启动。最后,我们可以使用70h/7Bh检测是否完成,是否成功。

Block Erase

命令字:60hD0h

擦除操作是以块为单位的,也就是128K。开始发出60h,然后发出3个地址序列仅行地址,最好发出D0h。具体操作,如下图。

Read Status

         命令字:70h

         读状态命令可以读出芯片的状态寄存器,查看是否读、写操作是否完成、是否成功。具体的状态见下图。

NAND FLASH控制器介绍

看了上面的命令时序图,大家肯定认为对NAND FLASH的操作比较复杂,因此S3C2440为了简化操作,为我们提供了几个寄存器,比如NFCMD寄存器,就是NAND FLASH命令寄存器,如果我们需要读命令,直接向此寄存器写00h30h即可。

基本操作步骤:

1.         设置NFCONFNFCONT寄存器,配置NAND FLASH

2.         NFCMD寄存器写入命令;

3.         NFADDR寄存器写入地址;

4.         /写数据,通过NFSTAT检测NAND FLASH的状态,读R/nB信号以确定是否完成操作,是否成功。

主要寄存器:

u  NFCONF

设置NAND FLASH的时序参考TACLSTWRPH0TWRPH1,这三个参数见下面时序图;设置位宽度;还包括一些只读位,用来批示是否支持其它大小的页。

TACLS:表示CLT/ALE的建立时间(setup time)

TWRPH0:表示CLE/ALE的持续时间。

TWRPH1:表示CLE/ALE的维持时间(hold time)

u  NFCONT

用来使能/禁止NAND FLASH控制器、使能/禁止控制引脚信号nFCE、初始化ECC

u  NFCMD

命令寄存器,用来向其写入命令。

u  NFADDR

地址寄存器,用来向其写入地址。

u  NFDATA

数据寄存器,用来读、写数据使用,只用到8位。

u  NFSTAT

状态寄存器,只用到一位,0busy1ready

 FLASH的操作(以读为例) NFCONFNFCONT

NFCONF主要是设置TACLSTWRPH0TWRPH1三个时间参数。根据手册的参数表,见下图:

三个参数只有最小值 MIN,没有最大值,因此,只要参数大于表格中的数即可,这里涉及时钟的一些概念,暂时不做解释,如果有不明白的,等学完时间一章再回过头来复习本章吧,这里直接将参数告诉大家:

TACLS=1TWRPH0=4TWRPH1=0

NFCONT设置为:NFCONT=1<<4|1<<1|1<<0) 表示使能NAND FLASH控制器、禁止控制引脚信号nFCE、初始化ECC

NAND FLASH

第一次使用,通常要复位一下。

NFCONT   &=  ~1<<1

NFCMD    =   0xFF

然后循环查NFSTAT0,直到它为1.

NFCONT  &=  ~1<<1

NFCMD  =  0

上面涉及的概念比较多,请大家反复阅读,并通过本实例来消化。

在以前的几章程序都是直接下载到NAND FLASH0x0地址,并且这些代码都小于4KB,因此通地NAND FLASH启动后会自动读入NAND FLASH内部的SRAM运行。

本章的程序将放在4096之后,当通过NAND FLASH启动后,再读入SDRAM中运行。

整个程序由head.s  init.c  nand.c  main.c Makefilenand.lds组成。先来看一下链接脚本。

SECTIONS {

  firtst         0x00000000 : { head.o init.o nand.o}

  second      0x30000000 : AT(4096) { main.o }

}

上面的链接脚本将整个程序分成两段,第一段由head.o  init.o  nand.o组成,加载运行地址都是0x0;第二段由main.o组成,加载地址为4096,运行地址为0x30000000

整个程序还是以head.s文件为入口,其调用的函数都在init.c  nand.c 文件中实现。依次完成设置堆栈、关看门狗、初始化Sdram、初始化NAND FLASH、复制第二段代码(main.o)到Sdram,重设堆栈到Sdram、执行main函数。

这里有两点要注意:

1、            复制第二段代码的时候调用了C语言的函数nand_read,而这个函数是带有3个参数的,因此根据调用规则,参数由r0,r1,r2传递进去,这里只用到3个参数。如果想要传递大于4个参数时,剩下的参数就只能用数据栈进行传递了,如果函数有返回值,用a0传递。

2、            第一段代码在NAND FLASH中的Sram中运行,因此堆栈设置为4096;第二段代码跳到Sdram中运行了,因此需重新设置堆栈。

我将同步上传程序,并且添加注释,如果有错误和疑问,请留言。

将编译好的程序烧录到nand中后,从nand启动,发现LED开始计数。本章涉及的内容较多,希望读者多用时间来理解消化。

1.韦东山 《嵌入式LINUX应用开发完全手册》

2.三星  《S3C2440手册》

3.GNU  《GNU链接手册》

阅读(5696) | 评论(11) | 转发(4) |
给主人留下些什么吧!~~

meen20122013-06-11 00:19:35

为什么不是:*p=0;*p=0;*p=(addr>>12)&0xff;;*p=(addr>>20)&0xff;;*p=(addr>>28)&0xff;
按照芯片手册,A0-A10已经是2K了,A11怎么还是Column Address?

meen20122013-06-10 23:40:57

NAND_SECTOR_SIZE不改成2048就用512行不行?5个字节的地址序列应该如何取值呢?

meen20122013-06-10 23:35:00

程序呢?

machoe2010-12-14 19:05:37

chinaunix网友: for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {   //for循环中读完一页数据
          *buf = read_data();
          buf++;
      }NAND_SECTOR_SIZE为2047,.....
我没弄懂你想问什么?我的程序是大页的nand flash是一页2048字节的。

chinaunix网友2010-12-14 18:06:04

for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {   //for循环中读完一页数据
          *buf = read_data();
          buf++;
      }NAND_SECTOR_SIZE为2047,即是循环2048次, 每次read_data()是一个字节,因为只有八位有用,所以循环一次为2048字节,与一页512字节矛盾为什么呢?