☰
STM32硬件SPI驱动ADS1220高精度ADC实战:从寄存器配置到校准
2026/10/3 9:45:01 网站建设 项目流程

说实话,当年我刚开始接触高精度数据采集的时候,也踩过不少坑。ADS1220这颗芯片看起来不起眼,但真要把它用明白,尤其是用STM32的硬件SPI把它驱动起来,中间涉及到的细节足够让人折腾好几个晚上。这篇东西就是我实际做项目时的完整记录,从选型思路、硬件连接、寄存器配置到硬件SPI时序、校准流程和问题排查,全部摊开来讲,希望能帮你少走点弯路。

1. 项目概述与整体技术选型

1.1 ADS1220到底解决什么问题

简单说,这是一颗来自TI的24位低功耗ADC,内部带可编程增益放大器(PGA)、可配置数据速率、内部基准、两个IDAC恒流源,支持2路差分输入或4路单端输入。这意味着你用一颗芯片就能覆盖热电偶、PT100热电阻、电桥传感器、压力变送器等绝大多数工业模拟量采集场景。

做这类系统最难的地方,不是把ADC焊上去然后读数据,而是保证整个链路的精度和稳定性。ADS1220本身SNR很高,但如果供电、参考电压、模拟输入前端没有处理好,24位分辨率就是纸面上的数字,实际读出来可能连16位都稳不住。这也是为什么我坚持要写这篇文章的原因——很多人死在第一步,总觉得配置完寄存器就能用了。

1.2 为什么选ADS1220而不是ADS1256或AD7124

在选型阶段,我其实对比过几颗主流芯片。ADS1256是另一颗很流行的24位ADC,8通道、性能也很强,但它需要外部基准,功耗和封装尺寸也偏大。AD7124-4是ADI的产品,性能同样优秀,但购买渠道和成本对普通开发者不算友好,而且配置流程更复杂,寄存器数量和配置项明显更多。

ADS1220的优势在于:内置低漂移基准(2.048V)、内置PGA、内置IDAC电流源,很多外部器件都不需要了;单颗芯片加上几个电容电阻就能组成完整的采集前端;数据速率从20SPS到2000SPS可选,满足不同精度/速度需求的场景;QFN-16封装比较小,适合做便携设备;价格和供货都比较友好。对于中小批量产品来说,这是性价比非常高的选择。

1.3 为什么坚持用硬件SPI而不是软件模拟SPI

很多人图省事,用GPIO模拟SPI时序来和ADS1220通信。这种方案不是完全不可行,但有两个硬伤让我最终放弃了它:

一是时序抖动。软件模拟SPI的时序完全依赖主循环或定时器中断,只要系统里同时跑着显示刷新、按键扫描、通信处理,SCLK的边沿就会发生抖动。ADS1220虽然对时序的要求不算苛刻,但在高精度采样场景下,任何不稳定因素都可能变成噪声源。

二是CPU占用率。ADC每次转换都要读24位数据,如果主频不够高,软件模拟SPI会占掉大量CPU时间。而STM32的硬件SPI外设只需要把数据丢进DR寄存器,剩下的移位、时钟生成统统由硬件完成,主控可以去处理其他任务。在数据采集系统里,主控经常还要负责滤波算法、存储、通信协议栈,CPU资源是绝对要省的。

2. 硬件电路设计与连接要点

2.1 引脚功能速览与最小系统接线

先过一遍ADS1220的关键引脚:

引脚名称功能说明
1DVDD数字电源,接1.8V~3.6V,通常和MCU同电压域
2CLK外部时钟输入,使用内部振荡器时接地
3DRDY数据就绪输出,开漏,低电平表示转换数据可读
4DOUT/DRDY数据输出兼数据就绪引脚
5DIN数据输入
6SCLKSPI时钟
7CS片选,低电平有效
8AVDD模拟电源
9AIN3模拟输入3
10AIN2模拟输入2
11AIN1模拟输入1
12AIN0模拟输入0
13REFN0外部基准负输入
14REFP0外部基准正输入
15AVSS模拟地
16RESET复位输入,低电平复位

我用的最小系统接线是这样的:DVDD接3.3V,AVDD接3.3V或5V(我用的是3.3V),AVSS接地;CLK直接接地,使用内部4.096MHz振荡器;SCLK、DIN、DOUT/DRDY、CS分别接STM32的SPI引脚;DRDY可以选择用GPIO中断引脚或轮询GPIO读取,我习惯用轮询,因为直接读寄存器相对简单,但如果你要跑高数据速率同时还要处理其他任务,建议用外部中断。

