STM32F103驱动AD7175-2高精度ADC:SPI配置与调试实战
简介本资源是一份面向嵌入式开发工程师与STM32初学者的高精度ADC驱动实践代码聚焦STM32F103与AD7175-224位Σ-Δ型高精度ADC的SPI通信与数据采集实现。资源解决工业传感、精密测量等场景中对低噪声、高分辨率模拟信号采集的核心需求涵盖初始化配置、寄存器写入、连续读取及24位数据解析等关键环节。压缩包为RAR格式仅含1个核心文件——main.c完整实现了GPIO/SPI外设初始化、AD7175-2寄存器配置含参考源、PGA增益、输出速率、带时序校验的SPI多字节读取及电压值换算逻辑代码精炼2KB结构清晰便于移植与调试。目前已有1821人学习下载适合需要快速掌握高精度ADC在Cortex-M3平台落地的开发者尤其适合作为工业仪表、环境监测类项目的底层采集模块参考范例。 搞高精度数据采集这几年我最大的体会是模拟前端设计得再漂亮ADC驱动写不好数据一样没法看。最近手上正好完成了一块基于STM32F103 AD7175-2的高精度采集板从画板到固件稳定跑通中间踩了不少坑。这篇就把整个项目的设计思路、SPI驱动移植、寄存器配置和调试经验完完整整梳理一遍给正在做类似项目的朋友一个参考。这个项目里主控选的是最常见的STM32F103负责通过SPI总线读写AD7175-2的寄存器、启动转换、读取24位转换结果最后通过串口把数据送出去。AD7175-2是ADI的24位Σ-Δ ADC通道数、速率、有效分辨率都非常适合做多通道精密测量。如果你正准备做称重、温度、压力、振动信号采集这类项目这篇文章的硬件接线和固件流程可以帮你少走很多弯路。1. 项目概述与整体设计思路1.1 为什么选择STM32F103 AD7175-2组合先说选型逻辑。项目需求是采集4路模拟信号分辨率要求不低于20位有效位每通道输出速率在几十到几百SPS之间同时要求主控容易采购、量产方便。看了一圈市面上能满足这个需求的ADC其实不少比如TI的ADS1256、ADI的AD7793、AD7175系列。但综合对比下来AD7175-2有两个点很吸引我。第一AD7175-2的最大输出速率能到250kSPS即使我只用几十SPS它也有充足余量做数字滤波和过采样。第二它的模拟输入通道可以灵活配置成2路全差分或4路单端这对我这种要兼容不同传感器类型的产品非常友好。至于主控选STM32F103原因就不多说了生态成熟、价格透明、参考资料丰富任意一个硬件工程师都能在几小时内把最小系统画出来而且它的SPI1最高可以跑到18MHz虽然跑不满AD7175-2的40MHz SCLK上限但实际200kSPS以下的吞吐量完全够用。我对比过几款常见ADC和主控组合见下表组合分辨率最大速率通道数驱动难度综合成本STM32F103 AD7175-224bit250kSPS4中等中高STM32F103 ADS125624bit30kSPS8较低中等STM32F103 AD779324bit4.17kSPS3较低较低STM32F103 内部ADC12bit1MSPS3低极低如果你的应用只需要低速测量AD7793确实更省钱省事。但一旦以后要扩展通道或者提高速率AD7175-2的潜力更大。顺便提醒一句如果项目通道数超过4路建议直接看AD7175-8引脚和寄存器大部分兼容省得后期换芯片大改代码。1.2 AD7175-2核心特性与选型理由AD7175-2这颗芯片有几个参数我在选型时必须确认清楚。参数数值实际意义分辨率24位No Missing Codes保证全量程不丢码最大输出速率250kSPS高速场景可用有效分辨率典型21.5位10SPS时低速时噪底极低模拟输入通道4路可配2差分/4单端支持多种传感器数字接口SPI支持CRC校验适合长距离数据线基准电压外部或内部灵活性高数字滤波器Sinc5Sinc1、Sinc3、Sinc5工频抑制可选以前做过一个项目用12位ADC量程0-5V理论上分辨率是1.2mV但实际因为噪声、参考电压漂移能做到10mV稳定就不错了。换到AD7175-2之后标称分辨率是量程除以2^24约0.3uV虽然不要指望24位全是有效位但在10SPS输出速率下有效分辨率达到20位以上是实实在在的。这就意味着小信号在低速率下能被分辨出来。选这颗芯片还有一个重要理由它的SPI接口里带一个非常方便的DOUT/RDY引脚转换完成会自动拉低主控可以轮询这个引脚而不是死等延时既省CPU又不会出现读了一半数据转换还没完成的老问题。这一点在下面固件设计部分会用到。1.3 系统架构总览整个采集系统的数据流大概是这样的传感器输出模拟信号经过一级简单的RC滤波后进入AD7175-2的模拟输入引脚。AD7175-2根据配置好的通道、增益、滤波器和输出速率完成模数转换转换结果通过SPI接口读入STM32F103。主控拿到原始码值后做偏移校准、量程换算再通过串口或者LCD显示出来。我在设计时把信号链路拆成三段模拟前端、ADC核心、数字处理。模拟前端和ADC核心之间用Jumper可断开方便做故障排查。这个习惯是从上一台仪器设计里学到的遇到数据异常时可以直接断开模拟前端用信号发生器直接灌信号给ADC快速定位问题是出在模拟侧还是数字侧。2. 硬件电路设计要点2.1 SPI接口接线与电平匹配STM32F103和AD7175-2的SPI连接我直接用了标准四线制SCLK、DIN、DOUT、CS外加一根DOUT/RDY复用引脚。信号对应关系如下STM32F103引脚复用功能AD7175-2引脚说明PA5SPI1_SCKSCLKSPI时钟PA7SPI1_MOSIDIN写入数据PA6SPI1_MISODOUT/RDY读出数据/转换完成标志PA4GPIO输出CS片选低有效任意GPIOGPIO输入RESETADC复位引脚这里有个容易被忽略的点AD7175-2的DOUT/RDY引脚是推挽输出和STM32F103的MISO直接相连没有问题。但如果AD7175-2用5V电源供电那就得确认STM32F103的引脚是否容忍5V。STM32F103大部分IO不是5V容忍的至少我查到的普通IO输入耐压在4V左右不建议直接接5V电平的输出。所以在原理图上如果ADC的DVDD用5V就必须加电平转换或者串限流电阻加外部钳位。我这次设计干脆把DVDD和AVDD都接3.3VSPI直连省掉一堆麻烦。另外SCLK和DIN两个输出引脚上我各串了一个33Ω电阻这个阻值不是算出来的而是根据经验选的一个缓冲值。它有几个好处一是减小信号边沿振铃二是如果杜邦线接错或短路能起到一定的保护作用。实际测试中加上串联电阻后SPI波形更干净误码率明显下降。CS引脚用的是普通GPIO而不是SPI的NSS硬件片选原因很简单硬件NSS在STM32上自动控制太“死”而AD7175-2的寄存器操作有时候需要在CS低电平期间连续传输多帧用软件控制CS更灵活。2.2 模拟输入与参考电压设计AD7175-2的模拟输入支持差分和单端两种模式。我用的是差分模式AIN0接正输入AIN1接负输入这样对共模噪声有很好的抑制效果。如果传感器本来就是单端输出的可以把负输入端接到参考地或者连到一个低噪声的偏置电压上但不建议直接把AINM接地因为ADC输入端需要一定的共模范围。根据手册输入共模电压应该在AVDDAVSS/2附近所以单端应用时最好把负输入接到AVDD/2上。参考电压这一块我选的是外部基准源ADR4525输出2.5V温漂低到2ppm/°C。为什么不用内部基准因为内部基准虽然省事但温漂和噪声都明显高于外部基准。对于24位ADC来说参考电压的噪声会直接折算到转换结果的噪声里。可以这样理解ADC好比一把尺子参考电压就是尺子上的刻度刻度本身都在抖量出来的尺寸自然不准。在参考电压引脚REF和REF-之间我并联了一个1uF和0.1uF电容并且在layout上让它们紧贴REF引脚。这点非常重要因为ADC内部的采样电容在转换过程中会从REF引脚抽取瞬态电流如果去耦电容离得太远走线寄生电感会让参考电压瞬间塌陷直接影响转换结果。这也是很多工程师明明是同样一颗ADC做出来的噪声指标却比原厂EVM差一大截的主要原因之一。2.3 供电与去耦布局AD7175-2有两组电源引脚AVDD1、AVDD2和DVDD。我在原理图上把它们分开走线最后在PCB上单点汇合。AVDD1和AVDD2各加一个10uF钽电容和一个0.1uF陶瓷电容DVDD加一个4.7uF和0.1uF。陶瓷电容要选X7R或C0G材质不要用Y5V虽然便宜但温漂和压降特性太差。电源入口我加了一个磁珠和LC滤波把数字电路的开关噪声和模拟部分隔开。STM32F103虽然只是做读写操作但它的IO翻转会产生微小的地弹噪声如果不隔离这些噪声会通过电源网络耦合到ADC的模拟电路里。一个很简单的验证方法用导线短接模拟地和数字地分别测一下ADC输出码值的标准差。我在调试时按住STM32F103的SPI引脚做连续读写测试发现数据噪声确实比不读写时稍大加了磁珠隔离后明显改善。3. 固件驱动开发全流程3.1 使用STM32CubeMX配置SPI基础工程固件开发我基于STM32CubeMX HAL库说实话用寄存器也可以但HAL库在代码可读性上更好后面接手的人维护成本低。CubeMX里关键配置项如下SYS - Debug: Serial Wire留出SWD调试口RCC - HSE: Crystal/Ceramic Resonator外部8MHz晶振SPI1 - Mode: Full-Duplex Master硬件NSS关闭SPI1参数Frame FormatMotorolaData Size8 BitsFirst BitMSB FirstPrescaler16即48MHz/163MHzCPOLHighCPHA2 EdgeCPOL和CPHA是关键。AD7175-2支持SPI Mode 0CPOL0, CPHA1和Mode 3CPOL1, CPHA1。等等这里要仔细说一下。AD7175-2的数据手册里有一张SPI时序图它要求外部主机在SCLK下降沿发送数据、在SCLK上升沿采样数据不对我实际测试中发现使用CPOL1、CPHA1Mode 3最稳。如果你用的是标准SPI库建议先按Mode 3配置读不到正确数据再试Mode 0。因为这个时序踩坑的人不少多说一句SPI的极性极性问题在芯片手册里写得比较晦涩直接查“AD7175 SPI”相关论坛讨论比看手册快得多。SPI时钟先设成3MHz而不是最高的18MHz是因为高速SPI对PCB走线和杜邦线质量要求很高。先低速把功能调通再逐步提速这是我一贯的做法。生成代码后还需要手动配置一个GPIO作为CS并在main函数里把AFIO时钟打开如果用了复用功能。CubeMX会自动处理大部分但CS引脚因为是普通GPIO需要自己在初始化代码里设置。3.2 AD7175-2 SPI读写时序实现AD7175-2的SPI读写核心是通信寄存器机制。每次访问任何寄存器之前主机需要先发送一个命令字节这个字节低7位是目标寄存器地址最高位是读写标志。写寄存器时命令字节的bit0为0读寄存器时为1。下面是我在工程里实际使用的两个函数// 写AD7175寄存器 void AD7175_WriteReg(uint8_t reg_addr, uint8_t *data, uint8_t len) { uint8_t cmd reg_addr 1; // 写命令 HAL_GPIO_WritePin(AD7175_CS_PORT, AD7175_CS_PIN, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); HAL_SPI_Transmit(hspi1, data, len, HAL_MAX_DELAY); HAL_GPIO_WritePin(AD7175_CS_PORT, AD7175_CS_PIN, GPIO_PIN_SET); } // 读AD7175寄存器 void AD7175_ReadReg(uint8_t reg_addr, uint8_t *data, uint8_t len) { uint8_t cmd (reg_addr 1) | 0x01; // 读命令 uint8_t dummy 0x00; HAL_GPIO_WritePin(AD7175_CS_PORT, AD7175_CS_PIN, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); HAL_SPI_TransmitReceive(hspi1, dummy, data, len, HAL_MAX_DELAY); HAL_GPIO_WritePin(AD7175_CS_PORT, AD7175_CS_PIN, GPIO_PIN_SET); }注意读寄存器时HAL库的HAL_SPI_Receive在全双工模式下需要主机同时发送数据才能产生时钟所以我用了HAL_SPI_TransmitReceive不管是发哑字节还是发0x00目的都是给从机提供时钟。这里如果直接用HAL_SPI_Receive某些版本的HAL库不会自动发送时钟读回来的数据永远是0xFF非常容易踩坑。还有一点CS引脚拉低之后所有字节的传输必须在CS低电平期间一次性完成中间不要拉高CS否则AD7175会认为主机结束了本次访问。这两个函数的节奏是被CS信号严格框住的。3.3 寄存器初始化顺序与关键配置初始化顺序这个事容易被忽略。AD7175-2不是所有寄存器上电后都可以直接改有些寄存器必须在ADC禁用状态下才能配置。我初始化时严格按照以下顺序操作不是随手写的每一项都有其目的。第一步复位芯片。通过RESET引脚拉低10us以上再拉高或者向DIN连续发送64个高电平脉冲。我习惯用引脚复位逻辑更清晰代码也更短。第二步等待至少500us让ADC完成内部初始化。然后读ID寄存器地址0x07确认SPI通信正常。这个检查可以在调试时做一个断言如果读到的值和手册不一致直接打印错误信息避免后面带着错误配置往下跑。第三步写接口模式寄存器IFMODE地址0x02。我会配置成连续读模式关闭、数据不附加状态字节、CRC关闭、16位/24位数据宽度选24位。如果项目里SPI数据线比较长可以开启CRC校验但初次调通不建议开CRC会把问题复杂化。第四步写ADC模式寄存器ADCMODE地址0x01。这里设置转换模式为连续转换模式基准选择外部基准。内部基准虽然方便但前面说过噪声问题如果外部基准做到了手册推荐的低噪声水平优先外部基准。第五步配置输入通道。比如把通道0配置为AIN0正输入、AIN1负输入启用该通道并关联到配置0和滤波器0。我项目里4路输入就依次配置CH0、CH1、CH2、CH3有些通道不用就关闭保持寄存器干净。第六步配置滤波器寄存器FILTER0地址0x28。实际项目使用Sinc5Sinc1滤波器输出速率设置为10SPS。这个配置下有效分辨率可以达到20位以上而且对50Hz工频干扰有很强的抑制。如果要测量快速变化的信号可以提高到100SPS甚至1kSPS但噪声会相应增大需要在采样率和分辨率之间做权衡。下面是初始化函数的代码示意void AD7175_Init(void) { uint8_t tmp[2]; // 1. 复位 HAL_GPIO_WritePin(AD7175_RESET_PORT, AD7175_RESET_PIN, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(AD7175_RESET_PORT, AD7175_RESET_PIN, GPIO_PIN_SET); HAL_Delay(1); // 2. 验证ID AD7175_ReadReg(0x07, tmp, 1); while (tmp[0] ! AD7175_EXPECTED_ID) { // 打印错误或LED闪烁方便调试 } // 3. 配置IFMODE: 连续读关状态字节关CRC关24位数据 tmp[0] 0x00; tmp[1] 0x00; AD7175_WriteReg(0x02, tmp, 2); // 4. 配置ADCMODE: 连续转换外部基准 tmp[0] 0x00; tmp[1] 0x0C; // 具体值依手册和实际需求调整 AD7175_WriteReg(0x01, tmp, 2); // 5. 配置通道0: AIN0正, AIN1负, 通道使能 tmp[0] 0x80; tmp[1] 0x01; AD7175_WriteReg(0x10, tmp, 2); // 6. 配置滤波器: Sinc5Sinc1, 10SPS tmp[0] 0x00; tmp[1] 0x0A; // ODR代码见手册 AD7175_WriteReg(0x28, tmp, 2); }3.4 主循环数据采集与转换处理初始化完成后AD7175-2已经在连续转换模式下运行了。每次转换完成DOUT/RDY引脚会拉低主机可以轮询这个引脚检测到低电平后再发起SPI读操作。我写了一个简单的数据读取函数uint32_t AD7175_ReadData(void) { uint8_t buf[4] {0}; uint32_t raw 0; // 等待DOUT/RDY拉低表示转换完成 while (HAL_GPIO_ReadPin(AD7175_DOUT_PORT, AD7175_DOUT_PIN) ! GPIO_PIN_RESET) ; // 读24位数据 AD7175_ReadReg(0x04, buf, 3); raw ((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | buf[2]; // 处理有符号数24位ADC输出用补码表示 if (raw 0x800000) raw | 0xFF000000; return raw; }主程序里就非常简单一个无限循环等待转换完成、读取数据、换算成电压、打印到串口。电压换算公式是电压 (raw / 8388607.0) * 2 * Vref / 增益这里raw是24位有符号数Vref是2.5V。如果配置成单极性模式分母就是8388608公式要对应调整。我刚开始写代码时忘了处理符号位导致负电压读出来的值像天书排查了很久才发现是这个问题。4. 调试中遇到的问题与排查实录4.1 SPI读不到数据ID寄存器全FF这是最常见的翻车现场。我把板子焊好程序烧进去串口打印出来ID全是0xFF。检查了一圈最后锁定在三个地方。第一个是SPI引脚复用。STM32F103的PA5、PA6、PA7默认不是SPI复用功能如果不手动配置AFIOCubeMX生成了配置一般没问题但如果你在main函数里又碰了GPIO相关寄存器可能把复用配置覆盖了。可以用调试器看GPIO寄存器里AFR值是否正确。第二个是时钟极性问题。CPOL和CPHA配不对从机接收到的命令字节就是错的读ID自然全FF。我尝试了CPOL1、CPHA1之后立刻恢复正常。第三个是CS引脚电平。因为CS是软件控制的如果在读数据期间CS提前拉高了或者CS初始状态没有拉高SPI通信就会乱。我在调试时用逻辑分析仪抓了CS和SCLK的波形一眼就看出CS在传输中途拉高了。排查方法建议先用逻辑分析仪或示波器抓取四个信号确认时钟是否连续输出确认DIN上有正确的命令字节确认MISO上是否有返回数据。抓波形不是浪费时间它能把问题范围直接砍掉一半。4.2 数据稳定但数值明显偏大或偏小这个问题也踩过坑。现象是数据读出来了重复性很好但换算成物理量后和实际值对不上偏大或偏小一个固定比例。仔细分析后发现是参考电压的问题。AD7175-2的数据手册规定外部基准电压有严格的共模范围REF引脚上的电压必须满足一定的输入条件。我用ADR4525输出2.5V直接接REFREF-接模拟地看起来没问题但忽略了ADR4525的输出噪声和负载驱动能力。后来在REF和REF-之间并了一对去耦电容数值误差就消失了。还有一种情况是把增益寄存器配错了PGA增益不是1导致换算公式里的分母错误。比如误设置了PGA2读数会比实际值偏大一倍。建议每次改了寄存器都读出来回读确认不要盲目相信写入值。4.3 数据跳动大有效位数达不到手册标称如果读数忽大忽小标准差达到几十上百个LSB那问题通常不在固件而在模拟布局或电源。我遇到过三种情况。一是模拟电源和数字电源没有处理好AVDD上叠着数字开关噪声。解决方法是给AVDD加独立的LDO或者在电源入口加强去耦。二是SPI时钟线离模拟输入线太近SCLK的翻转噪声耦合到了AIN引脚上导致转换结果随SPI时钟同步波动。这个用示波器看输入端波形就能发现。三是DOUT/RDY引脚上没有做处理每次转换完成的下降沿产生了一个小毛刺干扰了模拟部分。解决办法是在DOUT/RDY上串一个10kΩ电阻到地降低边沿速度。我在这里分享一个最常用的排查技巧把模拟输入端短路到参考地然后统计1000次转换结果的标准差。如果标准差很小说明ADC本身和数字链路是好的再去检查模拟前端。如果标准差依然很大问题就在ADC供电、参考电压或PCB布局。这个办法可以快速把问题收敛到某一侧省去一堆盲猜。5. 实测结果与经验补充5.1 实测数据表现经过几轮调试最终固件在10SPS输出速率、外部2.5V基准、差分输入短接的情况下1200次采样统计出来的有效分辨率大约在20.5位左右标准差约2.8uV。这个水平虽然离手册里标称的21.5位还有一点距离但对于工业现场应用已经足够了。如果提高输出速率到100SPS有效分辨率会降到18位左右这个趋势符合Σ-Δ ADC的普遍规律。做项目时可以根据需求动态切换滤波器和输出速率低速用来做精确测量高速用来捕捉瞬态变化。5.2 AD7175-2驱动代码的扩展方向目前固件只用到了最基础的读写和转换功能。后续可以往这几个方向扩展。一是增加连续读模式。AD7175-2支持一次片选周期内连续读取多个通道的数据配合STM32F103的DMA可以大幅降低CPU占用。实现时需要开启IFMODE寄存器里的连续读位然后按通道顺序读数据数据宽度要么都配成24位要么都开启状态字节变成32位否则无法区分通道边界。二是启用CRC校验。如果SPI线缆比较长或者现场电磁干扰严重开启CRC可以提高数据传输可靠性。AD7175-2支持两种CRC多项式需要按手册把数据包末尾附加的CRC字节参与计算通信双方都要算一次实现不算复杂但会给每个读操作多传几个字节。三是加入校准。AD7175-2内置了失调校准和增益校准寄存器可以在系统上电时进行一次自校准把系统级的失调和增益误差消除。这个对批量生产特别有用能弥补元器件个体差异带来的精度波动。另外一个很实用的技巧是在工程里加一个SPI错误计数变量每次HAL_SPI_TransmitReceive返回值不为HAL_OK时累加并打印这样可以量化SPI通信的稳定性。我调优时就是靠这个计数器发现旧版本固件在高速SPI下偶发通信失败从而定位到是时钟沿裕量不足最终通过降低SPI时钟解决了问题。最后分享一点个人经验做这种高精度ADC方案不要急着上高速先把寄存器配稳数据读回来再一步一步优化。我见过很多新人在一开始就试图跑到最高速率、开启所有高级功能结果排错排了整整一周。从10SPS开始确保1000次读数稳定再升速、再加功能每一步都有明确验证点项目反而推进得更快。这套思路不止适用于AD7175-2做任何精密ADC驱动都适用。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →