
简介一份基于51单片机的热敏电阻数字温度计毕业设计资料包面向电子类专业学生、单片机初学者及需要完成课程设计或毕业设计的读者。内容围绕PT100热敏电阻测温系统展开完整覆盖测温、信号放大、A/D转换、控制显示四大模块并包含AT89C51单片机选型、ADC0804转换电路、Keil µVision 4软件编程及Proteus仿真调试等关键环节。压缩包内仅有1个doc文档大小543KB文档为完整毕业设计说明书包含摘要、目录、硬件电路原理、软件流程与测试分析还提供误差分析和系统测试结论便于直接参考或二次开发。目前已有424人学习/下载尤其适合课程设计、毕业设计或工程入门阶段作为温度测量类项目从方案设计到仿真实现的全流程参考。1. 别再纠结DS18B20AT89C51PT100这套模拟链路其实更值得复现很多人一提到51单片机做温度计第一反应就是DS18B20单总线数字传感器接线简单、代码少好像理所当然。但真正在工业现场或者毕业设计里被反复验证的方案反而是“PT100铂热电阻 LM324运放 ADC0804并行AD”这条全模拟链路。原因很现实PT100在-200℃到650℃范围内线性度好、互换性强而且通过恒流源激励配合差分放大能明显削弱导线电阻和电源纹波的影响——这是DS18B20这类PN结传感器做不到的。这套设计用AT89C51做处理核心把PT100随温度变化的电阻值转换成电压再经ADC0804量化后查表显示到数码管。适合想搞懂“传感器信号调理到单片机接口”完整链条的嵌入式初学者也适合要写课程设计说明书的人。本文按硬件链路、仿真、C代码到标定坑的顺序把整个工程撕开讲。2. 信号链全局设计从PT100到数码管的每一级都在干什么2.1 为什么选PT100恒流源测温而非桥式电路PT100的测温本质是把电阻变化变成电信号。最常见的两种采样电路是电桥和恒流源。原设计用的是恒流源式即向PT100灌入固定电流I然后测量其两端电压UI×R。这样做的好处是输出电压与电阻值成正比后续计算不用处理电桥的非线性衰减。若取I2.55mA0℃时PT100阻值100Ω输出电压就是0.255V100℃时阻值138.5Ω输出电压约0.353V。这个电压范围对ADC0804来说偏小所以必须加放大级。这里有个容易忽略的点恒流源电流不是随便选的。电流太大PT100自身发热会引入测温误差电流太小输出信号幅度不足放大级噪声占比高。2.55mA是一个折中值PT100自热功率约0.65mW按100Ω计算对工业级测温可以接受。2.2 LM324同相放大电路参数计算LM324是四运放这里只用其中一路接成同相放大。设PT100输出电压为Ui放大倍数为A则ADC0804输入端电压Uo A × Ui。原设计取放大倍数25倍这样0℃时Uo≈6.375V超过ADC0804的满量程5V所以实际电路常把电流降到2.5mA或调整放大倍数到20倍左右。我在Proteus里仿真时习惯把恒流源调成2mA放大倍数设为20倍这样0℃对应4V100℃对应5.54V又超了。正确做法是先标定ADC0804参考电压再反推放大倍数。如果参考电压Vref5V想覆盖0~100℃需要0℃到100℃的电压差正好落在0~5V范围内。PT100在0~100℃电阻变化38.5Ω恒流源2mA时电压变化77mV放大倍数A 5V / 0.077V ≈ 64.9。所以原设计里“2.55mA 25倍”组合对应的电压变化是0.158V×253.95V接近满量程但0℃起点电压偏高。实际工程中要么加入偏移电路要么只用差分测量。这里先按原设计的参数继续讲后面第5章再讲怎么调。2.3 ADC0804的并行接口与工作时序ADC0804是8位逐次逼近型AD转换器转换时间约100μs对于温度采样绰绰有余。它和AT89C51的接口很简单数据线D0~D7并行接P1口或P0口需上拉控制端有CS片选、RD读、WR写、INTR转换结束。典型启动转换时序是先拉低CS再拉低WR然后拉高WR等待INTR变低表示转换完成之后再拉低CS和RD读取数据。下面是一段直接用P2口控制控制端的示例逻辑sbit CS P2^0; sbit RD P2^1; sbit WR P2^2; sbit INTR P2^3; #define ADC_DATA P1 // ADC0804数据口接P1 unsigned char read_adc(void) { unsigned char val; CS 0; WR 0; WR 1; // 启动转换 while(INTR); // 等待INTR变低表示转换完成 RD 0; val ADC_DATA; // 读取8位结果 RD 1; CS 1; return val; }这段代码里CS 0是选中芯片WR脉冲触发一次转换。while(INTR)是阻塞等待若INTR一直为高则死等。实际项目中建议加超时计数防止ADC0804损坏导致程序卡死。读取到的val范围是0~255对应参考电压0~Vref。注意ADC0804支持差动输入VIN和VIN-之间才是有效模拟输入单端接地时VIN-接GND。2.4 显示模块LED数码管的静态与动态切换显示部分采用共阴或共阳数码管。多数毕业设计用两个或四个数码管显示温度值。这里要区分静态显示和动态扫描静态显示占用I/O口多但程序简单动态扫描用段选和位选分时点亮实际中常用74HC573或三极管驱动位选。原设计用了P0口输出段码P2口的部分引脚做位选。动态扫描必须利用人眼视觉暂留刷新频率不能低于50Hz。3. 硬件电路搭建和Proteus仿真复现要点3.1 AT89C51最小系统连接在Proteus里搭建这个系统核心器件是AT89C51、PT100Proteus里用电阻代替赋值、LM324、ADC0804、数码管。先列一下引脚分配表方便对照连线。模块接口单片机引脚说明ADC0804数据D0~D7P1.0~P1.7并行数据输入ADC0804控制CSP2.0片选低有效ADC0804控制RDP2.1读信号低有效ADC0804控制WRP2.2写信号低有效ADC0804控制INTRP2.3转换结束输出数码管段码a~dpP0.0~P0.7经限流电阻接数码管数码管位选位1/位2P2.4/P2.5选择第1位或第2位连接时ADC0804的VIN接LM324输出VIN-接GNDVREF/2引脚通过一个电位器分压用于调整满量程。时钟输入CLK接一个RC振荡电路典型值10kΩ电阻和150pF电容也可以从AT89C51的ALE引脚分频获得。PT100的恒流源可以用一个TL431做基准再加三极管扩流但仿真阶段直接用电流源代替。3.2 Proteus仿真电路的操作步骤在Proteus中按下面步骤操作新建工程从元件库中选AT89C51、LM324、ADC0804、7SEG-MPX2-CA双位共阳数码管、电阻、电容、电源。按上面的引脚表连线。注意P0口作为I/O使用时必须接上拉电阻组阻值选10kΩ。PT100在仿真里可以用一个可变电阻阻值范围设在80~140Ω模拟-50℃到100℃区间。也可以用“电阻电位器”串联。设置LM324反馈电阻使放大倍数符合计算值。若反馈电阻Rf240kΩ反向端接地电阻Rg10kΩ则放大倍数A1Rf/Rg25倍。双击AT89C51载入编译好的HEX文件点击运行然后调节可变电阻阻值观察数码管显示是否随之变化。3.3 仿真中常见的信号失真问题仿真时最容易遇到的问题是LM324输出饱和。因为LM324在单电源供电下输出最高只能到VCC减1.5V左右如果VCC5V输出上限约3.5V。前面算过0℃输出可能到6V显然会削顶。解决办法是降低恒流源电流到1mA放大倍数取20倍这样0℃时输出2V100℃时输出2.77V都在线性范围内。另一个办法是给LM324提供双电源±5V但51单片机系统里通常没有负电源。现象原因解决方式数码管显示一直为0xFFADC0804数据口未接上拉或RD时序不对检查P1口是否接上拉或在程序中延长RD低电平时间温度数值跳变剧烈电源纹波大或LM324放大倍数过高在PT100输出电压端并一个0.1μF电容降低放大倍数温度显示非线性严重恒流源电流不稳定改用TL431基准源而非简单电阻恒流数码管亮度不均匀动态扫描频率太低将定时器中断间隔设为2ms刷新周期小于10ms4. 软件代码拆分从启动AD转换到显示温度的完整流程4.1 函数声明与管脚定义用Keil C51编写程序。先把头文件、引脚定义和全局变量写清楚。注意ADC0804的引脚定义要和硬件连线一致。#include reg51.h // ADC0804控制引脚 sbit CS P2^0; sbit RD P2^1; sbit WR P2^2; sbit INTR P2^3; // 数码管位选 sbit DIG1 P2^4; sbit DIG2 P2^5; #define ADC_DATA P1 // P1口读取ADC0804数据 unsigned char code table[] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, // 0~4 0x92, 0x82, 0xF8, 0x80, 0x90 // 5~9 }; // 共阳数码管段码表这里table[]是共阳数码管的段码0xC0表示00xF9表示1依此类推。如果用的是共阴数码管段码要取反。P0口外接上拉电阻后输出低电平点亮段码。4.2 启动AD转换与读取数据子程序unsigned char read_adc(void) { unsigned char value; CS 0; WR 0; WR 1; // 产生一个上升沿启动转换 while(INTR 1); // 等待转换完成INTR变低 RD 0; value ADC_DATA; // 读取转换结果 RD 1; CS 1; return value; }这个函数先拉低CS选中芯片再给WR一个低脉冲启动转换。while(INTR 1)循环等待转换结束——ADC0804转换期间INTR为高完成后变低。读数据时拉低RD数据线上的8位结果是有效的。这里没有做超时保护实际项目里建议加一个计数器超过1000次循环就跳出避免芯片异常时死循环。4.3 温度数据换算与线性化处理拿到ADC数值后第一步要换算出PT100的电压。设ADC实际输出为8位值adc_val参考电压为5V则输入电压v_in (adc_val / 255.0) * 5.0。再除以放大倍数得到PT100上的压降最后除以恒流源电流得到电阻值。温度则用简化线性公式T (R - 100) / 0.385计算。这里的0.385是PT100在0~100℃的平均电阻温度系数。float calc_temperature(unsigned char adc_val) { float v_in, r_pt100, temp; v_in ((float)adc_val * 5.0) / 255.0; // 反推ADC输入电压 r_pt100 v_in / 25.0 / 0.00255; // 除以放大倍数和恒流源电流 temp (r_pt100 - 100.0) / 0.385; // 线性化公式 return temp; }这段代码里25.0是LM324的放大倍数0.00255是恒流源电流2.55mA。参数需要和硬件实际焊接的电阻值严格对应。注意这个公式只适用于0~100℃范围超过后误差会增大。如果你在仿真里把放大倍数改了这里也要同步改。4.4 数码管动态显示函数显示部分采用两个数码管显示温度整数部分如果需要小数点可以加一位。典型动态扫描函数如下void display(unsigned int temp) { unsigned char ge, shi; shi temp / 10; ge temp % 10; P0 table[shi]; // 输出十位的段码 DIG1 0; // 打开位选1 DIG2 1; delay(2); // 延时2ms P0 table[ge]; // 输出个位的段码 DIG1 1; DIG2 0; // 打开位选2 delay(2); }这里的temp是整数温度值。位选低电平有效是因为共阳数码管公共端接高电平三极管反相驱动。delay(2)实现2ms延时保证亮度。两个位选轮流打开循环频率约250Hz肉眼看不到闪烁。4.5 定时器初始化与主函数主循环里不断读AD、换算、显示即可。但为了保持显示稳定建议用定时器中断做时基。void timer0_init(void) { TMOD 0x01; // 定时器0模式116位 TH0 0xFC; // 定时1ms初值12MHz晶振 TL0 0x18; ET0 1; TR0 1; EA 1; } void main(void) { unsigned char adc_val; unsigned int temp; timer0_init(); while(1) { adc_val read_adc(); temp (unsigned int)calc_temperature(adc_val); display(temp); } }主程序就是“采集-转换-显示”三件事。定时器0的中断服务程序里可以放一个计数器用于动态扫描的延时控制避免在display函数里用软件延时阻塞CPU。这里为了简化直接用了软件延时实际工程中建议把位选切换放在中断里。5. 参数标定和复现过程中的常见坑5.1 恒流源电流精度对温度计算的影响原设计用2.55mA恒流源这个值的来源是把0℃和100℃的输出电压差恰好映射到ADC0804的满量程附近。但实际电路里恒流源由三极管和稳压管构建温度漂移会让电流发生微小变化。比如电流从2.55mA漂到2.58mA同样电阻下电压会增大1.2%对应温度读数偏高约3℃。解决办法是用精密基准源LM334或REF200或者每次上电时用标准电阻校准电流。// 校准后的电流值实际测量后写入 #define I_REF 0.00255 #define AMP_GAIN 25.0参数定义成宏的好处是调试时只改一处。但注意宏定义是编译期常量如果要用电位器在线校准就得做按键输入或串口标定。毕业设计一般做到这里就够。5.2 LM324放大倍数的温度漂移LM324的输入失调电压典型值2mV最大7mV。放大25倍后输出端会有50~175mV的直流偏置这个偏置会被当成温度信号。所以实际电路里常在放大器输入端加一个调零电位器或者在软件里做“零点校准”把PT100放入冰水混合物0℃时记下ADC值之后每次读数减去这个偏移量。校准步骤操作1将PT100置于0℃冰水混合物中2读取ADC值并记录为adc_offset3正常工作时实际adc值减去adc_offset这种软件校零比硬件调零更快适合批量生产。5.3 ADC0804参考电压的设定陷阱ADC0804的参考电压由VREF/2引脚决定当该引脚悬空时内部默认Vref5V。如果VREF/2引脚接2.5V则满量程变成5.0V正好适合0~100℃的电压范围。但很多人会直接用5V电源作为满量程结果发现到100℃时ADC值才180左右分辨率不足。正确做法是用电位器把VREF/2调到1.5V这样满量程就是3.0V——但要注意输入电压不能超过3V否则会溢出。我测试时把恒流源设为1.5mA放大倍数20倍0℃时电压1.5V100℃时电压1.77V对应ADC值约77和90分辨率只有0.02V/bit相当于1.3℃/bit。这个精度偏低所以更好的办法是把恒流源提高到2mA放大倍数提高到40倍同时调节VREF/2让满量程匹配。总之ADC0804只有8位分辨率任何设计都要把被测电压范围尽量铺满整个量程。5.4 Proteus仿真和实际硬件的差异Proteus里的PT100是理想电阻不会自发热也没有导线电阻。但实际接线中PT100引线可能长达几米两根导线电阻各0.5Ω就相当于1℃误差。解决方法是使用三线制接法一根线接恒流源正极一根线接放大器的正输入端第三根线接恒流源回路。这样导线电阻被分到恒流源回路上不会影响电压测量。原设计里提到了三线制但电路图显示的是两线制。如果你要实际做板子建议改成三线制。另外Proteus里的LM324是理想模型输出可以接近电源轨但实际只能到3.5V5V供电。所以仿真时波形正常焊板后可能发现高温段读数封顶。可以改用轨到轨运放如LMV324信号范围会更宽。6. 进阶技巧用查表法替代线性公式做全温段修正前面用的T (R - 100) / 0.385在0~100℃内误差约±1℃到150℃时误差会到±3℃。这是因为PT100的电阻-温度曲线并非严格直线尤其是0℃以下非线性比较明显。一种工程上常用的做法是把PT100分度表存入单片机用二分查找定位阻值区间再线性插值得到精确温度。先建立一组标准分度表只取关键点code float pt100_table[] { -50, 80.31, -30, 88.22, 0, 100.00, 20, 107.79, 50, 119.40, 70, 127.08, 100, 138.51, 150, 157.33 }; // 每两个元素一对温度电阻查表函数传入计算出的电阻值返回对应的温度。核心逻辑是遍历数组找到电阻所在的区间然后做线性插值。float lookup_temperature(float r) { char i; float t0, r0, t1, r1; for(i 0; i 14; i 2) { // 每个条目占2个float t0 pt100_table[i]; r0 pt100_table[i1]; t1 pt100_table[i2]; r1 pt100_table[i3]; if(r r0 r r1) { return t0 (t1 - t0) * (r - r0) / (r1 - r0); } } return 0xFF; // 越界返回错误 }查表法比公式法多消耗几百字节ROM79C51有4KB完全放得下但能获得全量程接近0.1℃的精度。表越密精度越高。如果你不想手动维护分度表可以用PT100的分度多项式公式在PC端生成数组再粘贴到代码里。实际操作中要注意两点一是表必须按电阻升序排列否则二分查找失效二是表所在内存要使用code关键字放到ROM里而不是占用宝贵的RAM。查表函数的返回值是浮点数如果想节省资源可以用整数温度扩大10倍存储比如显示“23.5℃”就存235。这样数码管显示时只需要拆BCD码不需要调用浮点库。做完这一步整个数字温度计就从“能亮”进步到“能测准”了。后面如果再上RS485或者LCD1602其实只是在这个信号链的尾部加接口前端PT100的调理才是这套设计的核心门道。本文还有配套的精品资源点击获取