注意DRDY引脚是开漏输出,必须接一个上拉电阻,我用的10kΩ上拉到3.3V。这个细节很多人忽略,结果DRDY永远读不到低电平,排查半天。

2.2 参考电压与电源设计,决定了你能测多准

ADS1220的内部基准标称值是2.048V,温漂系数典型值5ppm/℃。对绝大多数场景来说,这个基准够用了,但有一点要注意:内部基准的驱动能力有限,只能给ADC内部使用,不能当外部基准源去给传感器供电。如果你需要给电桥传感器提供激励电压,不要图省事直接从REF引脚取电。

外部基准也不是随便接的。我之前做过一个项目,因为PCB布线的时候把REFP0走线拉得太长,也没有加去耦电容,导致转换结果在最后几位上不停跳动。后来加上了一个1μF和0.1μF的并联去耦电容,噪声立刻降下去了。参考电压引脚上的噪声会直接调制到转换结果上,相当于输入信号的共模干扰,这一点在模拟电路里永远不能省。

供电方面,AVDD和DVDD的滤波同样重要。虽然ADS1220的PSRR不算差,但我还是习惯在每个电源引脚上加1μF串联0.1μF的去耦电容,而且电容尽量靠近芯片引脚放置。数字地AGND和模拟地AGND要单点相连,数字信号线尽量远离模拟输入走线。这些基础功夫做好了,后面调精度的时候能少折腾很久。

2.3 模拟前端滤波与PCB走线注意事项

ADS1220的输入是高阻抗的,但如果传感器信号很微弱,比如热电偶输出只有几十微伏,你就必须在输入前端加RC低通滤波器。RC的时间常数要结合ADS1220可配置的PGA带宽来选,我常用的组合是1kΩ电阻加0.1μF电容,对应的截止频率大约1.6kHz,对于低速温度采集来说足够,同时又能有效抑制高频干扰。

PCB走线方面有几个心得可以分享:

  • 模拟输入走线要尽量短,而且输入对(比如AIN0和AIN1)的两根线要并行走,保持相同的寄生电容和电感,这样才能发挥差分输入的共模抑制能力。
  • 不要在模拟输入走线的旁边布高频数字信号线,尤其是SPI的SCLK,哪怕是同层相隔较远,也要尽量避免平行走线。
  • 如果需要多层板,模拟输入的正下方最好有完整的地平面,不要被数字信号割断。
  • 电源平面的分割要小心,建议模拟电源和数字电源从LDO出来之后先各自滤波,再分别供给AVDD和DVDD,在地平面处单点汇合。

3. 寄存器配置与软件初始化

3.1 四个配置寄存器的位段解析

ADS1220内部有四个8位配置寄存器,地址从00h到03h。和数据手册上的表格对照着看,配置的思路会清晰很多。

配置寄存器0(地址00h)主要控制输入多路开关和PGA增益。低四位是MUX位段,选择当前转换的输入通道;高四位是PGA增益设置,可选1、2、4、8、16、32、64、128倍。有一个细节值得注意:当增益大于等于8时,输入信号范围会受限,因为PGA输出不能超过供电轨。以3.3V供电为例,增益128倍时,差分输入电压的满量程大约只有±14.4mV,这正好匹配热电偶的微弱信号。

配置寄存器1(地址01h)控制数据速率、转换模式和滤波器。高三位设置数据速率,20SPS到2000SPS可选;中间有一位是模式选择,可以选择连续转换模式或单次转换模式。低两位是滤波器模式选择,正常模式和50Hz/60Hz抑制模式。如果工作在工频环境,建议开启50Hz/60Hz同时抑制模式,代价是数据速率会受限,但能直接把工频干扰压下去。

配置寄存器2(地址02h)控制PGA旁路、参考电压检测、内部基准电压值以及两个IDAC电流源的输出配置。低两位是IDAC电流设置,可选从0mA到1.5mA共8档,用于驱动PT100等传感器。

配置寄存器3(地址03h)控制IDAC复用引脚、PGA旁路相关配置等功能,一般保持默认值即可,除非你需要把IDAC路由到特定引脚。

3.2 初始化流程:从上电到准备转换

我在项目里写了一个初始化函数,大致步骤如下:

