背景: 本项目旨在设计一种可以本地运行轻量化机器学习算法的通用电子鼻底层系统,选用STM32采集传感器数据以及实时控制采样流程,i.MX6ULL负责数据处理算法以及结果展示。为了传输大量数据并节约传输开销选择SPI协议传输自定义帧,使用DMA搬运数据,降低CPU占用率。

1. 异步通信导致的 DMA 数据撕裂

问题现象: 如果 Linux 端来不及读取数据,STM32 的 DMA 会直接将旧数据覆盖。当 Linux 正在读取一段缓存,而 DMA 正好在覆写同一段缓存时,会引发严重的“数据撕裂”。

解决方法:

  • 增加硬件同步握手线: 当 STM32 准备好数据后,拉高同步引脚;Linux 检测到外部中断后开始读写 SPI;DMA 发现数据被读取完毕后,拉低同步引脚。
  • 增加协议层状态标志: 如果 Linux 读写不及时,当 DMA 写完一段缓存切换区域时,若发现同步线仍为高电平,则主动修改溢出标志错误码,通知应用层丢弃脏数据。

2. 数据量不满足 Linux DMA 触发阈值

问题现象: STM32 发送的一帧数据仅有 26 字节,未达到 Linux 端 SPI DMA 的启动阈值,导致 DMA 无法正常工作。

解决方法:

  • STM32 收集 50 次采样的数据集中发送。
  • 添加空白填充,将单帧数据字节对齐为 8 或 16 的倍数。

3. STM32 的 RX OVR 溢出错误

问题现象: STM32 的 SPI 在通信时不处理 Linux 发送过来的无用数据,导致产生 OVR(溢出)错误,通信卡死。

解决方法:

  • 在 STM32 端添加 RX DMA “垃圾桶”(实际未使用)。
  • 直接配置 STM32 的 SPI 工作模式为单向接收 SPI_Direction_1Line_Tx

4. 信号完整性导致的数据异常

问题现象: Linux 接收数据异常。尝试将 Linux 的 SPI 速率从 5MHz 降为 500kHz 依然无效。

解决方法:

  • 排查物理层发现是由于杜邦线太长导致高频信号衰减/串扰。更换为短杜邦线后,5MHz 速率下数据收发恢复完美正常。

5. 设备状态同步与热插拔重载

问题现象: STM32 无法感知 Linux 何时上线。如果 Linux 尚未准备好,STM32 就拉高同步线,Linux 将永远等不到上升沿中断。此外,Linux 注销驱动重新上线后,STM32 发送的数据包状态不会更新。

解决方法:

  • 添加 Linux Ready 硬件握手线:Linux 驱动加载上线时拉高 Ready 引脚,注销驱动时拉低 Ready 引脚。STM32 依此引脚状态决定是否发起传输。

6. 上电瞬间的假时序干扰

问题现象: 初次上电加载模块后,Linux 读到的数据全都是 Received Header: 0x0000, Seq: 21930(21930 转换为十六进制正是自定义帧头 0x55AA)。

解决方法:

  • 系统上电产生的引脚电平波动被 STM32 误认为了 SPI 时序。修改初始化逻辑:必须等待 DMA 完全准备就绪后,再使能 SPI 外设。

7. dma_alloc_coherent 与 mmap 内存映射掩码报错

问题现象: 尝试通过 mmap 进行内存映射后,应用程序读取时触发内核错误。

排查过程: 初期怀疑是自己裁剪的内核与 BusyBox 构建的根文件系统不完整导致无法使用 SDMA,重新移植正点原子Yocto构建的系统后应用层依然报错。

根本原因: 挂载在 ecspi3 节点下的自定义子节点没有配置 DMA 掩码空间(dma_mask)。

解决方法: 在驱动中显式配置,使其从父节点正确继承掩码空间。