NorFlash学习
 2015.11.25    |      嵌入式学习    |     AilsonJack    |     暂无评论    |     1353 views
By: Ailson Jack
Date: 2015-11-24
个人博客: http://www.only2fire.com/
<p style="text-indent: 2em;">最近由于要用到NoFlash因此就学习了一下,发现NorFlash的操作和使用都还是比较简单的,这里给大家分享一篇关于NorFlash操作的文章,比较通俗易懂,一看就会<img src="/UEditor/dialogs/emotion/images/face/i_f01.gif"/>,这里我整理了多位网友的讲解,谢谢这些网友!以下进入正文吧。</p><p class="artical_littlestyle1">1、NorFlash的简单介绍</p><p style="text-indent: 2em;">NORFLASH是Intel在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线总线的供应商,并允许随机存取存储器存储器的供应商上的任何区域,而且它可以忍受一万次到一百万次抹写循环,是早期的可移除式闪存储媒体的基础。</p><p style="text-indent: 2em;">NORFLASH是很常见的一种存储芯片,数据掉电不会丢失。NORFLASH支持ExecuteOnChip,即程序可以直接在FLASH片内执行(这意味着存储在NORFLASH上的程序不需要复制到RAM就可以直接运行)。这点和NANDFLASH不一样。因此,在嵌入式系统中,NORFLASH很适合作为启动程序的存储介质。NORFLASH的读取和RAM很类似(只要能够提供数据的地址,数据总线就能够正确的给出数据),但不可以直接进行写操作。对NORFLASH的写操作需要遵循特定的命令序列,最终由芯片内部的控制单元完成写操作。</p><p style="text-indent: 2em;">谈到Nor Flash通常就会涉及到CFI(Common Flash Interface)接口,一般Nor Flash都支持发命令来读取厂家ID和设备ID等基本信息,但并不是所有的Nor Flash都支持发命令来获取和芯片本身容量大小、扇区数、擦除块大小等信息。为了让将来的Nor Flash兼容性更好,引进了CFI接口,将芯片有关的信息都写入芯片内部,通过CFI命令就可以获取这些信息。</p><p style="text-indent: 2em;">从支持的最小访问单元来看,NORFLASH一般分为8位的和16位的(当然,也有很多NORFLASH芯片同时支持8位模式和是16位模式,具体的工作模式通过特定的管脚进行选择)。</p><p style="text-indent: 2em;">对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。</p><p style="text-indent: 2em;">FLASH一般都分为很多个SECTOR,每个SECTOR包括一定数量的存储单元。对有些大容量的FLASH,还分为不同的BANK,每个BANK包括一定数目的SECTOR。FLASH的擦除操作一般都是以SECTOR,BANK或是整片FLASH为单位的。在对FLASH进行写操作的时候,每个BIT可以通过编程由1变为0,但不可以由0修改为1。为了保证写操作的正确性,在执行写操作前,都要执行擦除操作。擦除操作会把FLASH的一个SECTOR,一个BANK或是整片FLASH的值全修改为0xFF。这样,写操作就可以正确完成了。由于NORFLASH没有本地坏区管理,所以一旦存储区块发生毁损,软件或驱动程序必须接手这个问题,否则可能会导致设备发生异常。在解锁、抹除或写入NORFLASH区块时,特殊的指令会先写入已绘测的记忆区的第一页(Page)。接着快闪记忆芯片会提供可用的指令清单给实体驱动程序,而这些指令是由一般性闪存接口(Common Flash Interface,CFI)所界定的。与用于随机存取的ROM不同,NORFLASH也可以用在存储设备上;不过与NANDFLASH相比,NORFLASH的写入速度一般来说会慢很多。</p><p class="artical_littlestyle2">2、说明</p><p style="text-indent: 2em;">以下内容,如无特别说明,处理器指的是ARM处理器,FLASH指的都是NORFLASH。另外,BYTE指的是8-BIT的数据单元,HALF-WORD代表的是16-BIT的数据单元,而WORD则代表了32-BIT的数据单元。</p><p style="text-indent: 2em;">ARM可以说是目前最流行的32位嵌入式处理器。在这里只提一下ARM处理器的寻址,为后面做个展垫。从处理器的角度来看,系统中每个地址对应的是一个BYTE的数据单元。这和很多别的处理器都是一样的。</p><p class="artical_littlestyle3">3、处理器和NORFLASH的硬件连接</p><p style="text-indent: 2em;">从前面的先容,我们知道从处理器的角度来看,每个地址对应的是一个BYTE的数据单元。而NORFLASH的每个地址有可能对应的是一个BYTE的数据单元,也有可能对应的是一个HALF-WORD的数据单元。所以在硬件设计中,连接ARM处理器和NORFLASH时,必须根据实际情况对地址信号做特别的处理。</p><p style="text-indent: 2em;">假如ARM处理器外部扩展的是8-BIT的NORFLASH,数据线和地址线的连接应该如图1所示。从图中我们可以看到,处理器的数据信号D0-D7和FLASH的数据信号D0-D7是逐一对应连接的,处理器的地址信号A0-An和NORFLASH的地址信号A0-An也是逐一对应连接的。</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.08.21/1534844620874077.jpg" onclick="preview_image(&#39;/uploads/AilsonJack/2018.08.21/1534844620874077.jpg&#39;)"/></p><p style="text-indent: 2em;">假如ARM处理器外部扩展的是16-BIT的NORFLASH,地址线必须要错位连接。图2给了一个ARM处理器和16-BITNORFLASH的连接示意图。如图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来说就被屏蔽掉了。</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.08.21/1534844620917165.jpg" onclick="preview_image(&#39;/uploads/AilsonJack/2018.08.21/1534844620917165.jpg&#39;)"/></p><p style="text-indent: 2em;">补充说明:</p><p style="text-indent: 2em;">一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的FLASH的位宽(8-BIT/16-BIT/32-BIT)。这样,处理器才知道在访问的时候如何从FLASH正确的读取数据;</p><p style="text-indent: 2em;">有些ARM处理器内部可以设置地址的错位。对于支持软件选择地址错位的处理器,在连接16-BITFLASH的时候,硬件上可以不需要把地址线错位。读者设计的时候,请参考MCU的数据手册,以手册为准,以免造成不必要的麻烦;</p><p style="text-indent: 2em;">假如处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。</p><p style="text-indent: 2em;">上面的描述可能比较抽象,下面让我们来看2个ARM处理器访问16-BITFLASH的例子:</p><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">例1:ARM处理器需要从地址0x0读取一个BYTE</span></p><p style="text-indent: 2em;">a、ARM处理器在地址线An-A0上送出信号0x0;<br/></p><p style="text-indent: 2em;">b、16-BITFLASH在自己的地址信号An-A0上看到的地址是0x0,然后将地址0x0对应的16-BIT数据单元输出到D15-D0上;</p><p style="text-indent: 2em;">c、ARM处理器知道访问的是16-BIT的FLASH,从D7-D0上读取所需要的一个BYTE的数据。</p><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">例2:ARM处理器需要从地址0x1读取一个BYTE</span></p><p style="text-indent: 2em;">a、ARM处理器在地址线An-A0上送出信号0x1;</p><p style="text-indent: 2em;">b、16-BITFLASH在自己的地址信号An-A0上看到的地址依然是0x0,然后将地址0x0对应的16-BIT数据单元输出到D15-D0上;</p><p style="text-indent: 2em;">c、ARM处理器知道访问的是16-BIT的FLASH,从D15-D8上读取所需要的一个BYTE的数据。</p><p class="artical_littlestyle4">4、从软件角度来看ARM处理器和NORFLASH的连接</p><p style="text-indent: 2em;">从软件的角度来理解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点:</p><p style="text-indent: 2em;">a、ARM处理器访问8-BITFLASH的时候,地址是逐一对应的;</p><p style="text-indent: 2em;">b、ARM处理器访问16-BITFLASH的时候,地址肯定是错位的。</p><p class="artical_littlestyle1">5、8-BITFLASH烧写驱动实例-HY29F040</p><p style="text-indent: 2em;">HY29F040是现代公司的一款8-BIT的NORFLASH。在这个小节里,我们以这个芯片为例子,讲述如何对8-BITNORFLASH进行操作。</p><p style="text-indent: 2em;">HY29F040的容量为512K-BYTE,总共包括8个SECTOR,每个SECTOR的容量是64K-BYTE。该芯片支持SECTOR擦除,整片擦除和以BYTE为基本单位的写操纵。HY29F040的命令定义如表-1所示。</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.08.21/1534844620249626.jpg" onclick="preview_image(&#39;/uploads/AilsonJack/2018.08.21/1534844620249626.jpg&#39;)"/></p><p style="text-indent: 2em;">下面,我们来看看如何实现基本的擦除和编程操作。在本节后面的描述中,我们使用了下面的2个定义:</p><pre class="brush:cpp;toolbar:false PrismJs">U32&nbsp;sysbase;//该变量用来表示FLASH的起始地址 //用来方便对指定的FALSH地址进行操作 #define&nbsp;SysAddr8(sysbase,offset)((volatileU8*)(sysbase)+(offset))</pre><p style="text-indent: 2em;">SysAddr16(sysbase,offset)首先定义了一个16-BITHALF-WORD的指针,指针的地址为sysbase,然后根据offset做偏移操作。由于HALF-WORD指针的地址是2个BYTE对齐的,所以每个偏移操纵会使得地址加2。最终,SysAddr16(sysbase,offset)相当于定义了一个HALF-WORD的指针,其最终地址为(sysbase+2*offset)。在使用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中,可以用下面的代码:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(0x10000000,0x0)&nbsp;=&nbsp;0xABCD; *SysAddr16(0x10000000,0x1)&nbsp;=&nbsp;0xABCD;</pre><p style="text-indent: 2em;">接下来,我们分别从ARM处理器的角度和FLASH的角度来具体分析一下。</p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">从ARM的角度来看:</span></p><p style="text-indent: 2em;">假设FLASH的起始地址为0x10000000,由于ARM处理器知道FLASH的地址空间为0x10000000~(0x10000000+FLASH容量–1),所以在对这个地址空间进行访问的时候,会设置好FLASH的片选信号,并将低位的地址输出到地址信号上。以*SysAddr16(0x10000000,0x1)=0xABCD为例。从ARM处理器的角度来看,该操作是把0xABCD写到地址0x10000002上。所以ARM处理器最终会在它的地址信号An-A0输出地址0x2,同时会在D15-D0上输出0xABCD。</p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">从FLASH的角度来看:</span></p><p style="text-indent: 2em;">还是以*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。</p><p style="text-indent: 2em;">通过上面的分析,我们知道<span style="color: rgb(255, 0, 0);">SysAddr16中指定的offset的值就是16-BITFLASH在自己的地址An-A0上看到的值。所以,我们可以很方便的通过SysAddr16(sysbase,offset)对FLASH进行操纵,其中sysbase代表FLASH起始地址,offset则代表了FLASH的第几个HALF-WORD(HALF-WORD偏移量或偏移地址)</span>。</p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">注意</span>:在本节后面的描述中,SysAddr16中的SYSBASE代表的是FLASH的起始地址,而SysAddr16中的OFFSET则代表了相对于FLASH起始地址的HALF-WORD偏移量或偏移地址。OFFSET的值也是16-BITFLASH在自己的地址信号An-A0上看到的值;</p><p style="text-indent: 2em;">在SST39VF160的命令定义中,所有的地址都是针对FLASH的HALF-WORD地址,指的是在FLASH自己的地址信号An-A0上看到的地址。</p><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">(1)、整片擦除操作</span></p><p style="text-indent: 2em;">整片擦除操作共需要6个周期的总线写操作:</p><p style="text-indent: 2em;">a、将0x00AA写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">b、将0x0055写到FLASHHALF-WORD地址0x2AAA;</p><p style="text-indent: 2em;">c、将0x0080写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">d、将0x00AA写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">e、将0x0055写到FLASHHALF-WORD地址0x2AAA;</p><p style="text-indent: 2em;">f、将0x0010写到FLASHHALF-WORD地址0x5555。</p><p style="text-indent: 2em;">对应的代码:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00AA;//将值0x00AA写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x2AAA)&nbsp;=&nbsp;0x0055;//将值0x0055写到FLASHHALF-WORD地址0x2AAA *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x0080;//将值0x0080写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00AA;//将值0x00AA写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x2AAA)&nbsp;=&nbsp;0x0055;//将值0x0055写到FLASHHALF-WORD地址0x2AAA *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x0010;//将值0x0010写到FLASHHALF-WORD地址0x5555</pre><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">(2)、SECTOR擦除操作</span></p><p style="text-indent: 2em;">SECTOR的擦除操作共需要6个周期的总线写操作:</p><p style="text-indent: 2em;">a、将0x00AA写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">b、将0x0055写到FLASHHALF-WORD地址0x2AAA;</p><p style="text-indent: 2em;">c、将0x0080写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">d、将0x00AA写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">e、将0x0055写到FLASHHALF-WORD地址0x2AAA;</p><p style="text-indent: 2em;">f、将0x0030写到要擦除的SECTOR对应的HALF-WORD地址。</p><p style="text-indent: 2em;">对应的代码:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00AA;//将值0x00AA写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x2AAA)&nbsp;=&nbsp;0x0055;//将值0x0055写到FLASHHALF-WORD地址0x2AAA *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x0080;//将值0x0080写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00AA;//将值0x00AA写到FLASHHALF-WORD地址0x5555 *SysAddr16(sysbase,0x2AAA)&nbsp;=&nbsp;0x0055;//将值0x0055写到FLASHHALF-WORD地址0x2AAA *SysAddr16(sysbase,addr&gt;&gt;1)&nbsp;=&nbsp;0x0030;//将值0x0030写到要擦除的SECTOR对应的HALF-WORD地址</pre><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">注意</span>:上面的代码中第6个操作周期中的ADDR是从ARM处理器的角度来看的BYTE地址,由于在擦除的时候,用户希望指定的是从ARM的角度看到的地址,这样更方便和更直观。而在SysAddr16的宏定义中,OFFSET表示的是相对于FLASH起始地址的HALF-WORD偏移量,或是FLASH在自己的地址信号An-A0上看到的地址。所以需要执行一个右移操作,把ADDR转换成HALF-WORD地址。</p><p style="text-indent: 2em;">举例说明,SST39VF160每个SECTOR的大小是4K-BYTE。从ARM处器的角度和用户的角度来看,SECTOR-0相对于FLASH起始地址的BYTE地址是0x0;从FLASH来看SECTOR-0的HALF-WORD地址是0x0。从ARM处理器的角度和用户的角度来看,FLASHSECTOR-1相对于FLASH起始地址的BYTE地址0x1000;从FLASH来看,SECTOR-1的HALF-WORD地址应该是(0x1000&gt;&gt;1)=0x800。</p><p style="text-indent: 2em;">假如要擦除SECTOR-0,上面代码的第6条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x0&gt;&gt;1)&nbsp;=&nbsp;0x0030;</pre><p style="text-indent: 2em;">假如要擦除SECTOR-1,上面代码的第6条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x1000&gt;&gt;1)&nbsp;=&nbsp;0x0030;</pre><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">(3)、HALF-WORD编程操作</span></p><p style="text-indent: 2em;">写一个HALF-WORD的数据到FLASH中,需要4个周期的总线写操作:</p><p style="text-indent: 2em;">a、将0x00AA写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">b、将0x0055写到FLASHHALF-WORD地址0x2AAA;</p><p style="text-indent: 2em;">c、将0x00A0写到FLASHHALF-WORD地址0x5555;</p><p style="text-indent: 2em;">d、将编程数据(HALF-WORD)写到对应的HALF-WORD地址。</p><p style="text-indent: 2em;">对应的代码:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00AA;//将值0x00AA写到FLASH地址0x5555 *SysAddr16(sysbase,0x2AAA)&nbsp;=&nbsp;0x0055;//将值0x0055写到FLASH地址0x2AAA *SysAddr16(sysbase,0x5555)&nbsp;=&nbsp;0x00A0;//将值0x00A0写到FLASH地址0x5555 *SysAddr16(sysbase,addr&gt;&gt;1)&nbsp;=&nbsp;data;//将数据写到对应的HALF-WORD地址</pre><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">注意</span>:上面的代码中第4个操作周期中的ADDR是从ARM处理器的角度来看的BYTE地址,由于在执行写操作的时候,用户希望指定的是从ARM的角度看到的地址,这样会更方便和更直观。而在SysAddr16的宏定义中,OFFSET表示的是相对于FLASH起始地址的HALF-WORD偏移量。所以需要执行一个右移操纵,把它转换成HALF-WORD地址。</p><p style="text-indent: 2em;">例如要将数据0x0123写到地址0x0处,对应的是FLASH的第0个HAFL-WORD,对应的HALF-WORD地址应该是0x0,上面代码的第4条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x0&gt;&gt;1)&nbsp;=&nbsp;0x0123;</pre><p style="text-indent: 2em;">又如要将数据0x4567写到地址0x2处,对应的是FLASH的第1个HALF-WORD,对应的HALF-WORD地址应该是0x1,上面代码的第4条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x2&gt;&gt;1)&nbsp;=&nbsp;0x4567;</pre><p style="text-indent: 2em;">再如要将数据0x89AB写到地址0x4处,对应的是FLASH的第2个HALF-WORD,对应的HALF-WORD地址应该是0x2,上面代码的第4条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x4&gt;&gt;1)&nbsp;=&nbsp;0x89AB;</pre><p style="text-indent: 2em;">还如要将数据0xCDEF写到地址0x6处,对应的是FLASH的第3个HALF-WORD,对应的HALF-WORD地址应该是0x3,上面代码的第4条指令应该是:</p><pre class="brush:cpp;toolbar:false PrismJs">*SysAddr16(sysbase,0x6&gt;&gt;1)&nbsp;=&nbsp;0xCDEF;</pre><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">AM29LV160</span>的一些数据位说明:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.08.21/1534844620368795.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.08.21/1534844620368795.png&#39;)"/></p><p style="text-indent: 2em;">NorFlash(AM29LV160)提供几个数据位来确定一个写操作的状态,它们分别是:DQ2, DQ3, DQ5,DQ6,DQ7, and RY/BY# 。其中DQ7, RY/BY#引脚, 和 DQ6 中的每一个都提供了一种方法来判断一个编程或者擦除操作是否已经完成或正在进行中。实际编程中只需要使用其中的一种。<br/><span style="color: rgb(192, 0, 0);"><strong>DQ7</strong></span>:Data# Polling bit,在<span style="color: rgb(0, 112, 192);">编程过程</span>从正在编程的地址中读出的数据的DQ7为要写入数据位的补码。比如写入的数据为0x0000,即输入的DQ7为0,则在编程中读出的数据为1;当<span style="color: rgb(0, 112, 192);">编程完成</span>时读出的数据又变回输入的数据0。在<span style="color: rgb(0, 112, 192);">擦除过程</span>中DQ7输出为 0;<span style="color: rgb(0, 112, 192);">擦除完成</span>后输出为1;<span style="color: rgb(255, 0, 0);">注意读取的地址必须是擦除范围内的地址</span>。RY/BY#:高电平表示‘就绪’,低电平表示‘忙’。<br/><strong><span style="color: rgb(192, 0, 0);">DQ6</span></strong>:轮转位1(Toggle Bit1),在编程和擦除期间,读任意地址都会导致DQ6的轮转(0,1间相互变换)。当操作完成后,DQ6停止转换。<br/><strong><span style="color: rgb(192, 0, 0);">DQ2</span></strong>:轮转位2(Toggle Bit 2),当某个扇区被选中擦除时,读有效地址(地址都在擦除的扇区范围内)会导致DQ2的轮转。</p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">注意</span>:DQ2只能判断一个特定的扇区是否被选中擦除,但不能区分这个扇区是否正在擦除中或者正处于擦除暂停状态。相比之下,DQ6可以区分Nor Flash是否处于擦除中或者擦除暂停状态,但不能区分哪个扇区被选中擦除,因此需要这2个位来确定扇区和模式状态信息。<br/><strong><span style="color: rgb(192, 0, 0);">DQ5</span></strong>: 超时位(Exceeded Timing Limits),当编程或擦除操作超过了一个特定内部脉冲计数时DQ5=1,表明操作失败。当编程时把0改为1就会导致DQ5=1,因为只有擦除擦做才能把0改为1。<span style="color: rgb(255, 0, 0);">当错误发生后需要执行复位命令才能返回到读数据状态</span>。<br/><strong><span style="color: rgb(192, 0, 0);">DQ3</span></strong>:(扇区擦除计时位)Sector Erase Timer ,只在扇区擦除指令时起作用。当擦除指令真正开始工作时 DQ3=1 ,此时输入的命令(除擦除暂停命令外)都被忽略,DQ3=0 时,可以添加附加的扇区用于多扇区擦除。<br/><span style="color: rgb(0, 112, 192);">例:判断扇区擦除操作是否完成:</span><br/></p><pre class="brush:cpp;toolbar:false PrismJs">int&nbsp;stat=0; int&nbsp;result=0; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x555)&nbsp;=&nbsp;0xaa; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x2aa)&nbsp;=&nbsp;0x55; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x555)&nbsp;=&nbsp;0x80; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x555)&nbsp;=&nbsp;0xaa; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x2aa)&nbsp;=&nbsp;0x55; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;addr)&nbsp;=&nbsp;0x30; stat=0; do { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;*(NORFLASH_BASE_ADDR&nbsp;+&nbsp;addr);&nbsp;//读取该扇区首地址里面的值 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//在擦除过程中DQ7输出为&nbsp;0;擦除完成后输出为1 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(result&amp;0x80)//判断DQ7的状态 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stat&nbsp;=&nbsp;1;//擦除完成 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//当编程或擦除操作超过了一个特定内部脉冲计数时DQ5=1,表明操作失败 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(result&amp;0x20)//判断DQ5的状态 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stat&nbsp;=&nbsp;2;//擦除失败 }while(!stat);</pre><p style="text-indent: 0em;"><span style="color: rgb(0, 112, 192);">例:判断编程操作是否结束:</span></p><pre class="brush:cpp;toolbar:false PrismJs">int&nbsp;stat=0; int&nbsp;result=0; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x555)&nbsp;=&nbsp;0xaa; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x2aa)&nbsp;=&nbsp;0x55; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;0x555)&nbsp;=&nbsp;0xa0; *(NORFLASH_BASE_ADDR&nbsp;+&nbsp;addr)&nbsp;=&nbsp;data;&nbsp;//往地址addr&nbsp;写入数据 stat=0; do { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;*(NORFLASH_BASE_ADDR&nbsp;+&nbsp;addr);&nbsp;//读取数据 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((result&amp;0x80)&nbsp;==&nbsp;(data&amp;0x80))//判断DQ7的状态 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stat&nbsp;=&nbsp;1;//编程完成 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//当编程或擦除操作超过了一个特定内部脉冲计数时DQ5=1,表明操作失败 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(result&amp;0x20)//判断DQ5的状态 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result&nbsp;=&nbsp;*(NORFLASH_BASE_ADDR&nbsp;+&nbsp;addr);&nbsp;//读取数据 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if((result&amp;0x80)&nbsp;==&nbsp;(data&amp;0x80))//判断DQ7的状态 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stat&nbsp;=&nbsp;1;//编程完成 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stat&nbsp;=&nbsp;2;//编程失败 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} }while(!stat);</pre>
欢迎关注博主的公众号呀,精彩内容随时掌握:
热情邀请仔细浏览下博客中的广告,万一有对自己有用或感兴趣的呢。◕ᴗ◕。。
如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^,当然了能够赞赏博主,那就非常感谢啦!
注: 转载请注明出处,谢谢!^_^
转载请注明来源: 本文链接:  By: AilsonJack
NorFlash学习  |  说好一起走
暂无评论,要不要来个沙发
发表评论

 
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号