#include <reg52.h> //包含頭文件,一般情況不需要改動,頭文件包含特殊功能寄存器的定義
#include "intrins.h"
#define u8 unsigned char
#define u16 unsigned int
#define uchar unsigned char
#define uint unsigned int
uchar yushe_wendu=50; //溫度預設值
uchar yushe_yanwu=45; //煙霧預設值
uint wendu; //溫度值全局變量
uchar yanwu; //用于讀取ADC數據
//運行模式
uchar Mode=0; //=1是設置溫度閥值 =2是設置煙霧閥值 =0是正常監控模式
//管腳聲明
sbit Led_Reg =P2^2; //紅燈
sbit Led_Yellow =P2^4; //黃燈
sbit Buzzer =P2^0; //蜂鳴器
sbit Fan =P3^3; //
/********************************************************************
* 名稱 : delay_1ms()
* 功能 : 延時1ms函數
* 輸入 : q
* 輸出 : 無
***********************************************************************/
void delay_ms(uint q)
{
uint i,j;
for(i=0;i<q;i++)
for(j=0;j<110;j++);
}
/***********************************************************************************************************
LCD1602相關函數
***********************************************************************************************************/
//LCD管腳聲明 (RW引腳實物直接接地,因為本設計只用到液晶的寫操作,RW引腳一直是低電平)
sbit LCDRS = P2^7;
sbit LCDEN = P2^6;
sbit D0 = P0^0;
sbit D1 = P0^1;
sbit D2 = P0^2;
sbit D3 = P0^3;
sbit D4 = P0^4;
sbit D5 = P0^5;
sbit D6 = P0^6;
sbit D7 = P0^7;
//LCD延時
void LCDdelay(uint z) //該延時大約100us(不精確,液晶操作的延時不要求很精確)
{
uint x,y;
for(x=z;x>0;x--)
for(y=10;y>0;y--);
}
void LCD_WriteData(u8 dat)
{
if(dat&0x01)D0=1;else D0=0;
if(dat&0x02)D1=1;else D1=0;
if(dat&0x04)D2=1;else D2=0;
if(dat&0x08)D3=1;else D3=0;
if(dat&0x10)D4=1;else D4=0;
if(dat&0x20)D5=1;else D5=0;
if(dat&0x40)D6=1;else D6=0;
if(dat&0x80)D7=1;else D7=0;
}
//寫命令
void write_com(uchar com)
{
LCDRS=0;
LCD_WriteData(com);
// DAT=com;
LCDdelay(5);
LCDEN=1;
LCDdelay(5);
LCDEN=0;
}
//寫數據
void write_data(uchar date)
{
LCDRS=1;
LCD_WriteData(date);
// DAT=date;
LCDdelay(5);
LCDEN=1;
LCDdelay(5);
LCDEN=0;
}
/*------------------------------------------------
選擇寫入位置
------------------------------------------------*/
void SelectPosition(unsigned char x,unsigned char y)
{
if (x == 0)
{
write_com(0x80 + y); //表示第一行
}
else
{
write_com(0xC0 + y); //表示第二行
}
}
/*------------------------------------------------
寫入字符串函數
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
SelectPosition(x,y) ;
while (*s)
{
write_data( *s);
s ++;
}
}
//========================================================================
// 函數: void LCD_Write_Char(u8 x,u8 y,u16 s,u8 l)
// 應用: LCD_Write_Char(0,1,366,4) ;
// 描述: 在第0行第一個字節位置顯示366的后4位,顯示結果為 0366
// 參數: x:行,y:列,s:要顯示的字,l:顯示的位數
// 返回: none.
// 版本: VER1.0
// 日期: 2013-4-1
// 備注: 最大顯示65535
//========================================================================
void LCD_Write_Char(u8 x,u8 y,u16 s,u8 l)
{
SelectPosition(x,y) ;
if(l>=5)
write_data(0x30+s/10000%10); //萬位
if(l>=4)
write_data(0x30+s/1000%10); //千位
if(l>=3)
write_data(0x30+s/100%10); //百位
if(l>=2)
write_data(0x30+s/10%10); //十位
if(l>=1)
write_data(0x30+s%10); //個位
}
/*1602指令簡介
write_com(0x38);//屏幕初始化
write_com(0x0c);//打開顯示 無光標 無光標閃爍
write_com(0x0d);//打開顯示 陰影閃爍
write_com(0x0d);//打開顯示 陰影閃爍
*/
//1602初始化
void Init1602()
{
uchar i=0;
write_com(0x38);//屏幕初始化
write_com(0x0c);//打開顯示 無光標 無光標閃爍
write_com(0x06);//當讀或寫一個字符是指針后一一位
write_com(0x01);//清屏
}
void Display_1602(yushe_wendu,yushe_yanwu,c,temp)
{
//顯示預設溫度
LCD_Write_Char(0,6,yushe_wendu,2) ;
//顯示預設煙霧
LCD_Write_Char(0,13,yushe_yanwu,3) ;
//時時溫度
LCD_Write_Char(1,6,c/10,2) ;
write_data('.');
LCD_Write_Char(1,9,c%10,1) ;
//時時煙霧
LCD_Write_Char(1,13,temp,3) ;
}
/*PCF8591*/
typedef unsigned char uint8;
typedef unsigned int uint16;
#define SLAVEADDR 0x90 //定義器件地址
#define nops() do{_nop_();_nop_();_nop_();_nop_();_nop_();} while(0) //定義空指令
sbit SCL = P2^1; //I2C 時鐘
sbit SDA = P2^0; //I2C 數據
void delay(uint16 n)
{
while (n--);
}
/**
* 函數: i2c_start()
* 功能: 啟動i2c 起始信號
*/
void i2c_start()
{
SCL = 1;
nops();
SDA = 1;
nops();
SDA = 0;
nops();
SCL = 0;
}
/**
* 函數: i2c_stop()
* 功能: 停止i2c
*/
void i2c_stop()
{
SCL = 0;
nops();
SDA = 0;
nops();
SCL = 1;
nops();
SDA = 1;
nops();
}
/**
* 函數: i2c_ACK(bit ck)
* 功能: ck為1時發送應答信號ACK,
* ck為0時不發送ACK
*/
void i2c_ACK(bit ck)
{
if (ck)
SDA = 0;
else
SDA = 1;
nops();
SCL = 1;
nops();
SCL = 0;
nops();
SDA = 1;
nops();
}
/**
* 函數: i2c_waitACK()
* 功能: 返回為0時收到ACK
* 返回為1時沒收到ACK
*/
bit i2c_waitACK()
{
SDA = 1;
nops();
SCL = 1;
nops();
if (SDA)
{
SCL = 0;
i2c_stop();
return 1;
}
else
{
SCL = 0;
return 0;
}
}
/**
* 函數: i2c_sendbyte(uint8 bt)
* 功能: 將輸入的一字節數據bt發送
*/
void i2c_sendbyte(uint8 bt)
{
uint8 i;
for(i=0; i<8; i++)
{
if (bt & 0x80)
SDA = 1;
else
SDA = 0;
nops();
SCL = 1;
bt <<= 1;
nops();
SCL = 0;
}
}
/**
* 函數: i2c_recbyte( )
* 功能: 從總線上接收1字節數據
*/
uint8 i2c_recbyte()
{
uint8 dee, i;
for (i=0; i<8; i++)
{
SCL = 1;
nops();
dee <<= 1;
if (SDA)
dee = dee | 0x01;
SCL = 0;
nops();
}
return dee;
}
/**
* 函數: i2c_readbyte
* 輸入: addr
* 功能: 讀出一字節數據
* 返回值: 0->成功 1->失敗
*/
bit i2c_readbyte(uint8 com, uint8 *dat)
{
i2c_start();
i2c_sendbyte(SLAVEADDR); //地址
if (i2c_waitACK())
return 1;
i2c_sendbyte(com); //控制字節
if (i2c_waitACK())
return 1;
i2c_start();
i2c_sendbyte(SLAVEADDR+1); //地址
if (i2c_waitACK())
return 1;
*dat = i2c_recbyte(); //讀數據
i2c_ACK(0); //因為只讀一字節數據,不發送ACK信號
i2c_stop();
return 0;
}
/**
* UART初始化
* 波特率:9600
*/
void uart_init(void)
{
ET1=0;
TMOD = 0x21; // 定時器1工作在方式2(自動重裝)
SCON = 0x50; // 10位uart,允許串行接受
TH1 = 0xFD;
TL1 = 0xFD;
TR1 = 1;
}
/**
* UART 發送一字節
*/
void UART_Send_Byte(uint8 dat)
{
SBUF = dat;
while (TI == 0);
TI = 0;
}
main()
{
uint8 ans;
uart_init();
while(1)
{
i2c_readbyte(0x43, &ans);
UART_Send_Byte(ans);
delay(50000);
}
}
/***********************************************************************************************************
DS18B20相關函數
***********************************************************************************************************/
sbit DQ = P1^0; //ds18b20的數據引腳
/*****延時子程序:該延時主要用于ds18b20延時*****/
void Delay_DS18B20(int num)
{
while(num--) ;
}
/*****初始化DS18B20*****/
void Init_DS18B20(void)
{
unsigned char x=0;
DQ = 1; //DQ復位
Delay_DS18B20(8); //稍做延時
DQ = 0; //單片機將DQ拉低
Delay_DS18B20(80); //精確延時,大于480us
DQ = 1; //拉高總線
Delay_DS18B20(14);
x = DQ; //稍做延時后,如果x=0則初始化成功,x=1則初始化失敗
Delay_DS18B20(20);
}
/*****讀一個字節*****/
unsigned char ReadOneChar(void)
{
unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; // 給脈沖信號
dat>>=1;
DQ = 1; // 給脈沖信號
if(DQ)
dat|=0x80;
Delay_DS18B20(4);
}
return(dat);
}
/*****寫一個字節*****/
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
Delay_DS18B20(5);
DQ = 1;
dat>>=1;
}
}
/*****讀取溫度*****/
unsigned int ReadTemperature(void)
{
unsigned char a=0;
unsigned char b=0;
unsigned int t=0;
float tt=0;
Init_DS18B20();
WriteOneChar(0xCC); //跳過讀序號列號的操作
WriteOneChar(0x44); //啟動溫度轉換
Init_DS18B20();
WriteOneChar(0xCC); //跳過讀序號列號的操作
WriteOneChar(0xBE); //讀取溫度寄存器
a=ReadOneChar(); //讀低8位
b=ReadOneChar(); //讀高8位
t=b;
t<<=8;
t=t|a;
tt=t*0.0625;
t= tt*10+0.5; //放大10倍輸出并四舍五入
return(t);
}
//=====================================================================================
//=====================================================================================
//=====================================================================================
/*****校準溫度*****/
u16 check_wendu(void)
{
u16 c;
c=ReadTemperature()-5; //獲取溫度值并減去DS18B20的溫漂誤差
if(c<1) c=0;
if(c>=999) c=999;
return c;
}
/***********************************************************************************************************
按鍵檢測相關函數
***********************************************************************************************************/
//按鍵
sbit Key1=P1^6; //設置鍵
sbit Key2=P1^7; //加按鍵
sbit Key3=P3^2; //減按鍵
#define KEY_SET 1 //設置
#define KEY_ADD 2 //加
#define KEY_MINUS 3 //減
//========================================================================
// 函數: u8 Key_Scan()
// 應用: temp=u8 Key_Scan();
// 描述: 按鍵掃描并返回按下的鍵值
// 參數: NONE
// 返回: 按下的鍵值
// 版本: VER1.0
// 日期: 2015-05-29
// 備注: 該函數帶松手檢測,按下鍵返回一次鍵值后返回0,直至第二次按鍵按下
//========================================================================
u8 Key_Scan()
{
static u8 key_up=1;//按鍵按松開標志
if(key_up&&(Key1==0||Key2==0||Key3==0))
{
delay_ms(10);//去抖動
key_up=0;
if(Key1==0) return 1;
else if(Key2==0)return 2;
else if(Key3==0)return 3;
}
else if(Key1==1&&Key2==1&&Key3==1)
key_up=1;
return 0;// 無按鍵按下
}
void main (void)
{
u8 key;
wendu=check_wendu(); //初始化時調用溫度讀取函數 防止開機85°C
Init1602(); //調用初始化顯示函數
LCD_Write_String(0,0,"SET T:00 E:000"); //開機界面
LCD_Write_String(1,0,"NOW T:00.0 E:000");
delay_ms(1000);
wendu=check_wendu(); //初始化時調用溫度讀取函數 防止開機85°C
while (1) //主循環
{
key=Key_Scan(); //按鍵掃描
yanwu=Adc0832(0); //讀取煙霧值
wendu=check_wendu(); //讀取溫度值
if(key==KEY_SET)
{
Mode++;
}
switch(Mode) //判斷模式的值
{
case 0: //監控模式
{
Display_1602(yushe_wendu,yushe_yanwu,wendu,yanwu); //顯示預設溫度,預設煙霧,溫度值,煙霧值
if(yanwu>=yushe_yanwu) //煙霧值大于等于預設值時
{
Led_Reg=0; //煙霧指示燈亮
Fan=0;
Buzzer=0; //蜂鳴器報警
}
else //煙霧值小于預設值時
{
Led_Reg=1; //關掉報警燈
Fan=1;
}
if(wendu>=(yushe_wendu*10)) //溫度大于等于預設溫度值時(為什么是大于預設值*10:因為我們要顯示的溫度是有小數點后一位,是一個3位數,25.9°C時實際讀的數是259,所以判斷預設值時將預設值*10)
{
Buzzer=0; //打開蜂鳴器報警
Led_Yellow=0; //打開溫度報警燈
}
else //溫度值小于預設值時
{
Led_Yellow=1; //關閉報警燈
}
if((yanwu<yushe_yanwu)&&(wendu<(yushe_wendu*10))) //當煙霧小于預設值并且溫度也小于預設值時 (&&:邏輯與,左右兩邊的表達式都成立(都為真,也就是1)時,該if語句才成立)
{
Buzzer=1; //停止報警
}
break;
}
case 1://預設溫度模式
{
SelectPosition(0,5) ; //指定位置
write_com(0x0d); //陰影閃爍
if(key==KEY_ADD) //加鍵按下
{
yushe_wendu++; //預設溫度值(閥值)加1
if(yushe_wendu>=99) //當閥值加到大于等于99時
yushe_wendu=99; //閥值固定為99
LCD_Write_Char(0,6,yushe_wendu,2) ;//顯示預設溫度
}
if(key==KEY_MINUS) //減鍵按下
{
if(yushe_wendu<=1) //當溫度上限值減小到1時
yushe_wendu=1; //固定為1
yushe_wendu--; //預設溫度值減一,最小為0
LCD_Write_Char(0,6,yushe_wendu,2) ;//顯示預設溫度
}
break; //執行后跳出switch
}
case 2: //預設煙霧模式
{
SelectPosition(0,12) ; //指定位置
write_com(0x0d); //打開顯示 無光標 光標閃爍
if(key==KEY_ADD) //加鍵按下
{
if(yushe_yanwu>=255) //當閥值加到大于等于255時
yushe_yanwu=254; //閥值固定為254
yushe_yanwu++; //預設煙霧值(閥值)加1,最大為255
LCD_Write_Char(0,13,yushe_yanwu,3) ;//顯示預設煙霧
}
if(key==KEY_MINUS) //減鍵按下
{
if(yushe_yanwu<=1) //當煙霧上限值減小到1時
yushe_yanwu=1; //固定為1
yushe_yanwu--; //預設溫度值減一,最小為0
LCD_Write_Char(0,13,yushe_yanwu,3) ;//顯示預設煙霧
}
break;
}
default :
{
write_com(0x38);//屏幕初始化
write_com(0x0c);//打開顯示 無光標 無光標閃爍
Mode=0; //恢復正常模式
break;
}
}
}
}
|