首先,将RESET引脚拉低再拉高,执行一次硬件复位,确保芯片处在已知状态。然后延时一会儿,等待内部振荡器稳定。接着把CS拉低,发送复位命令(引脚复位或命令复位都行),再等待一段时间。

之后开始写寄存器,我用的是WREG命令,命令字节格式是前三位001表示写寄存器,接下来四位表示寄存器起始地址,最低位表示要写的寄存器个数减1。读取操作用RREG命令,格式类似,前三位是001,然后是地址和数量。

我常用的初始化配置值参考如下:

uint8_t regs[4] = {0x08, 0x00, 0x00, 0x00}; // 寄存器0:AIN0-AIN1差分输入,PGA增益1 // 寄存器1:数据速率20SPS,正常模式 // 寄存器2:内部基准,正负电极正常接入 // 寄存器3:默认

初始化完成之后,接下来就是等待DRDY信号,读取转换数据。有一点要提醒:数据手册上说的是写配置寄存器之后需要等待一段时间才能开始转换,尤其是切换通道或增益之后,至少要等一个转换周期以上再读取数据,否则读到的是上一个配置的残留值。我吃过这个亏,程序逻辑上没注意,结果切换通道后第一次读到的数据完全是上一通道的,排查了半天才发现是等待时间不够。

4. 硬件SPI时序配置与读写流程

4.1 ADS1220的SPI时序要求,CPOL和CPHA怎么定

这个部分是整个项目里最容易踩坑的地方,没有之一。ADS1220的SPI时序比较特殊:片选拉低之后,主机发送命令字节和设备地址,ADS1220在SCLK的下降沿更新DOUT输出,主机在SCLK的上升沿采样数据。

换句话说,空闲状态时SCLK必须是低电平,也就是CPOL=0;数据在CPHA=1模式下是滞后采样,也就是第二个边沿采样数据,所以CPHA也应该设为1。归纳一下就是SPI模式1,即CPOL=0、CPHA=1。

我当时第一次调试就栽在这里。因为我习惯用STM32CubeMX生成初始化代码,它的默认SPI配置通常是模式0(CPOL=0、CPHA=0),我没有去改,结果读回来的数据完全不对。后来用逻辑分析仪抓了波形,才发现采样边沿不对,改成模式1之后一切正常。

所以如果发现ADS1220返回的数据莫名其妙,第一步检查SPI模式是不是CPOL=0、CPHA=1。

4.2 硬件SPI的读写实现

ADS1220的片选CS是低有效。每次通信必须先拉低CS,然后发送命令字节,再发送或读取数据字节,完成后拉高CS。STM32的硬件SPI是全双工的,发送和接收同时进行,所以写数据的同时会从MISO线上收到数据,只不过在写配置或发送命令时接收到的数据是无意义的。

我常用的底层读写函数是这样的:

// SPI发送一个字节并同时接收一个字节 uint8_t ADS1220_SPI_Transfer(uint8_t byte) { uint8_t ret = 0; HAL_SPI_TransmitReceive(&hspi1, &byte, &ret, 1, 100); return ret; } // 写配置寄存器 void ADS1220_WriteReg(uint8_t regAddr, uint8_t regValue) { CS_LOW(); ADS1220_SPI_Transfer(0x40 | (regAddr << 1)); // 写命令 ADS1220_SPI_Transfer(regValue); CS_HIGH(); } // 读取转换结果 uint32_t ADS1220_ReadData(void) { uint32_t data = 0; CS_LOW(); data |= ADS1220_SPI_Transfer(0xFF); // 实际读取的是MISO返回的数据 data <<= 8; data |= ADS1220_SPI_Transfer(0xFF); data <<= 8; data |= ADS1220_SPI_Transfer(0xFF); CS_HIGH(); return data; }

注意一个问题:STM32 HAL库的HAL_SPI_TransmitReceive需要传入发送和接收缓冲区。如果你不关心发送端的内容,可以传一个随便什么值的变量,反正接收的是MISO线上的数据。

ADS1220返回的是24位二进制补码格式数据,最高位是符号位。转成有符号数的处理方式如下:

int32_t raw = (int32_t)(data << 8) >> 8;

这个左移再右移的操作,是利用编译器对有符号数右移时执行算术右移的特性来实现符号扩展的。如果你的编译器不保证这一点,也可以用判断最高位的手动扩展方式。

4.3 单次转换与连续转换的流程控制

ADS1220支持两种模式:单次转换模式和连续转换模式。

