简介面向硬件开发者的 AD7190 高精度 24 位 Σ-Δ 模数转换器驱动代码包聚焦工业与医疗场景下的传感器接口设计解决微控制器与 AD7190 之间的 SPI 通信、采样配置与数据解析问题。压缩包共 21 个文件、约 57KB以 C 源码、头文件、Keil 工程文件、编译中间文件及 HEX 固件为主便于直接移植到嵌入式项目中。目前已有 698 人学习适合正在调试高精度测量电路的软硬件工程师及电子相关专业学生。驱动内容涵盖初始化、数据读取、错误处理、中断管理与电源控制模块并附带示例代码可帮助开发者快速掌握可编程增益放大器、数字滤波器及多通道输入的实际配置方法。围绕 SPI 接口时序、寄存器操作和典型硬件连接提供完整参考能有效减少底层驱动开发与排错时间。 前阵子在调试一块高精度称重信号采集板主控选了 STM32模拟前端定了 AD7190。同事发来一个“AD7190驱动程序.zip”解压后是一套 Linux 平台下的 SPI 参考驱动附带设备树片段、用户态读取例程和一份不算太完整的 README。这套现成驱动对快速启动项目帮助不小但真正调板子时会发现寄存器配置顺序、SPI 时序、校准时机这些点如果不搞清楚驱动大概率跑偏。这篇文章就按我这次实际调试的顺序把 AD7190 驱动从拿到 zip 到把数据稳定读出来的过程拆开讲一遍。内容主要面向刚接触 AD7190、要在 Linux 或 MCU 平台上做高精度数据采集的开发者。你把驱动文件重新整理过一遍之后会发现读原始数据并不难难的是把噪声和漂移压下去。1. 先看清压缩包里都有什么1.1 一份参考驱动的典型文件结构我拿到的这个 zip 解压之后常见的文件组织方式大概是这样的AD7190驱动/ ├── ad7190.c ├── ad7190.h ├── ad7190.dts ├── tools/ │ ├── ad7190_read.c │ └── read_ad7190.py ├── README.md └── Makefilead7190.c 是核心驱动文件实现寄存器读写、设备初始化、校准和 IIO 设备注册。ad7190.h 里面是寄存器地址、掩码、通道和增益相关的宏定义。ad7190.dts 是设备树示例用来告诉内核这个 SPI 设备挂在哪条总线上、主频多少、参考电压用什么。tools 目录下的两个程序是用户态读取示例一个用 C一个用 Python方便在驱动加载后直接读取转换结果。如果你拿到的包不是这个结构也不用慌。ADI 官方仓库里的源码以及网上流传的各种移植版本核心逻辑都差不多区别主要在平台相关的 SPI 接口封装上。拿到驱动第一步不是编译而是先打开 ad7190.c 找几个关键函数reset、write、read、init。把数据通路理清楚后面怎么改都顺手。1.2 为什么说“驱动”不只是读写寄存器很多人觉得 ADC 驱动就是通过 SPI 写几个寄存器然后读数据这个理解没错但对 AD7190 这种高精度 Σ-Δ ADC 来说只做表面那层是不够的。AD7190 内部有一个 24 位 Σ-Δ 调制器SPI 接口只是它的外部通道真正的难点在于时钟配置、滤波器配置、校准时序、转换完成判断。驱动需要处理的第一个问题是 DOUT/RDY 引脚。这个引脚在转换完成时会拉低告诉主机“可以读数据了”。如果驱动只按固定延时去读数据寄存器很可能读到新旧数据混合的半更新状态造成结果跳变。所以好的驱动要么把 DOUT/RDY 配成中断输入要么在每次读数据前先查询状态寄存器里的 RDY 位。另一个容易被忽略的是复位时序AD7190 上电后需要收到一段特定的时钟序列才能稳定进入工作状态这一步做不好后面写寄存器可能完全不生效。把这些问题统一封装成接口用户态拿到的就不是一堆底层 SPI 字节而是一个干净的in_voltage0_raw这也就是“驱动”真正的价值。2. 初始化与寄存器配置驱动能不能跑关键在这2.1 关键寄存器速览AD7190 的寄存器不算多但每个都要清楚用途。我把最常用的几个整理成了一张表寄存器位宽作用通信寄存器8 位每次访问其他寄存器的“指路牌”选择后续读写地址和操作方式状态寄存器8 位包含 RDY 状态位、校准状态、通道状态等模式寄存器24 位选择时钟源、滤波模式SINC3/SINC4、输出数据速率、校准模式配置寄存器24 位配置通道选择、PGA 增益、单/双极性、模拟输入缓冲等数据寄存器24 位存放最新的转换结果二进制补码格式ID 寄存器8 位芯片 ID可用于检查 SPI 通信是否正常第一次接触 AD7190 的时候我最容易搞混的是通信寄存器和实际寄存器地址。AD7190 的每一个寄存器访问动作都要先通过通信寄存器“投石问路”发送一个 8 位指令告诉芯片接下来是读还是写目标寄存器是哪一个。然后主机再继续发送或接收对应位宽的数据。这个机制决定了驱动里所有读写操作都不是简单的spi_write而是要先组织一个指令头再拼上目标数据。2.2 从驱动代码里读出的初始化流程AD7190 的初始化流程我建议严格按这个顺序来复位芯片。通过连续向 DIN 写 40 个 1 触发复位让芯片回到默认状态。复位后所有寄存器恢复默认值之后必须等待复位完成时间不同主频下等待时间不同。配置模式寄存器。选择时钟源是用外部晶振还是内部 4.92MHz 时钟选择滤波模式 SINC3 还是 SINC4设置输出数据速率。输出速率和滤波字直接决定噪声水平。配置配置寄存器。选择要用的模拟输入通道设置 PGA 增益确认是单极性还是双极性输出。做校准。AD7190 支持内部零标度校准和满量程校准。校准的目的是消除 PGA 和调制器本身的失调与增益误差。等待 RDY 信号读取数据寄存器。为什么不能颠倒顺序原因在于通信寄存器具有“粘滞性”芯片会记住上一次通信指定的目标寄存器如果上一步没读完整下一步可能直接错位。其次配置寄存器的通道和增益设置必须在校准之前稳定下来否则校准结果对应的是错误的配置。驱动代码里如果发现校准数据异常先回看配置寄存器有没有写对。下面是一段初始化函数的伪代码示意实际使用时要根据数据手册补全宏定义static int ad7190_init(struct ad7190_state *st) { ad7190_reset(st); // 连续发送 40 个 1 ad7190_write_mode(st, AD7190_MODE_SEL_SINC3 | AD7190_MODE_CLKSRC_INT | AD7190_MODE_RATE(100)); // 设置滤波和输出速率 ad7190_write_config(st, AD7190_CONF_CH_AIN1_AIN2 | AD7190_CONF_GAIN_1 | AD7190_CONF_BIPOLAR); // 差分输入、PGA1、双极性 ad7190_calibrate(st, AD7190_MODE_CAL_INT_ZSC); ad7190_calibrate(st, AD7190_MODE_CAL_INT_FSC); return 0; }这段代码里的函数名和宏是我按常见驱动风格写的示意不同版本可能有差异但逻辑顺序是一致的。实际调试时我会在每次写完寄存器后回读一遍确认写入生效这种“写后读”的习惯能帮你省大量排查时间。3. SPI 通信细节和内核驱动整合3.1 SPI 模式和指令格式AD7190 的 SPI 接口通常工作在 Mode 1对应 CPOL 0、CPHA 1也就是 SCLK 空闲时为低电平数据在第二个边沿被采样。主控端配置 SPI 时必须把模式对准否则读出来的数据每一位都是乱的而且这种错位往往不会让你看到规律性的错误只会觉得噪声大得离谱。每一次通信都由通信寄存器开头。比如我想读数据寄存器先发一个指令字节这个字节的高位表示写或读中间几位表示目标寄存器地址低几位表示后续访问方式。发完指令字节后根据寄存器位宽继续发或收 8/24 位数据。MSB 先行这是所有 AD7190 驱动共同遵守的规则。有个小坑如果主控 SPI 外设的片选是硬件自动拉低的而 AD7190 对 CS 拉低到 SCLK 第一个边沿之间的建立时间有要求某些高速主控可能达不到。为了保险建议把 CS 用 GPIO 控制软件拉低完成一组完整 SPI 操作后再拉高这样时序最可控。Linux 下如果使用 spidev 直接操作CS 模式也建议明确设置为mode SPI_MODE_1。3.2 Linux 驱动集成从一个 .c 文件到 sysfsLinux 下的 AD7190 驱动一般挂在 IIO 子系统下注册之后用户态可以通过 sysfs 目录访问。核心流程是在probe函数里设置 SPI 参数读取设备树属性初始化芯片最后注册struct iio_dev用户态就能在/sys/bus/iio/devices/iio:device0/下面看到in_voltage0_raw。内核配置方面需要开启CONFIG_IIO和CONFIG_AD7190。设备树里至少要声明 compatible、reg、spi-max-frequency 和 vref-supply。参考写法如下spi0 { status okay; ad71900 { compatible adi,ad7190; reg 0; spi-max-frequency 1000000; vref-supply vref_3v3; #io-channel-cells 1; }; };vref-supply要指向实际的参考电压节点驱动在初始化时会用它计算转换系数。如果你的板子上 DOUT/RDY 连到了某个 GPIO也可以在设备树里加drdy-gpios让驱动用中断方式等待转换完成而不是轮询状态寄存器。编译时需要注意驱动代码可能在很老的内核版本上写的API 和新的 IIO 框架有差异。常见的坑是spi_write_then_read和iio_validate_scan_mask_onehot这些接口在新版本里改了名字编译报错时不要硬改内核版本优先找对应内核的源码分支。3.3 裸机平台移植思路如果最终目标是 STM32 或 ESP32完全可以复用这份驱动的逻辑只是把 Linux SPI 相关调用替换成平台 HAL 库。需要改动的部分并不多主要是三处SPI 收发函数。把spi_write/spi_read替换成HAL_SPI_Transmit/HAL_SPI_Receive注意设置 CPOL/CPHA。CS 控制。Linux 里通常由 SPI 框架管理裸机下用 GPIO 输出控制。延时函数。Linux 内核用usleep_range裸机下用HAL_Delay/delay_us。驱动核心逻辑比如寄存器组织、校准流程、RDY 判断完全不用动。这也是我建议先读 Linux 驱动源码而不是直接找裸机例程的原因——Linux 版本往往考虑得更全面错误处理也更完整。4. 实际调试中的坑和解决实录4.1 读出来全是 0 或者满量程这是最常见的现象。我遇到的情况是 SPI 模式配成 Mode 0导致通信寄存器指令字节没有被正确解析芯片根本没进入读数据模式。排查方法很简单先读 ID 寄存器。AD7190 的 ID 寄存器有固定值如果能读到预期值说明 SPI 通信正常如果读出来是 0x00 或 0xFF基本就是时序、模式或者片选的问题和 ADC 本身的寄存器配置无关。排除 SPI 问题后再看通道和增益。AD7190 的配置寄存器有通道选择位和 PGA 增益位。如果通道没对上比如实际信号在 AIN1/AIN2而驱动配置的是 AIN3/AIN4读出来自然不对。另外把增益设置得太高而输入信号又比较大转换结果就会进饱和区表现为接近满量程。我整理了一个问题速查表现象可能原因排查动作读 ID 寄存器返回 0SPI 模式错误、CS 时序不对、接线错误检查 SPI Mode 1、示波器抓 SCLK/DIN/DOUT读数据全零输入信号为负、单极性配置、通道没接对检查配置寄存器、用万用表量输入电压读数据接近满量程PGA 增益过高、参考电压异常降低增益检查 VREF数值一直乱跳滤波设置不对、DOUT/RDY 未判断检查模式寄存器改中断等待4.2 噪声大、数据漂移AD7190 本身的噪声指标很漂亮但那是建立在良好的电源和参考电压基础上的。驱动层面能做的是把输出数据速率压低启用 SINC4 滤波并正确完成校准。实际调试中我把输出速率从 4800 SPS 降到 50 SPS峰峰值噪声肉眼可见地降了一个数量级。还有一点很多人忽略AD7190 的数据寄存器是 24 位的如果你在转换完成之前读取可能拿到半更新的数据。如果你的程序只是用HAL_Delay等固定时间而不判断 DOUT/RDY 引脚就会出现偶发跳变。最简单的做法是把 DOUT/RDY 接到外部中断引脚每次拉低时再启动 SPI 读取这样能保证每次读到的都是完整帧。校准对最终精度影响也很大。内部校准的前提是输入引脚处于零标度和满量程对应状态。如果接的是桥式传感器零标度校准要在传感器空载时做如果传感器已经受力校准结果会把当前受力当成零点后续读数就带了一个很大的固定偏移。4.3 Linux 下设备加载不到板子上电后/sys/bus/iio/devices/下面没有新设备。先确认内核编译时有没有把 AD7190 驱动编进去zcat /proc/config.gz | grep CONFIG_AD7190。再确认设备树 compatible 字符串和驱动里的 match 表一致很多问题出在字符串大小写或者多了一个空格。如果驱动加载成功但 sysfs 下没有读数查看dmesg输出。常见报错是 vref-supply 未定义导致 probe 函数返回失败。另外 SPI 的最大频率不要设太高我一般建议从 1MHz 开始跑通后再一点点往上加AD7190 对 SCLK 的上升沿要求比较严格频率太高容易采到毛刺。4.4 下载的驱动包被安全软件拦截如果你在 Windows 环境下解压驱动包系统提示“无法验证此设备所需的驱动程序的数字签名”或者“检测为易受攻击的驱动程序”多半是包里的 Windows 驱动没有微软签名或者版本太旧。遇到这种情况我一般不会贸然关闭系统安全设置而是去原厂官网找最新版本或者只把这类驱动放在隔离的测试机里使用。对 Linux 驱动来说源码编译的模块不受签名机制影响但如果是在 Windows 下通过 DLL 调用 AD7190就要格外留意驱动来源。5. 快速使用读取转换结果5.1 通过 sysfs 读原始码驱动加载成功后读取就很直接了cd /sys/bus/iio/devices/iio:device0 cat in_voltage0_raw cat in_voltage0_scalein_voltage0_raw是 ADC 原始码in_voltage0_scale是每个码对应的微伏数。实际电压换算公式是电压(V) raw * scale / 1e6如果配置的是双极性模式raw 是二进制补码可能为负值计算时直接用乘法就能得到正确符号不用自己转换。5.2 用 Python 写一个最简单的读取循环验证驱动有没有跑通我通常直接写个小脚本import time def read_adc(path/sys/bus/iio/devices/iio:device0): with open(path /in_voltage0_raw) as f: raw int(f.read().strip()) with open(path /in_voltage0_scale) as f: scale float(f.read().strip()) return raw * scale / 1e6 while True: voltage read_adc() print(f{voltage:.6f} V) time.sleep(0.1)这个脚本对新手小白也很友好不依赖任何第三方库直接把 sysfs 文件当普通文本读就行。如果每秒需要采几千个点用 Python 读 sysfs 就不太合适了这时候应该走 IIO buffer配合 trigger 批量读取。但先跑通这个简单循环确认硬件和驱动没问题再上高级功能是我自己的习惯。5.3 什么时候不能直接用现成驱动现成驱动覆盖了大多数场景但如果你要同时采集多个通道并且每个通道需要不同的滤波和增益那就需要对驱动做扩展。比如称重应用里一个通道接传感器另一个通道接温度传感器做冷端补偿这两个通道的配置不一样驱动需要支持通道切换和重新校准。另一个需要扩展的场景是同步采集。如果你有多片 AD7190 同时采样要求各片的数据严格对齐那就不能只靠 sysfs 一个个读要使用 GPIO 触发或外部同步引脚结合 IIO trigger 来实现。这部分内容比较深但顺序一定是先跑通单设备 sysfs 读取再去碰 buffer 和多设备同步否则出了问题很难定位。我这次调完这套驱动后最大的体会是AD7190 这种高精度 ADC寄存器配置只要顺序对了SPI 通信只要模式对了基本都能出数。真正让人头疼的是数据出来以后怎么判断准不准、稳不稳以及怎么把噪声压下去。你先学会看 ID 寄存器和 RDY 引脚再谈精度这比研究任何玄学都管用。如果你也正在折腾 AD7190希望这份驱动拆解能帮你把启动时间从一两天缩短到半天。本文还有配套的精品资源点击获取