protocols - 是否可以在 STM32F3 上使用 DMA 通过 SPI 传输无限数据?
问题描述
我正在开发一种基于新协议的 RF 调制解调器,该协议具有在一帧中流式传输 96 字节的功能 - 但它们会在通信结束之前不断发送。我计划在 STM32 中使用两个 96 字节的缓冲区——在接下来的几行中,我将解释原因。
我想通过 USB-CDC 将前 96 字节帧发送到 STM32 - 然后外部调制解调器芯片将生成一个“9600bps”时钟,STM 将不得不在指定的输出引脚上逐位写入有效负载(在每个时钟的后沿脉冲)。当 STM32 注意到它已经发送了一半的 96 字节帧 - 它向 PC 发送通知以发送更多数据时 - PC 将立即通过 USB-CDC 重新填充第二个 96 字节缓冲区。当 STM32 将结束发送第一个缓冲区时 - 立即开始发送第二个缓冲区内容。当它将发送第二个缓冲区的一半时 - 如前所述,它将向 PC 请求另一个 96Byte 帧。一直这样,在 PC 发送命令停止 tx 之前。
这种传输模式 - 串行,使用“触发时钟”。这是否可以使用 DMA,我该如何设置?我想使用 DMA 来使用 USB,同时已经将数据流式传输到无线电调制解调器芯片。这是正确的方法吗?
我正在构建一个具有数据包和流功能以及数字语音的开源无线电通信系统项目。我正在为 PC 无线电调制解调器设计和电子产品。项目名为 M17,由 Wojtek SP5WWP 维护。
解决方案
回覆。一般架构。USB ACM 上的串行通信不必使用相同大小的缓冲区,也不必与 SPI 上的下游通信同步。您可以使用尽可能大的缓冲区,以便 PC 可以提前发送数据。如果 PC 提供数据的速度不够快,这将减少缓冲区下溢的机会。使用循环缓冲区并在数据包从 USB 到达时填充它。
DMA 是正确的方法。尽管人们经常说 DMA 仅用于高带宽操作,但实际上使用 DMA 可能比处理每个字节的中断更容易,即使您每秒只处理 9600 位也是如此。
STM32F3 中的 DMA 控制器有一个半传输完成(DMA_ISR 中的 HTIF)位,您可以轮询或使其生成和中断。结合传输完成状态 (TCIF) 和循环位(DMA_CCR 中的 CIRC),您可以组织双缓冲数据管道,以便传输可以与 MCU 正在执行的任何其他操作重叠。应用程序将在 HTIF 事件上重新加载 DMA 缓冲区的前半部分。当 TCIF 事件发生时,它会重新加载后半部分。它必须尽快完成,在另一半完成之前。但是,只有当您需要持续传输数据时才需要双缓冲管道,即总量大于 DMA 缓冲区的大小。停止循环 DMA 可能很棘手。我想 STM32 和外部芯片都知道要发送多少字节。在这种情况下,收到这笔款项后,
由于外部芯片生成 SPI 时钟,因此您似乎需要 STM32 中的从属 SPI。
DMA 设置起来并不难,但是,它需要多个东西才能正常工作。我假设寄存器级编程,如果您使用某种框架,您需要了解它是如何实现这些功能的。启用 SPI 时钟、SPI 引脚的 GPIO 端口和 DMA,将引脚配置为 AF。为 SPI 外设找到正确的 DMA 通道。在 SPI DMA 的情况下,您通常需要两个通道:TX 和 RX,但对于从 SPI,您可能只需要一个。配置 SPI,注意时钟极性和相位,设置为每个 TX 和/或 RX 产生一个 DMA 请求。将 DMA CPAR 通道寄存器设置为指向通道中的 SPI DR 寄存器,并对所有其他 DMA 通道寄存器进行适当的编程。启用 DMA 通道。在从机模式下启用 SPI。当 SPI 主机在 MOSI/SCK 引脚上提供时钟数据时,DMA 控制器会将它们放入内存中。当缓冲区半满和满时,通道将设置 HTIF 和 TCIF 位并生成和中断,如果你告诉它。使用这些事件来实现流控制。
推荐阅读
- python - 仅在模式(“_”)之前保留部分 'id' 字符串
- kotlin - 具有泛型和实现接口的 Kotlin 类
- api - Z3 OCaml API 递归函数
- scala - 为什么 WritableConverter 是私有的?
- javascript - 测试类方法中的静态方法调用时出现 TypeError
- go - 与卡夫卡消费者一起去频道
- ios - SQLite.swift:由于在活动搜索中点击 UITableViewCell 时强制解包 nil 值,应用程序崩溃
- php - 有没有办法抑制 get 和 set 方法的“无摘要”
- javascript - ReactJS - 在 fetch() 完成之前运行的明显代码
- javascript - 找不到名称符号