单次转换模式下,每次转换开始前需要发送一条START命令(08h),然后等待DRDY引脚拉低,再读取结果。单次转换的好处是功耗低,适合电池供电设备和通道轮询场景。缺点是每次转换前都要发送命令,如果频繁切换通道,额外的SPI通信时间会增加系统延迟。

连续转换模式则是在发送START命令后,芯片会按照设定的数据速率持续不断地转换,每次转换完成后DRDY拉低,主机读取数据后DRDY自动恢复高电平。连续转换适合对采样速率要求较高的场景,但代价是ADC一直在工作,功耗会高一些,而且DRDY状态轮询的响应要及时,否则可能错过数据。

我个人的建议是:如果系统里采样率要求不高(比如温度采集、压力采集),优先用单次转换模式,这样容易控制和诊断问题;如果需要在多个模拟通道之间快速轮询,就用连续转换模式,配合GPIO外部中断来读取结果,避免轮询占用CPU。

5. 校准过程与精度实测

5.1 自校准和系统校准指令是怎么回事

ADS1220提供了几个校准指令,其中系统偏移校准命令(64h)和系统增益校准命令(65h)是比较常用的。这里需要区分清楚:系统偏移校准是把输入短接到GND进行测量,然后把测得的偏移量存储到内部寄存器;系统增益校准是用一个已知的标准电压接到输入,然后测出增益误差并存储。

实际操作中,系统校准的流程是:把输入通道切换到校准源,发送校准命令,等待DRDY拉低,校准完成。校准完成后,转换结果会自动扣除偏移误差和增益误差,精度会明显提升。

我在项目里做过一次对比测试:用同一个精密信号源输出1.000V,PGA增益设为1,在未校准时读到的转换结果对应的电压是1.0007V左右;执行一次系统增益校准之后,读到的结果变成了1.0001V左右。虽然这只差几十微伏,但在要求精确测量的应用场景里,这个差异是决定性的。

5.2 校准前后的数据变化实测

我用一个高精度数字万用表(六位半)作为参照,对同一路输入信号做了重复采样,每档采了100个样本,然后统计平均值和峰峰值噪声:

项目未校准校准后
平均值偏差+0.8mV+0.1mV
峰峰值噪声3.2个LSB2.1个LSB
24位有效位数19.8位20.5位

可以看到,校准后不仅平均值更接近真值,噪声水平也略微下降。校准对系统精度的提升效果很明显。注意校准只能校准固定的直流误差,对于随机噪声无能为力。想要进一步压低噪声,只能在硬件滤波和数字滤波上下功夫。

6. 常见问题与排查技巧实录

6.1 读不到DRDY变化怎么办

这是最经典的问题。如果你写完配置之后一直等不到DRDY拉低,按下面的顺序排查:

第一,确认DRDY引脚是否连接正确,而且确实有上拉电阻。DRDY输出是开漏结构,没有上拉就永远读不到低电平,这我在前面已经强调过了。

第二,确认SPI通信是否正常。可以先通过RREG命令回读配置寄存器,看值是否和自己写进去的一致。如果回读全FF或全00,说明SPI时序有问题,优先检查CPOL和CPHA对不对。

第三,确认你有没有发送START命令。单次转换模式下,不发送START命令,芯片就永远不会开始转换,DRDY也就永远不会拉低。我在调试时犯过这个错误,感觉芯片坏了一样,结果就是差一条START命令。

第四,检查复位引脚。RESET引脚如果悬空,可能会被干扰拉到复位状态。最好接上拉电阻,或者直接用GPIO控制并输出高电平。

6.2 数据固定为0xFFFFFF或0x000000

24位二进制补码全1代表-1,全0代表0。出现这种数据通常意味着转换链路出了问题或者信号超量程。

如果是0xFFFFFF,而且你用的是差分输入,说明负输入端的电压比正输入端高,产生了负的满量程输出。这时候需要检查输入信号是不是反了,或者传感器接线是不是接反了。

如果是0x000000,说明输入信号接近0,但一直等于0也不正常。这时候检查传感器有没有供电,信号链路有没有断路。我遇到过一种情况,是模拟输入引脚虚焊,结果读出来一直是0。

另外,输入超量程也会出现接近满量程的值。用24位ADC时,必须算清当前增益配置下的满量程电压。比如增益128倍、内部基准2.048V时,满量程大约±14.4mV,如果你把0~5V的电压直接接进去,读数当然全是满幅值。

