我正在开发一个音频应用程序,而不是将音频数据存储在 SD 卡(Arduino 上的Waveshield)上,而是将其存储在 SPI 闪存 IC 上,并使用 MCU、DAC 和放大器滚动我自己的电路板。
我正在使用Winbond W25Q80BVSSIG。
我非常熟悉使用AVRISP mkII或USBTiny对 AVR 进行编程,是否使用同一个程序员将数据写入闪存?在专门寻找 SPI 闪存编程器时,我一直找不到任何东西。
这个问题是对这个问题的跟进。
我正在开发一个音频应用程序,而不是将音频数据存储在 SD 卡(Arduino 上的Waveshield)上,而是将其存储在 SPI 闪存 IC 上,并使用 MCU、DAC 和放大器滚动我自己的电路板。
我正在使用Winbond W25Q80BVSSIG。
我非常熟悉使用AVRISP mkII或USBTiny对 AVR 进行编程,是否使用同一个程序员将数据写入闪存?在专门寻找 SPI 闪存编程器时,我一直找不到任何东西。
这个问题是对这个问题的跟进。
如果您只是在寻找一种使用“预加载”数据对 Winbond SPI 闪存进行编程的方法,您的微控制器在运行时会读取这些数据以供使用,那么您需要研究的是可以进行在线编程的程序员SPI Flash 芯片。这也称为系统内编程 (ISP)。
一种选择是来自 DediProg 的程序员. 如果您正确设计电路板,此 USB 连接设备可以在电路中编程。他们甚至出售可以连接到 SOW-16 封装中的适配器夹,而无需在您的板上设计单独的编程接头。DediProg 提供应用信息公告,可帮助正确设计电路使用。设计的主要策略是找到一种简单的方法来隔离 MCU 系统中的 SPI 接口驱动程序,以便它们不会干扰 SPI 编程盒中的驱动程序。最简单的方法是在 MCU 和 SPI Flash 之间的 MCU 驱动线上放置串联电阻。编程器将连接到串联电阻的 SPI 闪存侧。替代方法可以包括在驱动接口线路中添加 MUX 或模拟开关。一个更聪明的方案是添加一个“
另一个要考虑的选择是ASIX 的 USB 编程器。Presto 能够处理各种类型的 SPI 和 I 2 C 设备,包括 SPI Flash 设备。我有一个专门用于编程 Atmel MCU 和各种类型的 SPI 闪存设备的设备。它是比上述单元更具成本效益的解决方案,但不那么灵活。他们更昂贵的设备称为 Forte 能够做更多的事情,因为它有更多的目标接口引脚。
有时,无需添加编程接头即可将编程器连接到目标板可能是有益的。一个很好的解决方案是在一家名为 TagConnect 的公司定义的特殊封装中放置一小组焊盘。他们制造和销售一系列带有弹簧针的快速连接编程电缆,这些弹簧针与电路板上的特殊足迹相接合。有 6 针、10 针和 14 针版本的电缆可供选择,以适应各种应用。电缆的成本非常合理。
我敢打赌,你可以在不通过 MCU的情况下使用Bus Pirate来做到这一点……这让你可以使用 SPI、I2C 或 UART 通信直接对芯片执行一些任意的串行交互。编写“脚本”可能需要一些工作,但它可能会让你完成这项工作。
我还看到了直接通过 I2C 加载 EEPROM 的专用工具,但不是闪存,也不是专门的 SPI。
讨论有点晚了,但对于任何在搜索后阅读它的人来说......
我没有看到提到的一件事,在对 SPI 闪存芯片进行编程时绝对至关重要的是对芯片选择 (CS_) 引脚的控制。片选引脚用于标点对 SPI 闪存的命令。特别是,从 CS_high 到 CS_low 的转换必须在发布任何写操作操作码(WREN、BE、SE、PP)之前立即进行。如果在 CS_ 转换(即在 CS_ 变低之后)和发送写操作码之前有活动,写操作码通常会被忽略。
此外,SPI Flash 数据表中通常没有解释,因为它是 SPI 协议的固有部分,这也是至关重要的,即对于在 SPI 总线上传输的每个字节,一个接收一个字节作为回报。此外,除非发送一个字节,否则不能接收任何字节。
通常,用户命令的 SPI 主设备有一个发送缓冲器,它在 SPI 总线的 MOSI 线上发送字节,还有一个接收缓冲器,它从 SPI 总线的 MISO 线接收字节。
为了让任何数据出现在接收缓冲区中,一些数据必须已经发送到发送缓冲区。类似地,任何时候从发送缓冲区发送数据,数据都会出现在接收缓冲区中。
如果不注意平衡传输写入和接收读取,则将不知道接收缓冲区中会发生什么。如果接收缓冲区溢出,数据通常只是溢出和丢失。
因此,当一个人发送一个读命令时,它是一个字节的操作码和三个地址字节,一个人将首先在 SPI 主接收缓冲区中接收四个字节的“垃圾”。这四个垃圾字节对应于操作码和三个地址字节。当这些正在传输时,Flash 还不知道要读取什么,所以它只返回四个垃圾字。
在返回这四个垃圾字之后,为了在接收缓冲区中获取其他任何内容,您必须传输与您要读取的数量相等的数据量。在操作码和地址之后,发送什么都没有关系,只是将 SPI Flash 中的 Read DAta 推送到 Receive Buffer 中的填充物。
如果您没有仔细跟踪前四个返回的垃圾字,您可能会认为其中一个或多个是您返回的读取数据的一部分。
所以,为了知道你实际上从接收缓冲区得到了什么,重要的是要知道你的缓冲区的大小,知道如何判断它是空的还是满的(通常有寄存器状态位来报告这一点)并跟踪如何你传送了多少东西,你收到了多少东西。
在开始任何 SPI Flash 操作之前,最好“排空”接收 FIFO。这意味着检查接收缓冲区的状态并将其清空(通常通过执行接收缓冲区的“读取”来完成)如果它尚未为空。通常,清空(读取)已经为空的接收缓冲区并没有什么坏处。
以下信息可从 SPI 闪存数据表中的时序图中获得,但有时人们会忽略位。所有命令和数据都使用 SPI 总线发送到 SPI 闪存。读取 SPI Flash 的顺序是:
1) Start with CS_ high.
2) Bring CS_ low.
3) Issue "Read" op code to SPI Flash.
4) Issue three address bytes to SPI Flash.
5) "Receive" four garbage words in Receive Buffer.
6) Transmit as many arbitrary bytes (don't cares) as you wish to receive.
Number of transmitted bytes after address equals size of desired read.
7) Receive read data in the Receive Buffer.
8) When you've read the desired amount of data, set CS_ high to end the Read command.
If you skip this step, any additional transmissions will be interpreted as
request for more data from (a continuation of) this Read.
请注意,步骤 6 和 7 必须交错并重复,具体取决于读取的大小以及接收和发送缓冲区的大小。如果一次传输的字数超过接收缓冲区的容量,则会溢出一些数据。
为了执行页面编程或写入命令,请执行这些步骤。页面大小(通常为 256 字节)和扇区大小(通常为 64K)和相关边界是您正在使用的 SPI 闪存的属性。此信息应在 Flash 的数据表中。我将省略平衡发送和接收缓冲区的细节。
1) Start with CS_ high.
2) Change CS_ to low.
3) Transmit the Write Enable (WREN) op code.
4) Switch CS_ to high for at least one SPI Bus clock cycle. This may be tens or
hundreds of host clock cycles. All write operations do not start until CS_ goes high.
The preceding two notes apply to all the following 'CS_ to high' steps.
5) Switch CS_ to low.
6) Gadfly loop: Transmit the 'Read from Status Register' (RDSR) op code and
one more byte. Receive two bytes. First byte is garbage. Second byte is status.
Check status byte. If 'Write in Progress' (WIP) bit is set, repeat loop.
(NOTE: May also check 'Write Enable Latch' bit is set (WEL) after WIP is clear.)
7) Switch CS_ to high.
8) Switch CS_ to low.
9) Transmit Sector Erase (SE) or Bulk Erase (BE) op code. If sending SE, then follow
it with three byte address.
10) Switch CS_ to high.
11) Switch CS_ to low.
12) Gadfly loop: Spin on WIP in Status Register as above in step 6. WEL will
be unset at end.
13) Switch CS_ to high.
14) Switch CS_ to low.
15) Transmit Write Enable op code (again).
16) Switch CS_ to high.
17) Switch CS_ to low.
18) Gadfly loop: Wait on WIP bit in Status Register to clear. (WEL will be set.)
19) Transmit Page Program (PP = Write) op code followed by three address bytes.
20) Transmit up to Page Size (typically 256 bytes) of data to write. (You may allow
Receive data to simply spill over during this operation, unless your host hardware
has a problem with that.)
21) Switch CS_ to high.
22) SWitch CS_ to low.
23) Gadfly loop: Spin on WIP in the Status Register.
24) Drain Receive FIFO so that it's ready for the next user.
25) Optional: Repeat steps 13 to 24 as needed to write additional pages or
page segments.
最后,如果你的写地址不在页边界上(通常是 256 字节的倍数),并且你写了足够多的数据来跨越下一个页边界,那么应该跨越边界的数据将被写入到该页的开头。您的程序地址下降。因此,如果您尝试将三个字节写入地址 0x0FE。前两个字节将写入 0x0fe 和 0x0ff。第三个字节将被写入地址 0x000。
如果您传输的数据字节数大于页面大小,则前面的字节将被丢弃,只有最后的 256(或页面大小)字节将用于对页面进行编程。
与往常一样,对于上述任何错误、错别字、疏忽或混乱的后果,以及您如何使用它,概不负责。