1. 方案选型:为什么是 ADS122U04 + PIC18LF47K40
1.1 一颗带 UART 的 24 位 ADC,省掉的不仅是两根线
先聊一个很实际的问题:工业现场测温度,单片机选谁不重要,真正决定测量上限的,是ADC这一关。你拿一颗10位、12位的MCU内置ADC去测热电偶,热电动势本身就只有几毫伏到几十毫伏,一个LSB可能就是好几个摄氏度,根本没法用。所以我做温度采集项目时,习惯直接在方案前端放一颗高分辨率外部ADC,而不是反复打磨MCU内部ADC的软件均值。
ADS122U04这个型号最吸引我的地方,是它把24位Δ-Σ ADC和UART接口做在了一起。市面上常见的24位ADC,比如ADS1220,走的是SPI接口,主机要自己拉时钟、拉片选,时序紧张的时候还要用DMA或者中断去伺候。而ADS122U04这颗芯片,直接通过两根线(TXD/RXD)跟MCU通信,距离可以拉得更远,现场接线也更简单,还省掉了SPI时序上那些麻烦事。对于PIC18LF47K40这种8位单片机来说,UART通信简直是零压力,一个EUSART外设就搞定了。
再说一下为什么不用I2C。I2C虽然有地址概念,理论上可以挂多个设备,但I2C的上拉电阻在长线、强干扰的工业环境里特别容易出问题,速率也上不去。UART就不一样,标准RS-232电平或者配合外部RS-485收发器,在几十米的距离内依然能稳定跑9600、19200波特率。HVAC的现场控制柜到传感器端子箱之间,经常就是这个距离级别。
1.2 PIC18LF47K40:一颗执行力够用的老伙计
PIC18LF47K40是Microchip的8位单片机,工作电压1.8V到3.6V,正好可以跟ADS122U04共用3.3V电源轨。它内部有一个最高能跑到64MHz的振荡器(带PLL),Flash和RAM做温度采集协议栈绰绰有余。我选这颗片子还有一个私心:它的EUSART模块在8位机里算非常好用的,自动波特率检测、9位数据模式、多机通信这些功能都有,做Modbus或者自定义协议都很顺手。
有人可能会嘀咕,8位机处理24位数据是不是太吃力?其实压力不大。ADS122U04把数据包装成串行字节流,MCU只需要一个字节一个字节地从UART接收寄存器里取数,拼装成uint32_t类型。温度换算的无符号除法、查表这类操作,在PIC18LF47K40的1MIPS到16MIPS性能范围里完全够用。如果你跑在64MHz,一条除法指令也没慢到哪里去,整个采集周期里真正花时间的反而是ADC的转换时间。
1.3 系统整体架构与数据流走向
这个项目的整体结构并不复杂:前端是温度传感器,中间是ADS122U04负责把微小的模拟信号放大、数字化,再通过UART把数据丢给PIC18LF47K40,最后由MCU做单位换算、滤波、协议打包,送给上位机或者触摸屏。传感器可以是K型热电偶、PT100/PT1000铂电阻,或者NTC热敏电阻,取决于你是在做工业炉温监测还是HVAC的风管温度、水管温度采集。
整套数据流的处理量不大,真正的技术点集中在三块:硬件上传感器和ADC的接口电路怎么搭,固件上UART协议怎么稳定收发,以及软件上怎么把原始码变成有意义的温度值。下面我按这个顺序,把每一步展开讲清楚。
2. 硬件设计:把传感器信号变成干净的比特流
2.1 吃透 ADS122U04 的内部资源
在动手画原理图之前,最好先把ADS122U04的内部结构捋一遍。它本质上是一个24位Δ-Σ ADC,前端带了可编程增益放大器(PGA)、输入多路复用器、两个可编程电流源IDAC,内部还集成了一个温度传感器。
- 输入通道:4个外部模拟输入(AIN0~AIN3),可以配置成两个差分输入对,也可以配置成四个单端输入。
- PGA增益:从1倍到128倍可选,放大热电偶这种微伏级信号是它的看家本领。
- IDAC电流源:两个可调电流源,输出电流从50µA到1500µA可配,主要是给RTD和偏置电阻提供激励。
- 内部温度传感器:用于热电偶冷端补偿(CJC),省掉一个外置测温芯片。
- 参考电压:支持内部2.048V基准,也支持外部REFP/REFN差分基准。
- 采样速率:20SPS到2kSPS可配,测温场景我一般固定在20SPS,配合内部FIR滤波器,50Hz、60Hz工频干扰能压制得很干净。
这些资源对应到实际项目,就是不同的接法。比如测热电偶,用内部基准+高增益就够了;测PT100/PT1000,要动用IDAC和外部基准电阻做比例式测量。一开始我也是按PDF前面几页的最小系统乱接,后来才发现真正影响精度的,是对内部资源的使用方式。
2.2 最小系统接线与电源处理
ADS122U04的电源域分模拟电源AVDD和数字电源DVDD,我建议直接用一个低噪声LDO输出3.3V,同时给AVDD和DVDD供电,中间用磁珠隔开。参考电压这一块,测温项目我用的是内部2.048V基准,省去外部基准芯片,不过REFN引脚和REFP引脚之间要就近放一颗100nF电容,否则内部基准的噪声会把有效分辨率拉下来。
PIC18LF47K40和ADS122U04之间连线就四根:TXD接RXD、RXD接TXD,双方共地,再加一个复位信号。UART的TXD/RXD线如果走线超过10cm,建议串33Ω到100Ω电阻,减少振铃。PCB布局上,模拟输入走线要短,尽量远离数字信号线和时钟线,这一点在工业控制板上做起来比较难,但至少要把AIN走线的下面保持完整的参考地平面。
上电时序也要注意:先给ADC上电,等它完成内部上电复位,再发UART命令。PIC18LF47K40的固件里我会做300ms的延时,确保ADS122U04的晶体振荡器起振稳定,否则第一次通信大概率会失败。
2.3 三种典型传感器接入电路
第一种是热电偶。K型热电偶的两根线直接接到AIN0和AIN1,构成差分输入,PGA增益我习惯设到128倍。热电偶输出的毫伏级信号经过放大以后进入ADC,冷端补偿用片内温度传感器读出来的芯片温度去算。要注意的是,热电偶引线如果从金属铠装护套里面穿出来,暴露在外的端子部分存在热电动势的二次叠加,最好把冷端端子做在铜排或大面积铜箔上,减小热梯度。
第二种是PT100/PT1000比例式接法。用一个IDAC电流源给RTD提供激励电流,电流经过RTD后,在RTD两端产生一个电压,这个电压进入PGA差分输入。同时同一股电流经过一个精密基准电阻Rref,Rref两端的电压接到REFP和REFN。此时ADC测到的是Vrtd与Vref的比值,乘以Rref就能算出Rrtd,激励电流源的温漂和绝对误差在计算中被抵消了。注意Rref必须选低温漂电阻,我常用0.1%精度、5ppm温漂的金属箔电阻。
第三种是NTC热敏电阻。HVAC里常见的10kΩ NTC,可以用IDAC给NTC和参考电阻供同一个电流,NTC电压进PGA,参考电阻电压进VREF,同样构成比例式测量。NTC的B值离散性大,正规做法是出厂前做两点校准,或者直接采用NTC厂商提供的R-T分度表。
3. 固件开发:让 PIC18LF47K40 和 ADC 对话
3.1 UART 命令帧理解与同步策略
ADS122U04的串行协议是我用这颗芯片时花时间最长的地方。它不像普通外设那么简单,发个命令就完,而是要处理好帧同步和命令响应。手册里明确要求,每次命令以同步字节开头,这个同步字节是0x00,我实际调通的方式是:拉低TXD一段时间,产生一个UART的break状态,再恢复高电平,然后发送0x00同步字节,最后发命令字节和CRC字节。这一套操作能显著提升第一次通信的成功率,尤其在芯片刚上电、波特率尚未对齐的阶段。
命令集主要包括读寄存器(RREG)、写寄存器(WREG)、读转换数据(RDATA)、复位(RST)和掉电(PWDN)。执行读寄存器或读数据命令后,主机要等待从机返回数据帧,返回数据的第一部分是状态字节,后面跟着你要读的内容。我调试过程中发现,数据返回时间不能按“命令发完立刻读”来写,必须根据设置的采样速率预留足够的转换时间,否则读到的是上一次的结果,或者干脆读到全零。
CRC计算在协议里是可选项。工业环境建议把这个功能打开,PIC18LF47K40有硬件CRC运算支持,但操作起来未必方便,我最后还是用软件查表法算了8位CRC,占用的代码空间很小,运行时间也不长。
3.2 PIC 侧 EUSART 初始化与波特率计算
PIC18LF47K40的EUSART初始化要看数据手册里的波特率公式。在16位波特率模式下,SPBRG寄存器的值这样算:
SPBRG = Fosc / (16 × BaudRate) - 1
我用的系统时钟是64MHz(内部振荡器加PLL),目标波特率9600,那么:
SPBRG = 64,000,000 / (16 × 9600) - 1 = 416.67 - 1 ≈ 416
取SPBRG=416,实际波特率等于64,000,000 / [16 × (416 + 1)] = 9602.4bps,误差只有0.025%,完全没问题。如果你用3.6864MHz或者4MHz晶振,算出来的SPBRG可能不是整数,这时要手动验证一下是否落在1%误差范围内,UART通信超过2%的波特率误差就容易出乱码。
// PIC18LF47K40 EUSART1 初始化,3.3V供电,64MHz系统时钟,9600bps void uart1_init(void) { TRISCbits.TRISC6 = 1; // TX1 引脚设为输入(由外设控制输出) TRISCbits.TRISC7 = 1; // RX1 引脚设为输入 BAUD1CON = 0x00; // 8位数据,无奇偶校验,1位停止位 RC1STA = 0x40; // 使能接收模块 TX1STA = 0x20; // 使能发送模块 SPBRG1 = 416; // 64MHz / (16 * 9600) - 1 ≈ 416 SPBRGH1 = 0; TX1STAbits.TXEN = 1; // 使能发送 RC1STAbits.CREN = 1; // 使能连续接收 }我习惯在初始化之后,发送一个0x55(二进制01010101),自己抓一下回环波形,确认电平翻转正常再进入协议流程。
3.3 配置寄存器顺序和关键位
ADS122U04内部有多个控制寄存器,地址从0x00到0x04以上,分别对应输入多路复用器、PGA增益与操作模式、数据速率、IDAC配置、基准电压和传感器设置。这里我不逐位照搬数据手册,而是把一套经过实测的配置顺序记录下来。
第一步,发送RST命令让芯片复位到默认状态。之后延时至少10ms,让内部上电复位完成。第二步,写寄存器0x01,把PGA增益设置成128倍、测量模式选择为连续转换。第三步,写寄存器0x02,把数据速率设成20SPS,同时确认工作在正常模式而不是低功耗待机模式。第四步,配置IDAC电流源,设置输出电流大小,并把电流源路由到正确的AIN引脚。第五步,设置参考源,我用的是内部2.048V基准,并开启内部温度传感器。
寄存器配置顺序之所以重要,是因为某些位的组合是有冲突的。比如当PGA增益大于某档时,输入信号幅度必须低于供电电压的某个比例,你如果先设了增益再改输入通道,MUX的切换可能导致输出瞬态饱和,读数要等几个转换周期才能恢复。
3.4 核心代码:读取转换数据并换算温度
连续转换模式启动以后,ADC会按照设定的数据速率不断刷新转换结果。主机可以随时发送RDATA命令读取当前数据,也可以配置成数据就绪后自动上传的模式。自动上传模式虽然省一条命令,但要求主机的接收时序一定要稳,我这次的项目里用的是主动查询方式,简单靠谱,逻辑也更清晰。
下面这段代码是读取一次24位转换数据的核心逻辑。注意ADS122U04输出的是二进制补码格式,如果是负温度差分信号,最高位会是1,必须先把3个字节拼成uint32_t,再强制转换成int32_t再做除法,否则负数会被当成很大的正数。
int32_t ads122u04_read_raw(void) { uint8_t b0, b1, b2; int32_t raw; // 发送同步字节和RDATA命令 uart1_write(0x00); uart1_write(0x10); // RDATA, 读取24位转换结果 // 等待响应,根据20SPS的转换周期预留足够时间 b0 = uart1_read_blocking(); b1 = uart1_read_blocking(); b2 = uart1_read_blocking(); raw = ((int32_t)b0 << 16) | ((int32_t)b1 << 8) | (int32_t)b2; // 如果符号位为1,补齐高8位 if (raw & 0x800000) { raw |= 0xFF000000; } return raw; }拿到24位原始码之后,要换算成电压:
V_in = raw × (2 × Vref) / (Gain × 2^23)
拿这个电压值去查热电偶分度表,或者用PT100的Callendar-Van Dusen公式反算温度。查表求温度我建议直接用线性插值,分度表每隔5°C一个点就够了,非线性误差不会超过0.1°C。如果你嫌表格占Flash空间,HVAC这种0°C到100°C范围的场合,用多项式拟合NTC电阻-温度曲线也能做到0.2°C以内。
4. 校准与精度优化:24 位分辨率的正确打开方式
4.1 偏移校准和增益校准
24位ADC不代表24位精度,出厂后如果不校准,偏移误差和增益误差会让你实际测出来的温度偏个一两度。我在项目里做的是软件校准,工艺上叫“标定”,方法很简单。先把PGA输入端短接,采集1024次求平均,得到偏移码offset。再输入一个已知的精密电压Vcal,采集均值得到gain码。
校准后的电压值:
V_corrected = [(raw - offset) / (gain - offset)] × Vcal
如果测的是电阻比例值,增益校准可以等价写成一个系数。校准数据我建议存到PIC18LF47K40的Flash数据区里,每台设备出厂前标一次,换传感器后再标一次。很多HVAC控制器上的“传感器偏差修正”功能,本质上就是这一套。
4.2 热电偶冷端补偿与 RTD 比例式测量的细节
热电偶测的是热端和冷端的温差电动势,必须对冷端温度进行补偿。ADS122U04内部温度传感器可以直接读芯片温度,这一路数据帮我省了一个外置芯片。但要注意,内部温度传感器反映的是芯片的管芯温度,跟热电偶接线端子的实际温度会有差异,尤其是靠近CPU、电源模块等发热源时。严谨的做法是在热电偶端子附近贴一颗NTC,把NTC接到另一个ADC通道,用NTC读到的冷端温度去补偿,这样响应更贴近现场。
RTD比例式测量在硬件部分已经说过了,这里说计算结果。Rrtd = code_ratio × Rref,比率式的算法天然抵消了IDAC电流源的温漂,所以硬件上只要保证Rref足够精确就可以了。算完Rrtd之后,用多项式或者查表求温度。PT100在0°C到100°C的区间,线性度已经还行,但严格做还是要用ITS-90的分度公式。
4.3 现场抗噪与滤波策略
温度信号是缓变信号,完全可以用低采样率换取高噪声抑制。ADS122U04在20SPS下的有效分辨率明显高于高速模式,因为Δ-Σ调制器内部的数字滤波器把带外噪声压得很低。我在固件里又加了一层移动平均滤波,窗口长度取16个转换结果。实测下来,原本1°C左右的跳动可以压缩到0.05°C以内。
如果现场有变频器,那就要小心共模干扰了。HVAC系统里变频风机一启动,测温信号经常会被拉偏。我的经验是电源端加共模电感,信号线用屏蔽双绞线并把屏蔽层单端接地,ADC的模拟地和数字地在芯片下方单点连接。还有一个容易被忽视的点:给REFP、REFN引脚接的100nF电容,位置一定要紧贴芯片引脚,走线长了效果大打折扣。
5. 实战排雷:我在这套方案里踩过的坑
5.1 ADC 不响应命令
第一次上电调试时,满心欢喜地发RREG命令想读寄存器,结果UART接收端一个字节都没等到。查了一圈,发现根子是芯片还没从复位状态下缓过来。ADS122U04上电后需要一段时间完成内部初始化,我把上电延时从几百微秒改成300ms后,问题消失。另一个容易踩的是波特率设置,我一开始在PIC侧配错了分频系数,实际波特率偏了7%,芯片自然不理你。用示波器抓一下TXD引脚的高电平宽度,跟理论值一对比就能确诊。
此外,帧同步字节0x00还有个大坑。如果主机在发命令之前发送了0x00之外的数据,或者TXD线在空闲时电平不稳,芯片的帧同步状态机会错乱。我采取的对策是每次命令前先拉低TXD一段时间产生break,再把TXD恢复高电平20µs以上,然后才发出0x00同步字节。这个对策看起来原始,但在现场主机和从机上电时间不同步的场景里非常管用。
5.2 测量数据跳动剧烈
PCB做回来之后,我把K型热电偶接上,屏幕上看到的温度值乱跳,幅度高达三度。排查过程是从电源开始的。先用示波器看3.3V电源纹波,发现LDO的输出在130mV左右,这在ADC的模拟电源上是不能忍的。换了一颗低噪声LDO,并在AVDD引脚旁边加了10µF钽电容和100nF陶瓷电容组合,纹波降到了20mV以内。
第二个因素是输入悬空。差分输入的公共端必须要有一个偏置路径,不然浮空电势会把PGA打出虚高偏置。我给AIN0和AIN1各自对地接了一颗1MΩ电阻,用作输入偏置泄放,跳动问题基本消失。单片机侧的注意事项是:读取UART时如果长时间不读UART而让接收缓冲区溢出,后续所有时序都会错位,所以主循环里要定时清缓冲区的溢出标志。
5.3 温度漂移方向诡异
有一台样机,早上开机测室温是25°C,到中午变成25.8°C,但水银温度计一点没变。这种每天都往上跑的“温漂”,多半不是ADC本身温漂,而是板上其他发热源通过热传导影响了冷端。我把热电偶端子移到了板边,并用镂空隔热带把PCB上电源电路的热量隔开,读数就稳定了。
还有一次是RTD测量在高温段整体偏高。后来逐个排查,发现问题出在激励电流过大,导致RTD自热效应明显。PT100在1mA激励下自热约0.1°C,到几mA就可能会到0.5°C。我把IDAC电流从1.5mA调低到500µA,自热误差明显减小。这就回到一个基本原则:传感器被测对象受到的干扰和激励源大小密切相关,能小则小。
5.4 排查速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 完全无响应 | 上电时间不够、波特率偏差过大、TXD电平反接 | 延时300ms、重新计算SPBRG、对调TXD/RXD |
| 乱码错位 | 同步字节被干扰、CRC校验不通过 | break唤醒、开启CRC、降低波特率 |
| 读数剧烈跳动 | 电源纹波大、输入悬空、接地不良 | 换低噪声LDO、加偏置电阻、单点接地 |
| 数值随温度漂移 | 冷端热梯度、RTD自热 | 端子移出热区、降低IDAC电流 |
| 温度整体偏高 | 增益校准未做、Vref精度差 | 做标定、换更高精度参考电阻 |
| 低温段读数负值乱跳 | 24位符号扩展没做 | 检查int32_t符号扩展逻辑 |
最后分享一个在实际项目里很实用的小设计:我在这套PIC固件里预留了一组调试命令,可以在不重新编译程序的情况下,随时读ADC原始码、读校准系数、读芯片温度。现场出问题时,连一个串口调试助手就能把整条链路拆开看。这个习惯帮我省了不知道多少跑去现场诊断的时间。ADS122U04和PIC18LF47K40这套组合,整体成本不高、实现难度适中,但把测量链路里的每一个误差来源都治住之后,它能达到的稳定度和精度,绝对对得起这个价位。