☰
STM32 SPI驱动AD7606:8通道同步采样与DMA读取实战
2026/9/28 8:30:47 网站建设 项目流程

1. 项目缘起与整体方案拆解

AD7606 这颗芯片在电力监控、电机控制、振动采集这些场景里出镜率极高,8 通道同步采样、16 位分辨率、最高 200kSPS 的吞吐率,基本上把"多路模拟量同时抓"这件事做到了一个很舒服的性价比区间。但真到动手把它接到 STM32 上,很多人会卡在同一个地方:CubeMX 里 SPI 配置看着都填了,代码也生成了,结果读回来的数据要么全 0,要么全 0xFFFF,要么通道之间串得乱七八糟。问题往往不在 AD7606 本身,而在于采样时序、转换启动、SPI 读取这三件事没有对齐。

我这次做的项目,是用 STM32F103 系列通过 SPI 接口驱动 AD7606,实现 8 通道同步采样,采样率目标定在 50kSPS 左右,数据通过 DMA 搬到内存缓冲区,主循环只负责打包和上传。选 SPI 而不是并行接口,理由很直接:并行接口要占用 16 根数据线加若干控制线,F103 的 IO 本来就紧张,而且并行布线在双层板上很容易出问题;SPI 只需要 4 根线(SCLK、MISO、CS、BUSY),引脚省下来可以干别的。代价是读取速度受限于 SPI 时钟,但 50kSPS 这个量级,SPI 跑 10MHz 以上完全够用。

整体方案的核心思路可以概括成一句话:用定时器触发 CONVST 启动转换,用 BUSY 下降沿或者轮询判断转换完成,再用 SPI 把 8 个通道的数据一次性读回来。这里有个关键点容易被忽略——AD7606 的 8 个通道数据是在同一个转换周期内锁存的,SPI 读取的时候是按通道顺序依次移出,所以只要转换启动是同步的,读回来的就是同一时刻的采样值。这也是"同步采样"的真正含义,不是 SPI 读得快就叫同步。

提示:AD7606 的 CONVST 上升沿触发转换,BUSY 变高表示转换进行中,BUSY 变低表示转换完成、数据可读。这两个信号的时序关系是整个项目的地基,后面所有调试都围绕它展开。

方案选型上还有几个岔路口需要交代清楚。第一,用硬件 SPI 还是软件模拟 SPI?我选硬件 SPI,因为 F103 的 SPI1 支持 DMA,配合起来 CPU 占用极低。第二,用硬件片选还是软件片选?AD7606 的 CS 需要在读取期间保持低电平,硬件 NSS 在某些配置下会在字节间拉高,导致读取中断,所以我用软件片选,手动控制 GPIO。第三,用轮询 BUSY 还是外部中断?50kSPS 意味着 20us 一个周期,中断太频繁会拖垮系统,我最终用定时器触发 + 轮询 BUSY 的方式,简单可靠。

2. AD7606 关键引脚与时序细节解析

2.1 引脚功能与连接方式

先把 AD7606 的引脚按功能分组理一遍,这样接线的时候不会乱。模拟输入侧有 8 个通道 V1 到 V8,每个通道有对应的 AGND;数字侧关键信号包括 CONVST A/B、BUSY、CS、SCLK、DOUTA/DOUTB、RESET、RANGE、OS 等。我这次用的是串行模式,所以只接 DOUTA 一根数据线,DOUTB 悬空或者接地都行,具体看芯片手册的串行模式说明。

引脚方向作用我的接法
CONVST A/B输入启动转换,上升沿有效接 PA8,定时器触发
BUSY输出转换状态,高=转换中接 PA9,轮询读取
CS输入片选,低有效接 PA4,软件控制
SCLK输入SPI 时钟接 PA5(SPI1_SCK)
DOUTA输出串行数据输出接 PA6(SPI1_MISO)
RESET输入复位接 PA10,上电复位一次
RANGE输入量程选择接 GND,选 ±5V
OS输入过采样设置接 GND,不过采样

