STM32驱动ADS1220高精度ADC:硬件连接、寄存器配置与调试实战

📅 2026/8/26 8:44:40
STM32驱动ADS1220高精度ADC:硬件连接、寄存器配置与调试实战
简介在传感数据采集场景中STM32内置ADC往往难以满足微伏级小信号的测量需求。24位Δ-Σ ADC凭借高分辨率和低噪声特性成为高精度采集系统的核心器件。这类ADC通常通过SPI接口与主控通信并集成可编程增益放大器PGA可将毫伏级传感器输出调理至满量程范围从而充分发挥高位深优势。基于STM32平台驱动外部高精度ADC需要掌握SPI时序、寄存器配置、数据转换及信号调理等关键环节。本文以ADS1220为例详细说明其与STM32的硬件连接、寄存器配置方法、电压换算公式以及实际调试中常见的时序与噪声问题并给出可复用的驱动代码为称重、热电偶、RTD等低频高精度采集应用提供工程参考。 做高精度采集的时候STM32内部ADC是真的不够用。12位分辨率满量程3.3V下1个LSB约0.8mV测称重传感器输出、热电偶这些微伏级信号基本没法看。我上一个项目需要采集多路差分小信号精度要求24位查了一圈最后选了TI的ADS122024位Δ-Σ ADC、SPI通信、内置PGA增益和2.048V参考单芯片十几块钱性价比非常能打。这篇文章把我实际的STM32驱动代码和调试过程踩过的坑完整放出来硬件连接、寄存器配置、电压换算公式和排错思路都覆盖电路和代码可以直接参考。1. 选型逻辑为什么是ADS1220而不是ADS1115或HX711很多人在STM32上做小信号采集第一反应是“内部ADC凑合用”第二反应是“加个ADS1115”。ADS1115确实好用I2C接口、四通道、16位精度但它本质是低速Σ-Δ ADC数据速率最高860SPS16位分辨率对于称重、压力、热电偶这类应用勉强够用如果你想分辨更小的信号、有更低的噪声底16位就不太够了。ADS1220的定位和ADS1115不一样。它是24位Σ-Δ ADC支持四路模拟输入可以配成两路差分或四路单端内置可编程增益放大器PGA增益从1到128倍可调内部集成了2.048V低温漂参考源数据速率从20SPS到1000SPS可配。最关键的是它走SPI速率可以跑到几MHz虽然ADC转换本身不要求这么高速的通信但SPI在嵌入式里比I2C更“可控”时序、片选、时钟全部自己掌握多设备扩展也方便。HX711是另一个常见选择专为称重桥式传感器设计内部增益固定接口只有SCK和DOUT两根线但它本质上是一个面向称重行业的专用芯片不是通用的高精度ADC。你要是在同一块板子上既要称重、又要测温度、又要测电压HX711的灵活性就不够。ADS1220有四个模拟输入、寄存器可配、内部有电流源还能直接驱动RTD做一个通用采集前端更合适。我当初选ADS1220还有一个原因它支持PGA放大。很多传感器输出的满量程只有几十毫伏直接进ADC会浪费大量位深先用PGA放大到接近参考电压的幅度才能把24位的精度真正用起来。这种情况下ADS1220的“PGA差分输入内部参考”组合非常合适。当然ADS1220也有局限最大1000SPS数据速率决定了它不适合做高频采样你要做电机电流环这种需要几kHz采样率的场景还是老老实实用高速SAR ADC。ADS1220适合的是传感器采集、仪表测量、称重、温控这类“低频高精度”应用。2. 硬件连接SPI引脚、DRDY和电源去耦一次说清ADS1220的SPI从机端信号有SCLK、DINMOSI、DOUTMISO、CS外加一个非常重要的DRDY输出脚。DRDY是转换就绪标志每次新的转换完成DRDY会拉低主机应该等DRDY拉低后再读数据否则读到的是旧数据。很多第一次用ADS1220的人忽略DRDY结果读到的数据半天不变或者数据跳变规律完全对不上。所以DRDY必须接到STM32的GPIO输入不能用硬件中断也至少用轮询。接线表如下STM32引脚ADS1220引脚说明PA5SCLKSPI时钟PA6DOUTSPI MISO芯片输出数据给STM32PA7DINSPI MOSISTM32发送命令/配置给芯片PA4CS片选软件控制PB0DRDY转换完成标志必须接3.3VAVDD/DVDD模拟和数字电源建议分开走线GNDGND共地注意单点接地悬空/接0.1uF到GNDREFP0/REFN0使用内部参考时参考脚建议接去耦电容我这里用的是STM32F103的SPI1PA5/PA6/PA7/PA4是默认引脚排列实际项目里你可以换到SPI2或者用任意GPIO模拟SPI。这里要提醒一点如果你把SCK或CS安排在PB3/PB4/PA13/PA15这些JTAG复用脚上需要在初始化时关闭JTAG功能否则引脚根本拉不动。我最初就是把CS放在了PB4折腾了半天电平不变后来查出来是JTAG占用了引脚。电源去耦这块很多人随便接几个电容完事做高精度采集会吃亏。AVDD和DVDD虽然是同一个3.3V但建议在AVDD脚放一个10uF钽电容再并0.1uF陶瓷电容DVDD放0.1uF即可。参考电压如果用了内部参考REFP0和REFN0悬空即可如果要用外部基准比如接一个REF5025这样的精密基准就必须在REFP0和REFN0之间加去耦电容参考电压的噪声会直接进入ADC结果。SPI模式这里要说清楚。ADS1220要求SPI工作在Mode 1即CPOL0、CPHA1。这意味着SCLK空闲为低电平数据在SCLK的第二个边沿下降沿被采样。如果你用STM32CubeMX配置直接把SPI参数设为CPOL Low、CPHA 2Edge时钟速率先不要拉满。我调试时从2.25MHz开始杜邦线连接下经常读到乱码降到1MHz以下就稳定了。如果你的板子走线合理、距离短4.5MHz也能跑但性能余量没必要赌SPI通信速度对ADS1220来说不是瓶颈1MHz已经远高于1000SPS的数据量需求。关于CS的控制我建议用普通GPIO不用SPI的硬件自动NSS。硬件NSS在Multimaster模式下行为复杂容易出幺蛾子。软件控制CS的逻辑很简单发命令前拉低字节收发完再拉高。注意ADS1220的所有命令和数据必须在一个CS有效周期内完成不能中途拉高再拉低继续发否则芯片会认为这次通信结束命令序列就乱了。3. 寄存器配置四个8位寄存器搞定参考源、增益和采样速率ADS1220内部有四个8位配置寄存器地址从0x00到0x03。上电默认值都是0x00意味着外部参考、PGA增益1、20SPS、AIN0-AIN1差分输入。如果你的硬件正好是这个配置不写寄存器直接发START命令也能出数据但是这样做没法发挥芯片能力而且默认的“外部参考”意味着REFP0/RFP0N引脚必须接基准。实际项目中最好还是显式配置一遍。四个寄存器的作用大致如下CONFIG0地址0x00输入多路复用器选择、PGA增益、数据速率、参考源选择。CONFIG1地址0x01工作模式、转换模式、数据速率高字节等。CONFIG2地址0x02PGA旁路、基准监测、内部基准输出等。CONFIG3地址0x03内部电流源、GPIO控制、比较器等。我用内部2.048V参考、PGA增益1、20SPS、AIN0-AIN1差分输入时配置值如下uint8_t config[4] {0x80, 0x00, 0x00, 0x00};CONFIG0写成0x80二进制是1000 0000含义是bit7置1表示启用内部参考bit6到bit4为增益设置000表示PGA增益1bit3到bit1为数据速率000表示20SPS。如果你的信号很小比如称重传感器满量程输出只有20mV可以适当提高PGA增益比如增益128倍时CONFIG0的bit6到bit4就是111配合高增益后20mV信号也能铺满接近满量程。但要注意增益调高后输入噪声也会跟着放大不是越大越好。CONFIG1我保持0x00对应连续转换模式。如果做低功耗设备可以把转换模式改成单次转换每次转换完芯片自动进入待机需要再次触发才继续转换这样平均功耗能低很多。具体哪个位控制转换模式不同版本数据手册排版不同建议直接查你手头那份手册的寄存器表。CONFIG2和CONFIG3默认0x00即可如果你用到了内部电流源驱动RTD、或者用比较器输出才需要额外配置。寄存器配置速查表基于实测常用组合应用场景参考源PGA增益建议CONFIG0对应数据速率通用差分小信号内部2.048V10x8020SPS称重传感器内部2.048V1280xEAbit6-4111bit3-1101600SPS热电偶内部2.048V640xCAbit6-4110bit3-1101600SPS外部基准高精度采样外部基准10x0020SPS表格里的0xEA和0xCA只是示意具体应用时建议对照数据手册的寄存器表逐位确认因为硬件板子上参考源和输入范围不同配置值必须跟着改。写寄存器的SPI命令格式如下第一个字节的高4位是写命令0x4中间2位是寄存器地址低2位表示要连续写的寄存器个数减1。比如同时写四个寄存器命令字节是0x430x40 3后面跟四个数据字节。4. 核心源代码初始化、读数据和电压换算一条龙下面这套代码我基于STM32CubeMX生成HAL库SPI1硬件平台是STM32F103C8T6。如果你用的是标准外设库移植思路完全一样把HAL_SPI_TransmitReceive换成SPI_I2S_SendData/ReceiveData就行。引脚定义部分#define ADS1220_CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define ADS1220_CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) #define ADS1220_DRDY_READ() HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) #define ADS1220_CMD_RESET 0x06 #define ADS1220_CMD_START 0x08 #define ADS1220_CMD_RDATA 0x1C #define ADS1220_CMD_RDATAC 0x10 #define ADS1220_CMD_SDATAC 0x11 #define ADS1220_CMD_WREG 0x40 #define ADS1220_CMD_RREG 0x20发送单字节命令static void ADS1220_SendCommand(uint8_t cmd) { ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); ADS1220_CS_HIGH(); HAL_Delay(1); }读三个字节数据先发RDATA命令再连续收3个字节MSB在前。ADS1220返回的是24位有符号数据组合时要正确处理符号位。int32_t ADS1220_ReadRaw(void) { uint8_t buf[3] {0}; uint32_t timeout 10000; while (ADS1220_DRDY_READ() ! GPIO_PIN_RESET) { if (--timeout 0) return 0; } ADS1220_CS_LOW(); uint8_t cmd ADS1220_CMD_RDATA; HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Receive(hspi1, buf, 3, 10); ADS1220_CS_HIGH(); int32_t raw (int32_t)((buf[0] 16) | (buf[1] 8) | buf[2]); if (raw 0x800000) raw | 0xFF000000; // 24位有符号扩展到32位 return raw; }写寄存器函数void ADS1220_WriteRegister(uint8_t regAddr, uint8_t *data, uint8_t len) { uint8_t cmd ADS1220_CMD_WREG | (regAddr 2) | (len - 1); ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); HAL_SPI_Transmit(hspi1, data, len, 10); ADS1220_CS_HIGH(); HAL_Delay(2); }初始化函数void ADS1220_Init(void) { ADS1220_SendCommand(ADS1220_CMD_RESET); // 复位芯片 HAL_Delay(2); uint8_t config[4] {0x80, 0x00, 0x00, 0x00}; ADS1220_WriteRegister(0x00, config, 4); // 写CONFIG0-CONFIG3 ADS1220_SendCommand(ADS1220_CMD_START); // 启动转换 }主循环里读取一个通道的电压float ADS1220_ReadVoltage(void) { int32_t raw ADS1220_ReadRaw(); float vref 2.048f; uint8_t gain 1; float voltage (float)raw * vref / 8388608.0f / gain; return voltage; }这个公式的原理是ADS1220的满量程输入对应24位有符号值±2^23即±8388608。当输入等于2.048V时raw等于8388608。所以电压等于 raw × Vref / 2^23 / gain。如果你的参考源是外部2.5V就把vref改成2.5。要注意的是ADS1220在有PGA增益时可测的满量程范围是±Vref/gain。比如内部参考2.048V且PGA增益128满量程就只有±16mV超出这个范围会直接饱和。配置成高增益时电压公式里的gain一定要跟着改否则计算出的数值会差得非常离谱。5. 调试实录数据全0xFF、数据重复、噪声偏大怎么排查这块是最想分享的。ADS1220本身是一个很稳定的芯片很多“读不到数据”的问题几乎都出在外围配置和时序上。第一个坑SPI模式不对导致读出的数据全是0xFFFFFFFF。这个现象特别典型。SPI Mode 0和Mode 1只在相位上差一个边沿但ADS1220只在Mode 1下能正确响应。如果是Mode 0芯片收命令会错位读回来的数据就变成了乱七八糟的固定值。我在用逻辑分析仪抓波形时才看清SCK上升沿和下降沿采样的数据完全对不上。检查顺序是先确认CPOL0、CPHA1如果已经对了再检查CS在每个命令周期内是否保持低电平。第二个坑读到的数据一直是重复的同一个数。这个问题的根源基本都在DRDY的处理上。SPI通信本身没错但程序里每次直接发RDATA命令不管芯片有没有新的转换完成那读出来的自然就是上一次的结果。必须严格等待DRDY拉低后再读。另外ADS1220上电后虽然是连续转换模式但如果之前没有发START命令芯片可能根本没在跑转换。建议初始化流程固定为复位→配置寄存器→发START循环里再等DRDY。第三个坑切换通道后第一轮数据异常。ADS1220的输入多路复用器和PGA是电容开关阵列结构切换输入通道后内部采样电容需要几个转换周期才能稳定到新输入。我做了四个通道循环采样前两个通道的数据总是多了或者少了后来在每次切通道后丢弃一次转换结果问题立刻消失。这也是很多ADS1220应用笔记里明确提到的通道切换后至少丢弃第一个转换结果最好丢弃两个。第四个坑数据噪声比预期大。如果读出的数据在低几位来回跳先检查电源去耦电容是否足够其次检查参考电压是否是干净的。内部参考在大多数时候是够用的但如果主板上有个DC-DC开关电源离芯片很近纹波会直接耦合进去。我遇到过同一片ADS1220在面包板上噪声很大的值挪到PCB板上噪声立刻小了一半。PCB布局上模拟地和数字地要做单点连接ADC下面尽量铺地铜。第五个坑CS引脚在JTAG口上导致命令发不出去。如果你初始化SPI后用示波器看CS引脚始终没有电平变化十有八九是引脚被复用占了。STM32F103的PB3/PB4默认是JTAG的JTDO/JNTRSTPA13/PA14/PA15也是DEGUG口。这种情况下要在GPIO初始化前调用HAL_GPIO_DeInit、或者用GPIO_InitTypeDef里的复用功能配置把JTAG释放掉然后把引脚重映射为普通GPIO。排查这类问题我建议按这个顺序走先用逻辑分析仪抓SPI波形确认MISO上有没有数据返回然后写一个固定读寄存器函数把CONFIG0读回来看芯片是否真的把配置写进去了如果寄存器读写正常再等DRDY读数据。这三步可以快速把问题定位到“通信层”还是“转换层”而不是在应用代码里瞎猜。6. 代码移植与扩展从HAL库到DMA、多通道、多片ADS1220HAL库这套代码跑通之后整个结构已经很清晰了。如果你项目里用的是标准外设库或寄存器操作移植并不麻烦。SPI收发换成三层结构SPI初始化管SCK/MOSI/MISO/CS的模式一个发送函数、一个接收函数ADS1220驱动只调用这两个接口。这样MCU换型号只要改底层SPI接口就行。DMA方式读取是进一步优化CPU占用的方向。ADS1220读一次数据是3个字节原本用HAL_SPI_Transmit和HAL_SPI_Receive是阻塞方式对实时性要求高的系统来说不够理想。可以改成CS拉低→检查这个目标地址是否可以直接写入在SPI的DMA模式下把RDATA命令和三个字节的接收组织成一次DMA传输接收完成中断里再去解析。这里有一个细节DMA传输期间CS要保持低传输完成后再拉高DMA完成回调里做CS拉高会非常贴合SPI时序。多通道循环采样也值得说一下。ADS1220有四个模拟输入配置成四通道单端或两对差分输入都可以切换通道需要重新写CONFIG0中的MUX位段。每次切完通道等DRDY拉低后读出的第一组数据不要用直接丢弃然后从第二组数据开始记录。代码模板static uint8_t channel 0; uint8_t muxTable[4] {0x80, 0x84, 0x88, 0x8C}; // 假设低四位控制MUX void ADS1220_SelectChannel(uint8_t ch) { uint8_t cfg muxTable[ch]; ADS1220_WriteRegister(0x00, cfg, 1); ADS1220_SendCommand(ADS1220_CMD_START); HAL_Delay(5); // 等MUX稳定 ADS1220_ReadRaw(); // 丢弃第一次 }多片ADS1220扩展就更简单了SPI总线只有一套每个芯片的CS单独接一个GPIO。需要哪个芯片就拉低它的CS其他芯片的CS保持高。ADS1220在CS为高时DOUT是高阻态不会干扰SPI总线所以多片并联在MISO线上没有总线冲突问题。很多项目里一次用两到三片ADS1220分别做不同类型的传感器采集这套驱动只要复制一份把CS和DRDY引脚换掉就能工作。数据滤波方面我自己项目里常用的是去极值平均滤波。ADS1220在20SPS下噪声很低但偶尔会有尖峰脉冲取五个点去掉最大最小再求平均效果比单纯的滑动平均要好。如果数据速率已经很高比如1000SPS可以做16次平均等效降速到62.5SPS有效分辨率还能再提升两位左右这就是过采样技术利用Δ-Σ ADC自身特性获得额外ENOB。不过要注意过采样提高的有效位数有限一般每4倍过采样增加1位有效分辨率不能指望从24位变到26位实际噪声底还是由参考源和PGA决定的。7. 实际项目中的体会这套代码我在两个项目上跑过稳定运行包括一个三通道称重采集模块和一个RTD温度采集模块。最深的体会是ADS1220的驱动本身不复杂难点全在“对通信时序的敬畏”上。SPI Mode 1是数据手册明确写的CS的整个命令周期内保持低电平也是数据手册的时序要求DRDY必须等待更是基本操作但恰恰是这些最不起眼的细节会让你的调试时间从一小时变成一整天。如果刚拿到芯片建议先做一个最小实验STM32开发板加一块ADS1220模块用杜邦线连接先把配置寄存器读回来确认SPI通信通了再接传感器信号。不要一上来就写完整的应用代码那样出问题时排查范围太大。另外用逻辑分析仪比示波器更好用SPI协议在逻辑分析仪上直接解码出命令和寄存器值一眼就能看出来问题出在哪一层。最后分享一个我自己惯用的做法把ADS1220的寄存器配置做成一个结构体包含参考源、增益、数据速率、MUX选择、转换模式这些字段再写一个根据结构体生成CONFIG0到CONFIG3配置值的函数。这样从一套配置切到另一套配置只需要改结构体里的参数不用每次对着数据手册算二进制。后续项目需要换传感器、改量程改几行参数就行非常省事。本文还有配套的精品资源点击获取