文档库 最新最全的文档下载
当前位置:文档库 › 基于51单片机的数字温度计

基于51单片机的数字温度计

基于51单片机的数字温度计
基于51单片机的数字温度计

硬件课程设计实验报告课题:数字温度计

班级:

作者:

学号:

指导老师:

课设评价:

课设成绩:

目录

一.需求分析 (1)

二.概要设计 (1)

三.硬件电路设计 (3)

四.系统软件设计 (5)

五.软件仿真 (8)

六.实际连接与调试 (9)

七.本次课设的收获与感受 (11)

附录(程序源代码) (12)

一.需求分析

功能要求:

测量环境温度,采用接触式温度传感器测量,用数码管显示温度值。

设计要求:

(一)功能要求

(1) 由4位数码管显示当前温度。

(2) 具备报警,报警门限通过键盘设置。

(3) 精度为0.5℃。

(二)画出参考的电路原理图

(三)画出主程序及子程序流程图、画出MCS51部RAM分配图,并进行适当地解释。

(四)写出实现的程序及实现过程。并进行适当地解释说明。

二.概要设计

(一)方案选择

由于本设计是测温电路,可以使用热敏电阻之类的器件利用其感温效应,在将随被测温度变化的电压或电流采集过来,进行A/D转换后,就可以用单片机进行数据的处理,在显示电路上,就可以将被测温度显示出来,这种设计需要用到A/D转换电路,感温电路比较麻烦。进而考虑到用温度传感器,在单片机电路设计中,大多都是使用传感器,所以这是非常容易想到的,所以可以采用一只温度传感器DS18B20,此传感器,可以很容易直接读取被测温度值,进行转换,就可以满足设计要求。

(二)系统框图

该系统可分为以下七个模块:

(1)控制器:采用单片机STC89C52对采集的温度数据进行处理;

(2)温度采集:采用DS18B20直接向控制器传输12位二进制数据;

(3)温度显示:采用了4个LED共阴极七段数码管显示实际温度值;

(4)门限设置:主要实现模式切换及上下门限温度的调节;

(5)报警装置:采用发光二极管进行报警,低于低门限或高于高门限均使其发光;

(6)复位电路:对整个系统进行复位;

(7)时钟振荡模块:为整个系统提供统一的时钟周期。

(三)重要器件及其相关参数

(1)单片机STC89C52

P0.0~P0.7:通用I/O引脚或数据低8位地址总线复用地

址;

P1.0~P1.7:通用I/O引脚;

P2.0~P2.7:通用I/O引脚或高8位地址总线复用地址;

P3.0~P3.7:通用I/O引脚或第二功能引脚(RxD、TxD、

INT0、INT1、T0、T1、WR、RD);

XTAL1、XTAL2:外接晶振输入端;

RST/Vpd:复位信号输入引脚/备用电源输入引脚;

Vcc:接+5V电源;

Vss:地端。

(2)DS18B20

(a)通过单线总线端口访问DS1820 的协议如下:

?初始化

?ROM 操作命令

?存储器操作命令

?执行/数据

DS1820 需要严格的协议以确保数据的完整性。协议包括几种单线信号类型:复位脉冲、存在脉冲、写0、写1、读0 和读1。所有这些信号,除存在脉冲外,都是由总线控制器发出的。和DS1820 间的任何通讯都需要以初始化序列开始,初始化序列见图11。一个复位脉冲跟着一个存在脉冲表明DS1820 已经准备好发送和接收数据(适当的ROM 命令和存储器操作命令)

(b)当总线上只有一个器件时,DS18B20读温度的流程为:

复位发0CCH SKIP ROM命令发44H开始转换命令延时复位发0CCHSKIP ROM命令发0BEH读存储器命令连续读出两个字节数据(即温度)结束。

?Convert T [44h]

这条命令启动一次温度转换而无需其他数据。温度转换命令被执行,而后DS1820 保持等待状态。如果总线控制器在这条命令之后跟着发出读时间隙,而DS1820 又忙于做时间转换的话,DS1820 将在总线上输出“0”,若温度转换完成,则输出“1”。如果使用寄生电源,总线控制器必须在发出这条命令后立即起动强上拉,并保持500ms。

?Read Scratchpad [BEh]

这个命令读取暂存器的容。读取将从字节0 开始,一直进行下去,直到第9(字节8,CRC)字节读完。如果不想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。(c)DS18b20 用12 位存贮温值度最高位为符号位,下图为18b20 的温度存储方式,负温度S=1 正温度S=0

LSB:

三.硬件电路设计

本次实验采用了老师提供的单片机系统,所以整体的硬件电路设计需要在已知的硬件条件下进行设计。

(一)下图为已有的单片机系统部分电路图

