基于C51的温控报警系统

基于C51的温控报警系统

1 环境
开发板:HC6800-ES
MCU:STC89C52
温度传感器:DS18B20
蜂鸣器:ULN2003

2 设备驱动原理
2.1 温度传感器DS18B20
在这里插入图片描述
电路图如下:
在这里插入图片描述
DS18B20的功能很强大,包含64位ROM,9字节RAM(暂存器),有下面两种驱动模式。
a 多片驱动
意思就是多个DS18B20并联在一起,通过一根数据总线和所有的传感器通信,传感器的光刻ROM里包含64位的数据,可以当成传感器的物理地址,MCU每次发送数据都要先和对应的传感器进行握手,也就是发送传感器的物理地址。
b 单片驱动
只需要对一个传感器进行控制,这时的流程就可以忽略握手,直接进行控制命令的发送。

传感器读到的温度数据被保存到9位RAM的前两个字节,低字节在前,高字节在后,低位在前,高位在后。每一位的数据含义可以用下图展示。
在这里插入图片描述
举几个例子:
在这里插入图片描述
对传感器进行控制的命令如下:

在这里插入图片描述
在这里插入图片描述
我这次使用单片驱动模式,主要流程如下:

在这里插入图片描述
2.2 蜂鸣器

在这里插入图片描述
蜂鸣器包含两种,一种无源蜂鸣器,一种有源蜂鸣器。
无源蜂鸣器:无震荡源,需要外部施加震荡电平才能正常工作。
有源蜂鸣器:有震荡源,外部只需要施加控制电平就可以改变音调和音高。

本次实验用到的ULN为无源蜂鸣器,但是因为报警只需要发出简单的提示音,所以只需要往ULN2003的数据端发送一定频率的高低电平即可。

电路如下

在这里插入图片描述

2.3 ULN2003高耐压、大电流复合晶体管阵列
驱动蜂鸣器、电机等设备需要较大的电流,直接使用C51的端口无法有效驱动。所以加上了一个ULN2003器件,ULN2003本质上是级联了多个三极管进行放大的放大电路,包含7个输入和7个输出,能对7个输入进行放大。

电路图如下所示

在这里插入图片描述

2.4 数码管、74HC573
数码管用来显示温度,也可以直接将C51输出端口接数码管输入端,这里用到74HC573锁存器进行驱动,他有两个主要作用。
1、 锁存作用,也就是缓存作用,当LE为低电平的时候,输出端口不随输入端口变化,输入端数据全部被缓存,可以有效避免输入变化导致数码管闪烁。
2、 驱动作用,74HC573可以输出更大的电流,也就可以驱动更多的设备。
在这里插入图片描述

74LS138也就是38译码器,将三位数据转码成8位数据,数据的含义不同,3位输入数据可以是1和0的任意组合,8位输出只有一位能为0,其他都为1,这里用于选中8位数码管中的一位,配合74HC573进行一位数据的准确显示。.
在这里插入图片描述

3 代码

#include"reg52.h"
#include"intrins.h"

//#define ENABLE_AUDIO

typedef unsigned char U8;
typedef unsigned int U16;

sbit bzClock=P1^5;


typedef unsigned char u8;
typedef  char c8;
typedef unsigned int u16;
typedef  int i16;

u8 num_code[18]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0};//显示0~F,-1,无的值

sbit L_A=P2^2;
sbit L_B=P2^3;
sbit L_C=P2^4;
sbit P_Tempe=P3^7;

#ifdef DEBUG
sbit Point1=P2^0;
sbit Point2=P2^1;
sbit Point3=P2^2;
#endif

u8 hdata_,ldata_;
u8 isDisplay=1;
u8 isDangerous=0;
u16 timeCount=0;
//10us
void delay(u16 size)
{
	while(size--);
}

void delay10us(void)   //误差 0us
{
    unsigned char a,b;
    for(b=1;b>0;b--)
        for(a=2;a>0;a--);
}


void delay15us(void)   //误差 0us
{
    unsigned char a;
    for(a=6;a>0;a--);
}

