
上个月我调试一台北美客户的 HVAC 风机盘管控制器卡在温度精度上整整三天。STM32F303VE 的内置 ADC 在 070℃ 范围内标定完之后读数还是漂在 ±0.8℃ 左右而客户要求控制逻辑分辨率达到 ±0.3℃。最后我把测温链路换成 ADS122U04IPWR 这颗 24 位串口 ADC配合 STM32F303VE 重新做了硬件和软件问题才算真正解决。这篇文章就围绕这套组合展开为什么把温度测量独立出来、ADS122U04IPWR 与 STM32F303VE 之间怎么接线、寄存器怎么配、驱动怎么写、最终怎么把原始码换算成温度以及在 HVAC 和工业现场真正跑起来之后遇到的那些文档里不会写的坑。适合正在做楼宇自控、空调箱、冷链设备或者工业温度采集的朋友尤其是想从能用做到测得准的那批人。1. 温度链路选型为什么要把测量交给独立 ADC1.1 内置 ADC 的精度短板如果你只做按键、LED 和简单传感器检测STM32F303VE 的内置 ADC 完全够用。但温度测量不是只看分辨率更重要的是绝对精度、长期稳定性和抗干扰能力。F303 内置 ADC 是 12 位 SAR 型理论上还能做到不错的精度可它依赖 VDDA 和 VREF而板子上 VDDA 通常和数字电源混在一起毛刺一来读数就跟着抖。你也别指望靠软件滤波完全补回来因为某些噪声是共模性质的软件平均只能把白噪声压下去对工频干扰和电源纹波的抑制很有限。另外一个关键问题是参考电压。内置 ADC 的参考电压如果取 3.3V那么 12 位分辨率对应约 0.8mV/LSB。把 PT100 在 0℃ 附近的温漂系数折算进来这个 LSB 本身就代表了零点几摄氏度的变化。换句话说ADC 位数不够时末端传感器做得再准也白搭。1.2 为什么选 UART 接口的 ADS122U04IPWRADS122U04IPWR 是 TI 的一颗 24 位 ΔΣ ADC后缀里的U代表 UART 接口。同一内核还有 I2C 版本ADS122C04而 ADS1220 则是 SPI 版本。我当时选 UART 版本而不是 SPI 版本原因很实际MCU 串口资源比 SPI 更好分配。STM32F303VE 是多串口芯片USART1/2/3 加 UART4/5随便分一个出来就行不占用 SPI 外设而 SPI 可能要给 Flash、LCD 或另一颗 ADC 留着。线少隔离方便。UART 只要 TX、RX 两根线以后如果要做隔离数字隔离器选两通道就够成本低。点对点通信够用。HVAC 控制器里温度通道通常不会超过 8 路多路场景可以用多颗 ADS122U04 或者直接选带更多 MUX 的方案没必要在一条总线上挂一堆设备。这颗 24 位 ΔΣ ADC 内置了可编程增益放大器PGA、低温漂参考源和激励电流源可以直接接 PT100、PT1000、NTC 和热电偶。我手头项目主要用 PT100 和 K 型热电偶刚好覆盖 HVAC 和工业两种典型场景。1.3 它到底覆盖了哪些应用场景我整理了一下这套组合的适用场景比较有代表性的是这几类应用位置传感器类型典型量程精度要求HVAC 风管温度PT1000 / NTC070℃±0.3℃空调水管温度PT100-1095℃±0.2℃压缩机排气温度K 型热电偶50150℃±1℃冷库低温监测PT100-4020℃±0.5℃工业加热炉K 型热电偶0600℃±2℃ADS122U04IPWR 的 PGA 增益可以从 1 倍调到 128 倍所以低信号电平的热电偶可以直接放大而 PT100 则可以用激励电流加比例式测量来做后面我会专门说换算方法。2. 硬件接线细节ADS122U04IPWR 与 STM32F303VE 怎么连2.1 电源和地线安排ADS122U04IPWR 是 TSSOP-16 封装工业级温度范围后缀里的I就是这个意思。它供电可以是 3.3V 或 5V我用的是 3.3V 和 STM32F303VE 统一供电这样 UART 电平不用做额外转换。但这里有个必须注意的点ADC 的模拟供电 AVDD 要单独走线不要直接从 STM32 的 3.3V 引脚飞线。STM32 数字部分翻转时电流变化很剧烈会在 PCB 走线阻抗上产生压降这个压降直接反映到 ADC 参考上。我在这块板子上放了一个 10Ω 电阻加 10μF 电容组成的 π 型滤波从 3.3V 先滤波再进 AVDD实测噪声至少降了一半。地线方面我把模拟地 AGND 和数字地 DGND 在 ADC 下方单点汇合然后统一回到电源地。不要在 ADC 两侧分别打过孔否则地环路一大变频器或继电器动作时就会把噪声灌进来。2.2 UART 连接和潜在隐患STM32F303VE 的 USART1 我配成 9600 波特率、8 数据位、无校验、1 停止位。ADS122U04IPWR 的 UART 也是标准 TTL 电平连接方式就是 TX 接 RX、RX 接 TX、共地。需要提醒的是有些 UART 类芯片的 DOUT 引脚会兼任数据就绪指示比如转换完成时拉低。驱动里千万不要假定只要发一条读数据命令芯片立刻把转换结果吐出来。正确做法是先等就绪信号或延时再发读数据命令否则很容易读到上一次的旧数据或半帧数据。我会在第 4 节专门讲这个状态机。另外 UART 线的长度不要为了省事拉超过 20cmTTL 电平在长线上容易反射。如果确实要长距离建议加 RS485 或者用数字隔离器后转差分传输但这是另一个话题了。2.3 PT100 三线制接法PT100 是电阻型传感器0℃ 时是 100Ω阻值随温度变化很小所以要用恒流源激励然后测电阻上的压降。ADS122U04IPWR 内部有可编程激励电流源 IDAC能配置成 0.25mA、0.5mA、1mA、1.5mA 等档位。我用 0.5mA 档这样 PT100 在 0℃ 时产生的压降只有 50mV再配合 PGA 放大后送 ADC。三线制接法能消掉一部分导线电阻的影响原理是两根导线分别走激励电流和测量回路第三根线用于补偿。实际连接上我把激励电流接到 PT100 的一端另一端回参考电阻 RREF同时测量 PT100 两端电压。接线看起来复杂但做了之后长线传输的误差从几欧姆降到零点零几欧姆对 HVAC 现场走线 35 米的情况特别重要。如果只做两线制激励电流经过的引线电阻会直接串联进 PT1001Ω 的线阻对 PT100 来说相当于 2.5℃ 的误差这显然不能接受。所以只要传感器到板卡距离超过 1 米我都劝你用三线制。2.4 热电偶输入的偏置问题热电偶输出的是微伏级直流电压K 型热电偶大约 40μV/℃如果没有 PGA信号太小根本没法测。ADS122U04IPWR 的 PGA 可以把信号放大 32 甚至 128 倍但这里有一个容易忽略的坑热电偶是浮空的输入端必须提供偏置路径否则输入漏电流会把放大器偏置到不可预测的状态。这颗 ADC 内部有 VBIAS 功能可以给输入端加一个共模偏置电压。我在测量 K 型热电偶时把 VBIAS 连接到 AINN 输入让信号相对参考地有一个稳定的直流电平。很多人第一次用 TI 的 ΔΣ ADC 测热电偶时读数乱跳大多数就是 VBIAS 没开输入悬空导致的。3. 寄存器配置先定精度再谈速度3.1 四个寄存器分别管什么ADS122U04IPWR 的寄存器配置并不复杂核心就四个寄存器思路和 ADS1220 系列接近寄存器 0输入多路复用选择、PGA 增益寄存器 1数据速率、转换模式、滤波模式寄存器 2参考源选择、IDAC 电流档位寄存器 3IDAC 输出通道、VBIAS 配置我配置的顺序永远是固定的先想通道和传感器类型再定增益和电流最后才选数据速率。因为增益决定信号幅度IDAC 电流决定传感器自热和压降这两个是测量链路的核心。3.2 内部基准和 PGA 的选择逻辑ADS122U04IPWR 内部基准标称 2.048V温漂很低对大多数应用来说直接用它就行不用外挂基准芯片。外置基准的好处是可以做比例式测量消除电流源温漂但坏处是占 PCB 面积、增加成本而且基准芯片本身也需要时间稳定。我的做法是PT100 用内部基准加外部参考电阻 RREF 做比例式测量热电偶直接内部基准。这样电流源的绝对值温漂不会影响比例式结果因为激励电流同时流过 RREF 和 PT100参考电压和测量电压按相同比例变化误差被抵消了。PGA 增益选择上PT100 在 0.5mA 激励下 0℃ 时只有 50mV我一般用增益 8这样满量程范围约 400mV既不会让 150℃ 时 138.5Ω 对应 69mV 的信号被削顶又能获得足够的信噪比。热电偶则用增益 32 或 64把几十微伏的信号放大到 ADC 的舒适区间。3.3 数据速率与工频抑制HVAC 设备旁边全是 50Hz 和 60Hz 的工频源所以数据速率我基本固定在 20SPS。这个速率下 ΔΣ 调制器的数字滤波器会形成 50Hz 和 60Hz 的凹陷工频干扰被压得很干净。有人为了追求响应速度把数据速率拉到 1000SPS结果现场读数像心电图一样乱跳回头还怪 ADC 不好其实是自己把滤波优势扔掉了。3.4 参考配置的计算过程拿 PT100 三线制举个实际例子。设 IDAC 电流 I 0.5mA参考电阻 RREF 3300Ω那么参考电压 VREF 0.5mA × 3300Ω 1.65V低于内部基准 2.048V所以用外部基准输入也就是让 REFP 和 REFN 分别接在 RREF 两端。0℃ 时 PT100 阻值 100Ω信号电压 50mV增益取 8 时 ADC 输入约 0.4V满量程参考 1.65V 的 24%150℃ 时 PT100 阻值约 157Ω信号电压 78.5mV放大后 0.628V也不会过载。这样计算完了再去翻数据手册寄存器表把对应的位组合填进去配置就不会拍脑袋。我在这里必须强调一句不同批次或版本的数据手册对寄存器位的定义可能略有差异我每次写新项目都会重新对一遍 Register Map而不是把上一次的配置数组直接抄过来。结构体里加注释写明每个字节对应的是什么三个月后回来维护自己也看得懂。4. STM32F303VE 侧驱动状态机、读数据与错误处理4.1 CubeMX 下的串口初始化我用 STM32CubeMX 生成工程框架USART1 配置成 9600 波特率8 位数据无校验单停止位模式 TX/RX。这里有一个很容易被忽略的配置项串口中断优先级不要搞得太高否则 ADC 数据每 50ms 到一次中断里如果做滤波运算会把其他实时任务卡住。CubeMX 生成的初始化基本就是这样UART_HandleTypeDef huart1; void MX_USART1_UART_Init(void) { huart1.Instance USART1; huart1.Init.BaudRate 9600; huart1.Init.WordLength UART_WORDLENGTH_8B; huart1.Init.StopBits UART_STOPBITS_1; huart1.Init.Parity UART_PARITY_NONE; huart1.Init.Mode UART_MODE_TX_RX; huart1.Init.HwFlowCtl UART_HWCONTROL_NONE; HAL_UART_Init(huart1); }4.2 命令交互和读取时序ADS122U04IPWR 的命令帧非常简单发一个命令字节有时候紧跟参数然后芯片返回响应或数据。常用命令包括启动转换、读数据、写寄存器、读寄存器。读取转换结果的时序看起来简单但实际容易出错。我的状态机是这样的等待转换完成标志。ADS122U04IPWR 拉低数据就绪线后说明一个新的转换结果已经稳定输出。发送读数据命令。接收三字节数据拼成 24 位有符号数。把数据丢给滤波和温度换算模块。如果你在 RTOS 环境里跑转换完成事件可以做成信号量。如果裸机跑就在主循环里轮询不要在中断里做温度换算因为浮点运算虽然 F303 支持硬件浮点但还是会占用几十微秒中断里做多了会影响时序。4.3 驱动代码框架下面是我这台 HVAC 控制器上跑通的代码框架寄存器值我已经按注释写清楚具体位组合根据你的通道配置算出来之后填进去即可。#define ADS122U04_CMD_RDATA 0x1A #define ADS122U04_CMD_RREG 0x20 #define ADS122U04_CMD_WREG 0x40 static uint8_t ads_config[4] { 0x00, /* REG0: AIN1-AIN2差分输入, PGA8 */ 0x00, /* REG1: 20SPS, normal mode */ 0x00, /* REG2: 外部基准, IDAC0.5mA */ 0x00 /* REG3: IDAC输出到AIN1, VBIAS关 */ }; void ads_write_config(void) { uint8_t cmd[6]; cmd[0] ADS122U04_CMD_WREG; cmd[1] 0x00; /* 起始寄存器地址 */ cmd[2] ads_config[0]; cmd[3] ads_config[1]; cmd[4] ads_config[2]; cmd[5] ads_config[3]; HAL_UART_Transmit(huart1, cmd, 6, 100); } int32_t ads_read_result(void) { uint8_t cmd ADS122U04_CMD_RDATA; uint8_t buf[3] {0}; uint32_t raw 0; HAL_UART_Transmit(huart1, cmd, 1, 100); HAL_UART_Receive(huart1, buf, 3, 100); raw ((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | (uint32_t)buf[2]; if (raw 0x800000u) { raw | 0xFF000000u; } return (int32_t)raw; }4.4 通信异常判断UART 通信在温控器这种低频场景下很少出错但一旦出错问题会非常隐蔽。最常见的是波特率误差导致第一个字节错位。内部 RC 振荡器的精度在高温下可能漂移所以不要依赖内部时钟做 115200 波特率尤其是工业温度范围。我用外部 8MHz 晶振给 STM32F303VE 提供时钟9600 波特率的误差控制在 0.1% 以内稳定运行几个月没出过错。驱动里我还加了一个简单的心跳检测每读取 100 次就回读一次寄存器 0如果读回来的值和配置不一致说明通信链路已经异常这时候要重新初始化串口并重写配置而不是继续闷头读数据。这种保护机制在现场维护中非常有用至少你能从日志里看出问题是出现在通信链路还是传感器。5. 温度换算把 24 位原始码变成真正可用的温度5.1 PT100 比例式测量的换算拿到 24 位原始码之后第一步是把它换算成电阻。因为采用了比例式测量公式很简单R_pt100 (float)adc_code * RREF / (8388608.0f * gain)举个例子RREF 用 3300Ωgain 用 8假设读到的原始码是 254200那么R_pt100 254200 * 3300 / (8388608 * 8) ≈ 125.0Ω查 PT100 分度表大概对应 64℃。这个公式里的电流源绝对值被约掉了所以电流源温漂不影响结果。5.2 温度反查查表还是公式PT100 的阻值-温度曲线在 0℃ 以上可以用 IEC 60751 标准多项式描述R(t) R0 * (1 A*t B*t^2)其中 A 3.9083e-3B -5.775e-7。要反查温度理论上解一个一元二次方程就行。但我实际工程里更推荐直接做一张 0.1℃ 间隔的查表表格放在 Flash 里用二分法查。原因有两个查表的速度稳定执行时间不随输入变化不会在控制环路里造成抖动。查表的精度取决于你表格的密度想要 0.01℃ 分辨率就生成更密的表逻辑不需要改。对于 -200℃ 到 0℃ 的低温段公式里多了一个 C 系数查表还是更省心。MCU 上跑二分查找几百个元素最多十次比较根本感觉不到耗时。5.3 热电偶计算和冷端补偿热电偶的换算要比 PT100 麻烦一点。K 型热电偶的输出电压和温度之间是非线性关系通常在 0℃ 时是 0mV100℃ 时约 4.1mV600℃ 时约 24.9mV。工程做法是查 NIST 标准多项式或分度表。关键点是冷端补偿热电偶测量的是热端和冷端之间的温差不是绝对温度。如果冷端也就是热电偶接入 PCB 的地方温度是 30℃而你把它当作 0℃ 去查表读出来的温度就整整偏低约 30℃。我的做法是用 ADS122U04IPWR 内部温度传感器测冷端温度然后把它转换成对应的热电势加上测量得到的电势后再反查分度表。公式可以表达成E_total E_measure E_cold T f(E_total)冷端温度传感器不要放在电源或 MCU 附近我第一版 PCB 把它放在 ADC 旁边靠散热孔的位置结果读数整体偏高 2℃后来挪到热电偶端子正下方才恢复正常。这个细节看起来小实际影响很大。5.4 HVAC 量程设计和数据滤波HVAC 温度通道我一般把量程分成两段处理风管和水温用 PT100量程 095℃压缩机排气温度用 K 型热电偶量程 50150℃。这样每个通道的动态范围都集中在 ADC 量程的合适区间不会出现大量程浪费分辨率的问题。软件滤波我用的是滑动平均加一阶低通。20SPS 的原始数据先做 4 次滑动平均再进一阶低通filtered filtered alpha * (sample - filtered)alpha 取 0.2 0.5看控制环路的响应要求。风管温度变化本来就慢alpha 取小一点没关系压缩机排气温度变化快alpha 取 0.5 更合适。滤波参数做成宏定义调试时可以随时改。6. 现场抗干扰0.3℃ 的精度是怎么被环境毁掉的6.1 HVAC 现场真正的噪声源实验室里调好的板子一到现场就开始飘这是嵌入式工程师最痛苦的体验。我在风机盘管控制箱里遇到的干扰源主要是这三类变频器。现在很多 HVAC 设备用变频风机PWM 开关频率在几 kHz 到十几 kHz谐波成分很丰富会通过电源和空间耦合到模拟前端。继电器和接触器。动作瞬间产生几十伏的电压尖峰虽然持续时间短但足以让 ADC 转换结果出现一个离群值。开关电源。控制板上 24V 转 3.3V 的 DCDC 电源本身就有纹波如果模拟地和数字地没分开纹波直接串进参考电压。6.2 布局、布线和模拟滤波针对这些干扰我的处理方式是三层第一层PCB 布局。ADC 和相关模拟电路放在板子一角远离继电器、DCDC 和 MCU 高速数字线。模拟输入线尽量粗短不要和 24V 功率线平行走线如果不得不交叉就垂直交叉。第二层线缆。传感器到控制板之间的 PT100 线用双绞屏蔽线屏蔽层单端接地。双绞能够抵消磁场耦合屏蔽能挡住电场耦合两个作用都不可替代。我用普通平行线试过变频器一启动读数就跳 ±1℃换上双绞屏蔽线后恢复到 ±0.1℃ 以内。第三层硬件滤波。ADC 输入端加 RC 低通。RC 时间常数不需要很大ΔΣ ADC 本身就对带外噪声有很强的抑制加一个 1kΩ 电阻和 100nF 电容组成的截止频率约 1.6kHz 的低通就足够。R 不要用太大否则和传感器源阻抗叠加在一起会影响比例式测量的精度。6.3 软件上的最后一道防线即使硬件做得不错我还是会在软件里加离群值剔除。现场偶尔会有继电器动作瞬间造成的异常码滑动平均会被这种异常值拉偏。我的处理是先做中值滤波取最近 5 个点的中间值作为有效值再做滑动平均。这样单个尖峰最多影响一次采样不会持续污染输出。关于长期漂移ADS122U04IPWR 的温漂在数据手册里标得很低但我还是会在产品里保留定期自检功能。做法是把其中一个模拟输入通道接到一个精密电阻上每开机时测一次如果换算出来的阻值和标称值偏差超过阈值就上报传感器故障。这个功能在售后维护中能省很多排查时间。7. 校准与实测体会7.1 用精密电阻代替传感器做标定很多工程师一听到校准就想到冰水浴和油浴但那是计量实验室的做法产线和现场根本不现实。我自己的习惯是用 0.01% 精度的精密电阻模拟传感器做两点校准。具体做法是把 PT100 通道分别接上 100Ω 和 138.51Ω 的精密电阻这两个值对应 0℃ 和 100℃。记录 ADC 原始码然后算出增益误差和偏移量写入校准参数T_real (T_raw - offset) * gain_factor这个方法的优点是标准电阻稳定性远高于冰水浴而且可复现产线上任何一个工位都能操作。麻烦的是需要注意精密电阻的功率限制0.5mA 电流下的功率极小完全没问题。7.2 实测数据这块板子在实验室和现场分别跑了测试我把两组数据放在一起对比标称温度实验室读数现场读数偏差0℃0.03℃0.06℃0.06℃25℃25.02℃25.11℃0.09℃50℃50.01℃50.04℃0.03℃100℃99.98℃99.94℃-0.04℃现场数据比实验室略差但已经完全满足客户 ±0.3℃ 的控制要求。这个结果不是靠一颗 ADC 单独撑起来的而是传感器选型、三线制接线、比例式测量、电源滤波、软件滤波和校准流程叠加的结果。7.3 最容易踩的几个细节我把自己在这套方案上最后踩过的坑列出来每一条都是真实项目里花时间填过的启动顺序。上电后不要立刻写配置和读数据等 AVDD 稳定 50ms 再操作。否则第一次寄存器写入可能会失败而你已经把板子装进控制柜了排查起来很被动。未使用的输入通道不要悬空。闲置的 AIN 输入脚如果什么都不接会收到空间耦合的浮空电压这个电压可能串扰到相邻通道。我把未用通道通过 100kΩ 电阻接到参考地问题立刻消失。串口中断里的 CPU 占用。我在一个 RTOS 项目里把 ADS122U04IPWR 的数据解析放在高优先级任务中结果导致上位机通信周期性卡顿后来把数据放到低优先级任务处理只保留中断缓存问题就解决了。校准参数要存 Flash 或 EEPROM并且要带校验。出厂校准时如果直接把校准值写在易失变量里断电就丢了重新标定的时间成本很高。如果你也在做类似的温度测量项目我的建议是先不要急着换更贵的 ADC先把传感器的激励方式、参考源和布线这三件事理顺。很多时候 0.5℃ 的误差不是器件不够好而是测量链路里某个环节被忽略了。等到这些基础都算对了再回头看 ADS122U04IPWR 这颗串口 24 位 ADC你会发现它最大的价值反而是稳定——那种在变频器轰鸣的 HVAC 现场也能四平八稳输出温度值的稳定。