分析:(1)由上图可知,时钟振荡电路,复位电路均以在原系统中正确连接。

(2)数字温度计所需的显示电路电路中,原系统将段码输出连在P2接口中,但由图

可知,该图中的六个数码管中的小数点均无法点亮,原因在于图中并未对其进行连接,也无引脚供外界连接。而六个数码管的位选端口连在P1接口上。本次课程设计中,我们选择使用P1.0~P1.3上所连接的四个数码管。

(3)通过软件测试验证,原系统中的数码管为共阴极,且原系统中位选信号是通过

一个反向器之后才输入数码管。

(4)原系统中P0端口未使用,所以可以用排线引出,连接我们所需要补充的电路,

包括DS18B20总线输入电路、门限设置电路以及报警电路。

(二) 温度采集电路、门限设置电路及报警电路(其余电路省略)

图中:

(1)门限中的三个按键,分别为模式切换按键、加按键、减按键;

(2)报警电路中,超过高门限或者低于低门限时发光二极管被点亮,其余时刻均熄灭;(3)DS18B20温度采集电路中,需要注意的是DQ上需要一个上拉电阻,一般为4.7KΩ左右。

四.系统软件设计

(一)读取数据的流程图

DSl8820的主要数据元件有:64位激光Lasered ROM,温度灵敏元件和非易失性温度告警触发器TH和TL。DSBl820可以从单总线获取电源,当信号线为高电平时,将能量贮存在部电容器中;当单信号线为低电平时,将该电源断开,直到信号线变为高电平重新接上寄生(电容)电源为止。此外,还可外接5 V电源,给DSl8820供电。DSl8820的供电方式灵活,利用外接电源还可增加系统的稳定性和可靠性。下图读取数据流程图。

(二)温度数据处理程序的流程图

读出温度数据后,temp的低四位为温度的小数部分,可以精确到0.0625℃,temp的中间8位为温度的整数部分,temp的高四位全部为1表示负数,全为0表示正数。所以先将数据提取出来,分为三个部分:小数部分、整数部分和符号部分。小数部分进行显示时要使用另外的查询表,与整数显示查询表有所不同。因为本次课程设计只要求测试的温度围为0~70℃,所以符号位S必为0,软件设计中则默认所测数据为正,从而不设符号位判定。

(三)模式切换流程图

使用模式值st来标记不同模式,st=1时,表示在正常温度模式,st=2,表示在高门限模式,st=3表示在低门限模式,每次按下一次模式切换键,st+1,待其加至4时,将该值返回至1。从而形成了模式键不断按下,三种模式循环切换的情况。

防抖在这里是十分关键的。因为此处的按键是按下然后又返回为按了一次。所以按下时,

则进入程序,为了防止机械抖动等不确定情况,延时恰当的一段时间后再次检测是否确实按下。若确实按下,则对模式进行切换。切换后,延时等待按键返回高位,返回高位后,再次延时消除抖动,再次检验,确定按键返回高位后,退出程序。

五.软件仿真

电路图连接:严格按照硬件设计中已有的单片机系统的连接方式和自己补充的电路的连接方式进行仿真,要最贴近实际电路,才能更准确的得到仿真结果。

系统电路图

高门限下数码管显示

低门限下数码管显示

正常模式下数码管显

六.实际连接与调试

(一)仿真成功后,在实际操作中,电路图的连接应该严格按照仿真电路进行。

问题:在最开始实验中,我将DS18B20的DQ连接在P1.4,且仿真成功。但是在实际连

接中,我将DQ连接在了P0.4端口,但实际电路中无任何数据显示。再次仿真,将DQ接至

P0.4,仿真不成功。后查阅P0、P1、P2以及P3口的部结构后发现,除P0外另外三口均接有上拉电阻,而P0则无。所以P0在作为通用I/O接口使用时,必须外接上拉电阻,其余端口则不需要。所以,为方便起见,实际中,将DQ按照最初仿真设计,接至P1.4。

(二)原系统所拥有资料太少,数码管类型未知。

因只查阅到原系统的部分电路图,且无详细说明,所以为证明在网上搜索到的电路图的正确性以及确定数码管的类型。先编写一个简单的数字显示程序。最开始,我并没有意识到原系统中的片选信号接有反向器,在测试程序中,无论使用共阴还是共阳数字查询表,数码管均显示8888,后发现问题将所有片选数值取反,再次进行测试,确定数码管为共阴极。

同时,在测试过程中,发现原系统的小数点是无法点亮的。

(三)关于位选

在最开始的测试数码管类型中,我只测试了一位数码管,使其显示数值1,但测试成功后,6位数码管均显示为1。在测试过程中,我并未注意这个问题。

