去年接了一个工业温控面板的活,客户点名要求保留手里现成的 dsPIC30F4013 做主控,测温部分重新设计。我一开始按惯性选了 SPI 接口的 24 位 ADC,画完板子才发现线多不说,dsPIC30F4013 的 SPI 引脚还得跟别的外设抢。后来换成 TI 的 ADS122U04IPWR,一颗 UART 接口的 24 位 Δ-Σ ADC,一根 TX 一根 RX 就把高精度采集问题解决了。
整套方案做完,热电偶、PT100、NTC 三种传感器都覆盖了,工业现场跑了几个月没出幺蛾子。这篇文章就把这个项目里的选型逻辑、信号链、寄存器配置、校准滤波这些环节拆开讲,重点说清楚哪些地方容易踩坑,以及 HVAC 和工业场景的落地差异。
1. 选型复盘:UART 型 24 位 ADC 与 dsPIC30F4013 为什么合拍
1.1 一颗带 UART 的 ADC,省掉的不只是两根线
常规高精度 ADC 基本都是 SPI 接口,SCLK、DIN、DOUT、CS 四根线起步,如果板上还有别的 SPI 从设备,CS 分配就成了一个麻烦事。SPI 在长距离传输、光电隔离场景下也要额外考虑时钟同步问题,因为 SCLK 和 MOSI/MISO 的时序在线上经过隔离器件之后会产生额外延迟,频率一高就容易出错。
ADS122U04IPWR 用的是标准 UART 接口,物理上只有 TX、RX 两根信号线。对于 dsPIC30F4013 这种引脚资源相对有限的 16 位 DSC,省下的引脚可以留给按键、数码管、继电器控制或者别的外设。而且 UART 本来就是 dsPIC 的长项,硬件模块直接处理帧格式,MCU 端不用像 SPI 那样手动翻转 CS、等待字节收发,逻辑上更省心。
更重要的是,UART 在隔离方案上非常友好。工业设备为了保护 MCU 和操作人员,经常要在传感器侧和主控侧之间加隔离。SPI 做隔离需要隔离四根线,还要考虑时钟相位问题;UART 只需要隔离两根线,随便拿两颗数字隔离器或者光耦就能解决。
1.2 ADS122U04IPWR 的温度采集本钱
ADS122U04 是 TI 面向传感器测量推的一颗 24 位 Δ-Σ ADC,它把温度采集常用的外围都集成进去了:
- 4 通道输入多路复用器,可以做差分或者单端输入;
- 可编程增益放大器(PGA),增益从 1 到 128 可调,直接解决热电偶微伏级信号需要高增益的问题;
- 两个匹配的可编程电流源(IDAC),专门给 RTD 做激励,支持比例测量;
- 内部基准源 2.048V,省掉外部基准芯片;
- 内部温度传感器,可以配合热电偶冷端补偿做粗略参考;
- 最低数据率 20SPS,配合数字滤波器对 50Hz/60Hz 工频干扰有较好的抑制能力。
IPWR 后缀里的 I 代表工业温度等级,器件工作范围覆盖 -40°C 到 +125°C,PW 代表 TSSOP-16 封装。工业现场探头放在室外、冷冻库、锅炉房这些环境,芯片本体的温度等级不能马虎,消费级器件在这种场合很容易出隐性故障。
1.3 dsPIC30F4013 在里面的真正角色
dsPIC30F4013 是一颗 16 位数字信号控制器,48KB Flash、2KB RAM,最高工作频率 40MHz 左右,型号里带 F 说明是 Flash 版本,工业温度范围也是标配。拿它来做温度采集主控,性能完全够用。
它在这个方案里的任务主要有三块:
- 管理 ADS122U04 的 UART 通信,包括寄存器配置、数据读取、通信异常恢复;
- 执行温度线性化算法,热电偶查表、RTD 计算、NTC 反解 B 值公式;
- 对外提供控制逻辑,比如温度 PID 调节、超温报警、RS-485 组网等等。
dsPIC30F4013 内部自带 12 位 ADC,有些人会问:既然内部有 ADC,为什么还要外挂?答案是分辨率差了三个数量级。内部 12 位 ADC 做热电偶测温,1°C 对应的电压变化约 41µV,在 3.3V 参考下 LSB 约 0.8mV,温度分辨率也就 20°C 左右,根本不实用。外挂 ADS122U04 的目的就是把模拟前端和主控分离,把精度做上去,同时让主控专注于逻辑处理。
2. 模拟信号链搭建:热电偶、PT100、NTC 的接法与参数计算
2.1 热电偶:微伏级信号的增益与冷端补偿
热电偶的原理是两种不同金属接触产生热电动势,K 型热电偶的灵敏度大约是 41µV/°C。0°C 到 1000°C 对应电压跨度也就 41mV,这个信号电平非常低,必须用高增益 PGA 放大后再进 ADC。
我的接法是:热电偶正极接 AIN0,负极接 AIN1,配置成差分输入。PGA 增益选 64 或者 128,增益选多少取决于测温量程。K 型热电偶测 0-800°C 时热电动势最大约 33mV,内部基准 2.048V 除以增益 64 是 32mV,刚好接近满量程;如果量程更小,选 128 能得到更好的分辨率。
冷端补偿是个绕不开的问题。热电偶测到的是热端与冷端的温差电动势,冷端(即接线端子处)温度必须被测量并补偿。我这边有三种做法:
- 用 ADC 内部温度传感器:集成度高,精度一般,适合对绝对精度要求不高的场景;
- 板载一颗 TMP117 或者 NTC 贴在接线端子上:精度高,需要额外一路采集;
- 用 dsPIC30F4013 内部 ADC 测 NTC:省事,但 12 位分辨率在 -20°C 到 60°C 范围内也能做到约 0.3°C 的补偿精度,够用了。
断线检测也要提前设计。热电偶现场使用很容易断线或者接触不良,如果不做检测,系统会把断线读成满量程温度,造成误判。我习惯用 IDAC 输出一个 100µA 电流拉到热电偶正端,再接一个下拉电阻到地。正常工作时候 100µA 被下拉电阻吸收,对测量影响很小;一旦断线,输入引脚被拉高,ADC 读数直接溢出,MCU 就能判断出断线状态。
2.2 三线制 PT100:用两个 IDAC 消除引线电阻
PT100 在 0°C 时阻值 100Ω,灵敏度约 0.385Ω/°C。如果只用两根线测量,线缆电阻会直接叠加到传感器阻值上,10Ω 的引线电阻就能带来约 26°C 的误差,工业现场根本没法用。三线制是性价比最高的方案。
ADS122U04 的两个 IDAC 在这里派上大用场。IIDAC1 和 IIDAC2 设置为相同电流,比如 1mA,一个接到 PT100 的高端,另一个接到三线制传感器的第三根引线上。采集端差分测量两根引线内侧的电压,两根引线电阻产生的压降在数学上被抵消掉了。同时让同一个 IDAC 电流流经一个精密参考电阻 RREF,用它的压降作为 ADC 的基准电压,这样测量结果变成 RRTD 与 RREF 的比例,IDAC 电流本身的温漂和误差就被完全抵消了。这是比例测量的精髓。
我在这个项目里 RREF 选的是 1.5kΩ 精密电阻(温漂 5ppm/°C 以内),PGA 增益设成 8。PT100 在 0°C 时压降 100mV,基准电压 1.5V,用增益 8 后信号幅度接近满量程的 40%,分辨率足够覆盖 -50°C 到 300°C 的工业测温范围。
自热效应也要注意。1mA 电流在 PT100 上产生 0.1mW 功耗,对 0.385Ω/°C 的器件来说,自热误差通常在 0.1°C 量级,可以接受。如果做精密计量,可以降到 500µA,但要注意 PGA 增益也要相应调高。
2.3 NTC 热敏电阻的简易分压方案
HVAC 场景里大量使用 10kΩ NTC 热敏电阻,成本低、灵敏度高,-40°C 到 150°C 范围内响应快。NTC 的接法最简单:IDAC 给 100µA 恒流源,或者干脆用一颗精密电阻分压,把 AIN0 接到分压点,AIN1 接地。
NTC 的缺点是严重非线性。25°C 时阻值变化大约 -4.4%/°C,温度越高变化越慢。如果用 ADS122U04 的 24 位分辨率去采,配合软件查表,整个量程内的分辨率都能做到 0.01°C 以上,非线性问题交给软件解决就行。
有一点要提示:NTC 的 B 值一致性不像 RTD 那么标准。不同厂商、不同批次差异明显,批量产品必须做逐点校准,不能直接套公式。我在项目里是给每台设备写一个偏移量校正值,出厂前用恒温槽标定,写入 dsPIC 的 EEPROM。
3. UART 通信与寄存器配置:把 ADS122U04 调出稳定的数据流
3.1 波特率与通信格式的确定
ADS122U04 的 UART 采用标准 8 位数据、无校验、1 位停止位格式,波特率范围覆盖常用档位。我用的是 115200bps,配合 dsPIC30F4013 的 UART 模块,通信负荷很轻,即使 MCU 同时跑 PID 运算和显示刷新,也不会丢字节。
需要特别注意的一点是,ADS122U04 的 UART 不是简单的"发命令就立刻有返回"。它内部有一个命令状态机,不同命令字进入不同模式。读取转换结果时要按照芯片手册规定的命令顺序来:先写寄存器配置,再触发转换,等待 DRDY 状态后读取数据。我在开发时踩过坑,直接发 0x10 以为能读到数据,结果一片空返回,后来老老实实对着手册的命令表核对了一遍。
3.2 配置寄存器:PGA、基准、IDAC 的设置顺序
ADS122U04 的寄存器数量不多,每个寄存器 8 位,但设置顺序直接影响测量结果。我的建议是从输入到输出逐步配置:
- 先复位 ADC,确保内部状态机处于已知状态;
- 配置 MUX 选择输入通道,同时把未用引脚设为高阻态;
- 配置 PGA 增益和数据率;
- 配置基准源和 IDAC 电流值;
- 最后启动连续转换或者单次转换。
下面是一段我在 MPLAB XC16 里用的初始化函数框架,具体命令码以你手上的手册为准:
void ADS122U04_Init(void) { uint8_t cfg[3]; // 复位 UART1_SendByte(0x0F); // 复位命令,见数据手册 delay_ms(10); // 寄存器0:AIN0/AIN1 差分,PGA 128 cfg[0] = 0x00; // 寄存器1:数据率 20SPS,PGA 128 cfg[1] = 0x50; // 寄存器2:内部基准,IDAC1 = 1mA cfg[2] = 0x30; UART1_SendByte(0x40); // 写寄存器命令 UART1_SendByte(0x00); // 起始寄存器地址 UART1_SendByte(0x03); // 写入 3 个寄存器 UART1_SendBytes(cfg, 3); delay_ms(2); }寄存器地址和命令码我没有在这里写死,因为不同版本手册存在差异,最好的做法是直接打开数据手册的最后几页查命令表。写代码时把寄存器内容做成宏,方便后期调整。
3.3 dsPIC30F4013 端的驱动时序与超时处理
dsPIC 端的 UART 驱动先做波特率初始化。dsPIC30F4013 的 UART 波特率发生器寄存器 U1BRG 的计算公式是:
U1BRG = (FCY / (16 * 波特率)) - 1
如果 FCY 是 10MHz,115200 波特率时 U1BRG 约等于 4。注意 FCY 是处理器指令时钟而不是晶振频率,dsPIC30F4013 默认 FCY = FOSC/4,很多人在这一步算错导致波特率差一大截,串口全是乱码。
读取温度的驱动流程我封装成三个函数:
uint8_t ADS122U04_ReadTemp(float *temp) { uint8_t buf[4]; int32_t raw = 0; // 发送读取命令 UART1_SendByte(0x12); // 读数据命令,见手册命令表 // 等待数据返回,带超时保护 if (!UART1_ReceiveTimeout(buf, 4, 100)) { return 0xFF; // 超时返回错误 } // 检查状态字节,确认转换就绪且无报警 if ((buf[0] & 0x40) != 0) { // 状态位含义以手册为准 return 0xFE; // 数据未就绪 } // 拼接 24 位原始码 raw = (int32_t)((buf[1] << 16) | (buf[2] << 8) | buf[3]); if (raw & 0x800000) { // 有符号处理 raw -= 0x1000000; } // 转温度,具体公式见线性化章节 *temp = RawToTemperature(raw); return 0x00; }超时处理是我觉得整个驱动里最重要的一段。工控现场偶尔会有通信干扰,如果 MCU 发送命令后一直死等 ADC 回复,一次卡死就会拖垮整个控制循环。我的做法是加 100ms 超时,超时后重新发送复位序列,连续三次失败才上报通信故障。这条策略后来在实测中救了多次现场。
4. 原始码到温度值:线性化、两点校准和滤波的取舍
4.1 热电偶不是线性的,分段查找表最稳
K 型热电偶虽然灵敏度在宽量程内接近线性,但严格看它的热电动势曲线是有弯曲的。拿 NIST ITS-90 多项式去精确计算,需要长阶浮点运算,在 dsPIC30F4013 上不是不能跑,但代码体积和栈开销都比较大,没必要。
我用的办法是分段线性查表。把 K 型热电偶分度表按 50°C 间隔取值,存成一张 uint16_t 数组放在 Flash 里。查表时先定位区间,再做线性插值。50°C 间隔的线性插值误差在高斜率区域大约 0.2°C,对工业控制完全够用,如果嫌粗糙可以换成 20°C 间隔,Flash 成本多几百字节而已。
冷端补偿不能先算温差再查表,正确做法是:把冷端温度折算成热电势,加到热端测量热电势上去,再用新的热电势值查表得到真实温度。顺序错了会引入系统性偏差,而且温度越高偏差越大。
4.2 RTD 的 Callendar-Van Dusen 方程与动态校准
PT100 的电阻-温度关系在 0°C 以上用 Callendar-Van Dusen 方程计算非常精准:
R(t) = R0 * (1 + A*t + B*t^2)
其中 A、B 是铂的常数,0°C 以下还要引入 C 项。实际工程里我反而是直接查表,因为 RTD 的分度表在代码里就几十项,Flash 占用不大,而且查表天然支持任意非线性曲线,不用记一堆浮点系数。
偏差校正才是 RTD 测量的核心。ADS122U04 的增益误差、基准电阻的初始精度、引脚布线电阻都会带来系统性偏差。我的校准方法是两点校准:
- 在 0°C 恒温槽里测一次,记录偏移值;
- 在 100°C(沸水)或精密电阻箱模拟 100°C 处测一次,记录增益偏差;
- 把两个校正系数写入 EEPROM,之后每次转换结果都套用。
我还在 PCB 上预留了一个高精度 100Ω 电阻位,出厂校准用继电器自动切换到校准电阻,替代恒温槽,批量生产效率提高很多。
4.3 过采样、均值和中值滤波的组合拳
24 位 ADC 在小信号测量时,低 4 位经常处于抖动状态。如果直接拿单次转换结果去控制 PID,输出会毛刺不断。我试过几种滤波策略,最终组合是:
- 先做中值滤波:连续采 5 次,取中间值,把偶发尖峰毛刺去掉;
- 再做滑动平均:对最新 4 个中值结果平均,平滑信号;
- 数据率保持 20SPS,一组滤波下来响应延迟约 2 秒,对温度控制这个延迟完全可以接受。
在 dsPIC30F4013 上做这些滤波,循环和数组操作都是它的强项,Flash 负担很小。滤波器的系数不用太花哨,移动平均够用,工程设备讲究的是可预测和稳定,而不是极致响应速度。
5. 工业现场干扰实录:50Hz 工频、长线共模与 PCB 布局的坑
5.1 20SPS 数据率对 50Hz 工频抑制的妙处
ADS122U04 这类 Δ-Σ ADC 内置数字滤波器,数据率选择会直接影响工频抑制效果。当数据率设为 20SPS 时,数字滤波器的采样窗口恰好是 50ms,正好覆盖 50Hz 工频的两个完整周期,因此对 50Hz 干扰有理论上的完全抑制。要是环境在 60Hz 区域,数据率就要选对应的档位。
我在实验室验证过一次。直接用一台开关电源给传感器供电,工频纹波约 100mV,用 20SPS 采出来的温度数据抖动在 ±0.05°C 以内;换成 1000SPS 数据率,同一信号抖动飙到 ±5°C。所以除非你已经确认现场没有工频干扰,否则温度采集场景老老实实用低速档。
5.2 长线传输与共模电压
工业现场传感器经常离控制器几十米远。热电偶和 RTD 的差分输出在线缆上会感应出共模噪声,严重时候能到几十伏。ADS122U04 的模拟输入有一个共模范围,超出范围会导致转换结果直接饱和。
我的处理方式:
- 传感器线缆用双绞屏蔽线,屏蔽层在控制器端单点接地,避免地环路;
- 在 AIN 引脚对地各加一颗 1nF 电容,滤掉射频干扰;
- 如果现场共模干扰实在大,就在 ADC 前面加一颗仪表放大器做初级调理,把共模转成差分再进 ADC。
加 RC 滤波还有一个实际作用,就是给 Δ-Σ 调制器的采样电容提供电荷。很多工程师不理解为什么 ADC 前面要加大电容,因为 Δ-Σ 内部采样电容会在每个采样周期抽取电荷,如果前端阻抗太高,电荷建立时间不够,就会产生增益误差。所以我给 AIN 通路串电阻控制在 1kΩ 以内,对地电容 100nF 起步。
5.3 PCB 布局的细节
24 位 ADC 对布局的敏感度远超 12 位。我在 PCB 上吃过亏,第一版把 ADS122U04 放在 DC-DC 电感正下方,20SPS 下数据还是稳定在 ±3°C 跳动,后来搬走电感一侧,立刻恢复到 ±0.1°C。这教训不能更深刻。
几个关键布局原则:
- 模拟电源和数字电源分开走线,中间用磁珠隔离;
- AVDD 引脚和基准引脚各自放 10µF + 100nF 去耦电容,紧贴引脚;
- 模拟地单点连接,传感器地线单独铺一块铜皮;
- 所有数字信号线(TX/RX)不要跨越模拟区域,不要和 AIN 线平行走线。
表 1 是我项目里常用的一组去耦参数,供参考:
| 网络 | 电容组合 | 放置距离 |
|---|---|---|
| AVDD | 10µF + 100nF | 距引脚 < 5mm |
| VREF | 4.7µF + 100nF | 距基准引脚 < 3mm |
| AIN0/AIN1 | 1kΩ + 100nF RC | 靠近 ADC 输入引脚 |
| DVDD | 2.2µF + 10nF | 距引脚 < 5mm |
6. HVAC 多测点场景:扫描时序、功耗调度与批量校准工装
6.1 多路传感器扫描时的稳定时间
HVAC 项目里经常一拖多,比如一个控制器管 4 个风机盘管温度。ADS122U04 内部 MUX 只有 4 路,外部可以通过继电器或者模拟开关扩展。但通道切换之后不能立刻读数,因为 MUX 通道切换会引起 PGA 输入瞬态,需要至少几个转换周期等信号稳定下来。
我实测的经验是:20SPS 数据率下,切换到新通道后至少等 3 个完整转换周期再取数据,否则前 2 个读数会有明显跳变。4 路通道扫描一遍差不多要 0.6 秒,HVAC 的温度变化速率很慢,这个响应速度不是问题。
如果某一路长期不用,可以把它配置成高阻模式并关闭该通道的 IDAC,能省一点功耗,也能减少串扰。
6.2 功耗调度:从连续采集到按需唤醒
工业控制器可能常年在 24V 供电下跑,但对 HVAC 这类要过节能认证的产品,ADC 的功耗也得精打细算。ADS122U04 在关断模式下电流极低,支持单次转换触发,转换完成自动回关断。
我的调度策略是:dsPIC30F4013 进入休眠,用内部定时器每 10 秒唤醒一次,给 ADC 发单次转换命令,读完温度立即进入休眠。这样一个测点整机功耗可以压到 1mA 以下。电池供电的场景也适用。
6.3 从原型到批量:校准工装和生产一致性
原型阶段手工校准没问题,批量生产就不能靠人去调。我做了一套简单工装:
- 每块 PCB 上预留校准继电器,由产线测试治具控制;
- 治具上装精密电阻箱,模拟 0°C、50°C、100°C 三个温度点;
- MCU 出厂固件里包含校准模式,收到串口命令后自动采集三个点的原始码,计算偏移和斜率,写入 EEPROM;
- 生产完抽检,每块板子的温度示值和恒温槽标准值的偏差控制在 ±0.2°C 以内。
这套流程跑下来,几百块板子的数据一致性比手工校准好得多,而且每块板子都有校准记录,出了问题能追溯。
到了现场,我发现通信字节误码才是最容易忽略的坑。ADS122U04 的 UART 命令帧没有 CRC,工业现场强干扰环境下偶尔会收到错误字节。我的做法是每条命令都检查返回状态字节,连续错误三次就把 ADC 重新复位初始化。这个习惯帮我避免了好几次现场设备卡死的情况,算是这个项目里最能拿得出手的一条经验。