草庐IT

基于51单片机的温度报警系统

棺啊 2023-04-03 原文

目录

前言

一、温控系统是什么?

二、设计目的

1.环境需求

2.设计基础

三、设计

1.C51程序设计

2.Proteus仿真

(1).元器件如下

(2).原理图。

总结


前言

微型计算器自20世纪70年代诞生以来,得以迅速发展、普及和应用。随着增强型8051单片机和嵌入式微处理器技术的飞速发展,以及单片机C语言应用的普及,单片机应用领域越来越广泛。

 

一、温控系统是什么?

随着社会的快速发展,科技的加速进步,测温仪器在各个领域应用越来越广泛,自动化和智能化已经成为现代的温度控制系统的主流发展方向。因为各行各业对于温度控制有着越来越高的要求,所以对温度的控制和测量就显得较为重要。温度控制器的使用范围越来越广泛,各种能够应用于不同领域的智能自动温度控制器随着产生。自动温度控制系统主要完成数据采集,温度定时的显示,温度控制,温度定时的设定以及报警等功能.

二、设计目的

对51单片机所学知识进一步强化理解和应用。

1.环境需求

软件:Proteus  (文章中使用Proteus 8.6)

          Keil (文章中使用Keil C51)

2.设计基础

掌握《C语言》、《单片机原理及应用》两门课程。

三、设计

1.C51程序设计

/*-----------------------------------------------
  名称:18B20温度测量报警
  内容:在LCD1602第一行可以显示当前温度,
		第二行显示设定的温度区间,超过此温度区间,蜂鸣器报警。
		温度区间可以通过用户按键设置
------------------------------------------------*/
#include<reg51.h>     //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include<math.h>
#include<INTRINS.H>

#define uchar unsigned char
#define uint unsigned int 
/******************************************************************/
/*                    定义端口                                    */
/******************************************************************/
sbit led1=P1^3;// 温度超出范围指示灯
sbit led2=P1^4;

sbit buzzer=P3^3;//蜂鸣器引脚

sbit DQ=P3^7;//ds18b20 端口

sbit RS = P0^4;//1602数据/命令选择端(H:数据寄存器L:指令寄存器) 
sbit RW = P0^5;//1602读/写选择端 
sbit E  = P0^6;//1602使能信号端

sbit key1=P3^4;//用户按键
sbit key2=P3^5;
sbit key3=P3^6;
/******************************************************************/
/*                    全局变量                                    */
/******************************************************************/
int temp;//测得温度
char temp_max=40,temp_min=10;//设定的温度 
char TempH,TempL;
uchar flag_get,num=0;
uchar code tab[]={'0','1','2','3','4','5','6','7','8','9'};//液晶显示
uchar tab1[]="min:010  max:040";//液晶第二行显示内容
uchar  str[8];
/******************************************************************/
/*                    延时函数                                    */
/******************************************************************/
void delay1(uint i)//短延时函数
{
 	while(i--);
}
void delay(uint z) //长延时函数
{
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);
}
/******************************************************************/
/*                   DS18B20 初始化                               */
/******************************************************************/
void Init_DS18B20(void)
{
	uchar x=0;
	DQ = 1;    //拉高总线,等待
 	delay1(8);
 	DQ = 0;    //单片机将DQ拉低
 	delay1(80); //精确延时 大于 480us
 	DQ = 1;    //拉高总线,等待
 	delay1(10);
 	x=DQ;      //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
 	delay1(5);
 }


/******************************************************************/
/*                    DS18B20读一个字节                           */
/******************************************************************/
uchar ReadOneChar(void)	// DS18B20读一个字节
{
	uchar i=0;
	uchar dat = 0;
	for (i=8;i>0;i--)
 	{
  		DQ = 0; // 给脉冲信号
  		dat>>=1;
  		DQ = 1; // 给脉冲信号,主机在读时隙期间必须释放总线
  		if(DQ)
   		dat|=0x80;
  		delay1(5);
 	}
 	return(dat);
}


/******************************************************************/
/*                 DS18B20写一个字节                              */
/******************************************************************/
void WriteOneChar(uchar dat) //DS18B20写一个字节
{
 	uchar i=0;
 	for (i=8; i>0; i--)
 	{
  		DQ = 0;
  		DQ = dat&0x01;//取最低位
  		delay1(5);
  		DQ = 1;
  		dat>>=1;//左移
 	}
	delay1(5);
}