测试结束后,进行整体联调。但数码管始终无正确显示。当时,我首先着手于寻找软件程序中是否有错误。所以人为将temp设定为一个固定值,先确定数据处理程序和数据显示程序无误。然而数码管无常显示我预先设定的值,后经过反复改动,发现,在位选过程中,必须在选中一位数码管的过程中同时将其余所有的数码管关闭,否则将产生混乱。若逐一设定S1=1;S2=0;S3=0;S4=0未免太过麻烦,所以设定一个数组,分别赋值0x08,0x04,0x02,0x01。片选时按照规则将上值赋给,则一举多得。

(四)DQ数据采集

上述问题均解决后,再次进行调试,数码管显示25592。这个值非常特殊,且第5个数码管,在程序中所有地方均是将其熄灭的,为何此处会亮呢??显然采集的数据是有很大问题的,因为很明显2559中,9为小数部分,将2559换为二进制则为1111 1111 1111。这就说明电路并没有采集到温度数据,原因一:DQ上接有上拉电阻,在输出正常温度数据外始终为高电平,所以此处很可能并没有采集到正常输出温度数据;原因二:室温不可能为25592。

但是当时,我并没有对器件是否损坏产生怀疑。原因在于,大部分时候数码管显示的是12791,且两边的1很亮且基本无闪烁,279则在不断闪烁,频率很快,造成了27.9为数码管要显示的温度的错觉,且27.9是与当时实验室的温度非常接近的。同时,在我不断的对程序中DS18B20的初始化时等部分的时序进行调整时,279的闪烁频率发生变化,两个1并无变化。

在经历了无法显示正常温度的挫败后,我仔细查阅了DS18B20的数据手册,确定了在其初始化过程中,DQ电平应该如何变化且应当延时多少。同时也确定了DS8B20的在12位分辨率情况下数据A/D转换所需时间为500ms~750ms,所以在发出转换命令后需要给其充分的时间来进行A/D转换。

在经历了上述种种之后,我基本确定了程序中时序的正确性。

再次进行实验室,数码管始终显示的为850,第5位数码管无故亮起的情况消失了(到现在我都不清楚它当时为什么会亮)。上网搜索资料,总结原因有二。一是程序时序存在问题;二是DS18B20损坏了。

为排除第二个状况,我与同学更换了器件,数码管上显示出了令人欣喜的289!

但是,仍然存在的问题和解决过程是

(1)为保证充分的延时,导致数码管中数字不断闪烁,看起来非常不舒服。后通过改进程序,使程序采集一次数据,却多次扫描显示温度数值。以此法使其不再闪烁。但是实际上,这种方法是不完美的,完美的状况应当是多次采集数据,扫描显示多次采集值的平均值。但限于时间紧迫和后者的复杂度,只能采用前者的方法了。

程序如下:case 1:{

work_temp(read_temp());//采集处理温度数值

for(i=0;i<500;i++)

{

scan(); //显示温度

BEEP();

}

break;

}

(2)数码管大部分时间显示的是正常温度,但是仍然会在某些时候跳变为2559,,1279等数值。猜测可能是接触不良造成的,后将接线焊在板子上后,该问题得到了解决。但仍偶尔出现不稳定状况,则主要是排线,+5V电压线,地线等诸多因素的影响,稍作调整后即可消除。

(五)最终结果

七.本次课设的收获与感想

附录:

#include "reg51.h"

#include "intrins.h" //_nop_();延时函数用

#include "math.h"

#define disdata P2 //段码输出口

#define discan P1 //扫描口

#define uchar unsigned char

#define uint unsigned int

sbit duqu=P1^4; //温度采集输入口

sbit dian=P2^7; //LED小数点控制

sbit key1=P0^0; //模式切换键

sbit key02=P0^1; //加键

sbit key03=P0^2; //减键

sbit beep=P0^3; //LED报警

uint h;

uint temp;

uchar r;

char high=40,low=15;

uchar st=1;

uchar pp;

uchar code

ditab[16]={0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x08,0x 09,0x09};

//温度小数部分用查表法

uchar code

dis_7[15]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x67,0x00,0x40,0x76,0x38,0x3 9};

//共阴LED段码表"0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "不亮" "-" "H" "L" "C"

uchar code scan_con[4]={0x08,0x04,0x02,0x01}; //列扫描控制字

uchar data temp_data[2]={0x00,0x00}; //读出温度暂放

uchar data display[5]={0x00,0x00,0x00,0x00,0x00}; //显示单元数据,共4个数据和一个运算暂用

/*****************8us延时函数*************************/

void delay(uint t)

{

for (;t>0;t--);

{_nop_();}

}

/****************显示扫描函数***************************/

void scan()