void Delay1ms(i16 y)
{
	i16 x;
	for( ; y>0; y--)
	{
		for(x=110; x>0; x--);
	}
}


void delay45us(void)   //误差 0us
{
    unsigned char a;
    for(a=21;a>0;a--);
}


void select(c8 index)
{
    if(index<0 || index> 8)
	{
	    return;
	}
	L_C=(index-1)/4;
	L_B=(index-1)%4/2;
	L_A=(index-1)%2;
}

void display(c8 num,u8 isPot)
{
    if(num<0 || num>16)
	{
	    P0=num_code[17];
	    return;
	}
	if(!isPot)
	{
		P0=num_code[num];
	}
	else{
		P0=num_code[num] | 0x80;
	}
	delay(100);
}

//最多小数点后4位,最多3位整数,十进制输出,有符号,暂时省略范围检查
void display_all(u8 isPositive,u16 int_part,u16 decimal_part)
{
	c8 num_display[8]={0};
	u8 index_pot=4;
	c8 i=0;
#ifdef DEBUG
	while(1);
	return;
#endif
#ifdef ENABLE_AUDIO
	
	if(int_part>=40 && isPositive)
	{
		isDangerous=1;
	}else
	{
		isDangerous=0;
	}
#endif
	if(decimal_part>=10000)
	{
		return;//error
	}
	
	if(int_part>=10000)
	{
		return;//error
	}
	if(isPositive)
	{
		num_display[7]=-1;
	}else{
		num_display[7]=16;
	}
    
	num_display[6]=int_part/100;
	num_display[5]=int_part%100/10;
	num_display[4]=int_part%10;	
    
	if(decimal_part>=1000 && decimal_part<10000)
	{
	    num_display[3]=decimal_part/1000;
		num_display[2]=decimal_part%1000/100;
		num_display[1]=decimal_part%1000%100/10;
		num_display[0]=decimal_part%10;
	}
	else if(decimal_part>=100 && decimal_part<1000)
	{
		num_display[3]=decimal_part%1000/100;
		num_display[2]=decimal_part%1000%100/10;
		num_display[1]=decimal_part%10;
		num_display[0]=-1;
	}
	else if(decimal_part>=10 && decimal_part<100)
	{
	    num_display[3]=decimal_part%1000%100/10;
		num_display[2]=decimal_part%10;
		num_display[1]=-1;
		num_display[0]=-1;
	}
	else if(decimal_part>=0 && decimal_part<10)
	{
		num_display[3]=decimal_part%10;
		num_display[2]=-1;
		num_display[1]=-1;
		num_display[0]=-1;
	}	    

	while(isDisplay)
	{	
	   for(i=7;i>=0;i--)
		{  
		    if(i>7 || i<0)
			{
			    i=7;
			}
		    display(-1,0);
		    select(i+1);
		    if(i==index_pot)
			{
			    display(num_display[i],1);
			}
			else
			{
			    display(num_display[i],0);
			}
		}		
		//关键问题:为什么后面的循环不行,错误原因在于i的类型为u8,改为c8后没问题,所以可能是i--后i还大于0.所以不停循环了。
		//不知道为什么,几十在循环里面做出判断如果i不在区间时强制设为7,也不行
#ifdef ENABLE_AUDIO
		if(isDangerous)
		{
			bzClock=~bzClock;
		} 
#endif
		
	}
	return;
}

u8 getDataByte()
{
    u8 retValue=0;
    c8 i=8;
	u8 ubit;
    while(i--)
    {
        i16 threshold=0;
        P_Tempe=0;
        _nop_;//不能浮动,10um就无法测出有效值
        P_Tempe=1;
        delay10us();//不能浮动,2um就无法测出有效值
		ubit=P_Tempe;
        retValue>>=1;
        retValue=retValue | (ubit<<7);
        delay45us();//时间不需要准确,可以浮动(测试100us也行),应该是DS18B20会判断电平升降再去返回值     
    }
	return retValue;
}

