欢迎光临
我们一直在努力

DMA 传输 SPI 出错?别只查 Buffer,看看数据宽度与 FIFO

摘要:SPI 开了 DMA,数据却错位、丢字节?不是 DMA 坏了,而是 SPI 的 FIFO 阈值与 DMA 数据宽度不匹配。本文解析 8 位 SPI 如何对接 32 位 DMA。


一、问题描述(现象)

**SPI 发送 0xAA, 0xBB, 0xCC, 0xDD;

DMA 接收 Buffer 里却是 0xBB, 0xAA, 0xDD, 0xCC。**

很多工程师的排查方向是:

  • DMA 地址没递增?

  • SPI 模式配错了?

  • 字节序搞反了?


  • 二、原理分析

    1. 物理模型

    SPI 外设内部有一个 FIFO(先进先出队列)。

    SPI Bus -> [FIFO (16-bit)] -> [DMA Request]

    2. 核心参数

    • SPI Data Size:8-bit 或 16-bit。

    • DMA Data Width:Byte (8), Half-Word (16), Word (32)。

    3. 反直觉真相

    即使 SPI 配置为 8-bit,DMA 仍然可能按 16-bit 搬运。

    当 SPI 的 FIFO 阈值(FTHLV)配置为 16-bit 时:

    • SPI 会等待凑齐 2 个字节才向 DMA 发起请求。

    • 如果你的 DMA 配置为 Half-Word,数据顺序会发生混乱。


    三、工程级解决方案

    方案 1:对齐数据宽度(首选)

    SPI 为 8-bit 时,DMA 必须为 Byte。

    // SPI 配置
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;

    // DMA 配置
    hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

    方案 2:处理 FIFO 阈值

    如果使用 HAL 库,注意 SPI_CR2寄存器中的 FRXTH位。

    • 8-bit 访问:FRXTH = 1(FIFO 阈值 8-bit)

    • 16-bit 访问:FRXTH = 0(FIFO 阈值 16-bit)

    HAL 库的坑:

    HAL_SPI_Receive_DMA()在某些版本里不会自动设置 FRXTH,需要手动改寄存器。

    方案 3:丢弃伪数据

    在全双工 SPI 中,读的时候必须写。

    // 错误:只读不写
    DMA_Read();

    // 正确:写 dummy byte 触发时钟
    SPI->DR = 0xFF;


    四、选型避坑建议

  • 不要混用:SPI 8-bit 绝配 DMA Byte,SPI 16-bit 绝配 DMA Half-Word。

  • Cache 一致性:如果 MCU 有 D-Cache(如 STM32F7/H7),DMA 前必须 Invalidate或 Clean。

  • 中断与 DMA:不要同时开启 SPI RXNE 中断和 DMA 接收。


  • 五、总结 Checklist

    • [ ] SPI 数据位宽与 DMA 数据宽度是否一致?

    • [ ] FIFO 阈值(FRXTH)是否配置正确?

    • [ ] 是否处理了全双工下的 Dummy Write?

    • [ ] H7 系列是否处理了 Cache 一致性?


    💡 注意区分:本文讨论的是 DMA 数据搬运错位。

    若你的问题是 SPI 频率上不去,请检查 IO 速度,详见《SPI 速度上不去?》。


    References

    • STM32 Reference Manual – SPI FIFO threshold

    • STM32 Application Note AN4031 – Using DMA with STM32

    赞(0)
    未经允许不得转载:171主机测评 » DMA 传输 SPI 出错?别只查 Buffer,看看数据宽度与 FIFO
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址