电子说
步骤1:什么是SPI闪存?
我要去痛苦地快速解释下一部分。我在英特尔的第一份工作是在1993年的闪存组中,自那时起20年来,这项技术发生了很大变化,但有些概念仍然保持一致。
闪存是一种非易失性存储基于MOSFET技术的存储器。非易失性意味着器件在未上电时会保留其值。
MOSFET
如果您不熟悉MOSFET晶体管的工作原理,我将尝试用一句话来解释:一块硅片如果两端之间有电位差,则两端有两个端子,则不会导电,但是如果您将另一块金属粘贴在该平板的顶部,并将电介质夹在中间,然后在该金属上施加电压它会产生一个场,电流可以在两个终端之间流动。端子称为源极和漏极,金属称为栅极。这是一个超简单的解释,推论了50年的量子物理学,但从迈克尔·法拉迪(Michael Farady)的角度来看,这是合理可行的。
闪光灯晶体管
闪存通过将一堆电荷载流子喷射到栅极和衬底之间的电介质上来进行操作。这称为编程,通常使用更高的电压。它实际上损坏了材料,在100k程序循环后,门将失效。为了去除电介质中的电荷载流子,应使用同样高的电压但反向电位将载流子从栅极拉出。这称为擦除。
编程的闪存位的值为0,擦除的位的值为1,擦除的闪存字节为十六进制的0xFF。 (如今,闪存可以使用多个电压电平在每个单元中存储多个位,但这确实很复杂。)
闪存架构
通常,闪存存储器包含一个巨大的晶体管阵列,可以单独编程,但只能分组(扇区,块或整个芯片)擦除。这只是擦除电路工作方式的一个副作用:逐位擦除将需要过多的金属密度,并且并不是全部有用(实际上,擦除较大的块就可以了)。
由于对单个晶体管进行编程会由于升高高电压以及随之而来的所有控制而变慢,因此通常在页面中对闪存进行编程。通常,闪存设备将具有小的SRAM页面缓冲区(256位),主机将首先快速填充数据,然后主机发出页面写入命令,并且闪存芯片在大批量作业中写入所有页面字节。该批处理电路在较大数量的位上分配启动写入延迟。提供两个或多个页面缓冲区使主机可以使用双缓冲区技术来隐藏闪存设备的写入延迟。
SPI
串行外围接口是一项了不起的发明。它是一个简单的串行接口,使用片选,时钟,数据IN和数据OUT。 SPI设备有很多种,因为它是一个非常流行的接口,并且所有SPI设备都使用一个公共库:一旦您知道如何与一个SPI设备通信,就可以与任何SPI设备通信。
SPI的优势在于它的软件简单性,代码基本上在时钟的上升沿分别将数据移入和移出DI和DO引脚。时钟由主机控制,它不需要花哨的时钟电路:只要您遵守设备的最小周期宽度要求,相位就可以像您想要的那样不对称。
FLASH SPI
闪存SPI内存简单地结合了两者的优点。请注意,SD卡使用SPI以及此分立芯片。惊喜!编程界面没有太大区别,但是实际的指令和时间有所不同。
步骤2:WinBond设备界面
上面显示的引脚排列取自WinBond数据表。
引脚1:片选(/CS,有时称为/SS,用于“串行选择”)
CS是“片选”引脚。当您想要与该设备通信时设置CS引脚,因为您可能有十几个SPI设备共享同一总线,并且您通过其CS引脚唯一地识别每个设备。 CS前面的斜线表示“低电平有效”:与该器件通信,将该引脚拉至逻辑电平零;将其从共享总线中移除,驱动逻辑1级。
引脚2:数据输出(DO)
从该引脚读取串行数据。它将连接到总线的MISO(主输入/从输出)线。通常,您以预定的顺序向SPI设备写入命令。在该序列完成后,根据序列中的指令,然后从DO引脚读取数据。
引脚3:写保护(/WP)
此引脚禁用写入。有时您会看到连接到此引脚的跳线,以便对编程/擦除机制提供非常严格的控制:如果设置为低电平,则无法对器件进行编程或擦除。我通常将它硬连接到Vdd并允许我的软件通过串行命令控制写入启用/禁用(稍后我们将讨论)。
编辑(2016-12-16)感谢用户velsoft捕获一个错字:我把极性混淆了。
引脚4:接地
这只是接地引脚。
引脚5:数据输入(DI)
这是输入串行引脚。它将连接到总线的MOSI(主输出/从输入)线。主机系统将命令和数据写入该引脚。
引脚6:时钟(CLK)
时钟引脚决定数据位的传输方式在DI和DO引脚上。 DI/DO引脚在时钟引脚的上升沿上采样。
引脚7:保持(/HD)
我从没用过pin,但它允许主机设备暂停正在进行的任何事务。您可能永远不需要使用该引脚,因此我将其连接到VCC(低电平有效)。
引脚8:VCC
源电压。
步骤3:如何读取时序图
现在我已经解释了flash,SPI,以及SPI闪存器件的具体实现,接下来需要了解的是通信时序图*。时序图解释了引脚上数据的排序,以向器件发出指令。每个SPI设备都响应其自己的指令集(例如,闪存设备将具有读或擦除指令),时序图是该指令的概念行为与执行该指令的实际硬件协议之间的链接。 p》
在本节的图表中,我复制了数据手册中的芯片擦除时序图,因为这是最容易理解的。
底部轴是时间,垂直轴代表四个SPI引脚,随着时间的推移,序列数据应出现在它们上以执行指令。注意:“高阻抗”意味着您可以忽略该信号(它被驱动为非0或1,但是电阻极高,因此实际上是开路)。两条线出现的情况(如DI),简单表示某种转变正在发生但未知;单行表示存在特定的高值或低值。
让我们从左到右,从上到下查看图表。
为了与任何SPI设备通信,必须将芯片选择置于高电平然后驱动为低电平(记住/CS表示低电平有效)。当/CS变为低电平时,请注意,图中的时钟已非常明确地绘制为显示八个阶段。这意味着您必须将时钟脉冲八次,每位一次。在时钟频闪时,数据从高到低变为高。我认为DI图是错误的,因为如果你在每个时钟的上升沿画一条垂直线并计算这些点的DI的二进制值,你应该得到值11000111或0xC7。这是告诉芯片擦除自身的指令。一旦将芯片选择拉高,内部电路将开始执行0xC7/芯片擦除功能。该指令大约需要1到2秒的时间。
请记住,您实际上不需要将时钟引脚切换8次以发送8位字节,SPI库会执行此操作当你使用函数SPI.transfer()时为你。您仍然需要使用digitalWrite()手动驱动/CS,但SCK,MOSI和MISO都由SPI函数处理。
您将在源代码中注意到一个名为“not_busy()”的函数。该功能不断发出“读控制寄存器#1”并检查位0,指示内部操作是否已完成,闪存不忙。此操作的时序与数据表的图9.2.8相符。
*注意我并不是指电气时序图,它向纳秒级解释了内部数字逻辑的建立和保持时间;我所指的图是忽略纳秒并描述逻辑事件序列的逻辑图。 SPI接口的实际电气时序由Arduino SPI库处理。老实说,该代码不是很复杂,如果您要针对一种特定的设备进行设计,则可以进一步简化该代码。
步骤4:使用Level-Shifters与Arduino Uno接口
Arduino Uno的数字输出分别以逻辑低电平和高电平传输0V和5V。 WinBond闪存芯片仅在2.7V至3.6V之间工作。每当不同电压平面上的逻辑电路需要通信时,我们都必须使用电平转换器。
电平转换器最简单的形式是一个简单的齐纳二极管钳位。世界上还有许多其他类型的电平移位器,有些更快,有些用功率更少,齐纳钳方法快速简便。
所有二极管都具有反向击穿电压,此时它们开始进行。齐纳二极管专门设计用于在精细调谐的电压下击穿。就我而言,我将3.3V齐纳二极管与每个芯片的数字输入并联(参见原理图)。 (至于其他四个引脚,地是0V,Uno板为VCC提供3.3V电源,所以这些引脚不需要二极管,我硬连线/WP和/HOLD到3.3V Vcc。) 》更新:我忘了将330欧姆电阻与Uno驱动器的输出串联。通常,如果您将Uno的数字输出连接到另一台设备的数字输入,则只需一条简单的电线就可以了(因为您将一个数字逻辑信号连接到另一个,请参见ATmega328数据手册的第13.1节“等效于I/O引脚”原理图“)。但是由于输出路径现在通过齐纳二极管分支,因此您需要一个电阻来限制由Uno/ATmega芯片的逻辑输出驱动的最大电流。如果没有电阻,这条接地路径可能会超过器件的最大输出电流。雷,那会不好。
现在,每当Uno将5V逻辑高电平驱动到/CS引脚时,齐纳二极管就会切换到击穿模式,从而将电压钳位到3.3V,因此保护这些闪存芯片的输入逻辑。
使用这些夹具,我将Arduino Uno的数字输出引脚10(SS)连接到/CS,引脚11(MOSI)连接到DI,引脚12(MISO)连接到/CS。 DO和引脚13(SCK)到CLK。 (请注意,Atmega328的引脚与Uno的引脚不同,例如,Atmega引脚#19是Uno引脚#13。)SPI软件库假定引脚10 = SS,等等。
第5步:代码代码代码!
我写了一个草图,允许我通过串行监视器(或什至通过串行TTY通信)与Uno通信。一个Unix提示符,你可以看到)。这是一种调试新硬件的有用方法,因为我可以交互式发出命令。
“serialEvent()”函数是一个内置的回调函数,只要在默认的Serial对象上发生某些事情就会调用它。我使用此回调来构造命令字符串并设置一个布尔标志(当回调从流中读为分号“;”时,字符串的逐字节构造完成;我使用它代替换行符,因为没有办法从串行监视器发出换行符)。当回调构造字符串并设置标志时,“loop()”函数执行解码器。解码器根据命令字符串确定调用哪个函数,并解析命令字符串中的任何其他参数,并调用该函数。
每个函数本质上是WinBond的低级实现的包装器SPI功能时序图。我使用了一个包装器,以便低级函数保持通用:我可以在其他草图中使用简单的剪切和粘贴再次使用它们。此外,包装器会向用户输出一些反馈,这对于调试非常有用。
上面的屏幕截图显示了与串行监视器的交互式会话。我发出了四个命令,“get_jedec_id;”,“read_page 0;”,“write_byte 0 2 8;”和“read_page 0;”你实际上没有看到命令(串行监视器没有回声,我没有打印确切的命令。.我可能应该有),但你确实看到了响应。当我读/写/读第0页时应该最清楚。“read_page;”命令只是转储指定的页面(十进制)。 “write_byte;”函数有点奇怪,因为参数指定页码,该页的偏移量和字节。由于16位Atmega中没有本机32位寄存器,因此我不打算进行逻辑到物理转换,但您需要在某些时候考虑这种转换。无论如何,请注意页面零的第三个字节现在是“08h”。
我本可以发出“chip_erase;”然后“read_page 0;”为了说明擦除周期,但希望你得到图片。
低级函数以“_”开头,并命名为“_read_page”或“_write_page”或“_erase_chip”。这些函数明确地排序了数据表时序图中的SPI命令。每个函数都以调用“not_busy()”结束,以防止执行在芯片完成内部操作之前继续执行。
编辑(2014年3月11日):_read_page低级函数出现问题,我忘了在功能开始时将CS拉高,然后将其拉高,就像其他功能一样。这意味着如果_read_page是您调用的第一个函数,则CS可能尚未为高,因此如果没有有效的/CS 1-》 0转换,_read_page将无法正常运行,第一次调用它。第二次可以正常工作,因为它将/CS保留为1。小但烦人的错误。
步骤6:使用TTY下载数据
此Instructable的真正原因是演示如何将整个闪存下载到单个文件中。为此,我使用了Unix函数“ tail -f”和重定向。
Unix函数“ tail”将打印文本文件的最后10行。当给定参数“-f”时,“tail”将保持连接到重定向,直到它捕获SIGINT(例如,Ctrl-C)。
此屏幕截图中打开了三个窗口:Arduino IDE左侧是串行监视器,在右上方,而OSX POSIX终端在右下方。在OSX/POSIX平台上,Uno的USB控制器显示为/dev/tty设备,在这种情况下为“/dev/tty.usbmodem1411”。我将“tail -f”连接到此设备并将输出重定向到文件。
然后我发出了“read_page 0;”在串口监视器中命令,并且输出通过“尾”发送,因为它连接到TTY的输出,然后发送到文件。然后我用“ cat”文件来证明已捕获了串行流。
现在,我要转储整个IREIRE闪存芯片所需要做的就是在终端提示符下键入以下内容:
%tail -f/dev/tty.usbmodem1411》 1MB_of_flash.txt
然后在“串行监视器”窗口中键入以下内容:
read_all_pages;
然后键入CTRL -C在终端窗口中停止“尾部”过程。
完成并完成!这就是为什么Unix比任何其他操作系统都要优越得多,恕我直言。
第7步:结论
这是期待已久的数据记录器Instructables的续集。我已经答应了一种即将采用的方法将数据从闪存芯片中拉出来,就在这里。
我希望你发现这个Instructable非常有用:我所知道的99%是通过阅读这样的东西来学习的。其他人花时间写的网,我非常感谢他们的努力。
责任编辑:wv
全部0条评论
快来发表一下你的评论吧 !