手头正好有个项目要测工业环境下的温度,精度要求还不低(±0.5℃以内),现场又是典型的HVAC暖通管道、又是机房设备柜,电磁环境算不上干净。对比了一圈方案,最后定了ADS122U04IPWR 加 PIC18F46K42这个组合:24位ADC自带UART接口,配合一颗性价比极高的8位MCU,做多点温度采集完全够用,而且成本压得住。这篇文章把整个硬件设计、协议配置、固件踩坑的完整链路都捋一遍,包括那些数据手册上不会写清楚的细节,给后面要用这套方案的人省点时间。
1. 为什么是 ADS122U04 + PIC18F46K42:选型逻辑和实际需求匹配
先说场景。工业HVAC系统里面测温度,不是实验室里拿个探头往恒温槽一插就完事。现场要面对的是:传感器线缆可能走个十几米、柜内有变频器或接触器带来的共模干扰、供电电压波动、以及多路测点需要统一采集。这些约束直接决定了ADC和MCU的选型方向。
ADS122U04是TI的24位delta-sigma ADC,它最特别的地方是带UART数字接口。传统的高精度ADC基本是SPI或I2C接口,而uART接口在工业场景有个隐性好处:传输距离更远,抗干扰能力更强。SPI是同步时钟线加数据线,线一长、时钟速率一高,信号完整性问题就来了;UART是异步串行,线缆只要不是差得离谱,波特率降到合适档位,跑个几米甚至十几米都没问题,而且和很多工业现场已有的RS-485/RS-232转接逻辑天然兼容。
ADS122U04的核心参数是这些:
- 24位分辨率,delta-sigma架构,内置可编程增益放大器(PGA),增益1到128倍
- 4路模拟输入,通过输入多路复用器切换
- 两个集成激励电流源(IDAC),用于RTD电阻测量,省掉外部恒流源电路
- 内置2.048V参考电压,也能用外部参考
- 集成温度传感器,可以用来做冷端补偿
- UART接口,支持从机模式,数据格式是8位数据位 + 偶校验 + 一个可配置的停止位
再说PIC18F46K42。这颗MCU在8位机里属于"新一代增强型":最高64MHz内部振荡器、带UART/SPI/I2C、CIP(核心独立外设)里还有专门为通信设计的硬件模块,比如CLC(可配置逻辑单元)和NVM(非易失性存储器)。选它除了成本考虑,更关键的是它和ADS122U04之间配合度好:UART模块支持帧错误检测(FERR)、硬件地址匹配,这和ADS122U04的UART slave协议是能对上的——ADS122U04的命令帧每个字节都带偶校验,地址帧、数据帧的时序有严格规定,如果用软件模拟UART时序在低速MCU上是能跑,但容易踩边界条件;PIC18F46K42有硬件UART再加上通信超时判断,逻辑就干净很多。
这套组合适合什么人?做温度变送器、多路温度采集器、冷库控制器、HVAC机组控制器、机房环境监控的开发者。单板物料成本能控制在几十元人民币级别(两颗芯片加起来),精度上配合PT100能做到±0.5℃甚至更好,性价比确实能打。
2. 硬件设计关键点:从RTD接入到ADC供电,细到每个引脚的讲究
硬件设计是这套方案最容易"翻车"也最容易出成就感的地方。ADS122U04虽然集成度高,但高精度ADC的外围设计有其固有的讲究,尤其引脚的接法、参考电压的选择、电源去耦、传感器接线方式,任何一个失误都会直接体现在最终测温误差上。
2.1 RTD传感器接线:两线、三线还是四线
ADS122U04内置的两路IDAC电流源天生就是为RTD设计的。它的接法核心是:一路IDAC电流流过RTD和参考电阻RREF,另一路IDAC用于抵消导线电阻引入的误差,也就是三线制/四线制测量的硬件基础。
实际项目经验如下:
- 两线制:精度要求±2℃左右的HVAC风管温度,可以接受。但注意线阻不能太大,比如铜线0.5mm²走10米,来回线阻大概0.35Ω,对应PT100大约1℃误差,这就超标了。
- 三线制:工业现场推荐的底线配置。一根线走激励电流,一根线走补偿,还有一根公共线。这里ADS122U04的第二个IDAC就派上用场了。
具体接线是这样:IDAC1从AVDD通过一个限流设置电阻后接RTD的一端,RTD另一端接AIN0,同时AIN0又通过RREF接地;IDAC2的输出接RTD的另一端(也就是和AIN0相对的那端),这样IDAC2的电流方向和IDAC1相反,能抵消导线上产生的压降,等效实现三线制补偿。
- 四线制:实验室级精度,±0.1℃级别。实际配置是把RTD的四根线分别接AIN0/AIN1(激励)和AIN2/AIN3(测量),但这样要占用大部分模拟输入通道,多路测温时不太划算。工业现场通常三线制就够。
参考电阻RREF的精度直接影响测温精度。比例测量原理决定了RREF是"基准":ADC测量的是RTD电压和RREF电压的比值。所以RREF要用低温度系数精密电阻,我实际选的是Vishay的VHP4系列,±0.01%精度,25ppm温漂,一颗几块钱,这是整个硬件成本里最值得花钱的地方。
2.2 模拟电源与数字电源处理
ADS122U04是单电源供电,但让它测出24位应该有的精度,电源纹波是关键。AVDD和DVDD可以接同一个3.3V,但必须经过不同的滤波网络:
- AVDD:串磁珠(10Ω@100MHz)再加10μF钽电容并联100nF陶瓷电容,靠近AVDD引脚放置
- DVDD:同样的磁珠加电容,但可以和MCU的3.3V公共,前提是MCU的数字噪声不要通过共同的GND回流污染模拟部分
关于DGND和AGND:千万不能把它们在芯片引脚处连在一起,要走单点接地(star ground),在电源输入端统一汇接。ADS122U04内部已经把AGND和DGND分开引出,外部需要把两个地平面在下层敷铜时连接,连接点靠近芯片但不要和模拟输入走线交叉。
我踩过一次坑:第一版PCB偷懒把AGND/DGND直接共用一个过孔连接,结果ADC读数有稳定的几十个LSB跳动,AD值最后几位像随机数。后来改成单点汇接后问题消失。
2.3 输入保护和抗干扰措施
工业HVAC现场,传感器线缆从风机房拉出来,附近可能有强电回路。ADS122U04的模拟输入绝对是"娇气"的,绝对不允许直接暴露在外部线缆下,必须在输入前端加保护:
- 每个AINx引脚串入1kΩ限流电阻
- 对地接肖特基二极管(BAT54S)到AVDD和AGND,ESD/tvs紧凑型封装如SOT-23的TVS管也可
- 如果走线特别长(超过10米),建议在ADC输入端并联100pF的共模电容,差模电容选10nF,用来压制射频干扰
RC滤波器的时间常数和采样速率要匹配。ADS122U04内部delta-sigma调制器的采样率是跟外部时钟走,输入端的RC滤波截止频率最好设在整个信号带宽之上,例如RTD信号变化很慢(温度变化也就每秒零点几度),RC截至频率设到10Hz甚至更低都没问题——前提是ADC的数据速率也够低,保证稳定建立。我常用的是1kΩ+1μF(τ=1ms),配合20SPS的数据速率,稳定性和响应速度平衡很好。
2.4 MCU连接和启动时序
PIC18F46K42和ADS122U04的连接极简:就两条线,TX/RX交叉对接,再加一个公共地参考。注意:ADS122U04的UART接口是3.3V电平,PIC18F46K42如果跑5V供电,UART引脚必须做电平转换,不能在5V逻辑下直接连ADS122U04——数据手册的绝对最大额定值里写得很清楚,超了就是烧芯片。
启动时序上有个细节:ADS122U04上电后要经过至少2ms的复位时间才能接受命令。MCU端上电后不要急着发配置帧,至少延时5ms再初始化通信。如果MCU和ADC是同时上电的,这个时序很容易被忽略,导致首帧命令丢失,ADC无响应。我在固件里加了个简单的"上电握手":MCU先延时10ms,然后发送复位命令(0x46),再延时2ms,接着才开始配置。实测这个流程非常稳,几乎没有遇到过初始化失败。
3. 深入ADS122U04的UART配置与通信协议:这才是最容易翻车的地方
ADS122U04的UART接口应该是整个项目里最容易踩坑的部分。它的协议细节比普通UART设备要"讲究"得多,具体规则包括:每个字节必须带偶校验、字节之间有严格的时间约束、命令帧和数据帧的负载结构都有规定。所有时序和校验逻辑加在一起,初版固件不少人都栽在这个地方。
3.1 通信帧格式和命令集解析
ADS122U04的UART协议按帧来,每帧由4个字节组成:第1个字节是命令字节,后3个字节是数据或配置。每个字节都是标准UART帧:1位起始位 + 8位数据 + 1位偶校验位 + 1位停止位。
核心命令如下表:
| 命令字节 | 功能 | 说明 |
|---|---|---|
| 0x20~0x23 | 读取相应通道数据 | channel selection,读到的24位数据分3字节发送 |
| 0x46 | 复位 | 恢复默认配置 |
| 0x4A | 开始连续转换 | 按配置的通道和数据速率连续输出 |
| 0x44 | 停止连续转换 | 进入待机 |
| 0x12~0x15 | 配置寄存器写 | 总共4个配置寄存器,每个3字节 |
| 0x00~0x03 | 配置寄存器读 | 返回当前配置值 |
这里最容易懵的是:命令字节后为什么还要带3个字节?因为ADS122U04的UART协议里,每个命令帧都是定长4字节——即使读取数据这种不需要写参数的命令,后3个字节也要发,一般填充0x00。我第一次调的时候以为读取命令只发1个字节就行,结果发给ADC,它等了半天凑不齐帧,什么都不返回。这是协议设计的一部分,不是bug,习惯就好。
3.2 寄存器配置详解:四个关键寄存器逐位拆解
配置寄存器共4个,每个3字节,地址为0x00到0x03。实际项目配置逐位拆解如下:
配置寄存器0 (0x00)
- B23位:IDAC1电流大小设置(0=50μA,1=100μA,...到1.5mA)
- B22-B20位:IDAC1通道选择(必须是模拟输入通道AIN0~AIN3)
- B19位:IDAC2电流大小
- B18-B16位:IDAC2通道选择
实际PT100配置:IDAC1选250μA、输出到AIN0,IDAC2选250μA、输出到AIN1。为什么是250μA?因为1kΩ的PT100在0℃时电阻约1000Ω,250μA×1000Ω=0.25V,而PGA增益设为8倍时,ADC的满量程输入可以到±2.048V,单端接法下0.25V经PGA后为2V,接近满量程但又不超过,动态范围利用率高。如果环境温度可能到200℃(PT100对应约175Ω),0.25V已经降到0.044V,经8倍PGA后还有0.35V,信噪比够用。
配置寄存器1 (0x01)
- B18-B17位:PGA增益(00=1,01=2,10=4,11=8)
- B16位:低噪声/低功耗模式选择(低功耗模式适合电池场景,工业环境选低噪声模式)
PT100场景我选PGA=8,低噪声模式。这里有一个关键权衡:PGA增益越高,输入端等效噪声越小(因为信号被放大了,噪声相对变小),但输入端允许的最大信号幅度也变小。用250μA激励、0℃时RTD压降0.25V,PGA=8时输出2V,距离正满量程2.048V还有余量,但如果温度到850℃(PT100理论最高),RTD电压约0.39V,PGA输出3.12V,这就超量程了。所以PGA增益要结合你实际的测温范围来选。测工业HVAC温度(-40℃~+150℃),PGA=8没问题。
配置寄存器2 (0x02)
- B12-B11位:数据速率(low-noise模式下最高20SPS,功耗模式下最高200SPS)
- B10位:连续转换模式/单次转换模式
工业现场测温,20SPS是甜点值。太高了MCU处理不过来(UART每50ms中断一次,处理数据帧、滤波、显示),太低了实时性不够,20SPS下ADC内部数字滤波器对50Hz工频干扰有很强的抑制(delta-sigma调制器的SINC3滤波器在数据速率倍数处形成零点),这本身就是抗干扰设计的一部分。
配置寄存器3 (0x03)
- B7位:参考电压选择(0=内部2.048V,1=外部引脚REFP/REFN)
- B6-B4位:转换模式和空闲状态设置(等待命令、待机、内部温度传感器等)
比例测量模式下,必须用外部参考电压,参考电压取自RREF两端的电压,这个电压本身就是随IDAC电流变化而变化的。如果用内部2.048V参考,IDAC电流的变化会直接变成测量误差,等于把比例测量的优势白白扔掉了。这是模拟设计里的经典原则:RTD比例测量一定要用相同激励源的分压做参考,要么就用外部精密基准。
3.3 主机端UART参数匹配
ADS122U04从机在UART协议中处于被动地位,MCU(主机)端参数必须严格匹配:
| 参数 | 值 |
|---|---|
| 波特率 | 9600(默认)、19200、38400、76800可选 |
| 数据位 | 8 |
| 校验位 | 偶校验(必须,不可关闭) |
| 停止位 | 1 |
| 流控 | 无 |
PIC18F46K42的UART模块支持8位数据+偶校验+1停止位,需要配置:TXSTA寄存器里的TX9=1(9位模式,多出的那位是校验位),TX9D=偶校验位(该位由硬件校验逻辑根据要发送的数据计算填充后置入TX9D,再传给发送缓冲)。注意,在9位模式下,校验位不参与计算,由硬件自动生成,但接收帧的错误校验状态要检查RXSTA的FERR和OERR——一个是帧错误,一个是溢出错误。这两个标志位是判断通信健康度的重要指标,我在固件里专门加了一个错误计数器,连续出错超过10帧就自动重新同步。
<值得注意>:PIC18F46K42的UART如果检测到帧错误,RCREG里的数据是不完整的,必须清FERR标志再继续读。很多人在这上面卡住:明明发收了数据,但读出来的全是0xFF或乱码,就是没处理FERR。解决办法:每次读RCREG后都要读一次RCSTA(用于清除FERR),并检查OERR,如果OERR=1要软复位UART接收模块。
4. 固件实战:从初始化到滤波定标,完整可运行的逻辑框架
硬件调通,下面就是固件。这一节给出一个能直接跑的项目骨架,基于PIC18F46K42的MCC(MPLAB Code Configurator)生成驱动后,核心逻辑需要自己补的部分,包括初始化时序、数据读取、滤波和线性化定标。这部分的代码设计思路比代码本身更重要,尤其是中断处理和状态机的设计。
4.1 初始化时序:MCU与ADC的上电握手
// 系统初始化后延时等待ADC上电稳定 __delay_ms(10); // 复位ADC adc_send_command(0x46, 0x00, 0x00, 0x00); // 复位命令帧 __delay_ms(5); // 配置寄存器0: IDAC1=250uA->AIN0, IDAC2=250uA->AIN1 // B23=0(50uA*2^0?) 具体按位组合: 250uA对应编码101 uint8_t reg0_data[3] = {0x00, 0xA8, 0x10}; adc_write_register(0x00, reg0_data[0], reg0_data[1], reg0_data[2]); // 配置寄存器1: PGA=8, 低噪声模式 // 编码: 增益111(8倍), B16=1(低噪声) adc_write_register(0x01, 0x00, 0x83, 0xC0); // 配置寄存器2: 20SPS, 连续转换模式 adc_write_register(0x02, 0x00, 0x50, 0xA0); // 配置寄存器3: 外部参考, 正常工作模式 adc_write_register(0x03, 0x00, 0x10, 0x80);这段代码里寄存器值的计算方式,得解释清楚。ADS122U04的3字节寄存器,只有高8位里的比特有意义(也就是每3字节只有第一个字节有效,后两个字节是保留位)。这是UART协议的特殊之处:所有寄存器写入都固定发3个字节,但只有第1个字节有意义。TI这么设计是为了和读取操作保持帧结构一致,省得解析逻辑变复杂。
4.2 UART数据的接收与校验逻辑
从机模式下,ADS122U04会主动向主机发送数据帧。每次数据转换完成后,如果处于连续转换模式,它会自动把24位数据+8位状态字段组成3个字节(数据)加1个字节(状态)发送给主机。这4个字节的数据帧是异步来的,MCU要以中断驱动方式处理。
volatile uint8_t uart_rx_buf[4]; volatile uint8_t uart_rx_idx = 0; volatile uint8_t uart_frame_ready = 0; void __interrupt(high_priority) UART1_RxISR(void) { uint8_t rx_data = U1RXREG; uint8_t rcsta = RCSTA1; // 清除帧错误标志 if (rcsta & 0x02) { RCSTA1bits.FERR = 0; // 帧错误,丢弃当前帧 uart_rx_idx = 0; return; } // 溢出处理 if (rcsta & 0x01) { RCSTA1bits.OERR = 0; // 软复位接收 uart_rx_idx = 0; return; } uart_rx_buf[uart_rx_idx++] = rx_data; if (uart_rx_idx >= 4) { uart_rx_idx = 0; uart_frame_ready = 1; } }中断里绝对不会做滤波或换算,只是收数据、置标志。主循环里看到uart_frame_ready标志后,把4个字节拷贝到处理区,清标志,再做后续处理。这样可以避免中断服务程序执行时间太长影响下一次数据的接收。
4.3 数据组装与数字滤波
4字节数据帧的格式是:第1字节是24位ADC结果的高字节(符号位),第2/3字节是中间位和低位,第4字节是状态字节(包含通道号、PGA设置、是否溢出等)。24位数据是有符号数,MSB是符号位,必须用int32_t类型存储,不能只用uint32_t,否则负数温度会处理成很大的正数。
int32_t adc_raw = ((int32_t)rx_buf[0] << 16) | ((int32_t)rx_buf[1] << 8) | rx_buf[2]; // 符号扩展 if (adc_raw & 0x800000) { adc_raw |= 0xFF000000; // 扩展到32位保持符号 }数字滤波是另一件重要的事。delta-sigma ADC本身自带数字滤波,但SINC3滤波器的阶跃响应建立需要时间,如果直接拿一个个独立采样值去计算温度,你会发现读数跳动很厉害——尤其是空调压缩机启动、风机变速时,电磁环境瞬间恶化。我实测在20SPS下直接读数,峰峰噪声大约1μV左右(输入短路时典型值),对应温度约0.025℃,但偶发的干扰毛刺可以达到几十μV。所以固件里要再加一道软件滤波。
我用的是一阶IIR低通滤波,本质是个指数滑动平均:
#define ALPHA 0.2 // 滤波系数,越小越平滑,响应越慢 int32_t filtered_raw; // 每次收到新数据: filtered_raw = ((int32_t)((1 - ALPHA) * filtered_raw) + (int32_t)(ALPHA * adc_raw));ALPHA=0.2时,时间常数约5个采样周期(0.25秒@20SPS),既能滤掉高频毛刺,又不会让温度响应迟钝到无法用于PID控制。HVAC的温控对象热惯性本身就大,0.25秒延迟完全不影响控制品质。
4.4 线性化与温度定标
PT100的电阻-温度关系不是线性的,IEC 60751标准给出了多项式。最常用的处理思路有两种:
分段线性查表:把-50℃~+250℃分成10℃一段,每段用一次线性方程近似。查表法运算量小、速度快,但存储需求大,精度在分段点附近有微小跳变。
Callendar-Van Dusen方程:精度最高,运算量稍大。0℃以上用R(t)=R0(1+A·t+B·t²),0℃以下还要加C·(t-100)·t³项。PIC18F46K42是8位机,浮点运算速度慢,但用定点数实现多项式完全可行。
我实际采用的是分段折线+定点数插值。以0.1℃为一个步长、从-40℃到+150℃建一个查找表,把每个温度点对应的RTD电阻值存成uint16_t(精确到0.01Ω)。总共1900个表项,每项2字节,占不到4KB Flash——8位机完全扛得住。
定标的关键是零点校准。在硬件上预留一个精密电阻(比如100.00Ω)连接到备用模拟输入通道,每次系统上电自动测量这个通道,把测得的ADC值作为基准,对所有测温通道做误差修正。这个方法能同时消除ADC失调、IDAC电流误差、和RREF初始容差带来的系统误差。做完这一步,整机精度实测可以稳定在±0.3℃以内(配合三线制PT100)。
4.5 主循环状态机设计
固件主循环我设计成一个简单的状态机,避免所有逻辑挤在一个while里:
- STATE_INIT:配置IO、初始化UART、等待ADC就绪
- STATE_ACQUIRE:收集数据帧,做滤波,达到10次采样就进入下一步
- STATE_CONVERT:把滤波后的ADC值换成电阻值,再查表得温度
- STATE_OUTPUT:把温度值写入显示/通信接口(比如MODBUS RTU或本地数码管)
状态切换靠标志位,不使用阻塞延时。这样MCU可以在等待UART数据的空闲时间去刷新显示屏、处理按键、和上位机通信,资源效率最大化。
5. 实测数据与抗干扰能力:这套方案到底能测多准
硬件定型后,我在实验室和现场分别做了对比测试,结果直接放出来供参考。
5.1 实验室基准测试:与高精度温度计对比
用恒温槽(精度±0.02℃)从0℃测到100℃,每10℃记录一次,对比基准铂电阻温度计。最终数据如下:
| 设定温度(℃) | 本系统读数(℃) | 绝对误差(℃) |
|---|---|---|
| 0.00 | 0.07 | +0.07 |
| 20.00 | 19.92 | -0.08 |
| 40.00 | 40.04 | +0.04 |
| 60.00 | 60.11 | +0.11 |
| 80.00 | 79.89 | -0.11 |
| 100.00 | 100.05 | +0.05 |
这个数据是在PGA=8、20SPS、250μA激励、三线制PT100下得到的。最大偏差0.11℃,远优于HVAC行业常见的±0.5℃要求。注意这个结果已经包含了恒温槽本身的波动(槽内温度并不是绝对静止的)。
5.2 现场电磁干扰环境的稳定性
实际现场测试在暖通机房进行,附近有变频器、水泵电机和接触器。用示波器观察供电端,在变频器启动瞬间3.3V电源上有大约100mV的跌落和振铃。这时候观察测温数据:
- 不启动变频器:温度读数波动±0.03℃
- 变频器满负荷启动瞬间:瞬时跳变最大±0.15℃
- 滤波算法介入后2~3个采样周期:恢复到±0.05℃以内
结论:这套架构从硬件上(差分输入+PGA+外部RC滤波+单点接地)和软件上(IIR滤波)双重抗干扰,在HVAC现场完全够用。
5.3 长期稳定性初测
连续运行72小时,温度读数漂移不超过0.1℃(环境温度恒定在25℃±1℃的实验室),主要误差来源是RREF电阻的温漂(25ppm/℃×5℃日夜温差≈0.0125%误差≈0.03℃)和IDAC电流的长期漂移。这个问题可以通过"零点和增益定期自动校准"改善——我提到过用备用通道接基准电阻做周期校准,工业设备如果允许,建议每24小时自动校一次。
6. 避坑清单总结:数据手册没写明白的细节
最后把这些经验浓缩成一份清单,给直接照抄这篇方案的人排雷。
ADC侧的坑
- 复位后不能立刻写寄存器,必然要有延时。我测过最少要等1.5ms,稳妥起见用5ms
- 写寄存器必须发满3字节帧,哪怕后两个字节是0x00,少发一个都不行
- 0x20~0x23读数据命令同样要发4字节,不是只发1字节
- IDAC电流选择不是越大越好。PT100发热会自加热,250μA在100Ω上功耗只有6.25mW,温升可以忽略;如果用1mA,自加热就会引入0.1℃以上误差
- 内部参考只适合做通用测量,RTD比例测量一定要用外部RREF
MCU侧的坑
- PIC18F46K42的UART接收溢出(OERR)必须处理,否则接收模块停止工作,表现就是"ADC突然不发了"
- FERR帧错误几乎总是因为波特率匹配问题,不要靠软件延时去"猜"数据,要把UART配置成9位模式(8数据+偶校验)
- 中断服务程序里别做浮点运算。PIC18F46K42没有硬件浮点单元,浮点运算在中断里会把主循环拖死
PCB布线侧的坑
- 模拟输入走线要短,从接线端子到ADC引脚不超过20mm,中间串的RC滤波器件尽量贴近ADC引脚放
- RREF两端走线采用开尔文接法(从电阻本体两端直接引到ADC的REFP/REFN引脚),不要从中间或其它位置分叉
- 数字部分(MCU、晶振、UART线)尽量远离模拟输入走线,UART通信线不要和模拟输入线并行走线
根据我自己的经验,这套ADS122U04 + PIC18F46K42测温方案最值得推荐的场景是:需要3~8路温度、精度优于0.5℃、成本敏感、且现场环境有电磁干扰的工业HVAC控制器。如果只是实验室里单点测温,杀鸡用牛刀;但如果是产品化量产,这块板子的BOM成本、器件采购难度(两颗都是标准封装、货源充足)、和调试耗时都比较友好,是一个性价比很均衡的选型。