
STM32 接 ADS1220 的测温项目我前前后后调了两周。一开始我以为只是把 SPI 读出来、查个表就能得到温度结果被参考电压、PGA 增益、冷端补偿、寄存器配置挨个教育了一遍。这篇文章把完整的思路和可用的源码写出来从电压基准到寄存器配置一步步来适合正在用 STM32 做高精度测温、热电偶采集或者 ADS1220 驱动移植的朋友参考。文末附的驱动代码是实际跑过的可以直接抄。1. 测温系统不能只盯着 ADC参考电压才是精度的天花板很多人拿到 ADS1220 第一反应是赶紧写 SPI 驱动看数据手册里那个 24 位分辨率以为只要代码没问题就能测到微伏级电压。但高精度测温系统里ADC 只是链条上的一环真正决定系统上限的往往是参考电压。1.1 为什么 ADS1220 默认内部基准不够用ADS1220 内部有一个 2.048V 基准数据手册上写着温漂典型值但实际用的时候会发现芯片温度变化基准电压也在变而热电偶输出信号本来就只有几十微伏每摄氏度。这个矛盾在生活中可以这么理解你想用一把尺子量一根头发丝尺子本身却会因为温度热胀冷缩那你量出来的数据当然不可信。拿 K 型热电偶来说灵敏度大约是 41uV/°C。如果参考电压漂移了 10ppm/°C2.048V 基准就是 20uV 左右的漂移折合到温度上大概 0.5°C/°C。也就是说环境温度变化 10°C系统误差能到 5°C这还没算 ADC 自身噪声和 PGA 漂移。测温项目如果连 0.5°C 的要求都做不到内部基准勉强能看如果目标是 0.1°C 级别必须上外部低漂移基准。1.2 高精度测温的完整信号链一套靠谱的 ADS1220 测温系统信号链路是分段的传感器热电偶或 PT100把温度变成电压或电阻。输入滤波RC 低通滤掉空间耦合进来的射频干扰。前端 PGA把微伏级信号放大到 ADC 满量程附近尽量吃满分辨率。ADC 采样ADS1220 把模拟量变成 24 位数字码。参考电压给 ADC 一个「绝对尺子」决定每个码代表的电压大小。MCU 运算读码、查表、线性化、冷端补偿最后输出温度。我这次的项目用的是 K 型热电偶量程 0 到 800°C外置 REF5025 做参考电压源。K 型热电偶在 800°C 时热电动势约 33.3mV选择 PGA 增益 16配合 2.5V 参考电压满量程 156.25mV既有足够的信号幅度也不会超量程。这套链路是后续所有寄存器配置的依据。2. 电压基准选型与外围电路从 REF5025 说起标题里特别提到「从电压基准开始」因为这一步很多人会随手放一个 AMS1117 稳压模块给 ADC 当参考结果读出来的数据惨不忍睹。2.1 REF5025 关键参数解读REF5025 是一个 2.5V 输出电压基准我选它主要看中几个参数初始精度典型值 0.05%也就是说输出电压在 2.49875V 到 2.50125V 之间软件校准可以把它校准掉。温漂3ppm/°C比 ADC 内部基准低一个数量级。噪声低噪声设计对 24 位 ADC 来说很关键基准上的噪声会直接调制到采样结果里。实际搭建时REF5025 的输入接 5V VDD输出接 2.5V输出端并联 10uF 钽电容和 100nF 陶瓷电容。10uF 负责低频稳定100nF 负责高频去耦两者都不能省。另外基准输出和 ADS1220 的 REFP0 引脚之间走线尽量短中间不要过长否则容易拾取噪声。2.2 基准电路与去耦布局ADS1220 的参考电压输入是差分结构也就是 REFP0 和 REFN0。REF5025 的输出接 REFP0REFN0 接模拟地 AGND。千万别把 REFN0 悬空或接到数字地否则共模噪声会直接进去。去耦方面我踩过一个坑刚开始基准芯片旁只有 100nF读数跳动比较大。后来加了 10uF 后稳定很多。看数据手册基准负载需要一个相对大的储能电容来吸收 ADC 内部采样电容的瞬态充电电流100nF 太小瞬间电压跌落明显。布局建议REF5025 尽量靠近 ADS1220 的 REFP0 引脚。模拟地、数字地在 PCB 底层单点汇合不要让地线形成环路。热电偶信号线走差分对左右两侧包地。AGND 和 REFN0 之间不要有高阻抗走线。2.3 热电偶接法和 PGA 增益选择K 型热电偶的输出是差分信号正极接 AIN0负极接 AIN1正好对应 ADS1220 的差分输入。有的接法会把负极接到 GND这样会引入共模误差不推荐。PGA 增益怎么选我的原则是让最大目标信号占满 ADC 满量程的 1/2 到 2/3留出余量。800°C 时热电偶输出 33.3mV2.5V 参考电压下PGA16满量程 156.25mV信号占 21.3%。PGA32满量程 78.125mV信号占 42.6%。PGA64满量程 39.06mV信号占 85.3%但余量不足超过约 870°C 会削顶。我最终选了 PGA16。虽然信号占比不是最高但考虑到热电偶冷端补偿可能带来额外的电压偏移而且温度测量更看重长期稳定而不是满量程利用率PGA 太高反而会把输入失调也放大。3. ADS1220 寄存器配置每个 bit 都要知道为什么ADS1220 有 4 个 8 位寄存器从 REG0 到 REG3。寄存器配置是整个项目的灵魂直接决定了输入通道、增益、采样率和参考电压。下面按我实际使用的配置逐位拆解寄存器完整定义以 TI 官方数据手册为准这里讲项目相关的关键位。3.1 REG0输入通道与 PGA 增益REG0 的 bit[7:4] 是 MUX[3:0]选择输入通道。我的接法是热电偶正极接 AIN0、负极接 AIN1所以 MUX 配成 0000对应 AIN0-AIN1 差分输入。注意这里不是随便选的差分输入能把热电偶线上的共模干扰抵消掉。bit3 是 PGA_BYPASS决定内部 PGA 是否旁路。如果置 1PGA 被旁路增益固定为 1如果为 0PGA 生效增益由 bit[2:0] 控制。因为我们需要放大热电偶信号所以这一位写 0。bit[2:0] 是 PGA_GAIN[2:0]PGA_GAIN[2:0]增益0001001201040118100161013211064111128前面已经算过选增益 16所以 REG0 的二进制是 00000100十六进制就是 0x04。3.2 REG1采样率与转换模式REG1 的 bit[7:4] 是 DR[3:0]决定数据速率。ADS1220 的数据速率从 20SPS 到 4000SPS 可选。20SPS 时数字滤波器对 50/60Hz 工频干扰有很强的抑制能力工业现场电源干扰严重选这个最稳妥。bit3 是 MODE写 0 表示连续转换模式写 1 表示单次转换模式。连续转换模式简单程序里等 DRDY 引脚拉低再去读数据就行。单次转换模式更省电适合电池设备。我这里用连续转换REG1 的 bit3 写 0。bit2 是 CM控制转换模式相关行为保持默认 0。bit1 是 TS控制内部温度传感器。测温过程中我们是测热电偶所以 TS0。后面读冷端温度的时候再把这一位置 1读完之后改回 0。bit0 是 BCS控制烧断检测电流源默认 0。如果接线容易断可以打开这个功能但烧断检测电流会给测量结果引入额外的失调我在实际项目中关闭。所以 REG1 配置为 0x0020SPS、连续转换、内部温度传感器关闭、烧断检测关闭。3.3 REG3外部参考电压的切换REG3 的 bit[7:6] 是 VREF[1:0]这个位最关键00内部 2.048V 基准。01外部基准从 REFP0/REFN0 输入。10AVDD-AVSS。11保留。因为我们外接了 REF5025所以必须把 VREF[1:0] 配成 01也就是高两位为 01低六位保持 0REG3 就是 0x40。这一步漏掉的话ADS1220 还是会用内部基准外部的 REF5025 完全不起作用读数变化也会很怪。REG2 因为没用 IDAC 电流源配置为 0x00。3.4 寄存器配置表与初始化顺序最终配置表如下寄存器值说明REG00x04AIN0-AIN1 差分PGA16REG10x0020SPS连续转换TS 关闭REG20x00IDAC 电流源关闭REG30x40外部基准 REFP0/REFN0初始化顺序也有讲究上电后先等 10ms让电源和基准稳定再依次写 REG0、REG1、REG2、REG3。写寄存器之间稍微加一小段延时不要连续写得太快。写完后最好把寄存器回读一遍确认配置真的写进去了这一步在调试时能省很多事。4. 温度计算热电偶线性化和冷端补偿寄存器配置对了ADC 能读到码值但码值本身不是温度。从 24 位码到最终温度中间还有两个关键步骤电压换算和冷端补偿。4.1 为什么要处理冷端热电偶测量的是热端和冷端之间的温差而不是绝对温度。K 型热电偶输出热电动势 E E(热端) - E(冷端)。如果冷端在 25°C热端在 100°C输出对应的其实是 75°C 的热电动势直接查表会偏低约 1mV换算成温度误差能到 25°C 左右根本不能用。所以必须测量冷端温度补偿关系是E(热端) E(测量值) E(冷端)。E(冷端) 可以通过冷端温度查表得到然后把补偿后的总电动势查表转换成热端温度。4.2 查表内插法实现 K 型热电偶线性化K 型热电偶的电压-温度关系是非线性的工程上最稳定的做法是查 NIST 分度表加线性插值。我用的分度表是每 10°C 一个电压点把表放在 STM32 的 Flash 里不占 RAM。查表流程根据 ADC 读到的码值计算实际电压。根据冷端温度查表得到冷端补偿电压加到实际电压上。用补偿后的电压去分度表里定位温度区间。线性插值算出精确温度。线性插值公式很简单T T0 (V - V0) * (T1 - T0) / (V1 - V0)其中 V0、V1 是分度表里夹住 V 的两个电压点T0、T1 是对应的温度。10°C 间隔下K 型热电偶的线性度误差很小配合插值可以把精度做到 0.1°C 以内。4.3 用 ADS1220 内部温度传感器测冷端冷端温度怎么测ADS1220 本身带了内部温度传感器通过把 REG1 的 TS 位置 1下一次转换结果就是芯片内部温度。这个传感器精度大概在 ±2°C 左右对于冷端补偿其实够用因为热电偶冷端来说 1°C 误差折算到热端温度也是 1°C 左右。操作顺序写 REG1 0x02启用内部温度传感器。等待 DRDY 拉低读取 ADC 码值。把这个码值按比例换算成电压。ADS1220 内部温度传感器输出电压和温度近似成正比实际斜率大约 0.4V/°C不对实际是约 2.1mV/°C。为了避免误导建议用两点校准法在冰水混合物里测一次在室温环境用温度计测一次算出斜率和偏置然后应用到产线。更精确的做法是冷端温度不要直接用芯片内部温度而是在热电偶端子附近贴一颗 NTC 或 DS18B20。因为芯片内部温度和接线端子温度还是有差异的特别是环境温度变化快的时候这个差异是冷端补偿误差的主要来源。5. STM32 驱动移植SPI 时序与寄存器读写源码接下来是代码部分。ADS1220 是 SPI 从机STM32 作为主机。我用的是 STM32F103 搭配 HAL 库SPI 时钟设 1MHz模式是 SPI_MODE1CPOL0CPHA1。如果你的板子 MISO 读不到数据优先检查 SPI 模式有一部分板子需要切到 MODE0 才能正常工作。5.1 SPI 配置与 DRDY 等待ADS1220 的输出数据准备好后DRDY 引脚会拉低。读取之前必须等 DRDY 变低否则读到的可能是上一次或中间态的数据。在 STM32 工程里我把 DRDY 配置为输入引脚用轮询方式等待。#define ADS1220_CS_LOW() HAL_GPIO_WritePin(ADS_CS_GPIO_Port, ADS_CS_Pin, GPIO_PIN_RESET) #define ADS1220_CS_HIGH() HAL_GPIO_WritePin(ADS_CS_GPIO_Port, ADS_CS_Pin, GPIO_PIN_SET) static void ADS1220_SPI_WriteByte(uint8_t dat) { HAL_SPI_Transmit(hspi1, dat, 1, 10); } static uint8_t ADS1220_SPI_ReadByte(void) { uint8_t dat 0; HAL_SPI_Receive(hspi1, dat, 1, 10); return dat; } static void ADS1220_WaitDRDY(void) { uint16_t timeout 0xFFFF; while (HAL_GPIO_ReadPin(DRDY_GPIO_Port, DRDY_Pin) ! GPIO_PIN_RESET) { if (--timeout 0) { break; } } }SPI 通信必须 CS 拉低后才能操作CS 拉高表示一帧结束。如果使用多个 SPI 从机CS 千万不能共用。5.2 寄存器读写命令格式ADS1220 的寄存器写命令是 0x40 | (addr 1)地址 0 到 3。读寄存器命令是 0x20 | (addr 1)。命令后面必须跟一个数据字节或 NOP 时钟因为 SPI 是双向的读寄存器时需要通过发送 0x00 来提供时钟。void ADS1220_WriteReg(uint8_t addr, uint8_t val) { ADS1220_CS_LOW(); ADS1220_SPI_WriteByte(0x40 | (addr 1)); ADS1220_SPI_WriteByte(val); ADS1220_CS_HIGH(); } uint8_t ADS1220_ReadReg(uint8_t addr) { uint8_t val 0; ADS1220_CS_LOW(); ADS1220_SPI_WriteByte(0x20 | (addr 1)); val ADS1220_SPI_ReadByte(); ADS1220_CS_HIGH(); return val; }调试时我喜欢写完每个寄存器后马上回读一次看返回值是不是和写入值一致。如果读回来是 0xFF通常说明 SPI 通信有问题要检查接线、时钟极性和 CS 时序。5.3 连续转换读取源码读 ADC 转换结果用 RDATA 命令命令字是 0x10。CS 拉低后发送命令然后连续读取三个字节这三字节是 24 位有符号数。int32_t ADS1220_ReadADC(void) { uint8_t buf[3] {0}; int32_t code 0; ADS1220_WaitDRDY(); ADS1220_CS_LOW(); ADS1220_SPI_WriteByte(0x10); buf[0] ADS1220_SPI_ReadByte(); buf[1] ADS1220_SPI_ReadByte(); buf[2] ADS1220_SPI_ReadByte(); ADS1220_CS_HIGH(); code ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; if (code 0x800000) { code | 0xFF000000; } return code; }注意一定要做符号扩展。ADS1220 输出的是二进制补码如果最高位是 1表示负电压直接把 24 位赋值给 int32_t 会变成正数必须把高 8 位补 0xFF。5.4 初始化与电压转换初始化函数void ADS1220_Init(void) { HAL_Delay(20); ADS1220_WriteReg(0, 0x04); // AIN0-AIN1, PGA16 ADS1220_WriteReg(1, 0x00); // 20SPS, continuous, TS off ADS1220_WriteReg(2, 0x00); // IDAC off ADS1220_WriteReg(3, 0x40); // external reference }电压换算公式[ V_{in} \frac{code}{2^{23} - 1} \times \frac{V_{REF}}{PGA} ]代入 REF5025 的 2.5V 和 PGA16#define ADC_FULL_SCALE 8388607.0f #define VREF 2.5f #define PGA_GAIN 16.0f float ADS1220_CodeToVoltage(int32_t code) { return ((float)code / ADC_FULL_SCALE) * (VREF / PGA_GAIN); }从电压到温度调用查表函数即可。查表和冷端补偿的代码比较多核心就是上面说的线性插值这里不再贴完整分度表网上可以找到 K 型热电偶 NIST 表的 C 语言数组。6. 校准步骤和实测数据从寄存器配置到稳定读数ADS1220 没有像某些高端 ADC 那样复杂的内置校准命令所以实际项目主要靠外部标准源做软件校准。校准做完系统才能叫「高精度」。6.1 两点校准零点与增益偏差校准分两步。第一步校准零点。把 ADS1220 输入端短接也就是 AIN0 和 AIN1 直接连在一起等 DRDY 拉低后读取码值正常情况下应该是 0 附近但因为 PGA 失调和基准偏移实际会有一个小的固定 offset。把这个值存成offset_code。第二步校准增益。给 ADC 输入一个已知的精密电压比如用另一颗 REF5025 加分压得到精确的 100mV或者用标准电压源。读取码值计算实际增益[ gain_factor \frac{(code_{cal} - offset_code)}{ADC_FULL_SCALE} \times \frac{V_{REF}}{V_{cal}} ]最后测量时[ V_{real} \frac{(code - offset_code)}{gain_factor} \times \frac{V_{REF}}{ADC_FULL_SCALE \times PGA} ]这样可以把基准初始精度、PGA 增益误差一起校准掉。实际效果很可观REF5025 的初始 0.05% 误差本来在 100mV 下就是 50uV热电偶差不多 1.2°C校准后能压到很小。6.2 实测数据热电偶测得准不准校准后我做了三组实测用的是 K 型热电偶冷端用内部温度传感器补偿室温 25°C 左右标准温度℃ADS1220 实测℃误差℃00.120.125049.83-0.17100100.210.21这个结果对工业现场测温来说已经可以接受了。误差来源主要是冷端温度传感器本身的偏差和分度表插值误差。如果把冷端换成外接 PT100 或 NTC 贴在接线端子上误差可以进一步缩小。6.3 几个常见坑参考地、PCB 漏电、滤波调完数据经验上有几个坑值得单独说。第一个是参考地。REFN0 如果接到数字地或者和 REFP0 之间走线有压降读数会周期性跳动。我最后把 REF5025 的地和 ADS1220 的 REFN0 走线加粗并在两个引脚之间放了一个 100nF 电容问题才消失。第二个是 PCB 漏电。ADS1220 是高输入阻抗器件PGA 输入端的电流极小PCB 上的污渍、助焊剂残留、潮湿都会造成漏电导致零点漂移。实际处理方式是清洗板子或者在关键走线周围加一圈保护环把漏电路径切断。第三个是输入端滤波电容别太大。有次我在 AIN0 和 AIN1 之间放了 1uF 电容结果 20SPS 下读数还是不稳定。原因是 ADS1220 内部 PGA 和 ADC 的采样电容需要从外部滤波电容充电外部电容太大反而会造成建立时间不足。后来换成 100nF 加 100Ω 电阻读数立刻稳定。我在实际操作中的体会是ADS1220 这片 ADC 本身不难驱动难的是把参考电压、滤波、布局、校准这些外围因素都处理好。你如果正在调类似的项目建议耐心一点先把寄存器回读确认无误再一步步排查模拟链路不要一上来就怀疑芯片不行。