
简介基于51单片机与Proteus仿真的热敏电阻温度计设计系统面向电子类专业学生、嵌入式初学者及仿真爱好者用于掌握热敏电阻PT100的采集转换、ADC数据处理及数码管显示等完整流程。系统以51单片机为主控配合PT100热敏电阻和ADC采集芯片将模拟温度信号转换为数字信号并通过数码管实时显示温度值。整套设计包含Proteus仿真工程、51单片机C语言与汇编源代码、Hex烧录文件及编译中间文件共24个文件压缩包仅372KB轻量紧凑便于下载与本地运行。仿真图与源代码相互对应读者可先打开仿真工程观察电路连接再结合C语言源代码理解温度采集到显示的实现思路ADC芯片选型、PT100转换电路等关键环节均在工程中有直观呈现。目前已有161人学习适合课程设计、毕业设计参考或入门单片机测温项目实践也便于课堂演示与自主实验。1. 51单片机热敏电阻温度计仿真与源码先跑通电路再谈测温准不准很多做过51课设的人都有这种翻车经历NTC热敏电阻、分压电阻、ADC芯片买了一大堆焊好板子一上电LCD要么黑屏要么温度显示一个离谱的值最后查来查去是分压电阻选型不对或者ADC时序写错。这套基于51单片机proteus仿真的热敏电阻温度计设计系统把整个测温链路——热敏电阻分压、ADC0809采集、单片机换算、LCD1602显示——全部在proteus里跑通配套仿真图和源代码一起打包。它的价值在于你不用先买元器件、不用焊接就能在电脑上把电路和程序验证一遍确定没问题了再动手做实物。适合做课设的学生、刚入门51想完整走一遍模拟采集链路的爱好者以及被DS18B20这类数字传感器“黑匣子”搞烦了、想真正搞懂电阻测温原理的从业者。2. 热敏电阻测温原理与器件选型B值公式、分压电路与ADC精度2.1 NTC阻值和温度什么关系B值公式与分压采样NTC是负温度系数热敏电阻温度升高、阻值下降。最常用的型号是25℃时阻值10kΩ、B值3950的NTC资源包里的仿真和代码基本都按这个参数来设计。阻值与温度的数学关系可以用B值公式描述R(T) R25 × exp(B × (1/T - 1/T25))其中R25是25℃时的标称阻值T和T25都用开尔文温度。这个公式看着复杂但实际用的时候只需要做一次浮点运算Keil C51的math.h库提供了自然对数函数log()可以直接解出温度。单片机不能直接测电阻得先把电阻变化转成电压变化常见做法是分压电路一个固定电阻R_FIXED10kΩ做上拉接VCCNTC做下臂接地中间节点接ADC输入。分压点电压为Vout VCC × R_NTC / (R_FIXED R_NTC)温度升高→NTC阻值下降→Vout下降ADC读到的数值随之变小程序里再反解出NTC阻值、代入B值公式算出温度。这个链路的关键参数是R_FIXED的取值它决定了测温量程内分压曲线的线性度。10kΩ的NTC配10kΩ固定电阻在050℃区间内电压变化比较大ADC分辨率利用得也最好。如果换上20kΩ固定电阻高温段电压变化会变缓整条曲线往低温方向偏实际调试时不要随便换固定电阻换了就得重新标定。2.2 为什么用NTC51ADC0809与DS18B20、PT100的对比很多新手会问为什么不直接用DS18B20它单总线、数字输出、程序还好写。答案在于教学目标和项目定位。DS18B20内部已经把测温电路封装好了仿真里你只能看到一个器件温度数据从一条线上读出来整个模拟前端对你完全是黑匣子做完这个项目你对电阻测温还是一无所知。而这套设计用的是NTCADC0809从敏感元件、分压电路到逐次逼近型ADC每个环节都能在proteus里看到波形和数值这才是课设和毕设真正要考察的能力点。三种方案的实际对比方案测温原理精度电路复杂度仿真教学价值成本NTCADC0809分压AD采集软件换算1℃左右中高链路完整可见低DS18B20内部集成测温单总线0.5℃低低黑匣子低PT100运放电桥仪表放大器AD0.1℃高高但门槛高较高标准51单片机内部没有ADC所以这套设计外扩了ADC08098位、8通道、逐次逼近型。8位分辨率对应256个量化等级5V参考电压下每档约19.6mV。对10kΩ NTC来说在室温附近每1℃大约对应几十毫伏变化8位精度能分辨到1℃左右用来做温度计和温控演示完全够用。ADC0809在proteus里的模型很成熟仿真行为跟真实芯片基本一致这也是它大量出现在课设里的原因。3. 在proteus里搭这套温度计电路元件查找、接线表与电位器模拟NTC3.1 元件清单与proteus搜索关键字打开proteus第一步是找元件。这块有个最常见的翻车点proteus元件库里的命名跟教材不完全一样比如LCD1602在库里叫LM016L很多人输LCD1602搜不到就开始怀疑库没装全。下面是这套电路用到的元件和准确的搜索关键字元件proteus搜索关键字说明单片机AT89C51也可以用AT89C52引脚兼容模数转换器ADC08098位8通道逐次逼近型ADCLCDLM016Lproteus里的LCD1602热敏电阻模拟件POT-HG用可调电阻模拟NTC阻值变化固定电阻RES10kΩ×1、1kΩ×1等排阻RESPACK-8P0口上拉必须加晶振CRYSTAL12MHz电容CAP33pF×2复位用10uF电解电容电源POWER/VCC、GROUND仿真里直接放5V电源符号POT-HG是proteus里的可调电阻仿真运行中可以手动调节阻值。用它代替NTC的核心逻辑是单片机和ADC根本不知道你接的是热敏电阻还是电位器它们只看到一个随“温度”变化的电压值。你转动电位器分压节点的电压就变化ADC读数跟着变LCD上的温度数字自然变化。这正是仿真这种传感器系统时最常用的手段比在proteus里纠结怎么构造一个温度场环境要实际得多。提示proteus里找不到LM016L时检查一下元件库关键字的大小写或者直接输入“LM016L”不要输“LCD1602”。新版proteus 8.x的元件库比老版本全但命名习惯没变。3.2 接线步骤与关键参数设定这套电路的接线逻辑分四块NTC模拟分压、ADC0809、LCD1602、单片机最小系统。下面把每一块的连接关系列清楚照着接基本不会错。第一块是NTC模拟分压。VCC接10kΩ固定电阻一端电阻另一端接POT-HG的一个固定端POT-HG的滑动端接GND另一个固定端悬空。这样固定电阻和电位器就构成了一个串联分压支路分压节点在电阻与电位器相连的那个点。调节电位器阻值节点电压在0到接近5V之间变化。这个节点接到ADC0809的IN0通道。第二块是ADC0809。VREF接VCC5VVREF-接GNDIN0接分压节点。时钟CLK接DCLOCK信号源设500kHz。数据线D0-D7接单片机P1口。START、EOC、OE分别接P3.3、P3.4、P3.5。通道选择A/B/C三脚可以一起接地固定使用IN0通道代码里把通道号写死。如果要预留多通道可以把A/B/C接到P2.3-P2.5由程序控制选通道。第三块是LCD1602。数据线D0-D7接P0口同时P0口必须接RESPACK-8排阻上拉到VCC否则LCD显示会出乱码或残影。RS、RW、EN分别接P2.0、P2.1、P2.2。VO对比度引脚接一个10kΩ可调电阻的抽头电位器两端接VCC和GND用来调节LCD显示对比度这个脚不接的话仿真里屏幕经常是全黑的。第四块是单片机最小系统。12MHz晶振接XTAL1和XTAL2两端各接33pF电容到地。RST引脚接10uF电解电容到VCC、10kΩ电阻到GND构成上电复位。EA引脚必须接VCC让单片机从片内ROM启动漏接的话程序跑不起来这是proteus仿真里很隐蔽的一个坑。整体参数配置一句话总结固定电阻10kΩ电位器调到10kΩ时就是模拟25℃的标准NTC状态分压点正好2.5VADC读数约127LCD显示25℃左右。电位器阻值增大温度显示降低电位器阻值减小温度显示升高方向跟NTC一致——NTC阻值大对应低温阻值小对应高温。3.3 仿真运行前的检查时钟、上拉、对比度接完线先不要急着点运行按这个顺序快速检查一遍。第一ADC0809的CLK一定要有时钟信号用设计工具栏里的DCLOCK发生器双击把频率改为500kHz。500kHz是ADC0809的典型工作时钟proteus仿真给太高会让仿真速度变慢给太低又可能转换异常。第二P0口的RESPACK-8排阻有没有接上、有没有连到VCC这一步决定LCD能不能正常显示字符。第三LCD的VO脚电位器有没有放不放的话把对比度调到中间位置再启动仿真。proteus里调节电位器有两种方式仿真运行中直接用鼠标拖动电位器滑块阻值会实时变化或者暂停仿真双击POT-HG在属性对话框里直接输入精确阻值。拖动滑块适合直观观察温度随阻值连续变化输入精确阻值适合做定点验证两种方式搭配用。4. 源代码怎么读ADC采集时序、温度换算与LCD1602显示4.1 ADC0809读取函数启动、等待EOC、读取一次数据这套资源的源代码核心是ADC0809的读取时序时序写不对后面所有换算都是空谈。ADC0809的启动时序是START引脚先低电平拉高产生上升沿复位内部逐次逼近寄存器再拉低产生下降沿启动一次转换。转换期间EOC引脚输出低电平转换完成后EOC变为高电平。程序必须等待EOC拉高之后再把OE拉高使能三态输出从P1口读出转换结果。下面这段是标准的单通道读取函数也是源码包里最常见的样子// 定义控制引脚 sbit START P3^3; sbit EOC P3^4; sbit OE P3^5; unsigned char get_adc(unsigned char ch) { unsigned char val; P2 0xC7; // 清除P2.3-P2.5不影响LCD控制位 P2 | (ch 0x07) 3; // 通道选择ch低3位送到A/B/C START 0; START 1; // 上升沿复位 START 0; // 下降沿启动转换 while (EOC 0); // 等待转换完成EOC变高 OE 1; // 打开三态输出锁存 val P1; // 读取8位结果 OE 0; // 关闭输出 return val; }这段代码的逻辑顺序是固定的先选通道再启动转换然后死等EOC最后读数据。通道选择那里用了P2.3-P2.5三个引脚跟LCD的P2.0-P2.2控制引脚分离互不干扰这个引脚分配可以在仿真图里直接对照。如果实际接线把A/B/C接地固定用IN0那get_adc(0)和get_adc(1)读到的都是IN0通道因为地址线一直是0不影响使用。看完江科大51单片机笔记的朋友对这段会很熟悉ADC0809的时序跟外部中断、定时器这些外设不一样它靠的是电平判断而不是中断响应所以主循环里等待EOC时程序会阻塞在这里。好在温度采样不需要高实时性阻塞200微秒完全无感。4.2 把ADC值换算成温度浮点B值公式与查表法怎么选拿到ADC值之后要换算温度这一步有两个方案浮点B值公式和查表法。资源源码包一般默认用浮点公式因为它改参数方便、逻辑直观。换算分两步先由ADC值反解NTC阻值再代入B值公式求温度。#include math.h #define R_FIXED 10000.0 // 分压上拉电阻 10kΩ #define R25 10000.0 // NTC在25℃时的阻值 #define B_VALUE 3950.0 // NTC的B值 #define T25 298.15 // 25℃对应的开尔文温度 float adc_to_temp_c(unsigned char adc_val) { float vout, r_ntc, t_k; vout (float)adc_val * 5.0 / 255.0; // 反解分压点电压 r_ntc vout * R_FIXED / (5.0 - vout); // 反解NTC阻值 t_k 1.0 / (1.0 / T25 log(r_ntc / R25) / B_VALUE); return t_k - 273.15; // 开尔文转摄氏 }注意一个前提这个公式对应的是电路里NTC在下臂的接法也就是Vout VCC × R_NTC / (R_FIXED R_NTC)所以反解时是vout × R_FIXED / (5 - vout)。如果仿真图里NTC被接到了上臂这个公式返回的温度就是错的需要把分子分母对调。拿到别人的源码第一件事就是先确认仿真图里热敏电阻的接法再决定用哪个公式这是踩过坑的人都会养成的习惯。浮点方案的问题是Keil C51的浮点库和log函数会占用不少ROMAT89C51的4KB程序空间可能吃紧。如果编译时提示“CODE SIZE LIMIT EXCEEDED”有两个出路换AT89C52在proteus里重新放置芯片引脚完全兼容或者改用查表法把ADC值和温度的对应关系预先算好存到code区运行时查表加线性插值。查表法的表格可以按0100℃每10℃取一个ADC采样点。假设固定电阻10kΩ、参考电压5V、8位ADC各温度对应的ADC值参考如下温度℃NTC阻值(kΩ)分压点电压(V)ADC读数033.63.851961020.23.341702012.52.78142308.02.23114405.31.7388503.61.3267602.51.0051701.80.7538801.30.5629900.90.43221000.70.3317注意这里ADC值是按递减方向排列的因为NTC阻值随温度升高而下降分压电压也跟着下降。做查表时要注意方向程序里是倒着找区间不是正着找。这组数据是理论计算值proteus仿真里因为元件参数容差实测读数会略有偏差但差异不会超过几个LSB。查表的线性插值代码比浮点版省ROM得多性能也快一个量级// 查表ADC读数对应0~100℃每10℃一个点 code unsigned char adc_table[11] {196, 170, 142, 114, 88, 67, 51, 38, 29, 22, 17}; unsigned int adc_to_temp(unsigned char val) { unsigned char i; for (i 0; i 10; i) { if (val adc_table[i] val adc_table[i 1]) break; } if (i 10) i 9; // 线性插值温度 区间起点温度 比例 × 10℃ return i * 10 (unsigned int)((unsigned int)(val - adc_table[i]) * 10 / (adc_table[i 1] - adc_table[i])); }表格的物理意义是“每个ADC读数落在哪两个温度点之间就按比例算出温度”。因为NTC的阻值-温度曲线本身接近指数分布10℃一个点做线性插值中间点的误差大概在1℃以内跟8位ADC本身的量化误差在同一量级。要注意的是查表法只对同一套分压电路和参考电压有效如果你把固定电阻改成20kΩ这张表就得重算。4.3 LCD1602驱动与主循环均值滤波怎么加LCD1602的驱动是标准套路初始化、写命令、写数据、显示字符串。下面给出够用的精简版完整的花样代码在资源包里sbit RS P2^0; sbit RW P2^1; sbit EN P2^2; void lcd_write_cmd(unsigned char cmd) { RS 0; RW 0; EN 1; P0 cmd; delay_us(50); EN 0; delay_us(50); } void lcd_write_data(unsigned char dat) { RS 1; RW 0; EN 1; P0 dat; delay_us(50); EN 0; delay_us(50); } void lcd_init(void) { delay_ms(20); lcd_write_cmd(0x38); // 8位数据、2行、5×7点阵 lcd_write_cmd(0x0C); // 显示开、光标关 lcd_write_cmd(0x01); // 清屏 delay_ms(2); lcd_write_cmd(0x80); // 光标回第一行 }LCD1602的控制时序是“E引脚高电平时写入低电平时锁存”所以每次操作都是拉高EN、写数据、拉低EN。RS决定是命令还是数据RW在写操作时固定为0。proteus仿真里的LCD响应很快但51的IO操作还是需要一点延时让模块稳定这里给50微秒的量级就够。主循环里要做的事很明确读ADC、均值滤波、换算温度、刷新LCD、延时。ADC0809在仿真环境里的读数会有±12个LSB的抖动直接换算成温度显示的话个位数字会来回跳所以加了一个去极值平均滤波。连续采样8次去掉一个最大值和一个最小值剩下6个求平均unsigned char filter_adc(void) { unsigned char i; unsigned char buf[8]; unsigned char max, min; unsigned int sum 0; for (i 0; i 8; i) buf[i] get_adc(0); max min buf[0]; for (i 0; i 8; i) { if (buf[i] max) max buf[i]; if (buf[i] min) min buf[i]; } for (i 0; i 8; i) sum buf[i]; sum - max min; return (unsigned char)(sum / 6); }这个滤波对温度这种缓变量性价比很高8次采样本身耗时很短去掉极值又能有效抑制毛刺。硬件上如果你把电位器滑动端接触调得不好仿真里也能看到ADC读数的毛刺用这个函数能直接压掉。显示温度的格式建议保留一位小数代码里把浮点温度乘以10转成整数再逐位拆出来显示避免直接格式化浮点数浪费ROM。但这里必须说清楚显示精度是0.1℃测量精度只有1℃左右显示小数只是让你看到温度变化的趋势更平滑不要当真以为这套系统能精确到0.1℃8位ADC的硬件上限摆在那里这是两码事。主循环每次刷新间隔建议200ms以上。温度变化本身慢刷新太快反而让LCD不停地闪烁200ms的节奏既能及时跟随温度变化又能让显示稳定。5. 仿真常见问题排查从LCD黑屏到温度乱跳的六个典型坑5.1 电路连接层LCD显示与ADC读数的典型案例第一个坑LCD1602上电后一整排黑色方块。现象proteus运行后LCD显示区域全是黑色实心方块或者偶尔有几个方块闪动。原因VO对比度引脚没有接分压电位器。LM016L的VO脚电压决定了显示对比度悬空时初始电压状态不确定很多情况下显示模块进入“全亮”状态。解决在VO脚接一个10kΩ电位器两端分别接VCC和GND抽头接VO。仿真运行中调节这个电位器直到黑色方块消失、字符清晰显示。记住这个电位器和模拟NTC的那个电位器不是同一个别把两个搞混否则你调温度显示的时候顺便把对比度也改了现象会非常诡异。第二个坑LCD字符显示乱码、残缺或者第一行和第二行内容串位。现象能显示字符但是显示的内容是乱码有的字符变成横条或方块位置也不对。原因P0口没有加上拉排阻或者排阻的公共端没有接到VCC。AT89C51的P0口是开漏结构内部没有上拉直接驱动LCD数据总线时电平不稳定数据线高电平电压不够LCD就会误判指令和数据。解决放一个RESPACK-8排阻公共端接VCC8个引脚分别接到P0.0-P0.7和LCD D0-D7相连的网络上。这一步在proteus仿真里不是可选项是必选项不接你就等着看乱码。第三个坑ADC读数永远是0xFF或0x00温度显示固定在99℃或0℃。现象LCD上温度数值不动锁定在最大值或最小值转动电位器没有任何反应。原因八成是ADC0809的电源和参考电压没接全。VREF没接VCC的话内部DAC没有参考基准转换结果没有意义VREF-悬空同样会导致读出全0。另一个常见原因是START引脚没有产生有效的上升沿下降沿代码时序不对的话芯片根本没启动转换。解决检查VREF接VCC、VREF-接GND这两个引脚在proteus仿真里经常被漏掉因为元件图上它们的位置不起眼。再检查OE引脚仿真中可以直接把OE接VCC常使能这样可以省掉代码里操作OE的那一行把P1口的读取简化成一条指令。最后确认代码里START有没有完整的0-1-0翻转。5.2 仿真设置与软件层时钟、滤波与编译问题第四个坑proteus仿真运行极慢秒表基本不走。现象点了运行按钮LCD刷新像卡死一样几秒钟才变一次。ADC转换计数也慢得离谱。原因ADC0809的CLK时钟给太高了。很多人直接把DCLOCK设为12MHz或干脆用单片机的ALE引脚的2MHz信号导致proteus为模拟ADC的逐次逼近过程产生海量仿真步进。解决把DCLOCK降到500kHz这个是ADC0809数据手册里的典型值既能保证仿真速度又不影响转换逻辑。温度计的输入本身是缓变信号500kHz的采样速率绰绰有余。改成500kHz后你会发现仿真速度肉眼可见地变快。第五个坑电位器轻轻一碰温度数字跳好几度。现象电位器滑块停在同一个位置LCD温度却在小数位甚至个位上来回跳感觉像是随机数。原因8位ADC的量化噪声和proteus仿真步进带来的抖动叠加在一起。在温度曲线的两端尤其是低温段NTC阻值变化率大同样1个LSB的ADC误差对应好几摄氏度的温度误差。解决程序里加滤波前面写的8次采样去极值平均就是为这个场景准备的。如果滤完之后跳得还明显可以在显示层再加一个滞回处理温度只在变化超过0.5℃时才刷新显示否则维持上一次的值。这样显示效果马上稳定下来。这道坎是模拟采集系统的通病不是代码能完全消掉的硬件上用更高精度的ADC才治本。第六个坑Keil编译报错或者编译通过但烧进仿真里程序不跑。现象一编译时提示unable to open file reg51.h或大量未定义标识符错误。这是Keil工程选错了编译器。很多人电脑上装了Keil5的MDK版默认用ARM编译器编译C51工程51的头文件和语法它根本不认。解决用Keil C51的工程类型新建工程或者安装C51支持包后重新选择编译器版本。现象二编译通过代码量提示超出芯片ROM容量。AT89C51只有4KB程序空间浮点运算和log库加起来很容易超。解决把单片机换成AT89C528KB或STC89C52在proteus里直接替换芯片型号即可引脚和外围电路完全不用动。还有一个备选是把温度换算换成查表法查表版代码能省掉将近1.5KB的浮点库开销。现象三proteus打开下载的仿真图元件大面积显示为空白或问号。这是proteus版本和元件库不匹配导致的老版本工程在新版proteus里打开经常丢元件。解决下载资源时先看仿真文件是用哪个proteus版本生成的优先用对应版本打开如果只有新版proteus那就准备好元件库对照表把丢失的元件按第3章的搜索关键字重新放置一遍电路逻辑不受影响。6. 拿到手怎么验证阻值-温度对照表与三个可落地的扩展仿真图和源码都跑起来之后先别急着当成品交付花十分钟做一次定点验证确认整个链路是准的。验证方法很简单双击电位器POT-HG在属性对话框里把阻值设定为某个固定值然后把LCD显示的温度跟理论值对比。用第2章的分压电路和B值公式可以算出这样一张对照表电位器模拟阻值(kΩ)对应NTC温度(℃)理论ADC读数LCD理论显示266.20xD221066.2℃541.50x8613441.5℃830.10x7211430.1℃1025.00x7F12725.0℃2010.20xAA17010.2℃50-7.30xC4196-7.3℃这里ADC读数用的是10位格式的一个十进制和十六进制参考值注意它算的是NTC在下臂、固定电阻上臂的接法。验证时把电位器调到10kΩ显示应当在25℃附近偏差超过1.5℃说明分压电路或换算公式里的某一步不对。低温档调到50kΩ时显示会变成负温度这时候检查你的代码里温度变量是不是用int而不是unsigned int很多人的翻车现场就是负温度变成一个大正数。验证通过之后这套系统再做三个小扩展就能直接升级成功能更完整的温控装置。第一个是上下限报警在P3.6和P3.7上接一个LED和一个蜂鸣器代码里设定报警阈值温度超过上限或低于下限时拉高引脚这是课设里最常见的加分项。第二个是串口输出把温度值通过串口发送到proteus的虚拟终端每秒钟打印一次这个扩展只需要在main函数里初始化串口然后用printf格式化输出适合想看实时曲线的人。第三是改成双通道测温ADC0809的IN1再接入一个电位器代码里在通道0和通道1之间轮询LCD两行分别显示两个点的温度这对后续做多点温度采集是很好的起点。从那以后我每次拿到这套仿真图都会先拿阻值表把电位器和LCD显示对一遍确认测温方向正确、ADC时序没写反再去动原件和外壳。这个习惯帮我避免了好几次分压电路设计错误导致的“返工重焊”也让我在给别人看代码时不用反复解释为什么显示的温度方向是反的。希望帮到你。本文还有配套的精品资源点击获取