简介:压缩包内是基于STC12系列单片机实现的NTC10K温度计完整工程包,面向电子嵌入式初学者、电子爱好者及课程设计人员,解决热敏电阻测温与单片机程序开发的衔接问题。资源共17个文件、71KB,主要包含C语言源代码、相关头文件、可直接烧录的Hex固件、Keil工程配置文件(uvproj/uvopt),以及LST、M51、OBJ等编译中间文件,既能直接运行,也便于分析编译过程。已有2054人学习下载,适合作为单片机实训、毕业设计或传感器项目的参考。通过阅读并运行该工程,可以系统掌握NTC10K热敏电阻的非线性温度-电阻关系、分压采样电路设计、A/D转换参数配置、Steinhart-Hart公式的计算流程,同时理解串口发送、多位数码管显示等辅助模块的代码写法;工程结构清晰,代码注释明确,便于移植到同类51单片机项目中。 那台老旧的3D打印机给我惹了不小的麻烦:打印到一半,固件突然报了个“extruder temperature abnormal”错误,直接把任务中断了。拆开热端一看,插在加热块里的那根NTC10K热敏电阻引线已经断了一根,换新之后温度读数又开始在200℃上下乱跳。我一开始以为是传感器质量问题,后来把分压电路、AD采样和软件滤波整个过了一遍,才发现NTC10K这个看起来简单到“一个电阻加一根地线”的传感器,背后其实藏着不少容易踩的坑。
这篇文章就顺着我这次的实际调试过程,把NTC10K温度计从原理、电路设计、采样计算到校准流程完整梳理一遍。内容不挑平台、不依赖某个特定开发板,适合刚接触单片机和模拟电路的入门者,也适合想把手里的NTC10K从“能读数”做到“测得准”的进阶玩家。
1. NTC10K不是“一个电阻”:先从原理理解它为什么无处不在
1.1 25℃下的10kΩ,是定义也是约束
NTC三个字母是Negative Temperature Coefficient的缩写,意思就是负温度系数:温度升高,电阻下降。而“10K”指的是在25℃环境下,这个热敏电阻的标称阻值是10kΩ。很多人在这一点上没转过弯来,以为10K是型号编号,其实它是传感器最核心的电气参数。
为什么要在25℃下定义?因为热敏电阻没有“斜率恒定”这回事,它的电阻-温度曲线是严重非线性的,必须锚定一个标准参考点才能谈后续计算。10kΩ这个值也不是随便定的,它在常温下刚好能和常见的10kΩ上拉电阻组成一个分压比1:1的桥路,让ADC采样电平落在电源电压的一半,动态范围最充裕。
另外一个和NTC10K如影随形的参数是B值,常见的有3435K、3950K、3380K等。B值决定了电阻随温度变化的陡峭程度。同样是NTC10K,B值不同,在50℃时电阻可能差出几百欧姆,直接套用公式会踩大坑。所以买NTC的时候别只看“10K”三个字,还要确认B值,这是很多初学项目温度飘得离谱的第一层原因。
1.2 NTC、PT100、DS18B20,我为什么还是选NTC
市面上测温方案一大堆,PT100、DS18B20、热电偶、红外传感器……为什么NTC10K依然在消费电子和DIY项目里出镜率最高?关键看三点:成本、体积、灵敏度。
| 传感器类型 | 输出信号 | 25℃附近典型灵敏度 | 单颗参考成本 | 适合场景 |
|---|---|---|---|---|
| NTC10K热敏电阻 | 电阻变化 | -4.4%/℃左右 | 几毛到几元 | 3D打印机、空调、电池、恒温控制 |
| PT100铂电阻 | 电阻变化 | 0.385Ω/℃,约0.1%/℃ | 高几十倍 | 工业过程控制、精密测量 |
| DS18B20数字温度芯片 | 数字单总线 | 分辨率可选,精度±0.5℃ | 几元到十几元 | 室内环境监测、物联网节点 |
NTC的灵敏度在常温区间是PT100的几十倍,不需要高倍放大电路,一个电阻分压就能进单片机ADC。虽然它的绝对精度和线性度不如PT100,但经过校准后,在-20℃到120℃这个范围做到±0.3℃左右并不难。DS18B20虽然直接输出数字量、接线更省事,但它的热响应时间通常在几十毫秒到几百毫秒级别,封装体积也大,在加热块、电池极耳这类测点根本塞不进去。
所以NTC10K的定位很清晰:不是性能最顶的传感器,却是“体积、成本和精度最平衡”的传感器。理解了这一点,你就能明白为什么那么多产品内部都藏着一颗小小的黑色圆头热敏电阻。
1.3 两个公式之间的跨度:B值公式和Steinhart-Hart方程
拿到NTC10K之后,真正关键的是如何把测到的电阻值换算成温度。业界常用的有两套数学工具。
B值公式又叫简化指数模型:
R = R0 * exp(B * (1/T - 1/T0))其中R0是25℃时的10kΩ,T0是298.15K,T是目标温度(开尔文)。反推温度就是:
T = 1 / (1/T0 + (1/B) * ln(R/R0))这个公式的好处是只需要一个B值,算起来快,适合嵌入式单片机上跑。但它默认B值是恒定不变的,而实际上B值会随温度缓慢变化,所以温度跨度一大,误差就很明显。我在室温下用B值公式还算正常,一旦把NTC放到80℃以上的环境里,读数就比参考温度计低1℃到2℃。
要想全量程精度更高,得用Steinhart-Hart方程:
1/T = A + B * ln(R) + C * (ln(R))^3它通过三个系数拟合真实曲线,在-50℃到150℃的范围内可以把误差压到零点几摄氏度。缺点是计算涉及自然对数和三次方,对没有浮点单元的8位单片机来说,会占掉不少算力。实际项目里,如果测温范围只有20℃到80℃,用B值公式完全够用;如果要做宽温区精密测量,就直接上Steinhart-Hart。
2. 分压电路与ADC才是温度计真正的“战场”
2.1 上拉电阻的阻值选择:匹配不匹配,差得不是一点
NTC本身是可变电阻,单片机没法直接“读电阻”,常规做法是用一个固定电阻和NTC组成分压器,把电阻变化变成电压变化。最常见接法是:电源正极接固定上拉电阻,然后串联NTC到地,中间抽头接ADC引脚。
为什么上拉电阻优先选10kΩ?因为在25℃时NTC阻值也是10kΩ,输出电压刚好是电源电压的一半,也就是ADC量程的中点。在这个位置,输出电压随温度变化的斜率最大,ADC的分辨率利用得最充分。
把温度往上挪到100℃附近,NTC阻值会降到约700Ω(以B=3950计算),此时输出电压只有零点几伏,ADC读数会压缩到最低端的几十个LSB,分辨率和信噪比都会明显下降。这就是为什么如果你想精确测量100℃以上的高温,比起硬扛分压比,更合理的做法是换一颗更高阻值的NTC或者改用恒流源激励。
在实际选型时,我一般会把上拉电阻的阻值用万用表实测一遍再写进代码。标称10kΩ的金属膜电阻,实际可能只有9.8kΩ,这个偏差直接进入最终的电阻计算结果,做精密测温时不能忽略。
2.2 ADC参考电压漂移,让精度在第一道门就打了折扣
很多Arduino示例代码里会直接用analogRead()拿原始值,然后假设参考电压就是5.0V。这个假设我在普通点亮个LED的时候没问题,但接上加热丝、电机或者大电流负载之后就站不住脚了。
这里有个关键变量:分压电路的电源和ADC的参考电压是否来自同一个节点。如果你的分压器接在5V引脚上,而MCU的ADC参考也来自同一个5V,那么在理想情况下,参考电压的变化会被比例抵消掉。但很多单片机默认的模拟参考是内部基准电压,或者分压器电源经过了稳压芯片,而ADC参考取的是另一路电源,两边的电压波动不同步,ADC读数就会被供电噪声污染。
解决思路分两种。一种是让分压电源和ADC参考严格同源,比如都用内置稳压芯片的输出,然后在NTC分压输出端靠近MCU引脚处加一个0.1μF的滤波电容,滤掉高频耦合噪声。另一种是采用倒置式比例测量法:把NTC放在高端、固定电阻放在低端,然后用同一个参考电压同时驱动分压器和ADC基准,这样参考电压漂移几乎不会影响最后算出的电阻比例。
除此之外,ADC采样时间也要给够。AVR系列单片机的ADCSRA寄存器如果设置的采样分频太激进,内部采样电容没充满就转换,结果就会出现“越读越跳”的现象。经验值是采样时间至少大于10μs,条件允许的话打开ADC的硬件过采样,把四次结果累加再右移,等效于提升分辨率,成本几乎为零。
2.3 限流、保护与接线布局的隐藏影响
NTC在高温区间阻值会变得很小,如果上拉电阻太大,流过NTC的电流反而会变小,这不是问题。反而是常温下,如果供电电压足够高,要留意NTC自身的自热:通电电流通过热敏电阻时,电阻自己也会发热,导致测出来的温度偏高。这个效应在第二节第4点的误差估算里详细说。
接线方面,NTC远离MCU时,长导线会引入两个麻烦:一是线缆自身电阻叠加到NTC阻值上,二是导线像天线一样耦合周围噪声。针对噪声,可以用双绞线配合屏蔽层,屏蔽层单端接地。针对线电阻,短距离可以忽略,长距离就要考虑三线制接法,用一根独立的线去测参考点电压,抵消导线压降。
焊接时还有个容易忽略的点:烙铁头温度通过引脚导热,如果焊接时间太长,热量会顺着引线传到热敏电阻本体,可能让内部的半导体材料发生局部特性漂移。焊NTC的动作一定要快,最好用镊子夹住引脚根部帮助散热。我有一批NTC就是因为焊接时不够小心,焊完测25℃标称值就偏了1%以上。
3. 从ADC读数到温度值的换算:代码实现与常见翻车现场
3.1 第一步反算热敏电阻阻值
无论用哪种公式,第一步都是先把ADC原始值还原成NTC的电阻值。以10位ADC、NTC接低端、固定电阻接高端的标准分压结构为例:
Vout = Vref * Rntc / (Rpull + Rntc) adc = Vout / Vref * 1023把两个式子合并,Vref会消掉,得到:
Rntc = Rpull * adc / (1023 - adc)注意这里的adc是原始读数,取值范围0到1023。如果adc等于0或者1023,说明NTC短路或者开路,这在实际系统中是故障状态,必须在代码里做异常检测,不能直接代入公式算温度。
3.2 用代码实现温度换算:两种公式怎么选
B值公式实现简单,但要注意把温度单位换算成开尔文,而且日常测温范围不大时精度足够。Steinhart-Hart虽然算得慢,但在一颗带硬件浮点单元的单片机上也就几微秒的事。
我个人的选择标准是这样:如果NTC量程是-10℃到80℃的常温设备,B值公式加单点校准已经绰绰有余;如果要测量-40℃到150℃这样的宽温区,或者用在恒温箱这种对温度梯度敏感的场景,必须用Steinhart-Hart方程。反正现代单片机动辄几十兆主频,几次采样累加滤波的消耗远远小于无线通信和屏幕刷新。
3.3 一份基于Arduino的NTC10K温度计示例代码
下面这段代码是我常用于快速验证NTC10K节点的例程,兼容Arduino UNO这类AVR板子,也兼容ESP32和STM32的Arduino核心。它包含分压电阻反算、B值公式、异常保护和一个轻量级滑动平均滤波器:
#include <math.h> const float R_NOM = 9910.0; // 25℃标称阻值,最好实测填进去 const float R_PULLUP = 9810.0; // 实际使用的上拉电阻阻值,实测 const float BETA = 3950.0; // 热敏电阻B值,取决于买到的型号 const float T0_K = 298.15; // 25℃对应的开尔文温度 const int SAMPLE_COUNT = 16; // 滑动平均窗口 int readAdcFiltered(int pin) { long sum = 0; for (int i = 0; i < SAMPLE_COUNT; i++) { sum += analogRead(pin); delay(2); // 给采样电容充放电留时间 } return (int)(sum / SAMPLE_COUNT); } float readNtcR(int pin) { int adc = readAdcFiltered(pin); if (adc <= 0 || adc >= 1023) { // 短路/开路保护 return -1.0; } float r = R_PULLUP * (float)adc / (1023.0 - (float)adc); return r; } float rToTempBeta(float r) { if (r < 0.0) return NAN; float tK = 1.0 / (1.0 / T0_K + (1.0 / BETA) * log(r / R_NOM)); return tK - 273.15; } void setup() { Serial.begin(115200); analogReference(DEFAULT); // 使用AVcc,保证与分压电源同源 pinMode(A0, INPUT); } void loop() { float r = readNtcR(A0); float temp = rToTempBeta(r); Serial.print("R="); Serial.print(r); Serial.print(" Ohm, T="); Serial.println(temp); delay(500); }代码里的两个常数R_NOM和R_PULLUP,是我特别建议你实测修改的地方。别相信电阻上的色环,也別全信NTC规格书上的标称值,用万用表测一次再写进去,能直接把系统误差砍掉一大截。
3.4 实测翻车:电源电压波动带来的假温度
我在调试过程中遇到过一个最典型的翻车场景:板子通过USB供电,读室温是很稳定的26.5℃。可一旦接上12V加热模块,电源轨上就出现周期性波动,NTC温度读数开始以1Hz左右的频率上下跳动,幅度达到两度。
排查过程其实不复杂。先用示波器看了分压器输出和MCU参考电压的波形,发现两个节点都在动,但动的幅度和相位不一致。原因是加热模块的PWM负载直接从主电源上拉电流,而MCU的AVcc经过了板上LDO,被电解电容缓冲了一下。两边电压变化不同步,ADC读数自然就飘了。
解决办法也很直接:把分压器供电点从主5V挪到LDO之后的稳压输出,并且在AVcc引脚旁边加了一个100μF的电解电容和一个0.1μF陶瓷电容。改动之后,即使加热模块满功率运行,温度读数波动也控制在0.1℃以内。这个过程让我总结出一条经验:模拟采样电路的所有“基准”都必须干净,不只是NTC这一路,整个供电树都要梳理一遍。
4. 校准流程与误差控制:把一颗合格的热敏电阻调到“可用”
4.1 自热效应:同一个NTC,通不通电读数能差出1℃
自热效应是我在折腾过程中经常被初学者问到的现象。它的原理是:NTC在工作时本身会流过一定电流,电功率转化为热量,让热敏电阻本体温度高于被测介质温度。
估算自热误差的经验公式是:
ΔT = P * θP是热敏电阻上消耗的电功率,θ是热敏电阻的热阻,单位是℃/mW。以5V供电、10kΩ上拉、25℃时NTC也为10kΩ为例,NTC上的电压是2.5V:
P = U² / R = 2.5² / 10000 = 0.625mW如果热阻大约是0.5℃/mW,那么自热造成的温升大约0.3℃。这个数值在要求不高的场景可以忽略,但在大气流速很低、NTC被包裹在隔热材料里的时候,热量散不出去,θ会大得多,实测偏个1℃到2℃都有可能。
减小自热的方法不外乎三条:降低供电电压、调高上拉电阻、或者间歇式激励采样。其中间歇式激励最实用——只在需要采样的那一刻给分压器通电,采样完立刻切断,用占空比把平均功率压到最低。我做的低功耗温度记录仪就用了这个策略,一节纽扣电池能跑好几个月。
4.2 导线电阻和多点接地的账
如果你把NTC装在一个探头里,用一米长的线引回控制板,线缆电阻这笔账也要算进去。常见的22AWG铜导线每米电阻大约0.05Ω到0.06Ω,两根线一来一回就是0.1Ω以上。相对于25℃时的10kΩ,这个误差约0.001%,听起来微不足道。可到了100℃以上,NTC阻值降到几百欧姆,这时0.1Ω的线阻误差占比就明显了,而且它会叠加在温度读数上,让测量值偏低。
还有一种更隐蔽的坑是“多点接地”。当NTC返回地线路径中流过其他负载的电流,地线上会产生电压降,等效于给ADC参考电压串了一个干扰源。解决方法是星形接地,把所有模拟电路的地单独汇到一点,再和功率地连接,避免大电流地电流流经模拟地平面。
4.3 单点校准与两点校准的实操方法
拿到一颗新NTC,不要急着往系统里焊。先把探头放进一个能稳定控温的介质里,做一次校准再上机,能省很多调试时间。
单点校准适合只关心常温附近的应用。最常用的手段是冰水混合物:把碎冰和纯净水混合在一个保温杯里,搅动后放在大气压环境下,温度非常接近0℃。把NTC探头插进去稳定几分钟,记录读数。如果程序算出来的温度偏离0℃,就去微调R_NOM常数,直到读数归零。这个做法的依据是:在25℃和0℃这两个点附近,B值公式的误差方向是单调的,调整R_NOM可以在一定程度上摊平偏差。
两点校准则要额外增加一个高温参考点。一个粗苯的办法是用沸水,但沸点受当地大气压影响很大,海拔越高沸点越低,所以我并不建议纯用沸水做精密校准。更可靠的方式是找一个经过溯源的二等标准温度计,或者干脆用一颗低温漂精密电阻箱去模拟NTC在某个温度下的标准阻值,用两点法修正系统的整体增益和偏置。
我自己在实验室里做校准,通常是先记录0℃和40℃两个参考点,然后用线性插值的方式修正计算结果。两个点校准完之后,整个10℃到50℃区间的误差基本能压到±0.3℃以内。
4.4 如何评估整套系统的最终误差
校准做完,还要对整套系统做一次误差预算。NTC的阻值容差一般在±1%,B值容差范围更大,不同厂家能做到±0.5%到±3%。上拉电阻如果是不太讲究的碳膜电阻,温漂可能到几百ppm/℃。ADC的量化误差、参考电压噪声、滤波算法的滞后性,每一项都会贡献一点系统误差。
以我常用的这套配置为例——B值±1%的NTC10K、0.1%精度上拉电阻、10位ADC、软件滑动平均、单点冰水校准——最终在室温区间的实测误差大概在±0.8℃以内。如果换成0.01%精密电阻、16位ADC、两点校准加Steinhart-Hart,误差可以做到±0.1℃级别。这套误差评估的思路不只是针对NTC,任何模拟量测量系统都可以按这个框架逐项拆解。
5. 把NTC10K温度计变成真正能用的设备
5.1 显示、供电与外壳:从面包板到独立仪表
NTC温度计的核心电路就那么几个元件,但要变成一台能摆在桌面上用的独立设备,还差显示、供电和外壳三件套。
显示方面,OLED屏颜值高、可视角度大,但一颗ST7735或者SSD1306的功耗在背光全开时不低;段码LCD则非常省电,适合电池供电的移动温度计。我做的第一台NTC10K温度计用了0.96寸OLED加三颗按键,设置了高温报警阈值,长按按键进入校准模式,整体结构比较简单,软件核心部分就是上面那套换算加滤波逻辑。
供电上,如果只是室内桌面使用,用Micro USB配一个5V电源适配器就足够。可如果要做便携版,就必须考虑稳压效率。我建议用18650电池加一个低功耗LDO,并在地线上串一个毫欧级采样电阻测量整机电流,方便评估续航。别忘了NTC分压器本身也在持续耗电,这是很多人忽略的隐藏待机功耗来源。
外壳方面,有3D打印机的朋友可以直接画一个带标准耳机接口的盒子,把NTC探头做成可拆卸设计。注意不要在OLED窗和按键孔附近留明显缝隙,热敏探头插座要选带锁紧结构的音频座或航空插头,避免接触不良造成温度跳变。
5.2 不同应用场景下的参数调整:3D打印机、恒温控制、电池包
同样是NTC10K,用在不同场景里的处理方式完全不同。
3D打印机的热床和热端,很多机器用的是NTC100K而不是10K,原因很简单:热端要测200℃以上,NTC10K在这个温度区间阻值已经变得非常小,分压输出的变化率很低,ADC读数的分辨率根本不够用。NTC100K在高温区间阻值依然在几千欧级别,能保持更好的信噪比。所以买传感器前一定要先看设备原装型号的标称阻值和B值,别拿10K去替换100K,那不是校准能解决的问题。
恒温控制场景更看重的是热惯性匹配和稳定性。NTC探头要有足够的接触面积,要紧贴被控对象的表面,并且涂上导热硅脂。如果只是把探头随便悬在空气里,控制程序很难稳定住温度,会出现反复过冲和振荡,这与传感器无关,是热学回路没设计好。
电池包测温则要特别关注绝缘。NTC引线如果可能碰到电池正负极板,必须套热缩管或者做绝缘处理,否则一次意外短路就可能烧掉采样电路。同时电池表面温度变化速率很快,NTC封装尽量选小尺寸的贴片式,热时间常数越小,测到的越接近电池真实温度。
5.3 后续扩展:数据记录、低功耗与多点采集
NTC温度计做到能显示、能报警之后,再往下走就是数据记录和多点采集。
数据记录最简单的方式是接一个Micro SD卡模块,每个采样周期把时间戳和温度写到CSV文件里。后来我改用ESP32,直接把数据通过Wi-Fi上报到MQTT服务器,在家里攒了一个实时温湿度趋势曲线,用来记录书房空调温度波动。ESP32内部也有ADC,但注意它的ADC绝对精度比较一般,线性度在电压接近两端时会明显变差,所以精度要求高的场景还是得外挂一个独立的ADC芯片,比如ADS1115。
多点采集可以沿用同一套NTC电路,通过模拟开关选择不同测点。一个CD4051八选一模拟开关加三根地址线,就能让一个ADC引脚同时读8路NTC。模拟开关的导通电阻大约几十到上百欧姆,如果NTC阻值在10kΩ量级,影响很小;但测高温时NTC阻值只有几百欧姆,导通电阻就不容忽视了,必要时要增加校准系数。
如果想做低功耗无线温度节点,原则是“能睡就睡”。整个系统保持一个RTCC实时钟唤醒,每30秒醒来一次,上电解锁分压器、等NTC稳定几个毫秒、读ADC、滤波、无线发送,然后立刻关闭NTC电源和射频模块进入休眠。我在这样的模式下做过实测,两节AA电池供电,续航可以达到半年以上,而同样的NTC如果一直通电,电池几周就会被拖垮。
我一直习惯在NTC的封装表面和引线根部涂一层薄薄的三防漆,不仅防潮,还能防止盐雾腐蚀改变阻值。对长期运行的设备来说,这种不起眼的小习惯往往比选贵传感器更能稳定可靠性。每次把新的NTC装进探头座之前,我也会顺手用万用表测一下标称阻值,在25℃环境下是不是真的落在一万欧附近。多花这十秒钟,后面能少掉很多排查数据漂移的时间。
本文还有配套的精品资源,点击获取