这里有个细节值得说:CONVST A 和 CONVST B 在串行模式下可以短接在一起,用一个信号同时触发,这样 8 个通道就是严格同步启动的。如果你分开控制,A 组和 B 组之间会有微小的时间差,虽然对多数应用影响不大,但既然叫"同步采样",能短接就短接。

2.2 转换与读取的时序关系

AD7606 的完整工作周期分两段:转换阶段和读取阶段。转换阶段从 CONVST 上升沿开始,BUSY 拉高,芯片内部逐次逼近,8 个通道并行转换,耗时典型值 4us 左右(跟过采样设置有关)。BUSY 拉低后,数据就锁存在输出寄存器里,这时候才能开始 SPI 读取。

读取阶段的关键是:CS 拉低后,第一个 SCLK 下降沿开始移出数据,每个通道 16 位,8 个通道共 128 个 SCLK。注意 AD7606 是在 SCLK 下降沿更新数据、上升沿被采样,所以 STM32 这边要配成 SPI 模式 2(CPOL=1, CPHA=1)或者模式 3,具体要看你的采样沿。我实测下来用 CPOL=1、CPHA=1 最稳,也就是空闲时 SCLK 为高,第二个边沿采样。

注意:很多人读 AD7606 读不出来,就是因为 SPI 模式配错了。AD7606 手册里写的是数据在 SCLK 下降沿变化,主机应该在上升沿采样,对应 STM32 的 CPOL=0、CPHA=1(模式 1)或者 CPOL=1、CPHA=1(模式 3)。我建议先用模式 3 试,不行再换模式 1,这两个是最常见的正确配置。

时序上还有一个坑:CS 拉低到第一个 SCLK 之间需要至少 25ns 的建立时间,SCLK 最后一个下降沿到 CS 拉高之间也需要保持时间。STM32 的 SPI 硬件在 DMA 模式下,CS 是软件控制的,所以要在启动 DMA 之前拉低 CS,DMA 传输完成中断里再拉高 CS,这样时序最干净。

2.3 过采样与量程的取舍

AD7606 支持过采样,通过 OS 引脚或者寄存器配置,可以把多个采样结果平均,降低噪声。过采样倍数越高,转换时间越长,BUSY 高电平持续时间越久。我这次目标 50kSPS,如果开 4 倍过采样,转换时间会从 4us 涨到十几 us,50kSPS 的周期是 20us,留给读取的时间就不够了。所以最终选择不过采样,靠后端软件做滑动平均来降噪。

量程方面,RANGE 接 GND 选 ±5V,接 VDRIVE 选 ±10V。我的传感器输出是 ±5V 范围,所以接 GND。这里要注意,量程选择影响的是模拟输入范围,不影响 SPI 读取的 16 位数据格式,读回来的都是补码形式,正负满量程对应 ±32768。

3. STM32CubeMX 配置全流程

3.1 时钟与调试接口配置

打开 CubeMX,先选芯片型号,我用的是 STM32F103C8T6。第一步配 RCC,HSE 选 Crystal/Ceramic Resonator,这样可以用外部 8MHz 晶振。然后进 Clock Configuration,把 PLL 倍频到 72MHz,APB2 保持 72MHz(SPI1 挂在 APB2 上),APB1 分频到 36MHz。SPI1 的时钟源是 APB2,所以 SPI 波特率预分频器是基于 72MHz 来算的。

调试接口我保留了 SWD,PA13 和 PA14 不占用,方便下载和调试。这里提醒一句,如果你用的是 F103 的最小系统板,有些板子 PA13/PA14 被复用成别的功能了,配置前先确认原理图。

3.2 SPI1 参数配置

在 Connectivity 里选 SPI1,Mode 选 Full-Duplex Master,虽然 AD7606 只往外发数据,但 STM32 这边配成全双工也没问题,MISO 照样能收。Hardware NSS Signal 选 Disable,因为我们用软件片选。

