简介:STM32驱动AD7175的源码包面向嵌入式软硬件工程师、电子竞赛参赛者以及需要快速集成高精度ADC的开发者,解决在STM32平台上移植与调试AD7175驱动时的底层配置问题。源码采用SPI接口实现16位Σ-Δ ADC的通信,覆盖初始化、参数配置、转换结果读取等完整流程,能够帮助用户绕开寄存器手册查阅和时序调试的弯路。资源共2个文件,即1个头文件和1个源文件,RAR压缩包总大小仅3KB;头文件封装寄存器定义、命令字节与函数原型,源文件实现SPI读写、模式设置、滤波器配置及数据校验等驱动细节,结构紧凑,适合直接拷入工程中二次开发。已有3900余人学习下载,适用于工业测量、医疗设备等低噪声模拟前端场景。对照代码可加深对连续转换、单次转换及突发模式等关键机制的理解,并为后续扩展桥式输入或外部基准电压配置提供可复用的底层框架。 前些日子调一套多通道精密采集设备,MCU 选了 STM32F405,模拟前端却折腾了好一阵:一路差分信号来自称重传感器,满量程只有几毫伏;另一路是 0~5V 工业传感器输出,MCU 内置 12 位 ADC 的位数和噪声水平根本不够看。翻了一圈外置 ADC,最后落在 ADI 的 AD7175 上。这颗 24 位 Sigma-Delta ADC 自带可编程增益放大器和内部基准,SPI 接口直连 STM32,方案成熟资料也多。我把整套 STM32 驱动 AD7175 的工程整理成了源码包,包括初始化配置、SPI 读写封装、连续采样逻辑和调试笔记。这篇文章就把工程里最关键的部分拆开讲,重点说清楚硬件连接、寄存器配置、代码骨架和调参踩坑,给准备做高精度采集的朋友一条能直接走的路。
1. 选型逻辑:为什么高精度采集要外挂一片 AD7175
1.1 MCU 内置 ADC 在高精度场景下的三个硬伤
STM32 内置 ADC 在大多数控制类项目里完全够用,但放到精密采集场景就有明显短板。首先是位数,12 位分辨率在满量程 3.3V 时一个 LSB 大约 0.8mV,想分辨微伏级信号变化基本不可能。其次是参考电压,内置 ADC 的 VREF+ 如果直接接 VDD,电源纹波会毫无保留地耦合进采样结果,而外置高精度 ADC 可以搭配独立低噪声基准源。最后是模拟和数字部分的隔离,MCU 引脚翻转、总线活动产生的噪声会通过基板和电源平面串进模拟采样通道,内置 ADC 在这种环境下想做到高信噪比难度很大。
1.2 Sigma-Delta 架构的过人之处
AD7175 用的是 Sigma-Delta 架构,核心思想是“过采样 + 数字滤波”。它用远高于输出数据速率的采样频率对输入信号反复采样,再用数字滤波器把带外噪声压掉,换来的就是高分辨率和高有效位。打个比方,内置 ADC 像一次拍一张照片,拍完就走;Sigma-Delta 像连拍几百张再合成一张,细节自然丰富得多。
这颗芯片几个关键指标很能打:24 位无失码分辨力,集成 1~128 倍可编程增益放大器(PGA),内置基准源,输出数据速率从 1.25SPS 到 250kSPS 可配置,SPI 接口同时支持 CRC 校验。PGA 的意思是,毫伏级差分信号进来后可以先放大 128 倍再进 ADC,把微弱信号撑到满量程附近,等效分辨率大幅提升,这对称重传感器和热电偶这类小信号场景特别实用。
1.3 和常见替代方案的简单对比
做高精度采集,很多人还会考虑 TI 的 ADS1256。我实际对比过两颗芯片:
| 指标 | AD7175 | ADS1256 | STM32 内置 ADC |
|---|---|---|---|
| 分辨率 | 24 位 | 24 位 | 12 位 |
| 最高输出速率 | 250kSPS | 30kSPS | 1MSPS 以上 |
| 内置 PGA | 1~128 倍 | 1~64 倍 | 无 |
| 内置基准 | 有 | 无(需外接) | 部分型号有 |
| 工频抑制 | 数字滤波器可配置 | 有专用滤波选项 | 较弱 |
从表格能看出来,AD7175 的优势在于速率上限高、集成度高,对需要快速响应的称重检重、动态应变测量场景更友好。ADS1256 的驱动例程更多一点,但 AD7175 一旦跑通,性能和灵活性都好一截。我在这个项目里最终选 AD7175,就是看中它的 PGA 和数字滤波组合能力。
2. 硬件连接:电源、参考电压和 SPI 引脚的关键细节
2.1 模拟电源和数字电源分开敷设
高精度 ADC 的硬件设计,第一原则是“模拟域和数字域尽量不互相耦合”。我板子上 AVDD1、AVDD2 用一路 LDO 供电,DVDD 单独用另一路 LDO,两路之间串一个磁珠做单点连接,模拟地和数字地在 ADC 芯片下方单点汇合。这样可以避免数字电源纹波通过公共阻抗窜进模拟前端。实测下来,电源处理得好不好,对最终噪声底的影响比选哪颗 ADC 还大。
每个电源引脚旁边都放了 0.1uF 和 10uF 两级去耦电容,电容尽量靠近引脚放置。很多新手容易忽略 10uF 大电容,其实它对低频纹波和高频噪声都有缓冲作用。另外,AD7175 的 REF+ 和 REF- 引脚需要一个足够干净的基准电压。如果板载基准精度要求不高可以凑合,但做精密测量我建议外接 ADR4525、REF5025 这类低噪声基准芯片,基准源的噪声会直接叠加到测量结果上,这个钱不能省。
2.2 SPI 引脚分配和使用中的几个注意点
AD7175 的 SPI 接口是标准的四线:SCLK、DIN、DOUT、CS,另外还有 DRDY、SYNC、RESET 等控制脚。我用的是 STM32F405 硬件 SPI1,引脚分配是 PA5(SCK)、PA6(MISO)、PA7(MOSI),CS 用 PB0 普通 GPIO 控制。如果换别的板子,只要保证硬件 SPI 或软件模拟 SPI 的逻辑正确,引脚可以灵活调整。
连线时有几个细节要特别注意。VIO 引脚决定 SPI 接口的电平域,它接多少伏,SPI 引脚电平就是多少伏,所以要保证 VIO 和 STM32 的供电电压匹配,不然通信电平不在同一域里,读回来的数据会莫名其妙。DRDY 引脚是转换完成标志,每个转换周期结束后会拉低,适合接外部中断引脚,我正在用的 PD0 下降沿中断读取方式就是从这来的。RESET 引脚建议通过 RC 复位电路控制,确保上电时芯片能可靠复位。
2.3 模拟输入通道的接线方式
AD7175 的模拟输入支持差分输入和伪差分输入,实际使用时一定要搞清楚信号地怎么接。我这里称重传感器本身就是差分输出,AIN0 和 AIN1 直接接传感器差分正负即可;另一路工业传感器是单端信号,我把 AIN2 接信号正端,AIN3 接信号地,用伪差分方式减小共模干扰。模拟输入走线要尽量短、尽量粗,远离数字信号线,尤其是 SCLK 这种翻转频率高的线,串扰会直接影响采样精度。
3. 寄存器配置:把数据手册的时序图变成可运行的初始化代码
3.1 先理解 AD7175 的通信寄存器机制
AD7175 的 SPI 通信和普通外设不一样,它没有一个固定的“命令字表”,而是要求每次读写操作之前,先往通信寄存器写一个命令字,告诉芯片“接下来我要读还是写、操作哪个寄存器”。这个机制对新手来说是最容易卡住的地方。我刚接触这颗芯片时也在这里绕了半天,后来总结成一句话:所有操作都是“先发命令字,再传数据”。
命令字的构成在数据手册通信寄存器一节写得很清楚:最高位 WEN 必须为 0 表示允许写入,bit6 是读写方向位,0 表示写、1 表示读,剩余位是寄存器地址。比如读数据寄存器(寄存器地址 0x04)的命令字就是 0x40 | 0x04 = 0x44。这个流程想通了,后面的代码就顺了。
3.2 常用寄存器地址速查
我把工程里用到的几个寄存器地址整理出来,记不住的时候翻这张表就行了:
| 寄存器 | 地址 | 说明 |
|---|---|---|
| 通信寄存器 | 0x00 | 发命令字用,不直接读写数据 |
| ADC 模式寄存器 | 0x01 | 转换模式、时钟源 |
| 接口模式寄存器 | 0x02 | SPI 模式、CRC 开关、IO 模式 |
| 数据寄存器 | 0x04 | 24 位转换结果 |
| GPIO 配置寄存器 | 0x06 | 通用 IO 配置 |
| ID 寄存器 | 0x07 | 芯片 ID,通信自检用 |
| ADC 配置寄存器 | 0x08 | 极性、PGA 增益、基准选择 |
| 滤波配置寄存器 | 0x0C | 滤波器类型、输出速率分频 |
3.3 初始化序列和关键代码
初始化流程我建议按照“复位 → 配置接口模式 → 配置 ADC 模式 → 配置滤波 → 配置 ADC 参数 → 启动转换”的顺序来。通信读写函数封装得很简单:
static void ad7175_cs_low(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); } static void ad7175_cs_high(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); } static void ad7175_write_reg(uint8_t reg, uint8_t *data, uint8_t len) { uint8_t cmd = 0x00 | reg; /* 写命令,低 6 位放寄存器地址 */ ad7175_cs_low(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY); ad7175_cs_high(); } static void ad7175_read_reg(uint8_t reg, uint8_t *data, uint8_t len) { uint8_t cmd = 0x40 | reg; /* 读命令,bit6 置 1 */ ad7175_cs_low(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); HAL_SPI_Receive(&hspi1, data, len, HAL_MAX_DELAY); ad7175_cs_high(); }关键配置函数长这样:
void ad7175_init(void) { uint8_t buf[2]; ad7175_reset(); /* 连续发 64 个 0xFF 触发芯片复位 */ /* 接口模式寄存器:4 线 SPI,关闭 CRC,具体取值见手册 */ buf[0] = 0x00; ad7175_write_reg(0x02, buf, 1); /* ADC 模式寄存器:连续转换模式,内部时钟 */ buf[0] = 0x20; ad7175_write_reg(0x01, buf, 1); /* 滤波配置寄存器:Sinc3 滤波器,输出速率先设为 5SPS */ buf[0] = 0x00; buf[1] = 0x05; ad7175_write_reg(0x0C, buf, 2); /* ADC 配置寄存器:双极性,PGA=1,使用外部基准 */ buf[0] = 0x00; ad7175_write_reg(0x08, buf, 1); }这里有一个特别重要的顺序问题:滤波配置寄存器是 2 字节,必须先写会更改输出速率的分频值,再配置 PGA 增益。否则采样结果会受滤波器建立时间的干扰,开头几帧数据可能偏离真实值。
4. 采样程序骨架:从 DRDY 轮询到数据拼接与电压换算
4.1 连续转换模式和 DRDY 引脚的作用
AD7175 支持单次转换和连续转换两种模式。连续转换模式适合实时数据采集:芯片启动后自动按设定速率不断转换,每个转换周期结束,DRDY 引脚拉低一次。读取数据的标准做法是轮询或中断监听 DRDY,等它变低后再读数据寄存器。轮询方式简单,适合数据率不高的场景;数据率较高时建议用外部中断,避免主循环被 SPI 操作占用太久。
我在工程里用的是中断方式:DRDY 接 PD0,配置成下降沿触发外部中断。每次中断进来,就说明有一帧新数据准备好,中断服务函数里调用一次读取函数,把结果存进环形缓冲区,主循环再从缓冲区取数据做滤波和换算。这样结构清晰,也不会丢数据。
4.2 读取 24 位数据并做符号扩展
AD7175 的数据寄存器是 24 位,高位在前。读取时先发读命令 0x44,然后连续读 3 个字节:
int32_t ad7175_read_data(void) { uint8_t buf[3]; int32_t raw; uint8_t cmd = 0x44; /* 读数据寄存器 */ ad7175_cs_low(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); HAL_SPI_Receive(&hspi1, buf, 3, HAL_MAX_DELAY); ad7175_cs_high(); raw = ((int32_t)buf[0] << 16) | ((int32_t)buf[1] << 8) | buf[2]; if (raw & 0x800000) { raw |= 0xFF000000; /* 24 位负数扩展成 32 位有符号数 */ } return raw; }这段代码里最容易忽略的是符号扩展。AD7175 在双极性模式下,输出码是带符号的二进制补码格式,0V 对应 0x800000,正满量程接近 0xFFFFFF,负满量程接近 0x000000。如果不做扩展,负电压会被误判成很大的正数,后续换算全乱。很多新手第一次看到读数在 0x800000 附近浮动就以为芯片坏了,其实这是双极性 0V 的正常表现。
4.3 原始码换算成实际电压
拿到原始码之后,要换算成实际电压。双极性模式下电压计算公式为:
电压 = (原始码 - 0x800000) × 基准电压 / (0x800000 × PGA增益)
比如基准电压 5V、PGA=1、原始码 0x800000 时电压为 0V;原始码 0x9FFFFF 时电压约 1.249V。工程里我封装了一个函数做换算:
float ad7175_raw_to_voltage(int32_t raw, float vref, float pga_gain) { float voltage = ((float)raw - 8388608.0f) * vref / (8388608.0f * pga_gain); return voltage; }注意这里 8388608 就是 0x800000。如果不关心具体电压值,只想看信号相对变化,直接拿原始码做差分计算更省事。
4.4 主循环里怎么组织数据流
主循环的逻辑是:从环形缓冲区拿最新一组数据 → 判断数据有效性 → 换算电压 → 前端显示或上位机上传。如果做高精度测量,建议外加一个简单均值滤波,把连续 N 次采样做平均。不过要注意 AD7175 本身已经有很强的数字滤波能力,软件均值滤波的窗口不要太大,否则响应速度会受影响。我的经验是 8 次均值就够了,再多属于心里安慰。
5. 调试过程中的那些坑:噪声、读取错位与滤波参数
5.1 用 ID 寄存器验证 SPI 通信是否正常
移植完成后的第一件事,不是急着看采样数据,而是先读 ID 寄存器。AD7175 的 ID 寄存器地址是 0x07,正常读出来应该是 0xCD2。如果读到的值和这个差得远,说明要么 SPI 配置不对,要么硬件连接有问题。这个自检步骤能帮你把问题范围快速缩小,省去后面一堆弯路。
我调试时遇到过读回 0xFFFFFFFF 的情况,检查一圈发现是 SPI 时钟极性和相位配错了。AD7175 对不同板卡布局的 SPI 时序比较敏感,代码里我用的 SPI_MODE0,如果读到全 1 或全 0,先切换 CPOL 和 CPHA 试试看。另外,片选引脚必须用普通 GPIO 软件控制,不能依赖硬件自动片选,AD7175 的读操作需要 CS 全程拉低,中途不能松开。
5.2 数据在 0x800000 附近跳动的正解
如果你配置成双极性模式,采集一个稳定信号时读数始终在 0x800000 附近跳,不要慌。先确认输入引脚是不是浮空或者信号本身接近 0V,如果是,这就是正常现象。0x800000 是 0V 对应的中间码,周围几个 LSB 的跳动反映的是系统噪声水平。想判断芯片状态好不好,可以把模拟输入直接短接到地,测出来的噪声峰峰值才是真实底噪,一般几个微伏以内就说明系统正常。
5.3 噪声偏大的排查顺序
实际测试下来,如果噪声明显偏大,排查顺序我建议是:先看电源纹波,用示波器量 AVDD 引脚波形,纹波超过 10mV 就要处理电源;再看基准源,基准噪声会直接叠加在结果里;再看布局,SPI 时钟线是不是贴着模拟输入线走了;最后才是怀疑芯片本身。九成以上的“ADC 噪声大”问题都出在电源和布局上,跟芯片关系不大。我还遇到过一次诡异现象:接上 J-Link 调试器噪声变大,拔掉就正常,那是调试器通过地线引入了共模噪声,属于正常现象,不用太纠结。
5.4 滤波器和输出速率的配合
AD7175 的数字滤波器有 Sinc3、Sinc4 等选项,输出速率由滤波寄存器里的分频值决定。速率越高,噪声越大;速率越低,噪声越小,这是 Sigma-Delta ADC 最基本的规律。如果你的测量对象是缓慢变化的温度或压力,把速率设到 5SPS 左右,噪声能压到非常低;如果做动态称重需要快速响应,可能需要 50SPS 甚至更高,这时要做好噪声变大的心理准备。工程里我默认配的 5SPS,读者可以根据自己场景改分频值。
6. 最后补充两个移植时的小经验
第一个经验是:拿到任何一份 AD7175 驱动源码,不要急着整包烧进板子。先把初始化函数拆出来,只留最小配置,确认 ID 能读通,再加入数据读取,最后才接滤波和业务逻辑。这样每一步出错都能马上定位。
第二个经验是:AD7175 的寄存器虽然不多,但每个寄存器修改后都需要等待一小段时间让芯片内部稳定。我在代码里每个配置步骤之间都加了 1ms 延时,避免出现“配置写进去了但没生效”的玄学问题。
这个工程后面我打算再加两路通道,把 AD7175 的 4 路模拟输入全部利用起来。调试过程中踩过的坑都记在源码注释里了,如果按照这篇文章的顺序走一遍,应该能省下不少时间。
本文还有配套的精品资源,点击获取