u8 initTempe()
{
    i16 threshold=0;
	u8 isSucess=0;
    P_Tempe=0;
	delay(72);//480-960
	P_Tempe=1;
	delay(8);
	//detect 0
	threshold=6;
	while(threshold--)
	{
		delay(1);
		if(P_Tempe==0)
		{
#ifdef DEBUG
			Point1=0;
#endif			
		    isSucess=1;
			break;
		}
	}
	delay(48);
	return	isSucess;
#ifdef DEBUG
			Point1=1;
#endif
	return isSucess;
}


void setmode()
{
	//default mode,
}

void sendCommand(u8 command)
{
   u8 i=0;
   for(i=0;i<8;i++)
   {
	   P_Tempe=0;
	   delay15us();//必须准确,如果为20us则无法发出有效值
	   P_Tempe=command&0x1;
	   command>>=1;
	   delay(6);//可以变化,到100us也行,注意和初始化区别,初始化至少480us,这里如果到20us就不行了
	   P_Tempe=1;
   } 
}


void begainCheck()
{
    initTempe();//重要,每次发命令前都要初始化
	Delay1ms(1);
	//44H -> 1000100
    sendCommand(0xCC);//重要,每次发命令都要判断是否制定器件发送
	sendCommand(0x44);
}
    
void getData()
{
    initTempe();
	Delay1ms(1);
    sendCommand(0xCC);
	sendCommand(0xBE);
    ldata_=getDataByte();//从低位字节开始读取,读到的是原始数据,需要解析每一位的含义,转换成十进制数据
    hdata_=getDataByte();    
}

void displayTempe()
{
    u8 isPst=0,ldata=0,hdata=0;//局部变量一定要初始化,因为可能有意想不到的后果,我在这里没有初始化,结果第二次得到的值是第一次的两倍。
    u16 intPart=0,decPart=0;
    u16 compose=0,u16hdata=0;
    hdata=hdata_;
    ldata=ldata_;
#ifdef DEBUG
#ifdef DEBUG_PROC
     return;
#endif
	P2=hdata_;
#endif
    if((hdata&0xf8)==0 
	//&& !(ldata&0xf0)
	)//+
    {
#ifdef DEBUG
			//Point2=0;
#endif		
        isPst=1;
        intPart+=ldata >> 4;
        intPart+=hdata << 5 >>1;
        decPart+=(ldata&0x08?5000:0);
        decPart+=(ldata&0x04?2500:0);
        decPart+=(ldata&0x02?1250:0);
        decPart+=(ldata&0x01?625:0);
        display_all(isPst,intPart,decPart);
    }else if((hdata&0xf8)==0xf8 
	//&& (ldata&0xf0)
	)
    {
#ifdef DEBUG
			//Point3=0;
#endif		
        u16hdata=hdata;
        compose=u16hdata>>8+ldata;
        compose=-1;
        compose=~compose;
        isPst=0;
        hdata=(u8)compose>>8;//强制转换
        ldata=(u8)compose;
        
        intPart+=ldata >> 4;
        intPart+=hdata << 5 >>1;
        decPart+=(ldata&0x08?5000:0);
        decPart+=(ldata&0x04?2500:0);
        decPart+=(ldata&0x02?1250:0);
        decPart+=(ldata&0x01?625:0);
        display_all(isPst,intPart,decPart);
    }else
    {
        display_all(1,0,0);       
    }

}

void initTimer()//定时5s
{
	EA=1;
	TR0=1;
	ET0=1;
	TMOD|=0x01;
	TH0=0xFC;
	TL0=0x18;
}

void onInterrupt() interrupt 1
{
	timeCount++;
	TH0=0xFC;
	TL0=0x18;
	if(timeCount>=5000)
	{
		isDisplay=0;//C51单线程,不需要线程同步
		timeCount=0;
	}
}

int main()
{
	initTimer();
    while(1)
	{
		begainCheck();
		Delay1ms(1000);//750ms
    	getData();
		isDisplay=1;
    	displayTempe();
	}
	return 0;
}

4 展示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值