参数配置如下:

  • Clock Polarity: High(CPOL=1)
  • Clock Phase: 2 Edge(CPHA=1)
  • Data Size: 16 Bits
  • First Bit: MSB First
  • Prescaler: 8(72MHz / 8 = 9MHz)
  • CRC Calculation: Disabled
  • NSS Pulse Mode: Disabled
  • TI Mode: Disabled

9MHz 的 SPI 时钟,读 128 位需要约 14us,加上转换时间 4us,总共 18us,刚好卡在 20us 周期内。如果你要更高的采样率,可以把预分频改成 4,SPI 跑到 18MHz,但要注意 AD7606 的 SCLK 最高频率是 23MHz 左右,别超了。

提示:Data Size 选 16 Bits 很关键。AD7606 每个通道就是 16 位,用 16 位帧读,DMA 搬 8 次就正好是 8 个通道。如果你选 8 位,就得搬 16 次,还得自己拼字节,麻烦且容易错。

3.3 DMA 配置

在 SPI1 的 DMA Settings 里点 Add,选 SPI1_RX,Mode 选 Circular 或者 Normal 都行。我选 Normal,因为每次转换完读一次,读完在中断里重新启动。Data Width 选 Half Word(16 位),跟 SPI 的 Data Size 对齐。Priority 选 High。

DMA 的 Circular 模式适合连续采集,但 AD7606 是"转换一次读一次"的节奏,用 Normal 模式配合手动重启更符合逻辑。如果你要做连续高速采集,可以用 Circular,但要注意缓冲区覆盖的问题。

3.4 GPIO 与定时器配置

GPIO 需要配这几个:

  • PA4:GPIO_Output,初始 High,作为 CS
  • PA8:GPIO_Output 或者定时器通道,作为 CONVST
  • PA9:GPIO_Input,作为 BUSY
  • PA10:GPIO_Output,作为 RESET

CONVST 我用 TIM1 的 PWM 输出,这样触发频率精确可控。TIM1 挂在 APB2 上,72MHz,预分频设 71,得到 1MHz 计数频率,ARR 设 19,就是 20us 周期,对应 50kHz。PWM 模式选 PWM1,占空比设很小,比如 CCR=1,这样 CONVST 就是一个窄脉冲,上升沿触发转换。

BUSY 用轮询方式读,在定时器中断或者主循环里判断。如果用中断,可以把 BUSY 接到 EXTI,但 50kHz 的中断频率对 F103 来说有点吃力,所以我用主循环轮询 + 状态机的方式。

4. 代码实现与核心环节剖析

4.1 初始化与复位流程

上电后的初始化顺序很重要。先初始化 HAL 库、系统时钟、GPIO、SPI、DMA、定时器,然后给 AD7606 一个复位脉冲。RESET 引脚拉高至少 50ns 再拉低,芯片内部寄存器恢复到默认状态。复位后建议延时 1ms 再开始第一次转换,让内部电路稳定。

void AD7606_Init(void) { HAL_GPIO_WritePin(AD_CS_GPIO_Port, AD_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AD_RESET_GPIO_Port, AD_RESET_Pin, GPIO_PIN_HIGH); HAL_Delay(1); HAL_GPIO_WritePin(AD_RESET_GPIO_Port, AD_RESET_Pin, GPIO_PIN_LOW); HAL_Delay(1); HAL_GPIO_WritePin(AD_RESET_GPIO_Port, AD_RESET_Pin, GPIO_PIN_HIGH); HAL_Delay(1); }

复位完成后,启动 TIM1 的 PWM 输出,CONVST 开始周期性触发。第一次转换启动后,BUSY 会拉高,等它拉低就可以读了。

4.2 转换完成判断与数据读取

主循环里用一个状态机来管理:等待 BUSY 拉低,拉低后拉低 CS,启动 DMA 接收,DMA 完成中断里拉高 CS,处理数据。这里的关键是 DMA 接收的字节数要设对,8 个通道 × 16 位 = 16 字节,也就是 8 个半字。

