2026/11/5 6:26:35

ADS122U04+PIC18F86J50:UART接口24位ADC实现高精度温度测量方案

ADS122U04+PIC18F86J50:UART接口24位ADC实现高精度温度测量方案 做温度测量方案这么久我最常被问到的问题就是能不能用一套简单、稳、成本还不离谱的电路同时搞定HVAC里的风管温度和工业现场的设备测温。ADS122U04IPWR加PIC18F86J50这个组合是我在几轮改版之后真正定下来用的一对搭档——前者是带UART接口的24位Delta-Sigma型ADC内部集成了PGA、基准源和激励电流源后者是Microchip的J系列8位单片机。整个方案做下来最直观的感受是以前调SPI时序、折腾片选和时钟线的功夫现在用两根UART线就完成了模拟前端的外围元件也少了一大半。这篇东西不是芯片数据手册的翻译也不是开发板的Demo复刻而是我从原理图、布局、固件到现场调试完整走完一套测温链路后的记录。文章适合正在做温度采集、暖通控制、工业数据采集的嵌入式工程师也适合那些在ADC选型和模拟前端设计上犹豫不决的朋友。我会把选型逻辑、硬件接线、通信初始化、温度换算算法、抗干扰与校准以及我实际踩过的几个坑都摊开来讲你可以把它当成一份可以直接开工的参考。1. 为什么这个组合把温度采样做成了搭积木1.1 ADS122U04IPWRUART口版本的精密ADC到底改变了什么ADS122U04IPWR这个型号IPWR后缀对应TSSOP-16封装、工业温度范围-40℃到125℃。它本质上是一颗24位Delta-Sigma型ADC但和常见的ADS1220那种SPI接口版本不一样它把数字接口换成了UART这是它最特别的地方。在工业现场MCU和ADC之间的数字接口越简单越好。SPI接口虽然快但四根线SCLK、MOSI、MISO、CS在恶劣电磁环境下容易受干扰而且很多MCU的SPI外设数量有限一旦通道多、传感器多就得用GPIO模拟片选软件压力立刻上来。UART接口只需要DIN和DOUT两根线很多8位MCU天然就有两三个UART模块直接把收发脚一对就能通信这对PCB布局来说是非常大的解放。这颗芯片内部还集成了很多以前需要外部搭电路的东西可编程增益放大器、低漂移基准源、两路匹配的激励电流源还有专门为RTD和热电偶设计的内部温度传感器。换句话说一颗芯片把模拟前端最麻烦的部分全包了外置只需要放几个滤波电容和精密电阻。对做HVAC控制器或者工业测温板的人来说这几乎是开箱即用的体验。1.2 PIC18F86J50在整套系统里的分工PIC18F86J50是Microchip的中档8位MCU程序空间和RAM对温度采集这种任务来说相当宽裕。我在这套方案里让它专职做三件事UART协议解析、温度换算算法、对外通信与告警输出。8位内核处理24位ADC数据和多项式计算完全没有性能压力甚至在跑浮点温度换算的同时还能兼顾看门狗和通信超时检测。选择它还因为几个很实在的理由宽工作电压范围适合和传感器调理电路共用一个3.3V或5V电源域I/O引脚带5V容忍能力和外部逻辑接口时不用到处加电平转换片上EUSART模块多一路给ADC用另一路还能留给RS485或者Modbus等上位机通信。对于HVAC这种要长期无人值守运行的场景8位MCU的稳定性和低故障率反而是优点杀鸡确实不需要牛刀。1.3 工业现场与HVAC控制器对这套方案的需求差异虽然标题把工业和HVAC放在一起但这两个场景的需求其实很不相同我在设计里是分开考虑的。需求维度HVAC控制器工业现场测温测温范围-40℃~120℃为主可能覆盖-200℃~850℃传感器类型NTC热敏电阻、PT1000居多PT100三线制、K型热电偶常见精度要求通常在±0.5℃以内部分场景要求±0.1℃~±0.2℃电磁环境变频风机、水泵干扰较多动力线、变频器、大功率设备并存长期可靠性数年免维护恶劣环境下的连续运行成本敏感度很高整机成本竞争激烈中等更看重稳定这个对比表直接决定了电路怎么搭HVAC方向我会优先压低成本用PT1000配合高阻值分压或者低电流激励减少精密电阻的开销工业方向则认真做三线制或者四线制RTD接法用比率测量消除基准漂移把抗干扰和长期稳定性放在第一位。ADS122U04IPWR的可配置激励电流源和PGA恰好两边都能满足硬件上预留几个0欧电阻位固件里改一下配置寄存器就能切换场景这一点在我的项目里非常实用。2. 硬件链路从传感器到MCU的每个环节都要较真2.1 RTD三线制接法和激励电流源设计RTD是工业测温绕不开的传感器PT100在0℃时名义电阻100欧姆温度每升高1℃大约变化0.385欧姆信号非常微弱。如果用简单的分压法电源波动和引线电阻都会直接进入测量结果所以我的做法是让ADC内部的两个激励电流源IDAC1和IDAC2分别从RTD两端注入再用差分输入测量电压。三线制接法是最常见的工程折中两根引线在RTD两端各走一条激励电流第三根线作为参考电位。当IDAC1和IDAC2的电流匹配时两根引线电阻上的压降相互抵消测量结果基本不受线长影响。四线制更彻底但需要传感器多一根引出线HVAC的盘管和风管里往往没有那么方便的走线条件所以我默认用三线制。实际接法上我会把RTD的一侧接AIN0正输入RTD另一侧接AIN1负输入IDAC1从正输入脚注入电流IDAC2从负输入脚拉出电流第三根引线接到AIN2作为参考地。这里有一个细节IDAC电流值不要选太极端。电流大了自发热明显RTD本身温度会被抬升电流小了信噪比又不够。我实际用250µA或者500µAPT100在250µA下自发热可以忽略配合PGA增益64满量程能覆盖常用测温范围。激励电流源的稳定性直接决定测量精度所以IDAC的电源路径上我会加一个小容量的去耦电容而且这个电容的位置要尽量贴近ADC的电源引脚。很多人只注意基准电压去耦忽略了IDAC本身的电源纹波结果读数的跳动怎么也压不下去我在这上面吃过亏。2.2 热电偶输入与冷端补偿的板级实现热电偶的情况和RTD完全不同它输出的是毫伏级热电势而且必须知道冷端温度才能算出真实温度。ADS122U04IPWR内部自带一个温度传感器配合PGA的高增益可以直接做冷端补偿省掉外面单独贴一片温度传感器芯片的麻烦。K型热电偶在常用温度范围内灵敏度大约41µV/℃信号非常小。我习惯把PGA增益设置到128让ADC的24位分辨率充分压榨出来。热电偶的测量端接AIN0和AIN1差分输入接的时候要特别注意热偶丝和铜走线之间形成的第二热电偶效应——PCB上用铜走线连接热电偶引脚的位置一定要让两个接触点尽量靠近保持热平衡否则会产生意想不到的偏移电压。冷端补偿的具体做法是先用内部温度传感器测出接线端子附近的温度把它作为冷端温度然后查热电偶分度表得到对应的热电势再加上实测的热电势最后反查温度。这个算法看起来很绕但实际也就是查表和加法的关系固件里实现并不复杂。我在第三轮改版时把冷端补偿用的查表数据从浮点数组换成了缩放后的整数表MCU的浮点运算压力小了很多温度的稳定度反而更好。2.3 UART连接、电源去耦和PCB布线的具体做法AD122U04IPWR的供电我建议直接用3.3V单电源模拟电源和数字电源分别接中间用磁珠隔开模拟地AGND和数字地DGND在芯片下方单点相连。PIC18F86J50如果也用3.3V供电UART两线直接对接即可不需要电平转换。PCB布局的顺序很重要模拟输入和激励电流走的线放在顶层数字UART和电源放在底层中间留完整的参考地平面。RTD和热电偶的输入线一路过来都不要打过孔尽量短旁边不要走高频数字信号。我在第一版就是因为把ADC输入走线和UART信号挤在同一层挨着走实测的跳动始终在几微伏量级怎么改寄存器都压不下去后来重排布局才解决。电源去耦方面ADC的AVDD、DVDD各放一个10µF钽电容和0.1µF陶瓷电容参考基准引脚还要额外加一个低ESR电容。工业现场如果供电质量差最好在前面加一级LDO开关电源直接供电会让ADC的差分非线性变差这个我在后面踩坑章节还会细说。3. 通信初始化让ADS122U04在UART总线上开口说话3.1 UART协议的基本工作方式ADS122U04IPWR的UART接口和普通串口设备不太一样它没有发送使能和忙标志这种通用外设的概念而是采用命令应答的方式MCU通过DIN发送命令字节ADC通过DOUT回送结果。空闲状态下两根线都保持高电平每次通信从起始位开始典型的帧格式是8个数据位、无校验、1个停止位。主机可以发送的命令主要有复位、读取数据、读寄存器、写寄存器这几类。读取数据命令会带回一个24位的有符号转换结果而读写寄存器命令可以逐个配置ADC的内部寄存器。这里有个经验虽然UART是异步的但ADS122U04的数据输出速率是固定的读数据命令发出去之后DOUT引脚会按照芯片内部时钟逐个字节送回来所以MCU侧不需要额外的握手信号只要波特率完全一致就不会错位。我做一个建议用UART通信时把波特率尽量压低。ADS122U04支持的波特率很宽但我在现场项目里稳定用9600bps原因是工业环境里高速UART对时序容差的要求更高而9600bps对MCU的时钟误差容忍度最好。测温和暖通控制本来就不是高吞吐场景每秒几十个采样点完全够用。3.2 寄存器初始化流程和关键配置项上电之后芯片会先完成内部复位我建议MCU在发任何命令之前先给ADC一个确定的复位命令避免上电竞态。初始化顺序如下发送复位命令等待芯片内部复位完成。写配置寄存器0选择输入多路复用通道和PGA增益。写配置寄存器1选择基准电压源、转换模式和数据速率。写配置寄存器2使能内部温度传感器配置两个IDAC电流源。回读所有寄存器确认写入值。寄存器配置的实际位定义在每个版本的规格书里可能有细微差别我强烈建议以你手上那版数据手册的寄存器表为准。这里给一组我项目里验证过的典型值作参考PGA设为64数据速率设为100SPS左右基准源选内部基准IDAC一个是250µA输出另一个作为回流。写寄存器命令和读寄存器命令的编码也遵循TI ADC常见的约定写寄存器是0x40加寄存器地址读寄存器是0x20加寄存器地址我把这些命令用宏定义集中放在一个头文件里方便移植和核对。提示命令字和寄存器位定义务必和最新规格书对照一遍。TI芯片不同批次的手册偶尔会补充新的位定义但基本命令格式是稳定的。我吃过想当然的亏后来一律以手册为准。3.3 MCU侧UART配置与示波器实测时序PIC18F86J50的EUSART配置成普通异步模式即可下面这段是我用在项目里的初始化代码骨架用的是16MHz时钟源目标波特率9600bps// PIC18F EUSART1 初始化16MHz9600bps8N1 void uart1_init(void) { // 注意TX/RX引脚号以你自己的原理图为准 TRISBbits.TRISB2 0; // TX1 设为输出 TRISBbits.TRISB3 1; // RX1 设为输入 BAUD1CONbits.BRG16 1; TXSTA1bits.BRGH 1; // 高速波特率模式 SPBRG1 416; // 16MHz / (4 * 9600) - 1 TXSTA1bits.SYNC 0; // 异步模式 TXSTA1bits.TXEN 1; // 使能发送 RCSTA1bits.SPEN 1; // 使能串口 RCSTA1bits.CREN 1; // 使能连续接收 }初始化完成后我习惯先用示波器或者逻辑分析仪看一遍DOUT引脚上的波形确认起始位、停止位和字节间隔正常。第一次调通时DOUT上应该能看到周期性的数据字节流每个字节之间的空闲高电平清晰帧间隔稳定。如果波形上的起始位前沿有抖动优先怀疑波特率失配而不是ADC的问题。另外一个值得做的操作是上电后主动回读一次寄存器把读到的值和写入值逐位比对。UART通信偶尔因为上电时序丢第一个字节回读能第一时间发现问题省得后面数据错了还找不到原因。4. 从ADC原始码到温度读数固件算法与数据处理4.1 原始数据的读取、符号处理和电压换算ADS122U04IPWR返回的24位数据是二进制补码格式先要把它正确扩展成有符号数否则后面的计算全乱。读取三个字节后把它们组合成一个32位整数再通过左移和右移做符号扩展// 组合24位原始数据并做符号扩展 int32_t adc_read_signed(void) { uint32_t raw adc_cmd_read_data(); // 读取RDATA命令返回的3字节 raw 8; // 移到32位整型的最高24位 int32_t sign (int32_t)raw; sign 8; // 算术右移完成符号扩展 return sign; }得到有符号的原始码之后换算成实际电压用这个公式电压 原始码 × (2 × VREF) / (PGA × 2^23)比如PGA为64、内部基准2.048V时一LSB对应的电压大约是2×2.048/(64×8388608)换算出来不到8纳伏级别完全覆盖了温度传感器微伏信号的需求。这个公式我习惯封装成一个函数输入原始码和PGA输出浮点电压后续的电阻和温度计算都在电压基础上做代码结构清楚一些。4.2 RTD电阻计算与IEC 60751温度转换RTD的电阻值由激励电流和测量电压决定理论上直接用电压除以电流就行但电流源的绝对值会随温度漂移直接除会在宽温场景下引入误差。更好的做法是比率测量在激励电流路径上串一个精密基准电阻同时ADC在基准电阻和RTD之间轮流测电压RTD的电阻等于基准电阻乘以两者原始码的比值这样电流源的绝对值漂移被完全消掉。我板上放了一个1kΩ、温漂系数低于5ppm/℃的精密电阻作为比率参考。每次测量时多花一次转换读取基准电阻两端的电压然后用下面的关系换算R_rtd R_ref × (CODE_rtd / CODE_ref)这个式子里的CODE_rtd和CODE_ref都是同一套PGA配置下的原始码所以放大器的增益误差也被抵消了这是我在工业应用中特别看重的一点。得到RTD电阻之后温度换算按照IEC 60751标准的Callendar-Van Dusen公式。0℃以上范围内电阻和温度的关系是RR0×(1A×tB×t²)其中R0100欧姆A3.9083e-3B-5.775e-7。我不建议去解三次方程或者反复迭代直接把二次方程反解出来就够了// PT100电阻转温度适用于0~850℃范围 float rtd_res_to_temp(float r) { const float R0 100.0f; const float A 3.9083e-3f; const float B -5.775e-7f; float t ( -R0 * A sqrtf(R0 * R0 * A * A - 4.0f * R0 * B * (R0 - r)) ) / (2.0f * R0 * B); return t; }如果项目要覆盖-200℃到0℃的低温段公式里要多加一个C-4.183e-12的修正项处理起来也不复杂但HVAC场景基本用不到工业现场需要低温段的时候我才会把完整版公式放进去。4.3 热电偶冷端补偿与软件滤波、断线检测热电偶的算法核心是看两个表先查冷端温度对应的热电势再查总热电势对应的温度。实际操作上我会先把冷端温度送进K型分度表查到热电势E1然后把实测热电势加上E1得到总热电势最后用总热电势反查温度。为了节省运行时间和程序空间我把分度表做成每10℃一个节点的整数数组中间用线性插值在HVAC的精度要求下绰绰有余。软件滤波方面移动平均和中值滤波我都用上了。中值滤波主要对付偶发的尖峰干扰窗口取5个点就足够移动平均负责平滑温控回路的反馈信号窗口长度在HVAC里我取8到16个采样点太多会让温控环路反应变慢太少又压不住噪声。断线检测是温度采集必须考虑的功能不然传感器松了系统还在盲目控温那会出大问题。ADS122U04内置的激励电流源有个天然优势RTD断线时测量端的电压会直接顶到电源轨或者塌到零读数要么接近满量程、要么直接归零。我在固件里做了一层阈值判断原始码超出正常RTD工作范围上限或者归零时立刻打传感器故障标志上位机收到后进入安全状态。这套逻辑在HVAC机组里帮我挡过好几次端子松脱的事故。5. 工业与HVAC场景下的校准、抗干扰和长期稳定性5.1 两类场景的干扰画像与布线对策同样是测量温度HVAC控制器和工业现场的干扰来源完全不同。HVAC里最凶的干扰是变频风机和压缩机变频器工作时的开关噪声会通过电源线耦合进来干扰频谱集中在几十千赫兹到几兆赫兹之间。工业现场除了变频器还可能有大电机启停带来的地电位瞬变以及动力线对信号线的空间耦合。我的处理思路是分两道防线第一道在传感器入口模拟输入引脚前加RC低通滤波。RTD属于慢变化信号我用的截止频率大约在几百赫兹一下子就把高频干扰挡在外面。第二道在电源入口ADC的模拟电源用LDO单独从系统电源取电避免数字部分和通信接口的噪声串进来。布线层面有一条经验值得单独说ADC的模拟输入走线和激励电流走线尽量做到平行同层不要跨越参考地平面的缝隙。地平面如果有开槽或者断裂回流电流会被迫绕路形成环路天线差模干扰直接耦合进信号。很多精密ADC读数在换了布局之后莫名其妙变干净原因就在这份地平面的完整度上。5.2 两点校准与比率测量带来的稳定性收益芯片的初始增益误差和偏移误差虽然不大但要达到工业级的±0.2℃精度还是要做一次出厂校准。我的校准方法很简单准备一个精密电阻箱分别在100欧姆和138.51欧姆两个点对应0℃和100℃各测一次记录下理论电阻和ADC读数的差算出偏移和增益修正系数存到MCU的EEPROM里。校准系数在每次开机时加载到全局变量温度计算时先修正电阻值再查表。这里有一点我要特别强调校准最好在整个系统联调之后再做因为温度误差里很大一部分来自参考电阻、运放输入偏移和PCB上的热电动势这些只有在完整链路上测量才真实。如果只在开发板上校准换到生产板上系数基本要重来。比率测量带来的稳定性收益是另一个重点。因为RTD电阻是通过和基准电阻的原始码比值算出来的只要激励电流源不突变电流的绝对温度漂移就完全不影响精度。我做过高低温箱对比测试没有用比率测量的版本在-20℃到60℃范围内读数差了接近0.5℃改成比率测量之后整条曲线几乎拉平。这个差距足够说明问题了。5.3 自诊断设计看门狗、通信超时与传感器故障识别系统长期运行不能只在正常路径上考虑问题。我在固件里加了三层自诊断第一层是MCU看门狗主循环里喂狗任何软件死循环都能自动复位恢复第二层是UART通信超时如果单片机向ADC发了命令后超过规定时间没收到响应判定ADC异常执行一次软复位重试第三层是传感器故障识别包括断线、短路、读数超出工作范围直接上报故障码而不是输出一个离谱的温度值。这三层逻辑在HVAC机组的现场调试里非常有用。有一次通信线被老鼠咬断了一半上位机收到的是断断续续的异常温度自诊断第一时间报出传感器故障运维人员十分钟就定位了问题。要是没有这层保护控制系统很可能把异常温度当成真实值触发错误的启停机逻辑。6. 三轮改版踩过的坑错帧、漂移和上电异常6.1 波特率时钟累积误差导致的偶发错帧第一版样机在实验室里跑得很欢一到工业现场就开始偶尔出现温度跳变频率不高但很恼人。我用逻辑分析仪抓DOUT波形发现ADC返回的数据字节偶尔会缺少一个字节导致后面所有数据整体错位解析出来的温度直接飞了。根因是波特率容差。MCU内部振荡器的精度大约在±2%到±3%而且随温度变化ADC内部时钟也有自己的误差两边一叠加长时间运行后就可能超出UART接收的容限。这其实不是某个单字节立刻出错而是累积相位误差在某一次采样点超出阈值造成采样点落在数据位边缘。解决方法是三管齐下MCU换成外部晶振做时钟源把时钟精度拉到几十ppm波特率从19200降到9600单bit时间变长之后同样的绝对时间误差造成的相位偏移变小发送命令前加一个短暂延时确保DIN空闲足够时间避免芯片状态机进入错误分支。改动之后这套通信就再也没出现过偶发错帧。6.2 低温环境下读数漂移的真正来源第二轮改版遇到了更麻烦的问题整机在高低温箱里-20℃以下的时候ADC读数开始缓慢漂移温度回升后读数又恢复正常。一开始怀疑是ADC本身温漂换了芯片问题依旧后来排查到激励电流源的设置上。问题出在电流源回路上的一颗采样电阻。我一开始用的采样电阻温漂系数偏大温度一低阻值变化明显电流源的绝对值跟着漂而当时的硬件没有做比率测量所以这个漂移直接变成了测温误差。解决方法是把它换成一颗25ppm甚至更高精度的精密电阻同时固件里改用基准电阻与RTD的原始码比值算法。改了之后同样在-20℃环境下温度读数的重复性从原来的±0.5℃改善到±0.1℃以内。这件事给我的教训是精密模拟电路里没有随便放颗电阻的说法。凡是参与信号链的电阻TCR电阻温度系数和长期稳定性都要单独审查省这几毛钱会让整个系统的精度目标大打折扣。6.3 上电时序与复位竞态问题第三轮的问题出现在批量测试阶段偶尔有板子上电后MCU已经跑起来了但ADC一直没响应。排查发现是上电时序竞态ADC的电源轨爬升需要时间而MCU往往几十毫秒就完成了复位开始往外发命令这时候ADC还在内部上电复位中MCU发过来的第一条命令直接被丢弃。我并不建议用复杂的电源时序控制电路来解决成本高且没必要。简单可靠的做法是在MCU初始化ADC之前加一个固定延时我用的延时是200毫秒左右确保ADC完成内部上电复位。同时在发送任何写寄存器命令后都回读一遍寄存器做校验如果匹配失败就重新软复位、重新配置。这两条措施加进去之后批量测试的上电异常率直接降到零。6.4 做完这个项目后我会记住的几条设计准则第一精密ADC的参考和电流源路径上任何电阻都不允许随意选型。测温精度是系统级的一个温漂大的电阻就能毁掉整条信号链。第二UART通信看似简单波特率时钟的长期累积误差是真实存在的坑尤其是MCU用内部振荡器的时候必须在设计阶段就考虑到。第三所有温度采集系统都要把断线和故障检测当成必备功能写进固件而不是等现场出了事故再补。如果你也在做类似的温度采样希望这些记录能帮你少走一轮改版。