ADS1220驱动开发详解:基于STM32的24位ADC高精度温度采集实战

📅 发布时间:2026/9/8 2:11:43
ADS1220驱动开发详解:基于STM32的24位ADC高精度温度采集实战 简介ADS1220驱动代码为一套基于STM32F407的24位ADC芯片驱动资源面向嵌入式软硬件开发者和电子竞赛备赛者解决精密数据采集中底层配置与通信实现问题。压缩包共2个文件含一个头文件和一个C源文件整体仅4KB代码体量小、依赖少适合快速移植到现有工程。源码中完成了SPI主模式、GPIO引脚、中断标志等底层初始化封装了寄存器写入、控制命令发送以及24位转换结果读取等核心函数同时覆盖连续转换、单次转换及可编程增益放大器的应用配置便于适配不同幅度信号。目前已有507人学习与下载实测体验中结构清晰、注释明确尤其适合在传感器接口、工业测量或医疗设备项目中需要快速用上ADS1220的开发者参考复用。 做测量设备的朋友应该都有这种体会选ADC芯片比选MCU还纠结。前阵子我接了个温度采集模块的项目需要做热电偶冷端补偿要求24位分辨率、内置PGA可调增益、还得支持差分输入。翻了一圈手头常用的ADS1115和ADS1256最后定了TI的ADS1220。驱动写完调试通过后我把寄存器配置、SPI时序、校准方法整个捋了一遍今天就把这套ADS1220驱动代码拆开讲讲顺便把踩过的坑也一并交代清楚。这套代码是基于STM32 HAL库写的但寄存器配置和通信时序部分属于芯片通用逻辑换成其他MCU平台也就是改一下SPI底层的收发函数而已。文章适合正在选型ADC、或者刚拿到ADS1220不知道从哪里下手的嵌入式工程师也适合想搞明白“24位ADC数据到底怎么读出来才稳定”的朋友。1. 项目背景与芯片选型思路1.1 为什么是ADS1220而不是ADS1115/AD7793ADS1220是TI推出的低功耗24位Δ-Σ ADC内置可编程增益放大器PGA、2.048V基准源、两个激励电流源、温度传感器和比较器。从名字看它和ADS1115很像但定位完全不同ADS1115是16位、单端/差分通用型适合电池监测、便携设备这类对精度要求不算苛刻的场景而ADS1220直接对标的是高精度传感器测量比如热电偶、RTD、称重传感器、桥式压力传感器这些场合需要的正是24位分辨率、低噪声和可配置的PGA。我选它还有个很实在的理由——它自带双路激励电流源做RTD测温和热电偶冷端补偿时不需要额外搭恒流源电路BOM能省下一小块板子面积。相比之下AD7793也能干这活但价格和采购周期不如ADS1220友好TTL电平兼容性也没ADS1220省心后者电源范围2.3V~5.5V和3.3V MCU直连完全没问题。1.2 ADS1220的核心架构与关键引脚ADS1220的通道结构是四路输入可以配置成两路差分或三路单端加一路伪差分。我实际项目里用的是热电偶差分输入AIN0接热电偶正极AIN1接负极AIN2和AIN3留给冷端补偿用的NTC。芯片关键引脚就这几路引脚功能连接建议AIN0~AIN3模拟输入传感器信号接入注意滤波电容就近放置REFP0/REFN0、REFP1/REFN1外部基准输入用内部基准时可悬空或接去耦电容DRDY数据就绪输出必须接MCU的GPIO或EXIT用于判断转换完成CSSPI片选低电平有效SCLK、DIN、DOUTSPI时钟和数据线建议走线远离模拟输入DRDY这根线是整个驱动逻辑的核心。它默认输出高电平完成一次转换后拉低MCU检测到这个下降沿后发起SPI读取这个时序关系搞明白了驱动就成功了一大半。2. 驱动开发前的软硬件准备2.1 SPI时序与引脚分配ADS1220的SPI是Mode 1也就是CPOL0、CPHA1时钟空闲为低电平第二个边沿采样数据。数据格式是MSB先行指令和寄存器配置都是8位但输出数据是24位。引脚分配我推荐这样安排以STM32F103为例#define ADS1220_CS_PORT GPIOA #define ADS1220_CS_PIN GPIO_PIN_4 #define ADS1220_DRDY_PORT GPIOA #define ADS1220_DRDY_PIN GPIO_PIN_2 #define ADS1220_CS_LOW() HAL_GPIO_WritePin(ADS1220_CS_PORT, ADS1220_CS_PIN, GPIO_PIN_RESET) #define ADS1220_CS_HIGH() HAL_GPIO_WritePin(ADS1220_CS_PORT, ADS1220_CS_PIN, GPIO_PIN_SET)DRDY不要接在SPI的NSS上最好独立接一个普通IO因为ADS1220的DRDY是推挽输出而有些MCU的NSS引脚会自动控制反而容易搞乱时序。SPI速率我设置在2Mbps左右实测很稳定这个芯片本身不支持太高的SCLK频率手册推荐最大值是5MHz我留了余量。2.2 HAL库SPI配置与底层收发封装SPI初始化用CubeMX生成参数如下hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_2EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_32; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB;需要注意HAL库的HAL_SPI_TransmitReceive在收发完成前会一直阻塞如果系统里跑着RTOS建议把SPI句柄的超时时间调短或者改用中断方式收发。我这个项目里直接在裸机轮询方式下用问题不大。读24位数据的底层函数我这样封装void ADS1220_ReadData(int32_t *adc_code) { uint8_t tx_buf[4] {ADS1220_CMD_RDATA, 0x00, 0x00, 0x00}; uint8_t rx_buf[4] {0}; uint32_t raw 0; ADS1220_CS_LOW(); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 4, 10); ADS1220_CS_HIGH(); raw ((uint32_t)rx_buf[1] 16) | ((uint32_t)rx_buf[2] 8) | rx_buf[3]; if (raw 0x800000) { raw | 0xFF000000; // 24位补码扩展成32位有符号数 } *adc_code (int32_t)raw; }这里有个细节很容易被忽略发送RDATA命令之后不能只发一个字节就结束需要再多发三个SCLK时钟把24位数据全部移出来。所以tx_buf是4个字节第一个是命令后面三个是时钟填充读到的数据落在rx_buf[1]~[3]。3. 驱动代码核心实现3.1 寄存器地图与配置函数ADS1220有4个8位寄存器分别是REG0输入多路复用、PGA旁路、增益配置、REG1数据速率、模式、温度传感器使能、REG2基准源选择、电流源、IDAC、REG3DRDY模式、内部参考LDO、复位。写寄存器用WREG指令格式是0x40 | (地址 2) | 寄存器数-1比如写REG0就发0x40。读寄存器用RREG格式是0x20 | (地址 2) | 寄存器数-1。我为了方便调试把寄存器配置封装成了一个函数#define ADS1220_REG0_ADDR 0x00 #define ADS1220_REG1_ADDR 0x01 #define ADS1220_REG2_ADDR 0x02 #define ADS1220_REG3_ADDR 0x03 #define ADS1220_CMD_WREG(reg) (0x40 | ((reg) 2)) #define ADS1220_CMD_RREG(reg) (0x20 | ((reg) 2)) #define ADS1220_CMD_RDATA 0x10 #define ADS1220_CMD_START 0x08 #define ADS1220_CMD_RESET 0x06 void ADS1220_WriteConfig(void) { uint8_t buf[2]; uint8_t reg0_val (0x00 6) | (0x00 5) | (0x04 2); // AIN0-AIN1, PGA旁路关闭, 增益4 uint8_t reg1_val (0x02 5) | (0x00 4) | (0x00 3) | (0x00 1); // 45SPS, 正常模式, 关温度传感器 uint8_t reg2_val (0x00 6) | (0x00 3) | (0x00); // 内部2.048V基准, IDAC关闭 uint8_t reg3_val (0x00 3) | (0x00 1); // DRDY模式, 内部LDO buf[0] ADS1220_CMD_WREG(ADS1220_REG0_ADDR); buf[1] 0x03; // 连续写4个寄存器 ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, buf, 2, 10); uint8_t reg_data[4] {reg0_val, reg1_val, reg2_val, reg3_val}; HAL_SPI_Transmit(hspi1, reg_data, 4, 10); ADS1220_CS_HIGH(); }这里提一下增益配置的坑。REG0的GAIN位是[4:2]000对应增益1001对应增益2以此类推111对应增益128它的编码不是通常的二进制递增关系是直接一一对应的。我上面用的是0x04 2也就是二进制100对应增益8。如果你要写增益64那就是110对应0x06 2。这地方光靠背容易记混最好在头文件里做枚举可读性会好很多。3.2 单次转换模式的完整流程ADS1220有两种工作模式单次转换和连续转换。我推荐初次调试时先用单次转换逻辑简单容易定位问题。单次转换流程是配置寄存器后发START命令然后等待DRDY引脚拉低再读取数据。int32_t ADS1220_ReadSingleShot(void) { int32_t code 0; uint8_t cmd; cmd ADS1220_CMD_START; ADS1220_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); ADS1220_CS_HIGH(); uint32_t timeout 0; while (HAL_GPIO_ReadPin(ADS1220_DRDY_PORT, ADS1220_DRDY_PIN) ! GPIO_PIN_RESET) { HAL_Delay(1); timeout; if (timeout 500) { return 0x7FFFFFFF; // 超时返回错误 } } ADS1220_ReadData(code); return code; }注意发START命令时要先拉低CS发完后立刻拉高不能拖泥带水否则DRDY时序会受到干扰。有些工程师为了省事把START和RDATA放在同一个CS低电平周期里发这样操作ADS1220也能响应但不稳定尤其是SPI总线有毛刺的情况下容易出问题。我每次都是严格分开发一个命令一个CS周期。单次转换模式下每次转换完成DRDY拉低读完数据后DRDY自动恢复高电平整个过程很干净。如果用的是连续模式DRDY会周期性拉低读取时就要特别小心必须在DRDY低电平期间一次性读完24位否则数据会损坏或读到新旧混合的中间值。3.3 连续转换模式的实现与注意事项连续模式适合需要高速、实时采样的场景比如振动监测、动态称重。也适合配合MCU的DMA进行无CPU干预的数据搬移。连续模式配置和单次模式差别不大唯一不同的是配置完成后不执行START命令而是直接进入转换状态。实测发现连续模式下如果MCU读取速度跟不上数据速率会发生数据覆盖读到的数据是新的还是旧的完全取决于DRDY时序处理。我的做法是DRDY下降沿触发外部中断在中断服务函数里设置一个标志位主循环检测到标志位后立即读取数据。这样既不会漏读也不会因为中断里做SPI通信导致时序错乱。volatile uint8_t ads1220_data_ready 0; void EXTI2_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(ADS1220_DRDY_PIN) ! RESET) { __HAL_GPIO_EXTI_CLEAR_IT(ADS1220_DRDY_PIN); ads1220_data_ready 1; } } void ADS1220_ContinuousModeTask(void) { int32_t adc_code; if (ads1220_data_ready) { ads1220_data_ready 0; ADS1220_ReadData(adc_code); // 数据处理... } }连续模式还有个细节数据速率设置为1000SPS时SCLK频率不能太低否则24位数据还没移完下一次转换结果就出来了会导致新旧数据混读。实测2Mbps的SCLK下1000SPS时读取一次需要大约12us远小于1ms的转换周期所以没问题。但如果你把SCLK降到500kHz那就得重新算一下时间预算了。4. 精度校准与关键参数调试4.1 电压转换公式与增益误差修正ADS1220输出的是24位补码满量程范围是-VREF/Gain到VREF/Gain分辨率是2 × VREF / (Gain × 2^23)。电压计算公式电压 (ADC码值 × 2 × VREF) / (Gain × 8388608)以内部2.048V基准、增益8为例分辨率是2 × 2.048 / (8 × 8388608) 61.035µV/LSB。这个量级做热电偶测量已经绰绰有余K型热电偶的灵敏度约41µV/℃61µV的分辨率对应约1.5℃的温度分辨率如果觉得不够把增益提高到16或者32即可。不过拿理论分辨率去套实际精度是新手最容易踩的误区。ADS1220自身有一个增益误差参数典型值是0.02%最大0.05%如果不做校准直接按公式算电压在小信号下误差会非常明显。我的做法是上电自检的时候把输入端短路到GND采集一段偏移码值然后在正常测量时减去这个偏移。4.2 数据速率与滤波器的取舍REG1的DR[7:5]位控制数据速率可选值从20SPS到1000SPS。数据速率越低数字滤波器的陷波频率越低噪声越小但响应越慢。ADS1220内置的是Sinc3数字滤波器它的特性是在数据速率的整数倍处形成陷波对工频50Hz及其谐波有天然的抑制作用。我实测过几种配置的峰峰值噪声数据速率(SPS)增益8时噪声(µVpp)适用场景20约0.4高精度温度采集45约1.0一般传感器测量90约2.5动态测量/平衡时间短330约9.0快速巡检1000约30.0波形记录如果你的现场有严重的电磁干扰比如变频器、继电器建议把速率定在20SPS或45SPS让Sinc3滤波器把工频噪声顶掉。如果追求响应速度比如做称重传感器动态峰值捕捉那就得接受更高的噪声底线。滤波电容也不能乱加。输入端的RC滤波有一个“驱动器稳定性”的问题ADS1220内部PGA的输入级是开关电容结构外部电容太大反而会导致采样电荷补充不足产生增益误差。我实测外部加入10nF电容时信号还正常换成100nF后读数明显偏低最高误差到了几十个码。推荐做法是每个模拟输入对地加1nF~10nF的C0G电容串联电阻不要超过1kΩ。4.3 内部基准源与IDAC电流源的使用ADS1220内部基准是2.048V温度漂移系数典型值为10ppm/℃左右。对一般工业测量来说这个漂移可以接受但如果你要做的是0.1℃以内误差的高精度测温建议改用外部精密基准或者对内部基准做一次多温度点标定。IDAC电流源是ADS1220的亮点功能。做两线制RTD测量时可以用IDAC给RTD提供一个恒定的激励电流同时用同一个电流源经过精密电阻产生参考电压就能实现比例式测量电源漂移和基准漂移可以互相抵消。IDAC有多个档位50µA、100µA、250µA、500µA、1000µA、1500µA配置在REG2的ID[2:0]位。这里需要特别注意IDAC电流源输出脚的电压不要超过(AVDD-0.9V)否则电流源会进入饱和电流跌落不稳定。5. 常见问题与排查技巧实录我把调试过程中遇到过的典型问题整理成了速查表这些问题在论坛上也反复出现过排查思路很有通用性。现象可能原因排查与解决读数据一直返回0或满量程CS时序错误、SPI模式不符逻辑分析仪抓SPI波形确认CPOL/CPHA检查CS是否在命令期间拉低DRDY不拉低等待超时芯片未启动转换、START命令没发成功检查电源、复位时序用RREG指令读回寄存器配置确认写入成功数据跳动大噪声远超手册值PGA增益设置过高、输入悬空、电源去耦不良输入端短路测底噪确认模拟电源和数字电源分开检查AVDD引脚电容温度传感器读数明显不准内部温度传感器未使能、校准未做REG2的TS位使能后等待至少一个转换周期再读数连续模式下数据偶尔错位读取时序和DRDY没同步用DRDY下降沿中断触发读取不要在DRDY高电平期间发起读操作输入信号正确但读数偏低输入RC滤波电容过大减小输入电容到10nF以下或降低RC串联电阻SPI读取返回0xFFFFFF未按MSB顺序发送命令、字节错位确认首字节是RDATA0x10后续补3个时钟其中我特别想展开说的是“读配置寄存器验证写入”的这个习惯。ADS1220这类SPI设备最大的坑在于你看着发送了WREG但它实际可能没写入成功——比如CS时序不对、SCLK毛刺导致数据错位。所以配置完寄存器后一定要用RREG读回来比对这是最省时间的排查手段。我会在调试阶段专门写一个ADS1220_ReadRegister函数把四个寄存器的值全部打出来。void ADS1220_ReadAllRegs(uint8_t *regs) { uint8_t buf[2]; uint8_t rx[6] {0}; buf[0] ADS1220_CMD_RREG(ADS1220_REG0_ADDR); buf[1] 0x03; ADS1220_CS_LOW(); HAL_SPI_TransmitReceive(hspi1, buf, rx, 6, 10); ADS1220_CS_HIGH(); for (int i 0; i 4; i) { regs[i] rx[i]; } }和写配置一样这里也发6个字节前2个是命令和长度后面4个SCLK时钟把4个寄存器的值依次移出来。另外有个特别隐蔽的问题是RESET时序。芯片上电后内部会有一个POR复位如果外部再发RESET命令0x06需要等待tRESET时间大概512个时钟周期在复位未完成前操作寄存器会导致配置写入不稳定。我遇到过几次现象诡异的“偶发读错数据”最后查到是上电后立刻写配置闹的。解决方法是上电后延时10ms再操作或者先读一次寄存器确认芯片稳定。6. 一点实操心得这套ADS1220驱动代码写到现在我自己最大的体会是这类高精度ADC的驱动难点不在SPI那个层层的收发时序而在你对芯片寄存器语义的理解和对噪声来源的判断。SPI通信问题用逻辑分析仪一抓就露馅反而是那种“命令都发对了、数据也读出来了但结果就是不准”的情况最耗时间。再分享一个后期维护阶段的小技巧给驱动代码加一层数据校验功能。ADS1220本身不带数据CRC或者校验机制所以我在读取数据后会连续读两次比较两次结果差值的绝对值是否超过设定阈值。如果差值过大说明模拟前端有突发干扰或接触不良程序就主动报警。这种软件上的冗余虽然不能消除噪声但在无人值守的工业设备上能帮你及时发现传感器接线松动这类物理层故障比指望芯片自动纠错靠谱得多。这套驱动我已经在三个项目上复用过了换平台只需要改底层的HAL_SPI_TransmitReceive替换成对应平台的读写函数上层逻辑基本不动。如果你正在啃ADS1220的数据手册希望这篇文章能帮你少走点弯路尤其是寄存器配置和读时序那两步一次走对的感觉还是很爽的。本文还有配套的精品资源点击获取