/******************************************************************/
/*                   DS18B20读取温度                              */
/******************************************************************/
uint ReadTemperature(void) //DS18B20读取温度
{
	uchar a=0;
	uint b=0;
	uint t=0;
	Init_DS18B20();//DS18B20初始化
	WriteOneChar(0xCC); // 跳过读序号列号的操作
	WriteOneChar(0x44); // 启动温度转换
	delay1(200);       //延时以求信号的稳定
	Init_DS18B20();    //DS18B20再次初始化
	WriteOneChar(0xCC); //跳过读序号列号的操作 
	WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
	a=ReadOneChar();   //低位
	b=ReadOneChar();   //高位
	b<<=8;  //b左移8位
	t=a+b;  //把a与b结合,放在16位的t中   此处两行代码也可用t=((b*256)+1)替代 原理相同
	return(t);
}
/******************************************************************/
/*                   LCD1602写命令操作                            */
/******************************************************************/
void WriteCommand(uchar com)
{
	delay(5);//操作前短暂延时,保证信号稳定
	E=0;
	RS=0;
	RW=0;

	P2=com;
	E=1;
	delay(5);
	E=0;
}
/******************************************************************/
/*                   LCD1602写数据操作                            */
/******************************************************************/
void WriteData(uchar dat)
{
	delay(5);  //操作前短暂延时,保证信号稳定
	E=0;
	RS=1;
	RW=0;

	P2=dat;
	E=1;
	delay(5);
	E=0;
}
/******************************************************************/
/*                   LCD1602初始化程序                            */
/******************************************************************/
void InitLcd()
{
	uchar i;
	delay(15);
	WriteCommand(0x38); //display mode
	WriteCommand(0x06); //显示光标移动位置
	WriteCommand(0x0c); //显示开及光标设置
	WriteCommand(0x01); //显示清屏
	WriteCommand(0x80+0x40);//将光标移到第二行
	for(i=0;i<16;i++)//显示初始化内容
	{
		WriteData(tab1[i]);
		delay(10);
	}
}
/******************************************************************/
/*                  蜂鸣器报警程序                                */
/******************************************************************/
void alarm()
{
	if(TempH>=temp_max)//当温度高于设置的最高温报警
	{	buzzer=0;
		delay(50);
		buzzer=1;
		delay(50);
		led1=0;
		led2=1;
	}
	else if(TempH<temp_min)//当温度低于设置的最低温报警
	{
		buzzer=0;
		delay(50);
		buzzer=1;
		delay(50);
		led1=1;
		led2=0;	
	}
	else//报警解除
	{
	 	buzzer=1;
		led1=1;
		led2=1;
	}
}
/******************************************************************/
/*                   显示测得的温度                               */
/******************************************************************/
void handle_T()
{

	uchar i;
	str[0]=0x20;//显示为空
	str[1]=tab[TempH/100]; //百位温度
   	str[2]=tab[(TempH%100)/10]; //十位温度
   	str[3]=tab[(TempH%100)%10]; //个位温度,带小数点
   	str[5]=tab[TempL];
	str[4]='.';
    str[6]=0xdf;
    str[7]='C';
  	if(flag_get==1)       //定时读取当前温度
    {
  		flag_get=0;       //清标志位
  		temp=ReadTemperature();//读取温度值
  		if(temp&0x8000)
        {
     		str[0]=0xb0;//负号标志
     		temp=~temp;  // 取反加1
	 		temp +=1;
	 	}

        TempH=temp>>4;	//由权重表知移4位就是整数位
  		TempL=temp&0x0F;
  		TempL=TempL*6/10;//小数近似处理

		alarm();    //判断是否需要报警
    }
	WriteCommand(0x80+0x04);//光标指向第一行第一个字符
	for(i=0;i<8;i++)// 显示
	{
		WriteData(str[i]);
		delay(10);
	}
		
}
void display_range(uchar add,int t)
{
	uchar i;
	if(t<0)
	{
		str[0]=0xb0;//负号标志
	}
	else
    {
        str[0]=tab[abs(t)/100]; //百位温度
   	    str[1]=tab[(abs(t)%100)/10]; //十位温度
   	    str[2]=tab[(abs(t)%100)%10]; //个位温度
	    WriteCommand(0x80+0x40+add);
    }
	
	for(i=0;i<3;i++)
	{
		WriteData(str[i]);
		delay(5);
	}
}
/******************************************************************/
/*              独立键盘扫描函数并显示设定的温度                  */
/******************************************************************/
void keyscan()  
{	

	uchar flag1=0,flag2=0;//有按键按下标记
	P3=0xff;//拉高P3口,以读取P3口的值
	
	if(key1==0) //两个按键控制最高/最低温度的加减 按住key1时通过其他2个键控制最高温度的加减
	{
		delay(5);
		if(key2==0)//最高温加
		{
			delay(5);//延时消抖
			if(key2==0)
			{
				temp_max++;
				if(temp_max>=85)
					temp_max=85;
			}
			while(!key2);//松手检测
			flag1=1;
		}
		if(key3==0)//最高温减
		{
			delay(5);
			if(key3==0)
			{
				temp_max--;
				if(temp_max<=temp_min)
					temp_max=temp_min;
			}
			while(!key3);
			flag1=1;
		}
	}
	
	if(key1==1)  //松开key1时通过其他2个键控制最低温度的加减
	{
		delay(5);
		if(key2==0)//最低温加
		{
			delay(5);//延时消抖
			if(key2==0)
			{
				temp_min++;
				if(temp_min>=temp_max)
					temp_min=temp_max;
			}
			while(!key2);//松手检测
			flag2=1;
		}
		if(key3==0)//最低温减
		{
			delay(5);
			if(key3==0)
			{
				temp_min--;
				if(temp_min<=-10)
					temp_min=-10;
			}
			while(!key3);
			flag2=1;
		}
	}

	if(flag1)//如有设置最高温度的键按下,更新设定的温度
	{
		flag1=0;// 清标记
		display_range(0x0d,temp_max);
	}
	if(flag2)//如有设置最低温度的键按下,更新设定的温度
	{
		flag2=0;// 清标记
		display_range(0x04,temp_min);
	}
		
}