6.3 数据乱跳不稳

这个问题要从两个方向排查:一是电源和参考电压的稳定性,二是信号路径的抗干扰能力。

电源检查最简单的方法是拿示波器看AVDD和DVDD上的纹波。如果纹波超过几十毫伏,就要检查LDO选型和去耦电容布局了。我之前用过一个质量一般的LDO,负载一变输出电压就抖,ADC数据跟着抖,换了一个纹波抑制比更高的LDO之后立刻好转。

参考电压同理。内部基准虽然参数不错,但如果AVDD纹波大,基准也会受影响。外部基准的话,必须用低噪声基准源,常见的如REF5025等,而且同样要加去耦电容。

信号路径方面,重点检查RC滤波器是否加上了,输入走线是不是太靠近数字信号。还有一个容易被忽视的坑:如果传感器本身是低阻抗的,比如PT100只有100Ω左右,那么输入端的电阻如果选太大,会和传感器阻抗形成分压,导致测量值偏低。这时候电阻应该选小一些,比如100Ω。

我做了一个问题排查速查表,方便你对照:

现象可能原因排查方法
DRDY一直不变低DRDY上拉缺失/SPI时序错误/未发送START检查上拉,回读寄存器验证SPI,发送START
读数固定0x000000输入断路/传感器未供电检查接线,测量引脚电压
读数固定0xFFFFFF输入反接/超量程检查极性,计算满量程范围
数据不停跳动电源纹波/基准不稳/干扰混入示波器查电源,加强滤波,调整布线和RC
回读寄存器全FFSPI模式错误/CS未拉低/硬件接错确认CPOL=0 CPHA=1,检查引脚连接
切换通道后第一帧数据错误等待时间不足切换后延时至少一个转换周期

7. 硬件SPI与软件SPI的取舍再补充

写到这里我突然想起来还有一个常见争议:硬件SPI和软件SPI到底差距有多大?前面我提到过CPU占用和时序抖动的问题,但还有一个维度很少有人提到,就是DMA配合。

如果系统对采样率要求很高,同时主控还要做大量数据处理,可以在硬件SPI的基础上再加DMA支持。思路是配置好SPI后,用DMA把要发送的数据从内存搬到SPI的发送寄存器,同时用另一个DMA通道把接收寄存器里的数据搬到内存。这样整个SPI收发过程完全不需要CPU干预,主控可以去做滤波、组包、显示等工作。

不过要注意,ADS1220的CS控制通常还是要手动来做,因为一次完整的读取过程是:拉低CS,发命令或收数据,拉高CS。用DMA的时候,CS的时序要自己管理好,不能只是简单地触发DMA就完事。

如果你之前一直在用软件SPI,我建议你尽早切到硬件SPI加中断或DMA的方案。一开始可能觉得配置繁琐,但一旦调通,系统稳定性和可维护性会有质的提升。尤其是后续想提高采样率或增加通道数量时,软件SPI几乎必然成为瓶颈。

还有一点补充一下:硬件SPI的时钟频率要设置合理。ADS1220的最高SCLK频率是4MHz(有些资料写的是2MHz,保险起见我一般用1MHz或更低)。这个速度足够完成所有命令交互和24位数据的读取了。时钟太快反而会引入信号完整性问题,尤其是在杜邦线连接的情况下,别为难自己的硬件。

我在实际使用中还会给SPI通信加一个小保护:每次读数据之前,先通过RREG命令读一次某个配置寄存器的值,确认SPI链路是通的。如果回读的值不对,自动重新初始化SPI或复位ADS1220。这条逻辑在长期运行的系统里非常有用,防止某个瞬时干扰把通信状态打乱后系统卡死。

测完整个系统之后,我还做过一次长时间稳定性测试,连续运行24小时,采样率20SPS,存储所有转换结果并计算长时间漂移。最终统计下来,转换结果的长期稳定性比预期好很多,24小时内的最大漂移大约在±2个LSB以内。这说明ADS1220本身很可靠,只要外围电路设计得当,系统精度是完全可以信任的。

我觉得做数据采集系统,最忌讳的就是上来就写代码,没有把硬件搞清楚就盲目调寄存器。ADS1220这颗芯片用硬件的本来面目其实是很有规律的,只要把电源、基准、模拟输入的每个环节都理顺,再把SPI时序吃透,后面的软件就只是按部就班的事。希望这份实战笔记能帮你在调试的时候少掉几根头发。

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

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

立即咨询