uint16_t adc_buf[8]; void AD7606_Read(void) { HAL_GPIO_WritePin(AD_CS_GPIO_Port, AD_CS_Pin, GPIO_PIN_RESET); HAL_SPI_Receive_DMA(&hspi1, (uint8_t *)adc_buf, 8); } void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { HAL_GPIO_WritePin(AD_CS_GPIO_Port, AD_CS_Pin, GPIO_PIN_SET); adc_ready = 1; } }

注意 HAL_SPI_Receive_DMA 的第三个参数是数据个数,不是字节数。因为 SPI 配的是 16 位,所以传 8 就是 8 个半字,正好 16 字节。这个参数搞错是新手最常见的错误之一,传成 16 就会多读 8 个半字,数据全乱。

4.3 数据格式转换与校准

AD7606 读回来的是 16 位补码,范围 -32768 到 +32767,对应模拟输入的负满量程到正满量程。以 ±5V 量程为例,转换公式是:

电压 = (原始值 / 32768.0) × 5.0

如果原始值是 32767,对应约 +5V;如果是 -32768,对应约 -5V。实际使用中,零点可能有偏移,建议做一次校准:短接某个通道到 AGND,读一批数据求平均,把这个平均值作为零点偏移,后续所有数据减去它。

float AD7606_ToVoltage(int16_t raw) { return (raw / 32768.0f) * 5.0f; }

这里有个细节:adc_buf 是 uint16_t 类型,转成 int16_t 才能正确解释负数。直接拿 uint16_t 去算,负值会变成很大的正数,这是很多人踩过的坑。

4.4 定时器触发与采样率控制

TIM1 的 PWM 输出直接驱动 CONVST,所以采样率就是 PWM 频率。我设的 50kHz,实际测试时用示波器看 CONVST 和 BUSY 的波形,确认 BUSY 高电平时间不超过 20us 减去读取时间。如果 BUSY 还没拉低就开始下一次转换,数据会出错。

如果你需要动态调整采样率,可以改 TIM1 的 ARR 寄存器。比如要 10kHz,ARR 设 7199;要 100kHz,ARR 设 719。但 100kHz 时周期只有 10us,转换 4us 加读取 14us 就超了,所以 SPI 时钟得往上提,或者减少读取的通道数。

5. SPI 时序调试实战技巧

5.1 用示波器抓波形定位问题

调试 SPI 最有效的手段就是示波器,没有之一。把 CS、SCLK、MISO、BUSY 四根线同时接上,触发设在 CS 下降沿,看整个读取过程。正常的波形应该是:CS 拉低,SCLK 开始输出 128 个脉冲,MISO 在每个 SCLK 上升沿稳定,数据依次移出。

如果 MISO 一直是高电平,说明 AD7606 没在输出数据,可能是 CS 没拉低、SCLK 没出来、或者芯片没复位。如果 MISO 有数据但全是 0xFFFF,可能是 SPI 模式配错,采样沿不对。如果数据只有前几个通道对、后面全乱,可能是 DMA 长度设错或者 CS 提前拉高了。

提示:没有示波器的话,可以用逻辑分析仪,几十块钱的那种就够用。抓一次波形比盲调半天代码有效得多。

5.2 SPI 模式与采样沿的确认方法

AD7606 的时序图里,数据在 SCLK 下降沿更新,主机在上升沿采样。对应 STM32 的 SPI 模式,CPOL=0、CPHA=1 是模式 1,CPOL=1、CPHA=1 是模式 3。这两个模式都是上升沿采样,区别在于空闲电平。我实测模式 3 更稳,因为空闲时 SCLK 为高,第一个下降沿来得快,建立时间更充裕。

如果你不确定用哪个模式,可以写个小测试:固定读第一个通道,把两种模式都试一遍,看哪个读出来的数据稳定且符合预期。AD7606 的第一个通道数据在 CS 拉低后的前 16 个 SCLK 移出,如果模式对了,这个值应该跟输入电压对应。

5.3 DMA 传输中的常见异常

