本設計采用ADC0832對火焰傳感器和煙霧傳感器獲取到的數據進行轉采集,并通過單片機將采集到的數據和設定值進行比較,從而決定是否報警。
各個報警值都可通過按鍵進行設置,各數據都是用LCD1602液晶進行顯示的,簡單易懂。
火災報警器的原理圖如下:
部分程序如下:
#include <REGX51.H> //調用51單片機頭文件
#include <Define.c> //調用定義變量的C文件
#include <DS18B20.c> //調用DS18B20驅動C文件
#include <ADC.c> //調用ADCO832驅動C文件
#include <LCD1602.c> //調用LCD1602顯示屏的驅動C文件
#include <display.c> //調用各種模式的顯示C文件
#include <key.c> //調用按鍵C文件,其中包含關于各按鍵的函數
/***************定時器0中斷初始化函數****************/
void Timer0_Init(void) //使用定時器中斷0
{
TMOD = 0x01;//使用模式0
TH0 = (65536 - 2000) / 256;//定時器裝入初始值2ms
TL0 = (65536 - 2000) % 256;
ET0 = 1; //定時器0初始化
TR0 = 1;
EA = 1; //總中斷
}
/***************定時器1中斷初始化函數****************/
void Timer1_Init() //使用定時器中斷1
{
TMOD |= 0x10; //使用模式1
TH1 = (65536-2000)/256; //定時器裝入初始值2ms
TL1 = (65536-2000)%256;
ET1 = 1;//定時器1初始化
TR1 = 1;
EA = 1; //總中斷
}
/***************定時器0中斷服務函數****************/
void Timer0_isr(void)interrupt 1
{
TH0 = (65536 - 2000) / 256; //定時器裝入初始值2ms
TL0 = (65536 - 2000) % 256;
cp++;
if(cp >= 100)
{
temp_num = Read_Temperature();//獲取實時溫度
cp = 0;
}
key(); //調用key.c中的按鍵函數
}
/***************定時器1中斷服務函數****************/
void time1_isr(void)interrupt 3
{
TH1=(65536-2000)/256; //定時器裝入初始值2ms
TL1=(65536-2000)%256;
cp1++;
if(cp1 >= 100)
{
AD_smok = (GetAD0832(0)*100)/255; //獲取煙霧濃度
//*由于傳感器的AD輸出的特殊原因,這里用255減去輸出值以遍顯示的時候可以從0%到100%增加*//
AD_fire = ((255 - GetAD0832(1))*100)/255; //獲取火光亮度,
cp1 = 0;
}
}
void main(void)
{
Init_LCD1602(); //LCD1602初始化
Timer0_Init(); //定時器0初始化
Timer1_Init(); //定時器1初始化
LED1 = 0; //關閉LED1
LED2 = 0; //關閉LED2
LED3 = 0; //關閉LED3
while(1)
{
exchange(); //將各變量轉換為十進制進行顯示
if(dis_mode == 0)display0(); //正常顯示
if(dis_mode == 1)display1(); //進入修改界面
if(temp_num >= Temp_alam)LED1 = 1; //當溫度超過警報值時,打開紅色的LED
else LED1 = 0;
if(AD_smok >= S_alam)LED2 = 1; //當煙霧濃度超過警報值時,打開黃色的LED
else LED2 = 0;
if(AD_fire >= F_alam)LED3 = 1; //當火光亮度超過警報值時,打開藍色的LED
else LED3 = 0;
if((AD_fire >= F_alam)&&(AD_smok >= S_alam))Beep = 0; //當檢測到的三個值中有兩個超過警報值,則認為有火災發生,啟動聲音報警
if((AD_fire >= F_alam)&&(temp_num >= Temp_alam))Beep = 0;
if((temp_num >= Temp_alam)&&(AD_smok >= S_alam))Beep = 0;
if((AD_fire < F_alam)&&(AD_smok < S_alam)&&(temp_num < Temp_alam))Beep = 1; //當三個值都降到警報值以下時,聲音報警關閉
}
}
/********初始化函數********/
void Init_DS18B20(void) //基本操作函數1
{
unsigned char x = 255;
DQ = 1; //先讓DQ置1
DQ = 0; //單片機將DQ拉低
delay(80); //延時480-960us
DQ = 1; //釋放總線
while(DQ && x--); //等待返回的低電平響應 如果沒有響應,則做適量延時自動往下執行
delay(20);
}
/********從DS18B20中讀一個字節********/
unsigned char Read_OneChar(void) //DS18B20開始發送RAM數據依次從0~8字節,且低位在前 //基本操作函數2
{
unsigned char i = 0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; //發送啟動信號
dat >>= 1;
DQ = 1; //釋放總線
if(DQ) //判斷總線是否為高電平
dat |= 0x80; //如果是高電平,則把dat的最高位置1,如果不是,默認置0
delay(10);
}
return(dat);
}
/********向18B20中寫一個字節********/
void Write_OneChar(unsigned char dat) //基本操作函數3
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat & 0x01; //先寫入數據的最低位 //取dat的最低位
delay(10);
DQ = 1;
dat >>= 1; //再依次寫入數據的低二位,低三位
}
delay(8);
}
/********讀取溫度********/
unsigned char a,b,c;
unsigned char Read_Temperature(void) //應用操作函數1
{
unsigned char t;
Init_DS18B20();
Write_OneChar(0xcc); // 1100 1100 主機寫CCH跳過ROM操作 // 跳過讀序號列號的操作
Write_OneChar(0x44); // 0100 0100 // 啟動溫度轉換
Init_DS18B20();
Write_OneChar(0xcc); // 1100 1100 //跳過讀序號列號的操作
Write_OneChar(0xbe); // 1011 1110 主機寫BBH讀出RAM數據 //讀取溫度寄存器等(共可讀9個寄存器) 前兩個就是溫度
a = Read_OneChar(); //讀取溫度值低位
b = Read_OneChar(); //讀取溫度值高位
c = 0x0f & a; // 0000 1111 & //得到小數部分
a = a >> 4; //低位右移4位
b = b << 4; //高位左移4位
t = a | b; //得到8位溫度的整數部分,最高1位為符號位
return(t);
}
#define LCD1602_DB P0 //LCD1602數據總線
/*=================================================
*函數名稱:Read_Busy
*函數功能:判斷1602液晶忙,并等待
=================================================*/
void Read_Busy()
{
uchar busy;
LCD1602_DB = 0xff;//復位數據總線
LCD1602_RS = 0; //拉低RS
LCD1602_RW = 1; //拉高RW讀
do
{
LCD1602_EN = 1;//使能EN
busy = LCD1602_DB;//讀回數據
LCD1602_EN = 0; //拉低使能以便于下一次產生上升沿
}while(busy & 0x80); //判斷狀態字BIT7位是否為1,為1則表示忙,程序等待
}
/*=================================================
*函數名稱:LCD1602_Write_Cmd
*函數功能:寫LCD1602命令
*調用:Read_Busy();
*輸入:cmd:要寫的命令
=================================================*/
void LCD1602_Write_Cmd(uchar cmd)
{
Read_Busy(); //判斷忙,忙則等待
LCD1602_RS = 0;
LCD1602_RW = 0; //拉低RS、RW操作時序情況1602課件下中文使用說明基本操作時序章節
LCD1602_DB = cmd;//寫入命令
LCD1602_EN = 1; //拉高使能端 數據被傳輸到LCD1602內
LCD1602_EN = 0; //拉低使能以便于下一次產生上升沿
}
/*=================================================
*函數名稱:LCD1602_Write_Dat
*函數功能:寫LCD1602數據
*調用:Read_Busy();
*輸入:dat:需要寫入的數據
=================================================*/
void LCD1602_Write_Dat(uchar dat)
{
Read_Busy();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_EN = 1;
LCD1602_EN = 0;
}
/*=================================================
*函數名稱:LCD1602_Dis_OneChar
*函數功能:在指定位置顯示一個字符
*調用:LCD1602_Write_Cmd(); LCD1602_Write_Dat();
*輸入:x:要顯示的橫坐標取值0-40,y:要顯示的行坐標取值0-1(0為第一行,1為第二行)
dat:需要顯示的數據以ASCLL形式顯示
=================================================*/
void LCD1602_Dis_OneChar(uchar x, uchar y,uchar dat)
{
if(y) x |= 0x40;
x |= 0x80;
LCD1602_Write_Cmd(x);
LCD1602_Write_Dat(dat + 0x30);
}
/*=================================================
*函數名稱:LCD1602_Dis_Str
*函數功能:在指定位置顯示字符串
*調用:LCD1602_Write_Cmd(); LCD1602_Write_Dat();
*輸入:x:要顯示的橫坐標取值0-40,y:要顯示的行坐標取值0-1(0為第一行,1為第二行)
*str:需要顯示的字符串
=================================================*/
void LCD1602_Dis_Str(uchar x, uchar y, uchar *str)
{
if(y) x |= 0x40;
x |= 0x80;
LCD1602_Write_Cmd(x);
while(*str != '\0')
{
LCD1602_Write_Dat(*str++);
}
}
/*=================================================
*函數名稱:Init_LCD1602
*函數功能:1602初始化
*調用: LCD1602_Write_Cmd();
=================================================*/
void Init_LCD1602()
{
LCD1602_Write_Cmd(0x38); // 設置16*2顯示,5*7點陣,8位數據接口
LCD1602_Write_Cmd(0x0c); //開顯示
LCD1602_Write_Cmd(0x06); //讀寫一字節后地址指針加1
LCD1602_Write_Cmd(0x01); //清除顯示
}
/***********按鍵函數*************/
void key(void)
{
if(key1 == 0)
{
delay(200); //按鍵消抖
if(key1 == 0)
{
if(dis_mode == 0)
{
dis_mode = 1;
Temp_set = Temp_alam; //設置溫度報警值
Fire_set = F_alam; //設置火光報警值
Smok_set = S_alam; //設置煙霧報警值
}
else if(dis_mode == 1)
{
set_mode++; //設置標志位加
if(set_mode >= 4)set_mode = 0;
}
}
while(key1 == 0);
}
if(key2 == 0)
{
delay(200);
if(key2 == 0)
{
if((dis_mode == 1)&&(set_mode == 1)) //警報值加
Temp_set++;
if((dis_mode == 1)&&(set_mode == 2))
Fire_set++;
if((dis_mode == 1)&&(set_mode == 3))
Smok_set++;
}
while(key2 == 0);
}
if(key3 == 0)
{
delay(200); //按鍵消抖
if(key3 == 0)
{
if((dis_mode == 1)&&(set_mode == 1)) //警報值減
Temp_set--;
if((dis_mode == 1)&&(set_mode == 2))
Fire_set--;
if((dis_mode == 1)&&(set_mode == 3))
Smok_set--;
}
while(key3 == 0);
}
if(key4 == 0)
{
delay(200); //按鍵消抖
if(key4 == 0)
{
dis_mode = 0; //將顯示標志位置0
set_mode = 0; //將設置標志位置0
Temp_alam = Temp_set; //設置的警報值生效
F_alam = Fire_set;
S_alam = Smok_set;
}
while(key4 == 0);
}
if(Temp_set >= 255)Temp_set = 255; //上限值
if(Fire_set >= 100)Fire_set = 100; //上限值
if(Smok_set >= 100)Smok_set = 100; //上限值
if(Temp_set <= 0)Temp_set = 0; //下限值
if(Fire_set <= 0)Fire_set = 0; //下限值
if(Smok_set <= 0)Smok_set = 0; //下限值
}
uchar GetAD0832(uint Channel)
{
uchar i=0,Data1 =0,Data2 = 0;
AD0832_CLK = 0 ; //時鐘置低平
AD0832_DI = 1 ; //開始信號為高電平
AD0832_CS = 0 ; //片選信號置低,啟動AD轉換芯片
AD0832_CLK = 1 ; //輸入開始信號(構成一個正脈沖),時鐘上升沿,輸入開始信號
AD0832_CLK = 0 ; //時鐘下降沿
//模擬信號輸入模式選擇(1:單模信號,0:雙模差分信號)
AD0832_DI = 1 ;
AD0832_CLK = 1 ; //時鐘上升沿,輸入開始信號
AD0832_CLK = 0 ; //時鐘下降沿
//模擬信號輸入通道選擇(1:通道CH1,0:通道CH0)
AD0832_DI = Channel ; //選擇通道0
AD0832_CLK = 1 ; //時鐘上升沿,輸入開始信號
AD0832_CLK = 0 ; //時鐘下降沿
AD0832_DI = 1 ; //數據線置高,準備接收數據
for(i=0;i<8;i++) //從高位移入數據
{
AD0832_CLK = 1 ;
AD0832_CLK = 0 ; //時鐘下降沿,AD0832輸出數據,高位(MSB)先
Data1 = Data1<<1;
if(AD0832_DI == 1)
{Data1 = Data1 | 0x01;} //數據左移位,補0 ,如果數據為“1”,移入1, 如果數據為“0”,移入0,
}
for(i=0;i<8;i++) //從低位移入數據
{
Data2 = Data2>>1;
if(AD0832_DI == 1)
{ Data2 = Data2 | 0x80;} //數據右移位,補0,如果數據為“1”,移入1如果數據為“0”,移入0,
AD0832_CLK = 1 ;
AD0832_CLK = 0 ; //時鐘下降沿,AD0832輸出數據,高位(MSB)先
}
//數據線置高,釋放總線,完成一次轉換
AD0832_CLK = 1 ;
AD0832_DI = 1 ;
AD0832_CS = 1 ;
return (Data1==Data2)?Data1:0; // 如果相等輸出data1 or 0
}
本設計包含程序、仿真已及PCB設計,拿到之后可以直接使用。
|