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