{

char k;

for(k=0;k<4;k++) //4位LED扫描控制{

discan=scan_con[k]; //位选

disdata=dis_7[display[k]]; //数据显示

if (k==1){dian=1;} //小数点显示

delay(200);

}

}

/****************DS18B20复位函数************************/

ow_reset(void)

{

duqu=0;

delay(60); //拉低480~960us

duqu =1;

delay(10); //等待15~60us

if(duqu==0)

delay(30);

duqu=1;

}

/****************DS18B20写命令函数************************/

//向1-WIRE 总线上写1个字节

void write_byte(uchar val)

{

uchar i;

for(i=0;i<8;i++)

{

duqu=0;

delay(1); //严禁超时超过15us

duqu=val&0x01;

delay(5);

val>>=1;

duqu=1;

}

}

/****************DS18B20读1字节函数************************/ //从总线上取1个字节

uchar read_byte(void)

{

uchar i;

uchar value=0;

for(i=8;i>0;i--)

{

duqu=0;

value>>=1;

duqu=1;

delay(1);

if(duqu==1)

value|=0x80;

delay(5);

duqu=1;

}

return value;

}

/****************读出温度函数************************/

uint read_temp()

{

ow_reset(); //总线复位

write_byte(0xcc); //发命令

write_byte(0x44); //发转换命令

delay(200);

delay(200);

ow_reset();

write_byte(0xcc); //发命令

write_byte(0xbe);

delay(200);

delay(200);

temp_data[0]=read_byte(); //读温度值的第字节

temp_data[1]=read_byte(); //读温度值的高字节

temp=temp_data[1];

temp<<=8;

temp=temp|temp_data[0]; // 两字节合成一个整型变量。

return temp; //返回温度值

}

/****************温度数据处理函数************************/

work_temp(uint tem)

{

uchar n=0;

display[4]=tem&0x0f; // 取小数部分的值

display[0]=ditab[display[4]]; // 存入小数部分显示值

display[4]=tem>>4; // 取中间八位,即整数部分的值display[3]=display[4]/100; // 取百位数据暂存

display[1]=display[4]%100; // 取后两位数据暂存

display[2]=display[1]/10; // 取十位数据暂存

display[1]=display[1]%10;

r=display[1]+display[2]*10+display[3]*100;

if(!display[3]) //符号位显示判断

{

display[3]=0x0a; //百位为0时数码管熄灭

if(!display[2])

{

display[2]=0x0a; //十位为0时数码管熄灭

}

}

}

/******************二极管报警函数**************************/

void BEEP()

{

if(r>=high||r<=low)

{ beep=1;}

else

{beep=0;}

}

/******************单片机初始化函数**************************/

void init()

{

EA=1;

EX0=0;

EX1=0;

IT0=1;

IT1=1;

}

/******************温度/上下限调整切换**************************/ void key11()

{

while(key1==0) //当模式切换键按下则进入循环体

{

delay(300); //延时消除抖动

if(key1==0)

{

st++; //模式循环切换

if(st==4)

st=1;

}

while(!key1);//延时等待按键回到高电平

delay(300);

while(!key1);//延时消除抖动

}

}

/******************门限值加1**************************/

void key2()

{

while(key02==0)

{

delay(400);

if(key02==0)

{

if(st==3)

{

low=low+1;}

if(st==2)

{high=high+1;}

if(low>high)

{pp=low;low=high;high=pp;}

}

while(!key02);

delay(400);

while(!key02);

}

}

/******************门限值减1**************************/ void key3()

{

while(key03==0)

{

delay(400);

if(key03==0)

{

if(st==3)

{low=low-1;}

if(st==2)

{high=high-1;}

if(low>high)

{pp=low;low=high;high=pp;}

}

while(!key03);

delay(400);

while(!key03);

}

}

/******************上限温度显示**************************/ void high1()

{

uchar k;

display[0]=high%10;

display[1]=high/10;

display[2]=10; //个位熄灭

display[3]=12; //百位显示H

for(k=0;k<2;k++)

{

discan=scan_con[k];

disdata=dis_7[display[k]];

if (k==1){dian=0;}

delay(300);

disdata=0x00;

delay(100);

}

}

/******************下限温度显示**************************/ void low1()

{

uchar ki;

display[0]=low%10;

display[1]=low/10;

display[2]=10; //个位熄灭

display[3]=13; //百位显示L

for(ki=0;ki<2;ki++)

{

discan=scan_con[ki];

disdata=dis_7[display[ki]];

if (ki==1){dian=0;}

delay(100);

disdata=0x00;

delay(100);

}

}

/****************主函数************************/

void main()

{

uint i;

init();

disdata=0x00; //初始化端口

ow_reset(); //开机先转换一次for(h=0;h<100;h++) //开机显示"0000"

{scan();}

while(1)

相关文档