/****************************************************************/
/*                    主函数                                    */
/******************************************************************/
void main()
{	
	TMOD|=0x01;//定时器设置
	TH0=0xEF;//装初始值
	TL0=0xF0;    
	EA=1;// 开总中断
	ET0=1;//允许定时器0中断
	TR0=1;//开定时器0中断
	InitLcd();//lcd1602初始化
	flag_get=1;
	while(1)
	{
		handle_T();// 处理温度:获得、显示、报警
		keyscan();  //独立按键扫描
  	} 
}
/******************************************************************/
/*    定时0中断处理程序,用于温度检测间隔,大约1秒测一次温度      */
/******************************************************************/
void timer0(void) interrupt 1 using 1
{
	TH0=0xEF;//定时器重装值
	TL0=0xF0;
	num++;
	if (num==50)
    {
		num=0;
	  	flag_get=1;//标志位有效,开始测温
	}
}

PS:按键控制温度加减区块也可用此种类型做到松手检测   (具体如何操作不做叙述)当然如果觉得2个按键控制可能因为抖动影响结果,可以更换其他方法。

if(key2==0&&flag==0)
{
	flag=1;
}
if(flag==1&&key2==1)
{
	temp_max++;
	if(temp_max>=85)
	temp_max=85;
	flag=0;
}

因为涉及到C语言部分知识的解析,分装代码将在下一篇文章中更新。(不是没有做出来,下图。下篇打包发出,需要自取。)

2.Proteus仿真

以Proteus 8.6为例 (PS:本人在使用Proteus 8.9过程中出现LCD1602显示屏第一行无法正常工作,被迫使用Proteus 8.6。没有找到问题原因,欢迎大佬留言解答。)

(1).元器件如下

关于buzzer(蜂鸣器)的选择,选择第二个即可。

LCD1602在Proteus中选择LM016L。

(2).原理图。

(PS:框框里的红色部分可以不用画,多余的,自己测speaker的工作原理时用到了没有删。)

按键控制这块再说一下,此文章中使用的是两个的按键控制温度加减。

按住key0,key1-->最高温度加,key2-->最高温度减。

松开key0(即默认),key1-->最低温度加,key2-->最低温度减。

从上到下依次key0、key1、key2。

至于怎么按住第一个键,单击按键右侧按钮。(如图啊)


总结

以上便是基于51单片机设计的温度报警系统。细致的讲解在后面更新。

 

有关基于51单片机的温度报警系统的更多相关文章

  1. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  2. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  6. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  7. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  8. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  9. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  10. ruby-on-rails - 如何构建复杂的 Rails 系统 - 2

    关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭8年前。Improvethisquestion我们有以下(以及更多)系统,我们将数据从一个应用推送/拉取到另一个:托管CRM(InsideSales.com)Asterisk电话系统(内部)横幅广告系统(openx,我们托管)潜在客户生成系统(自行开发)电子商务商店(spree,我们托管)工作板(本土)一些工作网站抓取+入站工作提要电子邮件传送系统(如Mailchimp,自主开发)事件管理系统(如eventbrite,自主开发)仪表板系统(大量图表和

随机推荐