ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AD7768驱动开发实战:STM32H5 SPI+DMA帧同步采集全解析

AD7768驱动开发实战:STM32H5 SPI+DMA帧同步采集全解析 去年做一板八通道同步数据采集选了 AD7768。当时第一反应是8 通道、24bit、还支持 SPI随便找颗 STM32 都能驱动吧结果被现实狠狠打脸。数据全是乱的DRDY 信号看着就不对劲SCLK 怎么配都对不上最后翻手册翻到半夜才弄明白一件事——AD7768 的“SPI”根本就不是我们熟悉的 SPI 从机协议。这篇就把从原理、硬件到代码的完整过程写出来尤其是我踩过的那几个大坑希望能帮你少走弯路。1. 先搞清楚AD7768的“SPI”到底特殊在哪1.1 它和标准SPI从机最大的区别标准 SPI 从机的行为模式是主机发命令从机根据命令把某个寄存器或者存储地址的数据返回来。你可以随时发起一次读操作读哪个地址、读多长都由主机说了算。这套逻辑在 AD7768 上只适用于“配置寄存器”那部分功能。AD7768 的数据接口完全是另一套逻辑。它内部有 8 个 24 位转换结果寄存器每完成一次所有通道的转换这 192bit 数据就被整体推到输出端等待读取。问题是它不像标准 SPI 那样给你一个“读命令地址”而是直接要求主机在 DRDY 引脚拉低后持续送 SCLK把准备好的 192bit 数据一口气搬走。换句话说数据不是“问一句答一句”而是“货已经备好你自己来拉”。这就是“非标准”的第一层含义寄存器配置走的是 SPI 协议但数据读取走的是“帧同步串行输出”协议。如果你还按标准 SPI 的思维先发一个读寄存器命令再收数据那基本收不到任何有用的东西。1.2 数据是按“帧”给的DRDY就是帧同步信号AD7768 每完成一次全部通道的采样DRDY 引脚就会拉低一次。这个下降沿就是“新一帧数据已就绪”的硬通知。一帧数据在单 SDO 模式下等于 8 通道 × 24bit 192bit按通道 0 到通道 7 的顺序串行输出。我打一个比方ADC 内部有个打包员每到转换完成就把 8 个通道的 24bit 数据按固定顺序打成一个 192bit 的快递包然后拉低 DRDY 告诉你“包准备好了”。接下来你要在它规定的时间内把整个包收走收的过程里它按顺序把每个 bit 推到 SDO 脚上。这个机制强制要求你在软件上做到每一帧的第一个字节必须对应通道 0 的高字节。一旦中途丢了一个 bit或者一帧没读完整后面所有通道的数据就全错位了而且这种错位极难发现——数据看起来有波形但通道串了、字节边界也乱了。1.3 多SDO模式提速的路子藏在数据分配规则里AD7768 支持把 SDO 配置成 1 线、2 线、4 线、8 线。1 线就是 SDO0 一根线把所有 8 个通道的数据串行输出4 线模式下SDO0~SDO3 同时工作每根线负责 2 个通道8 线模式下每根线只负责 1 个通道。并行度上去了同样一帧数据需要的 SCLK 脉冲数就成倍下降。单线模式下一帧需要 192 个 SCLK4 线模式只需要 48 个8 线模式只需要 24 个。这对高采样率场景非常重要因为 SCLK 的需求直接决定你用多高的 SPI 时钟才能跑得动。但这里有个 MCU 端的尴尬标准 SPI 外设只有一根 MISO 线。就算 ADC 把 4 根 SDO 都给你普通 SPI 也只能接其中一根其余几根线全部闲置。也就是说多 SDO 模式仅仅在硬件上“支持”还不够MCU 还得有对应的多线接收能力这引出后面 H5 的 OCTOSPI 玩法。2. 上板之前时钟、电源、参考电压这三个雷先拆掉很多人配置全都对了数据还是乱的十有八九是硬件初始化没做到位。2.1 MCLKAD7768绝对不能没有主时钟标准 SPI 设备主机给 SCLK 就能工作。AD7768 不行它还需要一个 MCLK 主时钟来驱动内部的 Σ-Δ 调制器和数字滤波器。MCLK 决定采样率和滤波器特性SCLK 只是把转换结果搬走用的两者完全不是一回事。我第一次用 STM32 的 MCO 引脚输出 HSE 时钟给 MCLK结果采样数据波动明显偏大。后来换成低抖动有源晶振数据稳定多了。对于 24 位高动态范围的 ADCMCLK 的抖动会直接影响信噪比。HSE 分频出来的时钟通常不是为这种场景设计的如果对动态范围有要求建议直接上独立的低相噪有源晶振。注意 MCLK 频率和你想要的输出数据速率ODR不是随意定的要按数据手册里的分频表和抽取倍数来配置。我实际用了 MCLK 16.384MHz再通过 CONFIG 寄存器配置 FCLK 分频才得到合适的 ODR。先算好 ODR再选时钟别反着来。2.2 电源与参考电压24位ADC的精度一半在这AD7768 有 AVDD模拟电源、DVDD数字电源、IOVDD接口逻辑电源三组。AVDD 建议用低噪声 LDO 供电别直接从 DCDC 输出取电除非后面加了足够级数的滤波。IOVDD 按你板上的逻辑电平选我这边直接用了 3.3V和 STM32H5 保持一致省去电平转换。VREF 参考电压是 24 位 ADC 的“尺子”必须用专门的低噪声基准源不能拿普通 LDO 凑合。VREF 引脚旁边我放了 1μF 0.1μF 的组合去耦电容位置尽量靠近芯片引脚。如果模拟电源和参考电压的噪声压不下去你会观察到 FFT 底噪严重抬高动态范围怎么都达不到手册指标。2.3 前端驱动电路与引脚分配建议AD7768 是差分输入虽然输入阻抗较高但直接接传感器通常不行需要一级 ADC 驱动运放再加 RC 滤波。RC 取值要和 ODR 匹配选大了信号带宽不够选小了抗混叠效果就差。这个模拟前端对最终采集质量的影响往往比 MCU 端代码更大。引脚分配这块DRDY 必须接到一个支持外部中断的 GPIO用来做帧同步触发。SPI 的 SCK/SDI/SDO 接到 H5 的 SPI 外设引脚CSB 用普通 GPIO 手动控制不要用 SPI 硬件 NSS原因后面细说。DMA 通道要选择能关联对应 SPI 接收请求的那个。3. 软件框架H5的SPIDMA如何变成AD7768的帧采集器3.1 寄存器初始化顺序AD7768 上电后要先等电源稳定和内部复位完成然后按顺序做这几件事通过 SPI 写 CHANNEL_MODE 寄存器配置各通道是否使能。配置 PWR_MODE 和 DATA_CONTROL 寄存器选择功耗模式、抽取比、ODR。配置 SPI 相关寄存器决定 SDO 线数、数据输出格式。执行一次 SPI_SYNC 或软件同步让 ADC 从确定的状态开始转换。等待 DRDY 有效确认第一帧数据已经就绪。启动 DMA 接收。写寄存器时SPI 的数据帧长度要和寄存器位宽一致否则写进去的 bit 会错位。这类 ADC 的寄存器操作通常用 8bit 帧长比较顺手但具体看手册怎么定义别想当然。3.2 用DRDY下降沿触发读取而不是定时器轮询有人为了省事用定时器按固定周期去发起 SPI 读取。这在 ADC 的 ODR 非常精准、没有时钟漂移的前提下勉强可行但实际系统里晶振偏差、温度漂移都会让定时器和 ADC 转换完成时刻逐渐错开最后读到的帧边界越来越歪。我的做法DRDY 接外部中断引脚下降沿来的时候说明新一帧已经准备好在中断里直接启动一次 DMA 接收把数据搬进缓冲区。示意代码如下void EXTI0_IRQHandler(void) { if ((EXTI-PR1 EXTI_PR1_PIF0) ! 0) { EXTI-PR1 EXTI_PR1_PIF0; // 清中断标志 frame_ready 1; // 置位帧就绪标志 // 重置DMA传输长度并启动新一轮接收 hdma_rx-Instance-NDTR FRAME_BYTES; DMA2_Start((uint32_t)hspi1.Instance-RXDR, (uint32_t)rx_buf, FRAME_BYTES); } }在 50kSPS 以下的 ODR这种方案非常安全。H5 的中断延迟远小于一帧 SPI 读取时间。但如果跑到 256kSPS中断每 3.9μs 来一次如果中断里做的事太多就容易丢帧。所以中断里只做“启动 DMA / 置标志”数据解析和业务处理全部放主循环。3.3 SCLK连续性的坑与解法标准 SPI 每传完一个字节SCLK 都会停一下或者回一次空闲电平。如果主机配置成“软件 NSS CSB 固定拉低”SCLK 在两字节之间可能产生一个短暂的停顿。AD7768 的手册通常要求 CSB 拉低后 SCLK 尽量连续一旦停太久或者停在错误的位置轻则帧内数据错位重则整个后续读取序列全乱。我的解法有两个要点CSB 在整个 DMA 传输期间由软件强制拉低读取完一帧后再拉高。不要用硬件 NSS因为它会在每 8bit 或 16bit 传输结束后自动翻转 CSB直接破坏 AD7768 的帧读取过程。SPI 的发送端要一直发数据纯粹为了持续产生 SCLK。DMA 同时配置 TX 和 RXTX 发 0xFF 或者 0x00 都行关键是时钟不能断。STM32H5 的 SPI 有 TXFIFO可以缓存多个待发送字节能在一定程度上减少字节间隙。实际测试下来SCLK 跑到 10~20MHz 时这个方案非常稳AD7768 完全不受影响。但如果想跑极限速度SCLK 接近 50MHz间隙带来的风险就会放大那时候就要考虑下面的 OCTOSPI 方案。3.4 24位数据拼接与符号扩展DMA 收回来的是连续字节流每 3 个字节对应一个通道的 24 位数据。AD7768 输出的是二进制补码格式也就是有符号数输出顺序是先高字节后低字节。拼成 32 位之后必须做符号扩展否则负数会变成很大的正数。// buf[0]: 高字节, buf[1]: 中字节, buf[2]: 低字节 uint32_t raw ((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | ((uint32_t)buf[2]); // 左移8位把符号位移到最高位再算术右移回来得到正确的int32 int32_t value ((int32_t)raw 8) 8;通道顺序方面单 SDO 模式下默认按通道 0~7 顺序输出。如果你开了多 SDO 模式每根线负责哪几个通道一定要看手册里的数据分配表不同配置差异很大别照着单线的逻辑推。4. 实测数据与稳定性从10kSPS一路跑到256kSPS4.1 我的测试环境我用的芯片是 STM32H563ZIT6属于 H5 系列Cortex-M33 内核主频 250MHz。板子上 AD7768 的 MCLK 接了一个 16.384MHz 低抖动有源晶振VREF 用外部 4.096V 基准差分输入端接了一个 10kHz 正弦波信号源。SPI 配置成 20MHz 主机模式DMA 接收DRDY 接 PB0EXTI0每帧读 24 字节。整体连接参数如下项目配置主控制 MCUSTM32H563ZIT6ADCAD77688通道24bitMCLK16.384MHz 有源晶振VREF4.096V 外部基准SCLK20MHz低速/中速SDO模式单线SDO0帧长度8 × 24bit 192bit 24字节4.2 低速与中速结果10kSPS 和 50kSPS 的表现都很好波形干净FFT 看到的底噪在合理范围内符合 AD7768 手册动态范围预期。用“EXTI 启动 DMA DMA 完成中断取数据”这套流程CPU 占用很低主循环还能跑一部分浮点运算和显示刷新。这里我特别确认了一个细节如果把 DRDY 的 EXTI 中断里塞了太多任务比如打印日志、做浮点运算采样率一高就开始丢帧。数据表现为波形上随机出现“毛刺”或者整段数据跳变。解决办法就是中断只做两件事置标志、启动 DMA。4.3 高速256kSPS时的情况跑到 256kSPS 的时候单线 SDO 模式下带宽需求是 256k × 8 × 24bit 49.152Mbps。这意味着 SCLK 至少要 50MHz 以上H5 的 SPI 极限能到 60MHz 左右但余量非常小。再加上 DMA、中断处理的时间冗余实测很容易在连续采集几十万帧之后出现一次错位。我最后的折中方案是ODR 降到 200kSPSSCLK 跑到 40MHzDMA 和 EXTI 全力配合基本能稳定运行。如果非要跑满 256kSPS建议开启四线 SDO 模式再配合 H5 的 OCTOSPI 四线读取把 SCLK 需求降到 12.5MHz 左右这样才有充足的余量。5. 提速进阶用H5的OCTOSPI四线读取模式把SCLK压力降下来5.1 为什么四线模式在标准SPI下反而麻烦标准 SPI 只有一根 MISO 线。你把 AD7768 的 SDO0~SDO3 四根线全部接到 MCU 上普通 SPI 也无法同时接收四路数据。于是很多人只能继续用单线模式白白浪费了 AD7768 的多线输出能力。5.2 OCTOSPI的思路STM32H5 自带 OCTOSPI 外设它既可以接外部八线 Flash也能配置成类似四线/八线 SPI 的传输模式。虽然 OCTOSPI 的设计目标主要是内存映射读 Flash但用一个普通 GPIO 在 DRDY 下降沿时触发一次 OCTOSPI DMA 接收完全可以把 AD7768 的 SDO0~SDO3 四根线同时读进来。具体思路OCTOSPI 做主机负责产生 SCLK同时并行接收 4 根 SDO 数据。DRDY 下降沿触发外部中断中断里启动 OCTOSPI 的 DMA 接收读回固定长度数据。配置 OCTOSPI 的四线接收模式并调整采样边沿和数据字节序确保收到的一帧数据和 AD7768 输出的通道顺序一致。这个方案写起来不算特别复杂但 OCTOSPI 的参数比普通 SPI 多尤其是采样边沿、IO 方向、DMA 请求映射这几项要对着手册和实际波形慢慢调。跑通之后SCLK 需求直接除以 4哪怕 ODR 拉满 256kSPSSCLK 也只有 12.5MHz 左右非常从容。5.3 什么时候值得上八线8 线模式下SCLK 需求能进一步降到 6.144MHz但代价是 MCU 要占用 8 个引脚而且 OCTOSPI 八线模式本身是按 byte 访问和处理数据的字节序和数据分配会比四线更绕。除非你的应用对实时性和吞吐量有极高要求否则四线模式是实用性和复杂度比较均衡的点。普通项目到四线这步就够了。6. 最容易翻车的几个细节写这篇的时候我把自己踩过的坑重新整理了一遍按杀伤力排序上电复位时序AD7768 上电后要等电源稳定和内部复位完成太快写寄存器容易写不进去或者写到一半被复位清掉。手册会给复位时间参数代码里务必加足够延时。CSB 整帧低电平CSB 在数据传输到一半时拉高会直接中止当前读取导致帧错位。所以 CSB 要由软件控制整帧读取期间保持低读完后拉高。MCLK 和 SCLK 是两回事很多人一开始把 MCLK 当成 SPI 时钟配置了半天发现 ADC 完全不工作。MCLK 管 ADC 转换SCLK 管数据搬移两者独立。MCO 给 MCLK 的风险如果 MCU 的 MCO 时钟源质量和 HSE 抖动控制不够好直接喂给 24 位 ADC 会明显抬高底噪。用独立有源晶振是最省心的方案。寄存器写后校验配置完寄存器最好读回来验证一遍防止 SPI 时钟边际问题导致寄存器写错。尤其在高 SCLK 下寄存器读写本来就容易出错。通道顺序和增益偏移寄存器如果各通道的增益/偏移校正寄存器初始化失败采集出来的数据可能是“歪”的——幅值不对、直流偏置也不对——但看起来并不像硬件故障排查起来很迷惑。这整个项目做下来我最大的感受是AD7768 这种芯片硬件上把大坑都埋好了真正的问题几乎全在“你有没有真正理解它的接口时序”上。把这套帧同步读取的思路理清楚之后再从 STM32H5 迁到其他 MCU 平台其实也只是换汤不换药的事。希望这篇能帮你少熬几个夜。
返回列表