DMA 传输最常见的异常是"只收到一半数据"或者"数据错位"。原因通常是 DMA 的 Data Width 跟 SPI 的 Data Size 不匹配。SPI 配 16 位,DMA 也要配 Half Word,如果 DMA 配成 Byte,就会把 16 位拆成两个字节搬,数据全乱。

另一个坑是 DMA 的 Normal 模式下,传输完成后要手动重新启动,否则第二次读不到数据。我在 DMA 完成中断里拉高 CS 后,会在主循环里判断 adc_ready 标志,处理完数据再启动下一次读取。如果你用 Circular 模式,就不用重启,但要注意缓冲区覆盖。

5.4 采样率与读取时间的平衡

50kSPS 时,周期 20us,转换 4us,读取 14us,余量只有 2us。这个余量很紧,如果 SPI 时钟有抖动或者中断延迟,就可能超时。我的做法是把 SPI 预分频从 8 改成 4,SPI 跑到 18MHz,读取时间降到 7us,余量就充裕了。AD7606 的 SCLK 最高 23MHz,18MHz 完全在范围内。

如果你要更高的采样率,比如 100kSPS,周期 10us,转换 4us,读取必须压到 6us 以内,SPI 得跑到 21MHz 以上,接近极限了。这时候可以考虑用并行接口,或者换更高主频的 STM32。

6. 常见问题速查与避坑经验

6.1 数据全 0 或全 0xFFFF 的排查

这个问题我遇到过三次,每次原因都不一样。第一次是 CS 没拉低,AD7606 根本没被选中;第二次是 SPI 模式配错,采样沿不对,读到的都是无效位;第三次是 RESET 引脚没接好,芯片一直处于复位状态。排查顺序建议是:先确认 CS 波形,再确认 SCLK 有没有输出,最后确认 SPI 模式。

现象可能原因排查方法
全 0CS 未拉低、SPI 未启动示波器看 CS 和 SCLK
全 0xFFFFSPI 模式错误、MISO 悬空换 SPI 模式试,检查接线
数据错位DMA 长度或位宽错误检查 DMA 配置
通道串扰CS 提前拉高、读取不完整看 CS 和 SCLK 数量

6.2 BUSY 信号异常的定位

BUSY 一直高,说明转换没完成,可能是 CONVST 没触发、芯片没供电、或者 RESET 没释放。BUSY 一直低,说明转换没启动,CONVST 可能没接对或者定时器没输出。用示波器同时看 CONVST 和 BUSY,正常的波形是 CONVST 上升沿后 BUSY 拉高,几个 us 后 BUSY 拉低。

如果 BUSY 抖动厉害,可能是电源噪声或者地线干扰。AD7606 的模拟地和数字地要分开走,最后单点共地。去耦电容要靠近芯片引脚,100nF 和 10uF 并联。

6.3 采样精度不达标的改善

精度问题通常来自三个方面:参考电压噪声、模拟输入阻抗、PCB 布局。AD7606 内置参考电压,但如果你用外部参考,要确保参考源干净。模拟输入如果直接接传感器,要注意传感器的输出阻抗,太高的话会影响采样精度,必要时加缓冲运放。

软件上可以做滑动平均,比如连续采 16 次求平均,噪声能降 4 倍。但滑动平均会降低有效采样率,要权衡。我这次用 8 次平均,50kSPS 降到 6.25kSPS 的有效输出率,对多数监控场景够用了。

6.4 CubeMX 生成代码的注意事项

CubeMX 生成的代码里,SPI 和 DMA 的初始化顺序是固定的,但 AD7606 的复位和定时器启动需要手动加到 main 函数里。我建议在 MX_SPI1_Init 之后、MX_TIM1_Init 之前加 AD7606_Init,这样复位完成后定时器才开始触发,时序最干净。

另外,CubeMX 重新生成代码会覆盖用户代码,所以自己的函数要写在 USER CODE BEGIN 和 END 之间。我习惯把 AD7606 相关的函数单独放一个 .c 文件,CubeMX 只生成初始化,业务逻辑自己管理,这样重新生成不会丢代码。

7. 实测数据与性能评估

7.1 采样率与 CPU 占用实测

