1. 项目缘起与整体设计思路
多通道温度采集这个需求,在工业现场、实验室环境、设备老化测试台架上出现的频率极高。我最早接触这类项目是在一个电机温升测试的场景里,当时需要同时监测8个测点的温度变化,采样间隔要求1秒以内,数据要实时显示曲线并且能存下来做后续分析。市面上现成的多路温度巡检仪要么通道数不够灵活,要么通讯协议封闭不好集成,要么价格高得离谱。后来用Labview加单片机自己搭了一套,成本压到几百块,通道数想扩就扩,数据格式完全自己说了算,用到现在已经迭代了三版。
这套系统的核心思路其实很朴素:单片机负责前端的多路切换和模数转换,Labview负责后端的显示、存储和分析。为什么这么分工?因为单片机擅长做实时性要求高、逻辑简单的底层采集,而Labview擅长做界面交互、数据处理和文件管理。把两者用串口连起来,各干各擅长的事,整个系统的稳定性和开发效率都是最优的。
具体来说,系统由三个部分组成。第一部分是传感器与信号调理电路,负责把温度信号转换成单片机可以采集的电压信号。第二部分是单片机采集与传输模块,负责多通道轮询、AD转换、数据打包和串口发送。第三部分是Labview上位机,负责串口接收、数据解析、实时显示、报警判断和历史数据存储。
这个方案最大的优势在于灵活。通道数不够?加一片多路模拟开关就行。想换传感器类型?改一下信号调理电路和软件里的换算公式就行。想加远程查看功能?Labview这边加个网络发布就搞定。相比买成品设备,自己搭的系统在可扩展性上完全是另一个维度。
不过话说回来,这套方案也有它的门槛。硬件上需要懂一点模拟电路和单片机编程,软件上需要熟悉Labview的串口通信和数据处理。但这些都是可以边做边学的,我见过不少非电子专业出身的人,照着教程一步步来也能做出来。关键是理解每个环节在干什么,为什么要这么干。
2. 核心细节解析与实操要点
2.1 传感器选型与信号调理
温度传感器选型是整个系统的起点,选错了后面全白搭。常见的方案有热电偶、热电阻和集成温度传感器三类。热电偶适合高温测量,但信号微弱需要冷端补偿,电路复杂。热电阻精度高、线性度好,但需要恒流源激励,成本略高。集成温度传感器比如DS18B20这类,直接输出数字信号,用一根线就能读,但测温范围有限,一般不超过125度。
我最终选的是PT100铂热电阻,三线制接法。为什么选三线制而不是两线制?因为两线制没法消除导线电阻带来的误差,导线越长误差越大。三线制通过补偿导线电阻,能把误差控制在0.1度以内。具体电路上,我用一个恒流源给PT100供电,把电阻变化转换成电压变化,再经过仪表放大器放大,最后送入单片机的AD口。
这里有个关键细节:恒流源的精度直接决定了测量精度。我试过用普通的运放搭恒流源,温漂比较大,后来换成专用的恒流源芯片,稳定性好了很多。放大倍数也要仔细算,PT100在0度时是100欧姆,100度时是138.5欧姆,对应的电压变化范围要和单片机的AD量程匹配。比如用2mA恒流源,0度时电压是200mV,100度时是277mV,变化范围只有77mV。如果单片机AD是10位、参考电压5V,那最小分辨率是4.88mV,对应温度分辨率大概是6.3度,这显然不够用。所以必须放大,放大倍数选50倍左右,77mV变成3.85V,刚好覆盖AD量程的大部分,分辨率能到0.13度左右。
多路切换用的是模拟多路开关,比如CD4051这类8选1的芯片。用两片级联就能扩展到16通道。切换控制信号由单片机IO口给出,注意切换后要留足够的建立时间再启动AD转换,否则通道间会串扰。我一般留100微秒左右,实测下来这个时间足够信号稳定。
2.2 单片机采集与串口通信
单片机这边我选的是STC15系列,原因很简单:便宜、资源够用、串口下载方便。它自带10位AD,最多8通道,如果不够就外扩多路开关。主频跑到24MHz,做多通道轮询采集绰绰有余。
采集流程是这样的:定时器产生1秒的定时中断,中断里依次切换通道、启动AD、读取结果、存入缓冲区。所有通道采集完后,把数据打包成一帧,通过串口发送给上位机。帧格式我设计成:帧头两个字节加通道数据加校验和。帧头用0xAA和0x55,这两个字节在数据里出现的概率低,不容易误判。每个通道数据用两个字节表示,高字节在前。最后加一个校验和,把前面所有字节加起来取低八位。
串口波特率选的是9600,为什么不用更高的?因为采集周期是1秒,每次发送的数据量很小,16个通道也就30多个字节,9600的波特率完全够用。波特率低一点,抗干扰能力反而更强,传输距离也能更远。如果以后要缩短采集周期或者增加通道数,再考虑提高到115200。
这里有个坑要注意:串口发送和AD采集如果都在中断里做,要合理安排优先级。我的做法是AD采集用定时器中断,串口发送用查询方式在主循环里做。采集到的数据先存到缓冲区,主循环检测到缓冲区有新数据就发送。这样中断里的任务很轻,不会影响采集的实时性。
2.3 Labview上位机架构
Labview这边是整个系统的脸面,用户看到的就是这个界面。我的设计思路是:左边显示实时曲线,右边显示各通道当前值,底部是控制按钮和状态栏。曲线用波形图表控件,可以同时显示多条曲线,每条曲线对应一个通道,颜色自动分配。
串口通信用的是VISA模块,配置好波特率、数据位、停止位和校验位后,用“串口字节数”属性节点判断缓冲区里有没有数据,有就读出来。读出来的数据是字符串格式,需要转成字节数组再解析。解析的时候先找帧头,找到后按固定长度取出一帧,校验通过后再把每个通道的两个字节拼成16位整数,最后根据传感器特性换算成温度值。
换算公式这里要特别注意。PT100的电阻和温度关系是非线性的,但在0到100度范围内,用线性近似公式R=100+0.385*T,误差不超过0.5度。如果要求更高精度,可以用查表法或者分段线性插值。我一般用线性公式加一个修正系数,实测下来在0到150度范围内误差能控制在0.3度以内。
数据存储用的是TDMS文件格式,这是Labview自带的一种二进制格式,写入速度快,文件体积小,而且自带通道分组和属性信息,后期用DIAdem或者Excel都能打开。我一般按天建文件,文件名里带上日期,方便查找。每个通道存一列,时间戳存一列,这样后期分析的时候直接读进来就能用。
3. 实操过程与核心环节实现
3.1 硬件搭建与调试
硬件部分我分成三块来搭:传感器板、主控板和电源板。传感器板负责PT100的接线和信号调理,主控板放单片机和多路开关,电源板提供正负5V和3.3V。三块板之间用排线连接,方便单独调试和更换。
焊接的时候有个经验:模拟部分和数字部分的地要分开走,最后在电源入口处单点共地。我第一版没注意这个,结果AD采集出来的数据跳动很大,后来改了接地方式,跳动从十几个字降到两三个字。另外,PT100的引线要用屏蔽线,屏蔽层接模拟地,能有效抑制工频干扰。
调试步骤我一般是这样:先不接传感器,用精密电阻箱模拟PT100的阻值,从100欧姆到150欧姆逐点测试,看Labview上显示的温度值对不对。如果不对,先检查放大倍数和AD参考电压,再检查换算公式。确认采集通道没问题后,再接上真实的PT100,放到恒温水浴里对比标准温度计,做几点校准。
校准的时候用两点校准法就够了:选0度和100度两个点,分别记录AD值和标准温度值,算出斜率和截距,写进单片机或者Labview的换算公式里。如果要求更高,可以多做几个点,用最小二乘法拟合。
3.2 单片机程序关键代码
单片机程序用C语言写,开发环境是Keil。主程序框架很简单:初始化定时器、串口和AD,然后进主循环。定时器中断里做通道切换和AD采集,主循环里做数据打包和串口发送。
AD采集的关键代码如下:
void Timer0_ISR() interrupt 1 { static unsigned char ch = 0; static unsigned int adc_buf[16]; ADC_CONTR = 0x80 | ch; // 选择通道并启动转换 while (!(ADC_CONTR & 0x20)); // 等待转换完成 adc_buf[ch] = (ADC_RES << 2) | (ADC_RESL & 0x03); ADC_CONTR &= ~0x20; // 清除完成标志 ch++; if (ch >= CHANNEL_NUM) { ch = 0; data_ready = 1; // 一帧采集完成 } }这段代码里有个细节:STC15的AD结果是10位,高8位在ADC_RES,低2位在ADC_RESL的低两位。拼的时候要先把高8位左移2位,再或上低2位。我见过有人直接读ADC_RES忘了低2位,结果精度白白丢了4倍。
串口发送的代码更简单,把数据按帧格式填好,然后逐个字节写入SBUF,等发送完成标志置位后再发下一个字节。注意发送前要关串口中断,发完再开,否则中断里的接收逻辑会干扰发送。
3.3 Labview程序框图设计
Labview程序我分成三个并行循环:串口读取循环、数据处理循环和界面刷新循环。三个循环之间用队列传递数据,这样各循环的速率可以不一样,不会互相阻塞。
串口读取循环里,VISA读函数设置为读取缓冲区所有字节,读出来的数据直接入队。数据处理循环从队列里取数据,做帧同步和解析,解析出来的温度值再入另一个队列。界面刷新循环从第二个队列取数据,更新波形图表和数值显示。
帧同步的逻辑要仔细写。因为串口数据是流式的,可能从中间某个字节开始读,所以要先搜索帧头。我的做法是维护一个移位寄存器,把新读到的数据和上次残留的数据拼起来,然后从头搜索0xAA 0x55。找到后判断剩余长度够不够一帧,够就取出来校验,不够就留着等下次数据来了再拼。
校验和的计算用“商和余数”函数,把帧内所有字节求和后取低八位,和帧尾的校验字节比较。如果对不上就丢弃这一帧,重新搜索帧头。这个机制能过滤掉大部分干扰导致的数据错误。
波形图表刷新的时候要注意,如果数据点太多,界面会卡。我的做法是每50毫秒刷新一次,每次把队列里积累的数据一次性追加到图表上。这样既保证了实时性,又不会因为刷新太频繁拖慢程序。
4. 常见问题与排查技巧实录
4.1 温度读数跳动大怎么办
这是最常见的问题,原因可能有三个。第一个是电源纹波大,尤其是开关电源给模拟部分供电的时候。解决办法是在模拟电源上加LC滤波,或者干脆用线性稳压器给模拟部分单独供电。第二个是接地不合理,数字地和模拟地混在一起,数字部分的开关噪声串到模拟部分。解决办法是分开走地,单点共地。第三个是AD参考电压不稳,如果直接用电源做参考,电源波动会直接反映在AD结果上。解决办法是用专用的基准电压芯片,比如REF3033这类。
我遇到过一次特别诡异的跳动,排查了半天发现是PT100的引线太长,而且和电机的动力线捆在一起走,工频干扰直接耦合进来了。后来把信号线单独走线槽,远离动力线,跳动立刻消失。所以布线的时候一定要把信号线和动力线分开,交叉的时候尽量垂直交叉。
4.2 串口通信丢数据怎么排查
丢数据一般出在三个环节:单片机发送、传输线路、上位机接收。排查的时候先看单片机这边,用示波器或者逻辑分析仪抓串口波形,看发送的字节数对不对,波特率准不准。如果单片机发送没问题,再看线路,串口线太长或者周围有强干扰源都会导致数据错误。可以试试降低波特率,或者换成屏蔽双绞线。
上位机这边,VISA读取的时候如果缓冲区设置太小,数据来得太快就会溢出。解决办法是把缓冲区设大一点,比如4096字节,然后读取的时候一次性全读出来。另外,Labview的串口读取循环如果处理太慢,也会导致缓冲区溢出。可以把读取循环的优先级设高一点,或者把数据处理放到另一个循环里做。
我踩过的一个坑是:串口打开后没有清空缓冲区,结果上次残留的数据混进来,导致帧同步一直失败。后来在打开串口后加了一个清空缓冲区的操作,问题就解决了。
4.3 多通道之间的串扰问题
多路开关切换的时候,如果建立时间不够,上一个通道的电荷会残留在下一个通道上,导致读数偏高或偏低。解决办法有两个:一是增加切换后的延时,让信号充分稳定;二是在多路开关的输出端加一个电压跟随器,提高输入阻抗,加快建立时间。
我实测下来,CD4051在5V供电下,通道切换的建立时间大概在几十微秒量级。如果采集速率要求不高,延时100微秒就很保险了。如果要求高速采集,就得换建立时间更短的开关芯片,比如ADG系列。
还有一个容易被忽略的点:多路开关的导通电阻会随温度变化,如果信号源内阻比较大,导通电阻的变化会直接影响分压比。解决办法是保证信号源内阻远小于开关导通电阻,或者用导通电阻更小且更稳定的开关芯片。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 温度读数跳动大 | 电源纹波、接地不良、参考电压不稳 | 示波器看电源纹波,检查接地方式 | 加滤波、分开走地、用基准芯片 |
| 串口丢数据 | 波特率不准、线路干扰、缓冲区溢出 | 逻辑分析仪抓波形,检查缓冲区设置 | 校准波特率、换屏蔽线、增大缓冲区 |
| 通道间串扰 | 切换建立时间不够、开关导通电阻大 | 增加延时看是否改善 | 增加延时、加电压跟随器、换开关芯片 |
| 温度值偏差大 | 换算公式不对、校准点不准 | 用电阻箱模拟标准阻值 | 重新校准、修正换算公式 |
| 上位机界面卡顿 | 刷新太频繁、数据处理太慢 | 看CPU占用率 | 降低刷新率、用队列分离循环 |
5. 系统扩展与优化方向
这套系统搭好之后,扩展空间其实很大。通道数不够的话,用两片CD4051级联就能到16通道,再多可以用CD4067做16选1,四片级联就是64通道。传感器类型也可以混用,比如一部分通道接PT100测中低温,一部分通道接热电偶测高温,软件里针对不同通道用不同的换算公式就行。
数据存储方面,如果采集周期很短、数据量很大,TDMS文件可能会比较大。可以加一个数据压缩功能,比如相邻数据点变化小于某个阈值就不存,或者用二进制格式存,体积能小很多。另外,Labview这边可以加一个数据库连接功能,把数据直接存到数据库里,方便多台设备的数据汇总和分析。
远程查看也是一个很实用的扩展。Labview自带Web发布功能,可以把前面板发布成网页,局域网内任何一台电脑用浏览器就能看实时曲线。如果要做成手机也能看,可以用Labview的HTTP客户端功能,把数据推送到一个简单的Web服务上,手机端用网页或者小程序展示。
报警功能也值得完善。现在的报警只是界面上变个颜色,可以加上声音报警、邮件报警或者短信报警。Labview这边检测到超温后,调用邮件发送功能或者通过串口控制一个短信模块,就能实现远程报警。我做过一个项目就是超温后自动给值班人员发邮件,响应速度比人盯着屏幕快多了。
最后再分享一个小技巧:整个系统搭好之后,建议做一个自检功能。单片机上电后先读一个已知阻值的参考电阻,如果读数在合理范围内就发一个自检通过的命令给上位机,否则发故障代码。这样每次开机就能知道系统是不是正常,省去了很多现场排查的时间。我在一个长期无人值守的测试台上加了这个功能,后来每次出问题都是自检先报出来的,比等人发现要快得多。