一块PIC18F45K80,外挂一颗ADS122U04IPWR,专门用来做工业和HVAC场合的温度采集——这是我最近一个项目的核心构型。需求不算复杂:几十个温度点要精测,精度目标±0.2℃,现场还有变频器、交流接触器、风机启停带来的各种干扰,主控还得把最终温度以总线方式送出去。这种时候,工程师的第一反应往往是选个带24位ADC的高端MCU,但实际做下来,8位MCU+外部精密ADC的搭法反而更顺手。这篇文章就把这套方案的选型逻辑、硬件接法、固件流程和实测踩坑记录一次讲透,给正在做温度变送器、楼宇自控、冷冻站或者工业盘柜测温的朋友一个可以直接参考的底稿。
1. 为什么选用ADS122U04IPWR与PIC18F45K80这块组合
1.1 24位ADC是刚需,不是堆料
很多人一看到24位ADC就以为是“发烧级”配置,但在低电压、小信号温度测量场景里,这其实是算术题。PT100的电阻温度系数约为0.385Ω/℃,如果激励电流取1mA,那么温度每变化1℃,在传感器上产生的电压变化只有0.385mV。我们常用的MCU内部12位ADC,3.3V供电下1个LSB约等于0.8mV,换算下来一个码字差不多对应2℃。这还没算内部ADC的参考源漂移、增益误差和开关噪声,实际做出来能稳定读到±1℃都算运气好。
HVAC系统里很多房间温控、水管温度监测要求±0.5℃已经算松,工业场合做过程监控往往要求±0.2℃甚至更高。按0.1℃分辨率倒推,ADC至少要能分辨0.04mV级别的电压变化。内部ADC根本没有操作空间,必须先做信号放大或者换高分辨率ADC。ADS122U04这颗芯片把PGA、基准源、精密电流源和24位Δ-Σ调制器全部集成在一个TSSOP-16封装里,信号链从传感器到数字量在芯片内部就打通了,省掉大量分立运放电路,这是选它最直接的理由。
1.2 UART接口的ADC:看起来冷门,用对了很舒服
ADS122U04IPWR这个型号里,“U04”指的就是带UART接口的版本,后缀IPWR则说明它是工业温度范围的TSSOP-16卷带封装。市面上同类24位ADC大多数是SPI接口,比如经典的ADS1220。SPI本身不差,但在工业现场或HVAC柜内长距离布线时,SPI的SCK、MISO、MOSI三根线加上片选,对连接器、线缆和隔离方案的要求都比较苛刻,时钟频率一高,线间串扰和地电位差就会变成麻烦。
换成UART接口以后,事情简单很多:主机到ADC一根发送线,ADC到主机一根接收线,交叉一接就完事。PIC18F45K80自带两路EUSART,资源刚好匹配,而且UART通过光耦或者数字隔离器做电气隔离特别顺手——隔离器只需要两根数据通道,比SPI省的引脚和成本相当明显。采样率上虽然UART比SPI慢,但温度传感器本身是慢变量,这套系统跑几百SPS绰绰有余,单次转换后的满速数据速率也就2kSPS上下,UART完全不构成瓶颈。
1.3 PIC18F45K80不只是“老式8位机”
PIC18F45K80这颗芯片的核心优势从来不是算力,而是外设整合度和工业属性。它内部带有增强型CAN控制器,这在工业测温节点上非常关键。很多现场总线方案要求温度数据直接挂在CAN网络上,如果主控不自带CAN,就得外挂MCP2515之类的控制器,成本和板面积都会上去。K80系列本身是Microchip面向汽车和工业应用的型号,工作温度范围覆盖-40℃到+125℃左右,非常适合装在风道、水管井、配电柜这类温度环境不友好的位置。
另外它内部其实也有一个12位ADC,但我们这个项目没有用它测模拟量,只把它当作主控制器来调度转换、做滤波、跑线性化、组CAN报文。8位MCU在这种“数据量不大、逻辑不复杂、要求确定性”的任务里表现非常稳定,中断延迟可预估,代码审查也轻松。相比在STM32上跑HAL库,PIC18配上XC8编译器写出来的固件,现场维护的人更容易接手。
2. 传感器前端设计:RTD/NTC/热电偶怎么接进ADS122U04
2.1 PT100/PT1000三线制与四线制接法
ADS122U04有4个模拟输入通道AIN0到AIN3,还有两个可编程电流源IDAC1和IDAC2,这个组合基本就是为RTD测量准备的。PT100和PT1000在工业与HVAC里用得最多。PT100灵敏度高但阻值低,引线电阻影响大;PT1000功耗小、对引线电阻不那么敏感,但满量程信号电压也小,需要搭配更高增益。
两线制接线最简单:一根电流进、一根电流出,但长线缆的电阻会直接叠加到RTD阻值上。如果现场线长超过5米,两线制的误差很容易超过±0.5℃,这在HVAC机房巡检这种场景里还能忍,做工业过程监控就不能接受。
三线制是工业现场最常用的方案,核心思路是利用IDAC1和IDAC2两个相同电流源做补偿。一路电流流过RTD和一根引线,另一路电流流过另一根引线产生补偿压降,ADC用差分方式同时测量RTD和引线压降之和,再扣除补偿通道的压降。实际接法可以这样理解:从RTD高端到低端,设计上让两个IDAC分别驱动两条引线,使最终差分电压中引线电阻贡献被相减消除。工程上常用偏置电压法和两个输入通道的两次采样来完成补偿。只要两条引线电阻接近,三线制的残余误差就能压到0.05Ω以下,对应PT100大约0.13℃。
四线制则更彻底:两条线提供激励电流,两条线专门测量压降,测量回路基本不取电流,引线电阻理论上完全不进入结果。缺点是多一根线,成本和施工难度都增加,一般只有实验级或长距离高精度场合才用。我在实际项目中,HVAC本地节点用三线制,工业远传节点则直接预留四线制接入端子。
2.2 IDAC激励电流、PGA增益和内部基准的配合
激励电流的选择要平衡两个矛盾:电流越大,RTD上的信号电压越大,ADC越容易分辨;但电流流过传感器会产生自热,导致测量值偏高。PT100在1mA激励下典型的自热功率约0.1mW到0.25mW,如果传感器封装是小型贴片,这个温升可能已经有0.1℃到0.3℃。我一般PT100默认用500µA,PT1000用250µA或100µA,既保证0.1℃分辨率,又不至于把水温测成“加热后温度”。
PGA增益的选取可以按满量程来算。PT100在0℃时110? 实际是100Ω附近,在100℃时约138.5Ω,如果激励电流500µA,对应电压差大约19.25mV。3.3V供电、内部基准接近2.048V的场景下,PGA增益取16或32比较合适,让信号幅度占满量程的20%到40%,过高的增益反而会把PGA自身的噪声和失调同时放大。
ADS122U04内部有两个IDAC输出引脚,还有REFP/REFN基准引脚。实际设计时,我会把IDAC的电流同时引到一个精密基准电阻上,用这个电阻上的压降作为ADC的参考电压。这样参考电压和传感器信号来自同一个电流源,电流的任何漂移都会同时影响信号和参考,最后在ADC的计算里相互抵消,这就是后面要说的比例测量。
2.3 比例测量思想和外部基准电阻
比例测量是这套系统里最值得重视的一个设计点。假设你用一个高精度稳压源给RTD供电,稳压源的电压受温度影响产生了缓慢漂移,你测出来的电压就会跟着漂,结果就像一个“假温度变化”。但如果参考电压也是用同一个电流源在精密电阻上产生的,那么电流变大时,RTD信号变大,参考电压也变大,ADC输出的是两者的比值,电流源漂移被自动消除。
实现上,我会在REFN和REFP之间跨接一个低温漂精密电阻,比如阻值1.5kΩ、温漂系数10ppm/℃或更好。IDAC的电流经过这个电阻,产生基准电压VREF = I × RREF。同时RTD支路产生的信号电压Vs = I × RRTD。ADC输出码值正比于Vs / VREF,最终等效于RRTD / RREF。因为RREF是固定的,温度系数又低,整个系统的测量稳定性就落在了这颗基准电阻上,而不是落在电源或ADC内部基准上。
这个做法也解释了为什么现场温度数据长期漂移时,很多时候问题出在基准电阻而不是ADC本身。我见过有人在原理图里随便放了一颗普通的0603厚膜电阻当RREF,温漂几百ppm,结果实测温度每小时都能看到明显爬移。换成精密薄膜电阻后,数据立刻稳定下来。
3. 硬件实战:原理图到PCB布局的关键细节
3.1 电源轨、去耦电容和UART连接
ADS122U04IPWR的模拟电源AVDD和数字电源DVDD,实际工程中我直接共用一条3.3V电源轨,但电源进来先经过一级磁珠再分给AVDD,避免数字开关噪声直接进入模拟部分。PIC18F45K80同样工作在3.3V,整板电源设计比较干净。每个电源引脚旁边放0.1µF陶瓷电容,AVDD附近再并联一个10µF钽电容或者大容量MLCC,用来吸收低频扰动。
UART连接要注意交叉:单片机TXD接ADC的RXD,单片机RXD接ADC的TXD。ADS122U04的UART不是标准RS232电平,而是3.3V TTL电平,直接和PIC18的EUSART引脚相连即可。如果做工业远传或者需要隔离,我建议在UART线上串接数字隔离器,而不是老式光耦,因为高波特率下光耦的上升沿畸变会造成误码。
有一个非常容易被忽略的细节:ADS122U04的TXD在空闲状态下必须保持高电平,否则芯片会认为有持续起始位。如果单片机的TXD默认是低,连接后第一次通信会失败,但很多人不会查这一项。初始化时把对应的发射引脚先置高,再使能UART外设,能避免很多莫名其妙的“不通信”。
3.2 模拟输入走线和地平面处理
测量温度时的信号是毫伏级的,走线稍不小心就是灾难。我处理模拟输入区的基本规则是:模拟走线尽量短,远离数字线,尤其不能与串口线、PWM线长距离平行。RTD引线进来后先经过一个RC低通滤波器再进AINx,电阻一般取1kΩ到10kΩ,电容取1nF到10nF,具体截止频率按采样率来选,一般把100kHz以上的噪声滤掉就行。
地平面处理上,模拟地和数字地不要大面积分割。很多新手喜欢把地层切开一块给模拟部分,结果高速数字回流被迫绕路,反而产生更大的地噪声。正确做法是保持完整地平面,只是在布局上让模拟器件和数字器件分区放置,模拟信号的回流路径尽量短。ADS122U04底部的散热焊盘如果存在,也应通过过孔连接到地平面,同时注意不要在敏感模拟区域造成地环路。
RTD传感器如果通过长线缆接入,建议在端子处做开尔文布线,也就是把激励线和测量线分开走,在传感器端再汇合。PCB上看起来像四根线,实际上就是为消除主板内部的走线电阻压降。对于0.1℃级别精度,这些细节直接决定了最终能否达标。
3.3 工业/HVAC现场的ESD与EMC改造
工业现场和HVAC机房的电磁环境,比实验室恶劣得多。变频器启动、接触器吸合、大功率风机通断,都会在电源和信号线上感应出很强的共模干扰。我在输入端子上必加TVS管,选反向工作电压高于最高信号电压的型号,把瞬态尖峰钳位在安全范围内。RTD引线较长时,串联一个2.2kΩ或5.1kΩ的限流电阻再进ADC,就算外部误接高压,也能把进入芯片的电流限制到安全水平。
UART隔离状态下,隔离电源的输出地和输入地要明确分开,不能在PCB上连成一片。很多工程师做完隔离的“感觉”,但地没真正断开,共模干扰沿着地线又绕回系统,效果等于没隔离。用数字隔离器时,隔离器两边的电源去耦也要各自独立,靠近芯片电源脚放置。
还有一个HVAC特有场景:室外温度传感器要防雷击感应浪涌,室内传感器有时会靠近220V交流布线。如果空间允许,我会在TVS管之前再预留一个气体放电管的位置,成本不高,但给现场省下大量维修工单。
4. 固件实现:PIC18F45K80驱动ADS122U04的全流程
4.1 EUSART初始化:先让两颗芯片“对上话”
XC8编译器下,PIC18F45K80的EUSART初始化并不复杂。以EUSART1、默认引脚RC6/RC7为例,波特率用9600bps对ADS122U04来说足够,温度数据更新率又不高。初始化代码如下:
#define FOSC 16000000UL #define BAUDRATE 9600UL void UART1_Init(void) { TRISCbits.TRISC6 = 0; // TX1 输出 TRISCbits.TRISC7 = 1; // RX1 输入 SPBRG1 = (uint8_t)(((FOSC / (16UL * BAUDRATE)) - 1) & 0xFF); SPBRGH1 = (uint8_t)((((FOSC / (16UL * BAUDRATE)) - 1) >> 8) & 0xFF); BAUDCON1bits.BRG16 = 1; // 16位波特率发生器 TXSTA1bits.BRGH = 0; TXSTA1bits.TXEN = 1; RCSTA1bits.SPEN = 1; RCSTA1bits.CREN = 1; }为什么特意把BRG16打开?因为ADS122U04的UART要求波特率误差小于一定范围,16位波特率发生器可以把分频值取得更精确,特别是使用16MHz内部振荡器时。如果单片机用外部晶振,晶振旁边的两个负载电容一定要按数据手册选,晶振频率误差对UART通信的影响很大,我吃过这个亏:板子晶振没起振,程序跑在内部RC振荡器上,波特率偏差超过3%,ADC指令发过去全是乱码。
初始化UART后,先手动把TX线拉高几个毫秒,让ADC识别到空闲状态,再发复位命令:
void UART1_WriteByte(uint8_t byte) { while (!TXSTA1bits.TRMT); TXREG1 = byte; } void ADS122U04_Reset(void) { UART1_WriteByte(0x06); // RESET命令 __delay_ms(10); }ADC收到复位命令后会返回到默认寄存器状态,这个延迟时间必须给足。太快发下一条命令,ADC可能还在复位执行中,命令被吞掉,后续全乱。
4.2 寄存器配置与单次/连续转换调度
ADS122U04的寄存器配置通过WREG命令完成,命令字节为0x40加上寄存器地址,后面跟一个数据长度字节,再跟要写入的寄存器值。我这里给出一个典型配置思路:
void ADS122U04_WriteReg(uint8_t addr, uint8_t val) { UART1_WriteByte(0x40 | addr); UART1_WriteByte(0x00); // 写1个寄存器 UART1_WriteByte(val); }配置内容根据传感器类型和前端接法来定。PT100三线制场景,我大致会这样设定:
- 配置寄存器0:AIN0作为正输入,AIN1作为负输入,PGA使能,增益16;
- 配置寄存器1:连续转换模式,数据速率1000SPS;
- 配置寄存器2:IDAC1电流500µA,IDAC1输出接到AIN2,IDAC2输出接到AIN3;
- 配置寄存器3:外部基准使能,REFP-REFN作为ADC参考电压。
寄存器位的具体数值必须对着数据手册核对,不同版本芯片的默认值和位定义可能有细节差异。我固件里会专门维护一个配置表,把传感器类型、量程、增益、电流档位做成可配置项,现场更换传感器类型时只改几个宏,不用重新刷算法。
连续转换模式下,ADC会持续产生数据。读取时主机发送RDATA命令,然后接收3字节数据。也可以让ADC自动输出数据,但那样需要主机持续打开接收中断,在温度节点这种低功耗场景下并不划算。我更倾向于单次转换模式:需要温度值时发START命令,等服务好再发RDATA取数据,其余时间让ADC进入POWERDOWN状态,省电也减少噪声。
4.3 数据读取、FIR滤波和RTD线性化
取回3字节原始数据之后,先用24位补码的方式组装成带符号整数:
int32_t raw = ((int32_t)buf[0] << 16) | ((int16_t)buf[1] << 8) | buf[2];如果最高位是1,按24位补码扩展符号位。然后换算成电压:
double volt = (double)raw * VREF / (double)(1L << 23) / PGA_GAIN;再根据激励电流算出电阻值:
double rtd = volt / EXCI_CURRENT;得到电阻还不是温度,PT100的电阻-温度关系是近似非线性的,要用Callendar-Van Dusen方程或者查表来修正。0℃以上用简化形式:R = R0 × (1 + A·t + B·t²),其中A约3.9083×10⁻³,B约-5.775×10⁻⁷。如果温度范围超过100℃,最好用完整方程,加入C项修正。
直接解这个二次方程在8位机上有点啰嗦,工程上我更喜欢用分段查表加线性插值:把-50℃到250℃按1℃间隔建一张查找表,查表后做一次线性插值,得到的温度精度远高于0.01℃,足够项目用,而且代码执行时间固定,中断调度稳定。查表本质上就是先用已知数据逼近反函数,在线性区段误差很小,现场维护也直观。
滤波方面,温度系统不是高速过程,我习惯做三步:先做中值滤波剔除单点毛刺,再做滑动平均平滑,最后用一阶惯性滤波把输出变化速率限制住。中值滤波的窗口取5到7点,滑动平均取16点,一阶惯性系数取0.1到0.3。这样组合下来,有干扰时温度数据不会出现尖跳,但真正快速升温时又能跟上趋势,不会把真实变化给滤没了。
4.4 把温度结果挂到CAN总线节点上
PIC18F45K80的ECAN模块让我能直接把温度数据变成CAN报文,不需要外扩CAN控制器。典型的节点做法是:把地址、温度整数部分、温度小数部分、传感器状态放在一个标准数据帧里,比如ID固定为0x030,数据域安排成8字节:
typedef union { struct { uint8_t node_id; int16_t temp_centi; // 温度乘以100后的整数 uint8_t sensor_type; uint8_t status; uint8_t reserved[3]; } fields; uint8_t bytes[8]; } can_data_t;CAN波特率我一般配成250kbps或500kbps,具体看上位机协议。工业现场用CAN还有一个好处:总线仲裁机制让多个温度节点可以并发上报,不需要主机轮询,PLC那边按ID过滤就能拿到每个点的温度,这比RS485的一主多从模式省心不少。ADS122U04负责把模拟世界读准,PIC18F45K80负责让这个准数据被系统看见,两者的分工非常清晰。
5. 标定与实测:这些坑我都是实际踩过的
5.1 上电后完全无响应:先查这几个地方
第一次上电是最容易让人怀疑人生的阶段。板子通电,PIC程序跑起来,发了一条RESET命令,示波器看TXD的波形也正常,但ADC就是不回数据。我的排查顺序永远是固定的:
第一看TXD波形是否真的到达ADC的RXD引脚。没有最悲惨,只有更悲惨——原理图画的交叉是对的,PCB Layout时被自动布线器重新设成了直连,TXD对着TXD,信号永远到不了。第二看IO口复用冲突,RC6/RC7如果同时被调试器或者别的外设占用,数据会乱。第三看波特率误差,这个我前面提过,晶振没配好、分频值算错都会导致误码。第四直接量DVDD和AVDD,确认芯片不是处于复位状态。
排查时不要一上来就改固件。先用示波器看波形,确定主机发的字节是完整的10位格式,再确定ADC的TXD线上有没有回帧。ADS122U04对命令是有响应的,即使命令无效,也会有对应的返回。如果完全静默,大概率是硬件链路问题而不是寄存器配置问题。
5.2 温度读数漂移:不是ADC而是基准被忽视了
项目运行一段时间,客户反馈某几个温度点数据比现场玻璃温度计高0.5℃并缓慢漂移。查了一圈,ADC没问题,传感器换过也没用,最后发现是基准电阻的问题。我做比例测量用的那颗外部基准电阻虽然初始精度是0.1%,但温度系数只有100ppm/℃,环境温度从20℃升到60℃,电阻值变化了0.4%,对应的温度偏差能到1℃级别。
这种漂移在HVAC机房特别典型,因为机房夏天环境温度高,柜内温度可能超过50℃。后来我把基准电阻换成10ppm/℃的精密薄膜电阻,确定度立刻稳定下来。这里我强调一下:比例测量消除的是电流源漂移,不是基准电阻漂移,基准电阻本身必须是低温度系数的。选型时看温漂指标比看初始精度更重要。
另外还有一个容易忽略的点:ADS122U04的REFP/REFN引脚走线如果距离RTD输入走线太近,RTD的微小信号会耦合到基准电压上,形成交叉干扰。PCB上把基准电阻的走线当成模拟信号精心对待,不要为了方便就铺一条很长又细的线。
5.3 毛刺并不都是工频干扰
现场数据偶尔会出现一个异常大的跳变,很多人第一反应是工频50Hz干扰。但我遇到的真正原因往往是多路输入切换时的电荷注入。ADS122U04的模拟前端在切换通道后,内部的采样电容需要一定时间稳定,如果主机在切换后立刻读取数据,读到的很可能就是一个未稳定的残值。
解决方式很简单:通道切换后先丢弃第一次转换结果,或者把每次切换后的前几个转换样本全部丢弃。我在固件里做了一个状态机,每次切换通道都需要完成“丢弃→稳定→有效采样”三个阶段,毛刺几乎消除干净。数据速率和通道切换频率之间也要留出余量,不是越快越好。
工频干扰确实存在,尤其是在RTD引线长、又没做屏蔽层接地的情况下。处理方式是加差分输入滤波电容,并让RC滤波的截止频率远低于采样率。如果设备供电来自交流侧24V电源,电源模块的隔离电容也会引入共模扰动,必要时在信号侧加共模电感。
5.4 标定流程参考
我最后给的标定流程,是现场调试时用30分钟能跑完的版本。准备好两个恒温点,或者更简单一点:用标准电阻箱模拟RTD阻值。0℃对应PT100约100Ω,100℃对应约138.5Ω。先用电阻箱给100Ω,记下ADC换算出的原始电阻值,再给138.5Ω,记下第二点,做两点线性校正,把增益和偏移修正到软件里。
标定完成后一定要做回程验证:分别给105Ω、120Ω和150Ω,看软件输出的温度是否和理论值一致。偏差超过0.1℃时,检查是不是线性化表查错了区间,或者增益设置导致满量程被截断。量产阶段我一般把标定系数存到EEPROM里,每块板子在出厂前单独写一组系数,而不是所有板子共用一个默认值,因为ADC的失调和参考电压的初始精度板间会有分散。
6. 排查速查表:异常现象、根因与处置
下面这个表格是我在实际调试中整理出来的,按现象归类,基本覆盖这套系统最常见的问题。遇到异常时可以拿着它一项项对。
| 现象 | 可能根因 | 检查与处置 |
|---|---|---|
| 完全无响应,TX有波形RX无数据 | UART交叉接错、IO冲突、ADC电源异常 | 核对TXD/RXD连接,断开调试器,量DVDD/AVDD |
| 偶发乱码或读到全0xFF | 波特率误差偏大、晶振起振不稳 | 用示波器测波特率,调整SPBRG分频值 |
| 温度读数整体偏高或偏低 | 基准电阻初始精度差、增益设置不对 | 校准放大器和偏移,换0.1%精密电阻 |
| 长期缓慢漂移 | 基准电阻温漂、激励电流变化 | 换成低温漂电阻,检查IDAC设置 |
| 转换结果周期跳动 | 通道切换未丢弃首数据 | 固件状态机增加稳定阶段 |
| 温度值偶尔尖跳 | 外界ESD/EMC干扰、滤波不足 | 加TVS、RC滤波,检查屏蔽接地 |
| 数值噪声大(±0.2℃以上) | PGA增益不够、PCB布线耦合 | 提高增益,优化模拟走线,加均值滤波 |
| CAN报文丢帧或错误 | CAN波特率不匹配、终端电阻缺失 | 检查总线末端120Ω电阻,确认波特率 |
| 低温段线性误差大 | 没有用CVD完整公式 | 查表插值范围扩大,低温段增加修正 |
| 上电第一次读数不准 | ADC未稳定、基准未建立 | 等待至少100ms再启动转换 |
最后说一个我个人的习惯:在PCB上给每个RTD输入通道预留一组RC滤波器的空位——可以先贴0Ω电阻做直通,调试时再按需要替换成滤波电阻电容。这样在现场出现干扰时,不用重新改板,直接换上阻容值就能验证方案。这个做法帮我处理过好几起“现场和实验室完全不一样”的问题,成本几乎为零,强烈建议在做这套方案时也留一手。