在 72MHz 主频下,50kSPS 采样时,CPU 占用大约 15%,主要是 DMA 中断和数据处理。如果把采样率提到 100kSPS,CPU 占用涨到 30% 左右,还在可接受范围。SPI 跑 18MHz 时,读取 8 通道耗时约 7us,转换 4us,总周期 11us,100kSPS 的 10us 周期就有点紧张了,实测会丢数据。

我用定时器打时间戳的方式测过,从 BUSY 拉低到 DMA 完成,平均 7.2us,最大 8.1us,抖动主要来自中断响应延迟。如果对时序要求极严,可以把 DMA 中断优先级设到最高,减少延迟。

7.2 通道一致性与串扰测试

8 个通道同时接同一个信号源,读回来的数据最大偏差在 ±3LSB 以内,一致性不错。通道间串扰测试是给一个通道加满量程信号,其他通道接地,读其他通道的数据,偏差在 ±2LSB 以内,基本可以忽略。

这里要提一句,AD7606 的通道间隔离做得不错,但前提是 PCB 布局要合理。模拟输入走线要远离数字信号,尤其是 SCLK 和 CONVST 这种高频信号。我第一版板子没注意,SCLK 走线从模拟输入下面穿过,串扰明显,后来改版把数字和模拟分区,问题就解决了。

7.3 长时间运行的稳定性

连续跑了 72 小时,采样数据没有出现跳变或者丢帧。温度方面,AD7606 表面温度比环境高 10 度左右,在正常范围内。电源电流稳定在 20mA 左右,没有异常波动。

长时间运行最怕的是 DMA 溢出或者缓冲区覆盖。我用的是 Normal 模式加手动重启,每次读取前检查 adc_ready 标志,确保上一次数据已经处理完。如果处理不过来,就丢弃本次数据,避免缓冲区错乱。

8. 项目扩展与优化方向

8.1 从 8 通道扩展到多片级联

如果需要更多通道,可以用多片 AD7606 级联。SPI 支持菊花链模式,DOUTA 接下一片的 DIN,CS 共用,SCLK 共用,这样一片读完自动读下一片。但 AD7606 的菊花链需要配置,具体看手册。另一种方式是每片独立 CS,STM32 分时读取,简单但占用更多 IO。

8.2 结合 FreeRTOS 的任务划分

如果项目复杂度上来了,可以上 FreeRTOS。把 AD7606 的读取放在一个高优先级任务里,数据处理放在低优先级任务,通过队列传递数据。注意 SPI 和 DMA 的中断优先级要配好,FreeRTOS 的 configMAX_SYSCALL_INTERRUPT_PRIORITY 要设对,否则中断里调用 RTOS API 会出问题。

8.3 数据上传与上位机对接

采集到的数据可以通过串口或者 USB 上传到上位机。我用的是串口,115200 波特率,每帧打包 8 个通道的数据加时间戳,上位机用 Python 解析。如果要更快的上传速度,可以用 USB CDC 或者以太网,但 F103 没有内置以太网,得外接模块。

提示:串口上传时要注意数据帧的同步,建议加帧头和校验,避免上位机解析错位。我用的是 0xAA 0x55 帧头加 CRC16 校验,实测很稳。

8.4 低功耗场景的优化

如果是电池供电的场景,可以在两次采样之间让 STM32 进 Sleep 模式,定时器唤醒。AD7606 也有待机模式,通过 STBY 引脚控制。但要注意唤醒时间,Sleep 唤醒很快,几个时钟周期,对采样率影响不大。

最后分享一个我在这个项目里踩过的坑:AD7606 的 CONVST 脉冲宽度不能太窄,手册要求最小 25ns,我一开始用 TIM1 的 CCR 设 1,在 72MHz 下脉冲宽度约 14ns,结果偶尔触发失败。后来把 CCR 改成 3,脉冲宽度约 42ns,就稳定了。这种细节手册里写了但容易忽略,实际调试时如果遇到偶发问题,不妨回头查查时序参数。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询