今天才真正清楚 SPI 外设怎么用捏。
用以下三个典型 API 讨论
发送:HAL_SPI_Transmit_DMA(Spi, RBuf, ui32Len)
接收:HAL_SPI_Receive_DMA(Spi, RBuf, ui32Len)
收发:HAL_SPI_TransmitReceive_DMA(Spi, RBuf, ui32Len)
此前遇到过的问题
作为 spi 主机时,调用接收 api却无法真的收到数据,所以在所有接收处都用了收发 api。
因为 spi 用的比较少,功能也很单一,没有真正考虑过为啥,只当是 api 实现有些问题(因为在底层上又套了兼容层)。
今天重写 spi-flash 驱动,才又考虑这个问题。
总而言之
spi 是一种依赖时钟线边沿做移位的硬件通信,没有时钟,那么主机或者从机都不会真正发送数据。除非是自己奇怪的非标实现。
而且通常来说,只有主机允许产生时钟。
那么问题就显而易见了,起码对于 stm32,只有在发送时产生时钟,所以哪怕是仅接收数据,也要使用收发 api。
接收 api仅在作从机时有意义,或者有 GPT 说的由外部硬件产生时钟(这太恐怖了,真有这种用法吗)。
思考
spi 通信的特性似乎适用于无限地双向通信,不适合任何单向的通信。这会在作为主机尝试大批量的读时存在问题,因为发送线路依然需要占据一块相同大小的 buffer 空间。
可能是 stm32 的 hal 实现问题,但是并没有深究,可能真的遇到再说吧。
与 Copilot 的对话(SPI 时钟与接收实现讨论)日期:2026-01-21 · 参与者:stbanana(用户),Copilot(助手)stbanana如果我希望调用某 API 时也可以产生时钟,可以有这样的配置吗?如果这样也需要用收发 API,
那么当我只需要读取时,发送连续的依然需要占据一块 buffer,这或许会产生一些问题。
CopilotDone: Recommending SPI read approach可以,但要区分“有无硬件自动送时钟”和“软件显式送时钟”两种方式。结合当前的 STM32H743 + HAL 配置,结论如下:
1)纯接收也自动产生时钟?在 STM32H7 的主机模式下,默认不行。HAL_SPI_Receive_DMA() 只是开启 RX DMA,不会主动驱动 TX,所以通常不会产生时钟。某些框架/库函数可能在 RX-only 时自动送 dummy(你现在的代码没有)。2)实现可用的方式(推荐)继续用HAL_SPI_TransmitReceive_DMA(),但给一个 dummy TX buffer。这个 buffer 不必很大,也不必动态分配。可选:
常驻的 TX buffer(比如一直是0xFF 之类),按块重复发送;单字节 + DMA,关闭 TX DMA 的MemInc,重复发送同一字节。 3)“占用一块 buffer 会有问题吗?”通常不会。你已经有fspiTxBuf,纯空发 dummy 用即可。关键是 CS 期间必须持续使能时钟,所以 TX 必须存在。如果担心占用大 buffer,可以:
分块收发;把 dummy buffer 做成固定 1 byte,循环使用。 建议实现方式(与你当前代码最兼容)保持现有的TransmitReceive + dummy 方案。如果将来某 API 也自动送时钟,可把FlashSpiReceivePort() 改为内部调用SpiTransmitReceive(),
并在内部使用一个固定的 dummy buffer,不让调用者关心 TX。stbanana不,我目前的代码应用只关于spiflash,这不是一个统一通用的做法,我的spistdio旨在产生一个最为通用的用法。请不要再关注我的spiflash的prot函数包装,关注stm32的spi实现
CopilotDone: Acknowledging request and outlining response明白,我只聚焦 STM32 SPI 实现本身。下面给出思路与结论。
思路说明 STM32H7 SPI 主机模式下的时钟来源;解释HAL_SPI_Receive_DMA 在主机/从机模式下的行为差异;给出“通用 SPI stdio”层面的合理设计建议。结论在 STM32 主机模式下,时钟由主机主动产生,通常只有 TX 或全双工操作才会驱动 SCK。HAL_SPI_Receive_DMA() 在主机模式下不保证产生时钟,因此无法作为通用“只读”接口
(除非硬件/库支持 RX-only 自动时钟)——你当前配置里未体现。
在从机模式下,HAL_SPI_Receive_DMA() 很有意义,因为时钟来自外部主机。对“通用 SPI stdio”的建议保留SpiReceive() 作为 API 语义,但在主机模式下内部改为 “dummy TX + RX”(全双工),保证时钟;在从机模式下,SpiReceive() 才走真正的 RX-only;这样接口对外保持一致:语义稳定,内部根据主/从模式选择时钟策略。如果需要,我可以基于你现有的spistdio.c 框架,给出一个“主从自适应”的
SpiReceive() 实现方案(不涉及具体 spiflash 逻辑)。stbanana这似乎并没有很大的改善,请先暂时保持现状吧