STM32 G070 SPI DMA循环模式接收数据偏移问题求助
SPI DMA循环模式+双缓冲首次传输偏移问题分析
硬件与软件环境
- MCU:STM32 G070CB(64MHz主频)
- 通信链路:两块PCB分别作为SPI Master/Slave,通过TI SN65LVDS系列芯片转换实现4线SPI(CSn、MISO、MOSI、SCK)通信
- 软件栈:STM32 HAL库,使用SPI DMA收发函数
- 传输逻辑:
- Master发送20字节数组(内容为0-19,最后一位为递增Tx计数器)
- Slave返回20字节数组(内容为19-0,第一位为传输计数器)
- 通信触发:通过UART同步,Slave就绪后Master才启动传输
问题现象
- Slave使用DMA普通模式时,通信完全正常
- Slave切换为DMA循环模式+双缓冲技术后:
- Master接收的数组固定偏移3-4字节,但Slave接收数据完全正常
- 复位Slave后,通信恢复同步且稳定运行
- 该现象与SPI时钟频率(1MHz8MHz)、传输触发频率(10Hz5kHz)无关
- Slave调用
HAL_SPI_TransmitReceive_DMA返回HAL_OK后,已延迟100ms才通过UART通知Master就绪
问题分析与解答
1. 为何Slave的SPI Tx DMA首次传输错过前3-4个时钟周期,Rx DMA正常,后续同步?
这是STM32 G0系列DMA循环模式+双缓冲初始化阶段的时序对齐问题:
- 配置DMA循环模式+双缓冲时,HAL库会在初始化阶段预加载第二缓冲区地址到DMA寄存器,但此时SPI Tx FIFO可能未完全就绪(比如复位后默认空状态,DMA启动时序与SPI外设时钟使能、FIFO填充存在微小延迟)。
- Slave的SPI Rx DMA由Master的SCK时钟被动触发,只要SCK到来就能立即捕获数据;但Tx DMA是主动填充FIFO,首次启动时,DMA的请求响应、缓冲区地址加载存在几个时钟周期的滞后,导致前几个SCK周期内SPI Tx FIFO为空,只能发送默认电平或旧数据,进而造成Master接收的前几个字节偏移。
- 首次传输完成后,DMA循环模式已完成缓冲区切换,后续FIFO填充时序与SPI时钟完全对齐,因此同步恢复正常。
2. 为何复位Slave后能正常同步,代码无差异?
复位操作会强制重置所有外设和DMA寄存器到默认状态,消除了初始化阶段的时序残留:
- 复位后,SPI外设、DMA控制器的所有寄存器(包括FIFO状态、缓冲区地址指针、请求标志位)都被清零,此时调用
HAL_SPI_TransmitReceive_DMA时,DMA初始化与SPI外设就绪状态完全匹配。 - 非复位情况下,若之前运行过DMA普通模式,部分DMA寄存器(比如上次传输的残留地址、请求标志位)可能未被完全清零,导致循环模式初始化时出现时序偏移;而复位后不存在这些残留状态,初始化时序完全对齐。
3. 为何仅在DMA循环模式下出现该问题,普通模式无异常?
DMA普通模式和循环模式的初始化与运行机制存在本质差异:
- 普通模式:每次传输完成后DMA自动停止,且不会预加载下一个缓冲区地址。调用
HAL_SPI_TransmitReceive_DMA时,DMA会等待SPI外设完全就绪后才开始填充FIFO,时序完全同步,不会出现滞后。 - 循环模式+双缓冲:DMA会持续运行,且初始化阶段就预加载两个缓冲区的地址,此时SPI外设的FIFO可能还未进入就绪状态(比如外设时钟刚使能,FIFO同步逻辑未稳定),导致首次传输的DMA请求响应滞后于SPI时钟,从而出现字节偏移。
内容的提问来源于stack exchange,提问作者Chris_B
相关产品推荐
相关产品推荐

