基於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 展示

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章