简介面向STM32微控制器的ADS8681系列高精度模数转换器驱动代码适用于嵌入式数据采集、工业自动化、精密仪器与多通道测量等场景主要解决开发者在STM32平台上快速初始化ADC、配置采样率与增益、读取转换结果等基础问题。驱动采用简洁的C语言编写接口清晰适合具备一定嵌入式基础、希望减少底层开发工作量的工程技术人员也适合作为学习外设驱动开发的参考范例。压缩包内共两个文件包括一个头文件和一个源文件整体大小约1KB头文件声明了寄存器映射、参数宏定义与对外函数源文件则实现了基于串行外设接口SPI的通信、转换启动、数据读取与软件延时等核心逻辑代码量小且注释完整便于直接移植到项目或按需修改。目前已有约一千七百九十五人浏览学习对于需要高采样速率和十六位分辨率的测量类项目这份驱动能帮助开发者跳过繁琐的寄存器配置快速获得可用的采样函数同时可参照ADS8681的实现思路扩展到ADS8685、ADS8689等同系列芯片降低重复开发成本提高整体开发效率。1. 选型那点事ADS8681凭什么被塞进这个项目做工业数据采集的人应该都有这种经历现场要测的电压范围乱七八糟有的传感器出来是 0~10V有的运放输出是 ±5V还有的要直接怼上电网互感器二次侧的 ±10V 信号。你要是为了照顾不同量程去堆一堆分压电阻、运放跟随器、电平搬移电路那板子面积和调试工作量直接起飞。我这次做的是一个单通道高精度数据采集模块选型阶段在几款 ADC 之间反复横跳最后落地的就是 TI 的 ADS868116 位、1 MSPS、SPI 接口、内置可编程增益放大器最大的卖点是它能直接吃 ±10V 这种宽范围工业信号前面几乎不用再加任何整形电路。ADS8681 属于 TI ADS868x 家族里的单通道型号同系列还有 ADS8684、ADS8688 这种多通道版本。我做单通道模块所以选它刚刚好。很多人刚接触这颗芯片时容易被“16 位 1 MSPS”吸引但真正让它和普通 SAR ADC 拉开差距的是输入结构——芯片内部集成了输入缓冲器、可编程增益放大器和高压输入保护前面不用接运放也不用考虑钳位二极管误差源少了一大截。我之前用 ADS1256 做低速高精度采集再用 AD7606 做多通道并行采集这次换到 ADS8681最直观的感受是整个模拟前端设计压力小了很多。横向对比一下三颗 ADC方便你判断自己项目到底该选谁型号分辨率采样率输入范围通道数接口典型定位ADS868116 bit1 MSPS±10V/±5V/±2.5V/0~10V 等1SPI单通道快速宽范围采集ADS125624 bit30 kSPS±5V需前端调理8 路差分/单端SPI低速高精度、多点测量AD760616 bit200 kSPS±5V/±10V8 路同步并行/串行多通道同步、电力监测如果你要测的通道数多又不需要太快的速度ADS1256 确实便宜大碗如果你要 8 路同步采样AD7606 是经典方案但如果你和我一样只需要一路或者两路却要求采样率在几百 kSPS 以上输入范围还要覆盖工业信号全量程那 ADS8681 这类单通道高集成度 ADC 就非常合适。我的模块最终每秒采 100 万个点做电机电流波形记录毫无压力这个速度在 16 位这个精度档位里相当能打。1.1 内置基准和PGA系统BOM被砍掉近一半ADS8681 内部集成了一个低温漂基准源默认 4.096V也支持外部基准输入。对于大多数应用直接使用内部基准就够了这意味着你省掉了一颗基准芯片和配套去耦电容。PGA 更是关键——芯片内部自动把不同输入范围折算到 ADC 核心的测量区间你不需要在外部做增益电阻网络。设计这个模块时我算过一笔账如果不用 ADS8681 而用普通 SAR ADC 外部基准 运放 保护电路BOM 材料至少多出七八颗料面积也要大出三分之一。1.2 数字接口电压灵活3.3V 主控直接对接ADS8681 的数字电源脚 VIO 支持 1.65V 到 5.5V这意味着你不管用 3.3V 的 STM32还是 1.8V 的 FPGA电平都能直接匹配不需要额外加电平转换芯片。我第一次画板子时还给它专门留了电平转换的占位后来翻了手册发现完全多此一举。这个细节适合注意一下特别是有强迫症喜欢把所有接口都做成“保险设计”的人可以省掉这部分成本。模块硬件框图大致是这样工业信号进来经过一个可选的 RC 低通滤波直接进入 ADS8681 的 AINP/AINN 引脚SPI 四根线接主控主控通过 DMA 把数据搬到内存再通过串口或者 USB 转发给上位机。整个模拟链路上除了滤波电容没有一个有源器件这在以往的设计里几乎不敢想。2. 打开ADS8681.zip压缩包里的工程到底组织成什么样我上传的 ADS8681.zip 不是一个空壳项目里面是我整个调试过程中的完整交付物。解压之后能看到硬件设计、固件驱动、上位机脚本和测试报告四个部分。很多人下载开源工程喜欢直接看源码但我还是建议先花十分钟把目录结构过一遍因为你只有知道一个工程的文件组织逻辑才能判断哪些可以直接抄、哪些要根据自己的 MCU 改动。目录结构长这样ADS8681.zip ├── Hardware │ ├── ADS8681_Module_SCH.pdf │ ├── ADS8681_Module_PCB_Rev1.2.pdf │ └── BOM_ADS8681_Module.csv ├── Firmware │ ├── stm32_driver │ │ ├── ads8681.h │ │ ├── ads8681.c │ │ └── spi_config_example.c │ └── README.md ├── HostSoftware │ ├── adc_plot.py │ ├── adc_ranging_tool.py │ └── requirements.txt └── TestReport ├── noise_test_results.csv └── linearity_test_notes.md2.1 模块工作的完整链路从模拟输入到上位机波形这套系统的工作流程可以拆成四段。第一段是模拟信号输入ADS8681 根据你配置的输入范围用内部 PGA 把信号调整到 ADC 核心可处理的电压区间。第二段是模数转换16 位 SAR 核心在采样时钟控制下完成转换结果通过 SPI 输出。第三段是主控读取我用的 STM32 以 DMA 方式读取避免 CPU 被频繁中断。第四段是数据上送主控把原始码值加上通道信息打包通过串口发到电脑Python 脚本负责解析和画波形。2.2 硬件文件里最该看的是 BOM 表很多下载工程的人只看原理图 PDF但我的建议是先把 BOM 表导入立创 EDA 或者 Excel 里看一遍。电源芯片型号、电容容值耐压、磁珠型号这些都直接影响性能。我在 BOM 里特意标注了哪些电容是 X7R 材质哪些必须用 C0G——这不是随手写的C0G 电容的电压系数比 X7R 低得多在采样保持电路里能减少信号相关失真。2.3 固件和上位机脚本哪些可以直接抄如果你也是 STM32 用户ads8681.c和ads8681.h基本上是复制粘贴就能用只需要把你的 SPI 句柄传进去。如果你用的是 ESP32、GD32 或者其他 MCU驱动层的移植重点在于实现三个函数片选拉低、SPI 收发 16 位数据、片选拉高。代码里我把这三步封装在ads8681_spi_transfer()里你换平台时只要改这一个函数即可。上位机脚本则完全不挑硬件串口 JSON 格式兼容各种各样的设备改一下波特率就能用。3. 硬件设计真正决定成败输入通道、参考电压与Layout细节说句掏心窝的话ADS8681 这种高集成度芯片原理图画起来不复杂但硬件设计的难度全部转移到了 PCB 布局和电源处理上。这部分我要多写一点因为我在原型板阶段吃过亏。3.1 输入通道的 RC 滤波怎么选直接照抄手册也会有麻烦ADS8681 的数据手册里会给一个推荐的输入滤波电路通常是几十欧姆电阻加纳法级电容。第一次打样时我完全照着手册的典型值来结果采样率跑满 1 MSPS 时波形出现轻微失真一度以为是芯片坏了。后来对比发现手册给的 RC 值测的是芯片评估板而我的前端输出阻抗比它高导致建立时间不够。解决办法也很直接把滤波电容稍微加大或者在采样率要求不高时把驱动源阻抗降下来确保在采样孔径时间内信号能完成建立。这里给一个经验公式参考RC 时间常数最好小于采样周期的 1/10如果按 1 MSPS 算采样周期是 1μs那 RC 尽量控制在 100ns 以下。R 取 20Ω 的话C 就不应该超过 5nF。你实际布线时如果发现信号源阻抗比较高可以放宽采样率换取稳定。3.2 内部基准不是什么时候都能用这个细节坑了我一晚上ADS8681 的内部基准确实方便但它有两个容易被忽略的约束。第一内部基准输出电压会通过 REFIO 引脚输出这个引脚必须接一个 1μF 到 10μF 的低 ESR 电容到地否则基准噪声会明显变大。第二如果你需要两片 ADS8681 做同步采集最好让它们共用同一个外部基准否则两片芯片的增益误差不一致做幅值比对时会看到固定偏差。我在做双通道同步采样的扩展版本时就发现左右通道测同一个直流电压读数相差了十几个 LSB。开始以为是 PCB 布线问题查了半天才发现是两片芯片内部基准的实际输出电压有细微差异。后来改成外部共用一个基准问题立刻消失。所以做同步多通道时外部基准不是可选项而是必须项。3.3 电源去耦和地平面被噪声毁掉的一块原型板第一版原型板测出来的噪声底比手册典型值高了将近一倍排查过程相当痛苦。最后的元凶是 LDO 输出电容离 ADS8681 的 AVDD 引脚太远中间还穿过了数字 SPI 线。SPI 信号翻转时的高频分量通过电源平面耦合进了模拟电源。正确的做法是AVDD 引脚的 0.1μF 去耦电容要尽可能靠近芯片引脚最好小于 2mm模拟电源和数字电源在芯片附近用磁珠或 0Ω 电阻做单点连接SPI 信号线尽量远离模拟输入和基准引脚。另外如果 PCB 是两层板底层要保持完整的地平面不要在 ADC 下方乱走线。这些不是玄学每一处都是我用示波器和频谱仪一点点验证过的。ADC 的采样噪声在没有输入信号时主要来自电源噪声、基准噪声和内部量化噪声。实测下来当一个干净的数字电源和一个干净的模拟电源在芯片附近短接时噪声从原来的 1.2 LSB 左右降到 0.6 LSB 以下。如果你要追求最后的两个比特Layout 比原理图更值得花时间。4. 固件里那些手册没直说的坑SPI时序、范围寄存器和读取流程ADS8681 的控制方式不复杂但不看时序图直接写代码很容易翻车。我调试驱动时的第一版代码就是对着 Arduino 的shiftOut习惯写的结果读回来的数据完全是乱的。后来老老实实去翻数据手册的 SPI 时序图才理解了问题出在哪。4.1 SPI 时钟沿与 SCLK 频率从时序图确认正确配置ADS8681 的 SPI 是 16 位帧格式主机在片选拉低后输出 16 个 SCLK同时从 MISO 上读回 16 位数据。关键的坑是时钟极性和相位这颗芯片要求 SCLK 空闲时保持低电平数据在 SCLK 的下降沿被主机采样本质上属于 SPI Mode 0 的变体具体要以你手上的最新版手册时序图为准不同批次描述可能存在差异。我当时犯的错是在 STM32 里图省事直接用了默认的 Mode 0。第一次读数据时发现码值完全不随输入电压变化后来才想到去看时序图把 CPOL/CPHA 重新确认之后数据立刻正常。另一件事是 SCLK 频率ADS8681 的 SCLK 最高可以到 20MHz但主控的 SPI 外设在 20MHz 时信号边沿可能不太干净建议先降到 10MHz 调试稳定了再往上提。4.2 上电之后第一件事读设备 ID 和配置输入范围固件初始化我总结了一个固定顺序先拉高片选等待上电稳定时间至少 1ms然后发一个读取设备 ID 的命令验证 SPI 通信是否正常接着写范围选择寄存器确定输入范围最后设置采样模式开始读取数据。这套流程每一步都有意义尤其是读设备 ID它能一次性确认接线、SPI 参数、芯片供电是否都正确。如果设备 ID 读不上来后面调多少都是白费功夫。下面这段代码是从我的驱动里精简出来的关键部分基于 STM32 HAL 库/* ADS8681 驱动关键配置示例 */ #define ADS8681_CMD_READ_ID 0x0000 // 具体命令码以手册为准 #define ADS8681_CMD_WRITE_RANGE 0xC000 // 范围寄存器写命令 static uint16_t ads8681_spi_transfer(uint16_t cmd) { uint16_t rx 0; HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi, (uint8_t *)cmd, (uint8_t *)rx, 2, 100); HAL_GPIO_WritePin(ADC_CS_GPIO_Port, ADC_CS_Pin, GPIO_PIN_SET); return rx; } uint16_t ads8681_read_id(void) { uint16_t id ads8681_spi_transfer(ADS8681_CMD_READ_ID); return id; } void ads8681_set_range(uint8_t range_code) { /* range_code: 例如 ±10V 对应某个寄存器值具体查手册 */ ads8681_spi_transfer(ADS8681_CMD_WRITE_RANGE | range_code); }配置完范围寄存器后最好延时一小段时间再开始采样让内部 PGA 和基准稳定下来。我实测在切换输入范围之后立刻采集前几十个点会有轻微偏移等 10ms 左右读数才完全稳定。4.3 连续采样与单次采样什么时候选哪种ADS8681 支持自动读取模式和手动读取模式。手动模式适合需要精确控制采样时刻的场景比如和外部触发信号同步自动模式适合连续高速采波形主控只要定时读数据即可。我做的电机电流记录模块用的就是自动连续读取SPI 和 DMA 配合CPU 占用率可以控制在很低水平。如果你做的是低速巡检类应用比如每秒钟只采几十个点那其实没必要让 ADC 一直满速跑。可以让芯片工作在单次转换模式每次需要时才发起转换既省电又减少不必要的电磁干扰。ADS8681 的功耗本来就不算高但能省一点是一点特别是在电池供电的便携设备里。4.4 一个让我排查了两天的 Bug片选控制太随意我不止一次在社区的帖子里看到有人问 ADS8681 读数偶发异常其实就是片选时序的问题。这颗芯片的片选信号必须在整个 16 位传输期间保持低电平而且两次传输之间要给它足够的休止时间不能把片选当普通 GPIO 随意拉来拉去。我调试时用了逻辑分析仪抓波形发现 STM32 的 SPI 在传输间隙会有几个空闲时钟导致我手动控制片选时偶尔把数据周期切错了。解决方式是在片选拉高之后加一个极短延时或者干脆让片选和 SPI 传输严格同步确保每次片选低电平期间只发生一次 16 位传输。听起来很简单但当时排查不到原因时真的很折磨人。如果你也遇到“数据 90% 是对的偶尔跳一个大数”的情况优先怀疑片选控制。5. 实测数据复盘ENOB、噪声底和线性度到底能到多少驱动跑通只是开始一块 ADC 模块好不好用最终要看实测数据。我在拿到样板后做了几组比较系统的测试包括短路噪声、直流线性度和不同输入范围下的有效位数对比。这些数据不是实验室级别的严格计量但足以反映模块在真实使用场景下的能力。5.1 测试环境与接线测试时我把模块放在桌面上输入端用一根短屏蔽线接一个精密电压源屏蔽层接地。SPI 的 SCLK 设定为 10MHz主控用 STM32F103数据不加任何滤波直接记录原始码值。每次测试采集 8192 个点统计均值和标准差再换算成电压值。整个过程我只改输入范围和输入电压不碰其他变量。5.2 实测结果不同输入范围下的噪声表现以 ±10V 量程为例输入接地时原始码值分布在零点附近。8192 个点的标准差大约在 0.7 个 LSB对应的电压噪声大约是 0.7 × 20V / 65536 0.21mV 左右。这个结果和手册典型值是吻合的。换成 0~5V 单极性量程后同样接地测量噪声略低大约 0.5 个 LSB这可能和 PGA 增益配置不同有关。输入范围实测噪声LSB RMS等效电压噪声备注±10V0.7 LSB0.21 mV默认量程适合工业信号±5V0.6 LSB0.09 mV略好于 ±10V 量程0~10V0.6 LSB0.09 mV单极性应用常用如果你只是做 12 位级别精度的控制类应用看到这个噪声根本不需要担心。即便你的目标是 16 位指标也不用慌后处理还有救。5.3 为什么有效位数到不了16给新手的心理预期很多人第一次用 16 位 ADC会期待读出来的每一个码都像教科书一样精确但实际上受基准噪声、电源噪声和 PCB Layout 影响有效位数能稳定做到 13~14 位已经很不错了。我这个模块在 ±10V 量程下的 ENOB 大概是 13.5 位左右和理论上的 16 位还有距离。这中间的差距主要来自基准源的带内噪声和输入信号的残留噪声。但这不代表 ADS8681 不行而是所有高精度 ADC 都面临同样的问题。你在数据手册里看到的 92dB SNR 之类的指标都是在极其纯净的测试条件下测出来的。工程里要做的是通过合理的平均和滤波把有效位数拉回来。比如我做的电机电流采集目标是波形分析而不是绝对精度13.5 位有效数据完全够用。如果你的应用要求更高的分辨率那就得认真考虑过采样和校准。5.4 提升精度的后续改造过采样与校准ADC 数字输出之后后端能做的第一件事就是过采样平均。对慢变信号把每 16 个采样点平均成一个点理论上能把噪声降低到原来的四分之一等效分辨率提高约 2 位。我写的adc_ranging_tool.py里就内置了这个功能可以用来快速评估不同采样率和平均次数下的噪声表现。硬件上的下一步改造是加装外部基准比如用 REF5040 这类低噪声基准芯片替代内部基准。虽然内部基准的温漂在数据手册上看已经很不错了但外部基准能在低频段提供更干净的噪声底。另外一个值得做的校准是零点校准和增益校准在模块出厂前用精密电压源测一组零点电压和满量程电压把修正系数存进 EEPROM这样能把每一块模块个体之间的差异消除掉。这个方法在量产设备里几乎是必做的否则每块板子接口互换后数据会有几个 LSB 的偏差。最后再分享一个我个人很满意的小技巧这个采集模块配套的 Python 脚本可以直接通过串口把实时波形画出来做现场调试时比连着示波器到处跑方便得多。你拿到 ADS8681.zip 之后硬件可以按你的需求改驱动可以直接抄上位机的可视化逻辑也能单独拆出来用。做数据采集系统这两三年我最大的体会是 ADC 本身只是一半另一半在系统的每个细节里——电源、布局、时序、后处理每一个环节都值得认真对待。本文还有配套的精品资源点击获取