Nor Flash数据存储规则与数据读写方法.docx
《Nor Flash数据存储规则与数据读写方法.docx》由会员分享,可在线阅读,更多相关《Nor Flash数据存储规则与数据读写方法.docx(12页珍藏版)》请在冰豆网上搜索。
NorFlash数据存储规则与数据读写方法
NORFlash
NORFLASH是INTEL在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,而且它可以忍受一万次到一百万次抹写循环,是早期的可移除式闪存储媒体的基础。
目录
∙NORFlash的访问方式
∙NORFlash的烧写方式
∙NORFlash的原理
NORFlash的访问方式
∙ 在NORFLASH的读取数据的方式来看,它与RAM的方式是相近的,只要能够提供数据的地址,数据总线就能够正确的挥出数据。
考虑到以上的种种原因,多数微处理器将NORFLASH当做原地运行(Executeinplace,XIP)存储器使用,这其实以为着存储在NORFLASH上的程序不需要复制到RAM就可以直接运行。
由于NORFLASH没有本地坏区管理,所以一旦存储区块发生毁损,软件或驱动程序必须接手这个问题,否则可能会导致设备发生异常。
在解锁、抹除或写入NORFLASH区块时,特殊的指令会先写入已绘测的记忆区的第一页(Page)。
接着快闪记忆芯片会提供可用的指令清单给实体驱动程序,而这些指令是由一般性闪存接口(CommONFLASHmemoryInterface,CFI)所界定的。
与用于随机存取的ROM不同,NORFLASH也可以用在存储设备上;不过与NANDFLASH相比,NORFLASH的写入速度一般来说会慢很多。
NORFlash的烧写方式
∙ 相对于硬件工程师和嵌入式软件工程师一般在完成设计之后常常需要验证FLASH是否在工作。
在应用当中,也有很多时候需要对FLASH进行写操作。
该文章简单介绍了基于ARM芯片的NORFLASH烧写,并提供了2个具体的实例和源代码,希望对有需要的朋友有点帮助。
在开始之前,先声明一下,这篇文章只是介绍了如何写NORFLASH的烧写驱动,和H-JTAG/H-FLASHER没有直接的联系。
在后面的介绍里,如无特别说明,处理器指的是ARM处理器,FLASH指的都是NORFLASH。
另外,BYTE指的是8-BIT的数据单元,HALF-WORD代表的是16-BIT的数据单元,而WORD则代表了32-BIT的数据单元。
1。
NORFLASH的简单介绍
NORFLASH是很常见的一种存储芯片,数据掉电不会丢失。
NORFLASH支持ExecuteONChip,即程序可以直接在FLASH片内执行。
这点和NANDFLASH不一样。
因此,在嵌入是系统中,NORFLASH很适合作为启动程序的存储介质。
NORFLASH的读取和RAM很类似,但不可以直接进行写操作。
对NORFLASH的写操作需要遵循特定的命令序列,最终由芯片内部的控制单元完成写操作。
从支持的最小访问单元来看,NORFLASH一般分为8位的和16位的(当然,也有很多NORFLASH芯片同时支持8位模式和是16位模式,具体的工作模式通过特定的管脚进行选择)。
对8位的NORFLASH芯片,或是工作在8-BIT模式的芯片来说,一个地址对应一个BYTE(8-BIT)的数据。
例如一块8-BIT的NORFLASH,假设容量为4个BYTE。
那芯片应该有8个数据信号D7-D0和2个地址信号,A1-A0。
地址0x0对应第0个BYTE,地址0x1对应于第1BYTE,地址0x2对应于第2个BYTE,而地址0x3则对应于第3个BYTE对16位的NORFLASH芯片,或是工作在16-BIT模式的芯片来说,一个地址对应于一个HALF-WORD(16-BIT)的数据。
例如,一块16-BIT的NORFLASH,假设其容量为4个BYTE。
那芯片应该有16个数据信号线D15-D0和1个地址信号A0。
地址0x0对应于芯片内部的第0个HALF-WORD,地址0x1对应于芯片内部的第1个HALF-WORD。
FLASH一般都分为很多个SECTOR,每个SECTOR包括一定数量的存储单元。
对有些大容量的FLASH,还分为不同的BANK,每个BANK包括一定数目的SECTOR。
FLASH的擦除操作一般都是以SECTOR,BANK或是整片FLASH为单位的。
在对FLASH进行写操作的时候,每个BIT可以通过编程由1变为0,但不可以有0修改为1。
为了保证写操作的正确性,在执行写操作前,都要执行擦除操作。
擦除操作会把FLASH的一个SECTOR,一个BANK或是整片FLASH的值全修改为0xFF。
这样,写操作就可以正确完成了。
2。
ARM处理器的寻址
ARM可以说是目前最流行的32位嵌入式处理器。
在这里只提一下ARM处理器的寻址,为后面做个铺垫。
从处理器的角度来看,系统中每个地址对应的是一个BYTE的数据单元。
这和很多别的处理器都是一样的。
3。
处理器和NORFLASH的硬件连接
从前面的介绍,我们知道从处理器的角度来看,每个地址对应的是一个BYTE的数据单元。
而,NORFLASH的每个地址有可能对应的是一个BYTE的数据单元,也有可能对应的是一个HALF-WORD的数据单元。
所以在硬件设计中,连接ARM处理器和NORFLASH时,必须根据实际情况对地址信号做特别的处理。
如果ARM处理器外部扩展的是8-BIT的NORFLASH,数据线和地址线的连接应该如图1所示。
从图中我们可以看到,处理器的数据信号D0-D7和FLASH的数据信号D0-D7是一一对应连接的,处理器的地址信号A0-An和NORFLASH的地址信号A0-An也是一一对应连接的。
如果ARM处理器外部扩展的是16-BIT的NORFLASH,数据线必须要错位连接。
图2给了一个ARM处理器和16-BITNOR FLASH的连接示意图。
如图2所示,ARM处理器的数据信号D0-D15和FLASH的数据信号D0-D15是一一对应的。
而ARM处理器的地址信号和NORFLASH的地址信号是错位连接的,ARM的A0悬空,ARM的A1连接FLASH的A0,ARM的A2连接FLASH的A1,依次类推。
需要错位连接的原因是:
ARM处理器的每个地址对应的是一个BYTE的数据单元,而16-BIT的FLASH的每个地址对应的是一个HALF-WORD(16-BIT)的数据单元。
为了保持匹配,所以必须错位连接。
这样,从ARM处理器发送出来的地址信号的最低位A0对16-BITFLASH来说就被屏蔽掉了。
补充说明:
1。
一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的FLASH的位宽(8-BIT/16-BIT/32-BIT)。
这样,处理器才知道在访问的时候如何从FLASH正确的读取数据。
2。
有些ARM处理器内部可以设置地址的错位。
对于支持软件选择地址错位的处理器,在连接16-BITFLASH的时候,硬件上可以不需要把地址线错位。
读者设计的时候,请参考MCU的数据手册,以手册为准,以免造成不必要的麻烦。
3。
如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。
上面的描述可能比较抽象,下面让我们来看2个ARM处理器访问16-BITFLASH的例子:
例子1:
ARM处理器需要从地址0x0读取一个BYTE
1-ARM处理器在地址线An-A0上送出信号0x0;
2–16-BITFLASH在自己的地址信号An-A0上看到的地址是0x0,然后将地址0x0对应的16-BIT数据单元输出到D15-D0上;
3–ARM处理器知道访问的是16-BIT的FLASH,从D7-D0上读取所需要的一个BYTE的数据;
例子2:
ARM处理器需要从地址0x1读取一个BYTE
1-ARM处理器在地址线An-A0上送出信号0x1;
2–16-BITFLASH在自己的地址信号An-A0上看到的地址依然是0x0,然后将地址0x0对应的16-BIT数据单元输出到D15-D0上;
3–ARM处理器知道访问的是16-BIT的FLASH,从D15-D8上读取所需要的一个BYTE的数据;
4。
从软件角度来看ARM处理器和NORFLASH的连接
在上一个小节里,我们简单了解了ARM处理器和FLASH的硬件连接。
在这个小节里面,我们从软件的角度来理解ARM处理器和FLASH的连接。
对于8-BIT的FLASH的连接,很好理解,因为ARM处理器和8-BITFLASH的每个地址对应的都是一个BYTE的数据单元。
所以地址连接毫无疑问是一一对应的。
如果ARM处理器连接的是16-BIT的处理器,因为ARM处理器的每个地址对应的是一个BYTE的数据单元,而16-BITFLASH的每个地址对应的是一个HALF-WORD的16-BIT的数据单元。
所以,也毫无疑问,ARM处理器访问16-BIT处理器的时候,地址肯定是要错开一位的。
在写FLASH驱动的时候,我们不需要知道地址错位是由硬件实现的,还是是通过设置ARM处理器内部的寄存器来实现的,只需要记住2点:
1–ARM处理器访问8-BITFLASH的时候,地址是一一对应的;
2–ARM处理器访问16-BITFLASH的时候,地址肯定是错位的。
这一点对理解后面的例子会很有帮助。
5。
8-BITFLASH烧写驱动实例-HY29F040
HY29F040是现代公司的一款8-BIT的NORFLASH。
在这个小节里,我们以这个芯片为例子,介绍如何对8-BITNORFLASH进行操作。
HY29F040的容量为512K-BYTE,总共包括8个SECTOR,每个SECTOR的容量是64K-BYTE。
该芯片支持SECTOR擦除,整片擦除和以BYTE为基本单位的写操作。
HY29F040的命令定义如表-1所示。
下面,我们来看看如何实现基本的擦除和编程操作。
在本节后面的描述中,我们使用了下面的2个定义:
U32sysbase; //该变量用来表示FLASH的起始地址
#defineSysADDR8(sysbase,offset) ((volatileU8*)(sysbase)+(offset)) //用来方便对指定的FALSH地址进行操作
先解释一下SysAddr8的定义。
这个宏定义了一个BYTE(8-BIT)指针,其地址为(sysbase+offset)。
假设FLASH的起始地址为0x10000000,如果要将0xAB写到FLASH的第一个BYTE中去,可以用下面的代码:
*SysAddr8(0x10000000,0x1)=0xAB;
注意:
在本节后面的描述中,SYSBASE代表的是FLASH的起始地址,而SysAddr8中的OFFSET则代表了相对于FLASH起始地址的BYTE偏移量。
OFFSET也是8-BITFLASH在自己的地址信号An-A0上看到的地址。
整片擦除操作
整片擦除操作共需要6个周期的总线写操作
1–将0xAA写到FLASH地址0x5555
2–将0x55写到FLASH地址0x2AAA
3–将0x80写到FLASH地址0x5555
4–将0xAA写到FLASH地址0x5555
5–将0x55写到FLASH地址0x2AAA
6–将0x10写到FLASH地址0x5555
对应的代码:
*SysAddr8(sysbase,0x5555)=0xAA; //将值0xAA写到FLASH地址0x5555
*SysAddr8(sysbase,0x2AAA)=0x55; //将值0x55写到FLASH地址0x2AAA
*SysAddr8(sysbase,0x5555)=0x80; //将值0x80写到FLASH地址0x5555
*SysAddr8(sysbase,0x5555)=0xAA; //将值0xAA写到FLASH地址0x5555
*SysAddr8(sysbase,0x2AAA)=0x55; //将值0x55写到FLASH地址0x2AAA
*SysAddr8(sysbase,0x5555)=0x10; //将值0x10写到FLASH地址0x5555
SECTOR擦除操作
SECTOR的擦除操作共需要6个周期的总线写操作
1–将0xAA写到FLASH地址0x5555
2–将0x55写到FLASH地址0x2AAA
3–将0x80写到FLASH地址0x5555
4–将0xAA写到FLASH地址0x5555
5–将0x55写到FLASH地址0x2AAA
6–将0x30写到要擦除的SECTOR对应的地址
对应的代码:
*SysAddr8(sysbase,0x5555)=0xAA; //将值0xAA写到FLASH地址0x5555
*SysAddr8(sysbase,0x2AAA)=0x55; //将值0x55写到FLASH地址0x2AAA
*SysAddr8(sysbase,0x5555)=0x80; //将值0x80写到FLASH地址0x5555
*SysAddr8(sysbase,0x5555)=0xAA; //将值0xAA写到FLASH地址0x5555
*SysAddr8(sysbase,0x2AAA)=0x55; //将值0x55写到FLASH地址0x2AAA
*SysAddr8(sysbase,addr)=0x30; //将值0x30写到要擦除的SECTOR对应的地址
BYTE编程操作
写一个BYTE的数据到FLASH中去,需要4个周期的总线写操作
1–将0xAA写到FLASH地址0x5555
2–将0x55写到FLASH地址0x2AAA
3–将0xA0写到FLASH地址0x5555
4–将编程数据(BYTE)写到对应的编程地址上去
对应的代码:
*SysAddr8(sysbase,0x5555)=0xAA; //将值0xAA写到FLASH地址0x5555
*SysAddr8(sysbase,0x2AAA)=0x55; //将值0x55写到FLASH地址0x2AAA
*SysAddr8(sysbase,0x5555)=0xA0; //将值0xA0写到FLASH地址0x5555
*SysAddr8(sysbase,addr)=data; //将一个BYTE的数据写到期望的地址
6。
16-BITFLASH烧写驱动实例-SST39VF160
SST39VF160是SST公司的一款16-BIT的NORFLASH。
在这个小节里,我们以SST39VF160为例子,介绍如何对16-BITNORFLASH进行操作。
对8-BITFLASH的操作很好理解,但对16-BITFLASH的操作理解起来要晦涩很多。
我尽力描述得清楚些。
SST39VF160的容量为2M-BYTE,总共包括512个SECTOR,每个SECTOR的容量是4K-BYTE。
该芯片支持SECTOR擦除,整片擦除和以HALF-WORD为基本单位的写操作。
SST39VF160的命令定义如表-2所示。
在表2中,因为所有命令都是从FLASH的角度来定义的。
所以, 所有的地址都是HALF-WORD地址,指的是16-BITFLASH在自己的地址信号An-A0上看到的地址。
在本节后面的描述中,我们使用了下面的2个定义:
U32sysbase; //该变量用来表示FLASH的起始地址
#defineSysAddr16(sysbase,offset) ((volatileU16*)(sysbase)+(offset)) //用来方便对指定的FALSH地址进行操作
SysAddr16(sysbase, offset)首先定义了一个16-BIT HALF-WORD的指针,指针的地址为sysbase,然后根据offset做个偏移操作。
因为HALF-WORD指针的地址是2个BYTE对齐的,所以每个偏移操作会使得地址加2。
最终,SysAddr16(sysbase,offset)相当于定义了一个HALF-WORD的指针,其最终地址为(sysbase + 2offset)。
在使用SysAddr16的时候,将sysbase设置成FLASH的起始地址,offset则可以理解为相对于FLASH起始地址的HALF-WORD偏移量或是偏移地址。
假设FLASH的起始地址为0x10000000,SysAddr16(0x10000000,0)指向16-BITFLASH的第0个HALF-WORD,SysAddr16(0x10000000,1指向16-BITFLASH的第1个HALF-WORD。
依次类推。
如果要将0xABCD分别写到FLASH的第0个和第1个HALF-WORD中去,可以用下面的代码:
*SysAddr16(0x10000000,0x0)=0xABCD;
*SysAddr16(0x10000000,0x1)=0xABCD;
接下来,我们分别从ARM处理器的角度和FLASH的角度来具体分析一下。
从ARM的角度来看:
假设FLASH的起始地址为0x10000000,因为ARM处理器知道FLASH的地址空间为0x10000000~(0x10000000+FLASH容量– 1),所以在对这个地址空间进行访问的时候,会设置好FLASH的片选信号,并将低位的地址输出到地址信号上。
以*SysAddr16(0x10000000,0x1)=0xABCD为例。
从ARM处理器的角度来看,该操作是把0xABCD写到地址0x10000002上去。
所以ARM处理器最终会在它的地址信号An-A0输出地址0x2,同时会在D15-D0上输出0xABCD。
从FLASH的角度来看:
还是以 *SysAddr16(0x10000000,0x1)=0xABCD为例,FLASH看到的地址是多少呢?
接着分析。
ARM处理器在执行操作的时候,会设置好相应的FLASH片选使能信号,并在ARM的地址信号An-A0上输出0x2。
因为ARM和16-BITFLASH的地址信号的连接是错开一位的,所以,FLASH最终在自己的地址An-A0上看到的信号是0x1,相当于将ARM
处理器输出的地址往右做了一个移位操作,刚好对应的是FLASH的第1个HALF-WORD。
同时,FLASH会在自己的D15-D0上看到数据0xABCD。
通过上面的分析,我们知道SysAddr16中指定的offset的值就是16-BITFLASH在自己的地址An-A0上看到的值。
所以,我们可以很方便的通过SysAddr16(sysbase,offset)对FLASH进行操作,其中sysbase代表FLASH起始地址,offset则代表了FLASH的第几个HALF-WORD(HALF-WORD偏移量或偏移地址)。
注意:
1。
在本节后面的描述中,SysAddr16中的SYSBASE代表的是FLASH的起始地址,而SysAddr16中的OFFSET则代表了相对于FLASH起始地址的HALF-WORD偏移量或偏移地址。
OFFSET的值也是16-BITFLASH在自己的地址信号An-A0上看到的值。
2。
在SST39VF160的命令定义中,所有的地址都是针对FLASH的HALF-WORD地址,指的是在FLASH自己的地址信号An-A0上看到的地址。
整片擦除操作
整片擦除操作共需要6个周期的总线写操作
1–将0x00AA写到FLASHHALF-WORD地址0x5555
2–将0x0055写到FLASHHALF-WORD地址0x2AAA
3–将0x0080写到FLASHHALF-WORD地址0x5555
4–将0x00AA写到FLASHHALF-WORD地址0x5555
5–将0x0055写到FLASHHALF-WORD地址0x2AAA
6–将0x0010写到FLASHHALF-WORD地址0x5555
对应的代码:
*SysAddr16(sysbase,0x5555)=0x00AA; //将值0x00AA写到FLASHHALF-WORD地址0x5555
*SysAddr16(sysbase,0x2AAA)=0x0055; //将值0x0055写到FLASHHALF-WORD地址0x2AAA
*SysAddr16(sysbase,0x5555)=0x0080; //将值0x0080写到FLASHHALF-WORD地址0x5555
*SysAddr16(sysbase,0x5555)=0x00AA; //将值0x00AA写到FLASHHALF-WORD地址0x5555
*SysAddr16(sysbase,0x2AAA)=0x0055; //将值0x0055写到FLASHHALF-WORD地址0x2AAA
*SysAddr16(sysbase,0x5555)=0x0010; //将值0x0010写到FLASHHALF-WORD地址0x5555
SECTOR擦除操作
SECTOR的擦除操作共需要6个周期的总线写操作
1–将0x00AA写到FLASHHALF-WORD地址0x5555
2–将0x0055写到FLASHHALF-WORD地址0x2AAA
3–将0x0080写到FLASHHALF-WORD地址0x5555
4–将0x00AA写到FLASHHALF-WORD地址0x5555
5–将0x0055写到FLASHHALF-WORD地址0x2AAA
6–将0x0030写到要擦除的SECTOR对应的HALF-WORD地址
对应的代码:
*SysAddr16(sysbase,