? ? ? ?系統(tǒng)提供2種工作模式,在顯示模式中:1.顯示溫濕度2.超出溫濕度限定的范圍時(shí)蜂鳴器,LED實(shí)現(xiàn)報(bào)警3.加減溫度時(shí)顯示標(biāo)志;在設(shè)置模式中:分別設(shè)置溫濕度上下限
? ? ? ?硬件:單片機(jī)AT89C52,液晶LCD1602顯示器,溫濕度傳感器DHT11,存儲(chǔ)器AT24C02
? ? ? ?else:I2C總線,Time0定時(shí)器
目錄
LCD1602
?液晶判忙
液晶初始化,寫入指令,數(shù)據(jù)
液晶顯示行,列的位置
液晶數(shù)據(jù)轉(zhuǎn)換成字符串,以及顯示
溫濕度傳感器DHT11
DHT11采集數(shù)據(jù)?編輯
讀取數(shù)字0?編輯
讀取數(shù)字1?編輯
校驗(yàn)
?I2C總線
?存儲(chǔ)器AT24C02
?通過(guò)I2C總線來(lái)設(shè)置AT24C02
Time0定時(shí)器
主函數(shù)
proteus仿真圖
LCD1602
?液晶判忙
? ? ? ? LCD 1602的響應(yīng)速度相對(duì)于單片機(jī)的速度來(lái)說(shuō)是偏慢的。
? ? ? ? 舉個(gè)簡(jiǎn)單的例子,把一桶油通過(guò)漏斗向一個(gè)瓶子里倒,倒油的速度,即流量必須維持在一定范圍之內(nèi),倒得太快油會(huì)從漏斗頂部溢出來(lái),這樣就浪費(fèi)掉了。我們通過(guò)眼睛可以判斷并使油面保持在頂面以下,以漏斗的額定流量來(lái)倒油,這樣效率最高。
? ? ? ? 而對(duì)于單片機(jī)來(lái)說(shuō),1602好比那個(gè)瓶子漏斗,寫入1602中要顯示的數(shù)據(jù)好比油,如果以單片機(jī)的高運(yùn)行速度向1602寫數(shù)據(jù)就很可能造成上面所說(shuō)的溢出,比如連續(xù)寫入abc,結(jié)果只顯示出了a,這是因?yàn)?602的顯示芯片每次都要花時(shí)間來(lái)處理輸入的ascii碼數(shù)據(jù),并把它顯示出來(lái)。而我們卻不容易主動(dòng)地去控制寫入數(shù)據(jù)的速度,所以1602使用忙信號(hào)就有必要了,每次單片機(jī)只有檢測(cè)到忙信號(hào)為0,即不忙時(shí),才向1602發(fā)數(shù)據(jù)。比如要顯示abc,則這樣操作,寫a---判忙---寫b---判忙---寫c---判忙。這樣就不會(huì)出錯(cuò)了。
/*等待液晶準(zhǔn)備*/
void Lcdready()
{
unsigned char sta;
P0 = 0xFF; //P0在使用時(shí)要規(guī)定置1
RS = 0; //數(shù)據(jù)/指令選擇位 1為數(shù)據(jù),0為指令
RW = 1; //讀/寫位 1為讀,0為寫
do
{
EN = 1; //使能位
sta = P0;//讀取狀態(tài)字,即把P0口的數(shù)據(jù)賦值給sta
EN = 0;
}while(sta & 0x80);//當(dāng)sta最高位為0時(shí)則跳出循環(huán);若為1時(shí)則繼續(xù)循環(huán),相當(dāng)于單片機(jī)停止讓液晶先工作
}
液晶初始化,寫入指令,數(shù)據(jù)
/* 初始化 1602 液晶 */
void init1602()
{
WriteCmd(0x38);
WriteCmd(0x0C);
WriteCmd(0x06);
WriteCmd(0x01);
}
/* 向 LCD1602 液晶寫入命令,cmd-待寫入命令值 */
void WriteCmd(unsigned char cmd)
{
Lcdready();
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
/* 向 LCD1602 液晶寫入數(shù)據(jù),dat-待寫入數(shù)據(jù) */
void WriteData(unsigned char dat)
{
Lcdready();
RS = 1;
RW = 0;
P0 = dat;
EN = 1;
EN = 0;
}
液晶顯示行,列的位置
/* 設(shè)置顯示 RAM 起始地址(x,y)-對(duì)應(yīng)屏幕上的字符坐標(biāo) */
void Lcdaddr(unsigned char x,unsigned char y)
{
unsigned char m;
if(y==0)
m = 0x00+x; //第一行字符地址從 0x00 起始
else
m = 0x40+x; //第二行字符地址從 0x40 起始
WriteCmd(m | 0x80);//設(shè)置 RAM 地址
}
液晶數(shù)據(jù)轉(zhuǎn)換成字符串,以及顯示
/* 整型數(shù)轉(zhuǎn)換為字符串,str-字符串指針,dat-待轉(zhuǎn)換數(shù),返回值-字符串長(zhǎng)度 */
unsigned char IntToString(unsigned char *str, int dat)
{
unsigned char i = 0;
unsigned char len = 0;
unsigned char buf[6];
if (dat < 0) //如果為負(fù)數(shù),首先取絕對(duì)值,并在指針上添加負(fù)號(hào)
{
dat = -dat;
*str++ = '-';
len++;
}
do //先轉(zhuǎn)換為低位在前的十進(jìn)制數(shù)組
{
buf[i++] = dat % 10; //取最低位
dat /= 10;
} while (dat > 0);
len += i; //i最后的值就是有效字符的個(gè)數(shù)
while (i-- > 0) //將數(shù)組值轉(zhuǎn)換為ASCII碼反向拷貝到接收指針上
{
*str++ = buf[i] + '0'; //加0其實(shí)是加ASCII碼中的0x30
}
*str = '\0'; //添加字符串結(jié)束符
return len; //返回字符串長(zhǎng)度
}
/* 在液晶上顯示字符串,(x,y)-對(duì)應(yīng)屏幕上的起始坐標(biāo),str-字符串指針 */
void Lcdshow(unsigned char x,unsigned char y,unsigned char *str)
{
Lcdaddr(x,y);
while(*str != '\0') //表示一直循環(huán)到字符串結(jié)尾
{
WriteData(*str++); //表示從高到低依次寫入str數(shù)組的值
}
}
注:ASCII碼中,數(shù)字0地址為0x30,1為0x31,2為0x32......因此,在指針*str=buf[i]中,為了表示數(shù)字0,1,2.......要加上0x30的地址('0')。
溫濕度傳感器DHT11
DHT11采集數(shù)據(jù)
讀取數(shù)字0
讀取數(shù)字1
校驗(yàn)
"8bit濕度整數(shù)數(shù)據(jù)+8bit濕度小數(shù)數(shù)據(jù)+8bi溫度整數(shù)數(shù)據(jù)+8bit溫度小數(shù)數(shù)據(jù)"相加所得結(jié)果的末8位。
#ifndef _DHT11_H_
#define _DHT11_H_
#define uchar unsigned char
#define uint unsigned int
sbit DHT11_DB=P3^4;
uchar Temp_H,Temp_L,Humi_H,Humi_L,Check_data;//溫度整數(shù)位,小數(shù)位;濕度整數(shù)位,小數(shù)位,數(shù)據(jù)校驗(yàn)位
uchar U8FLAG;
void Delay_ms(uint n); //ms延時(shí)函數(shù)
void Delay_us(uchar n); //us延時(shí)函數(shù)
uchar DHT11_receive(void); //DHT11接收數(shù)據(jù)函數(shù)
void DHT11_read(void); //DHT11讀取溫濕度函數(shù)
bit DHT11_Check(void); //DHT11校驗(yàn)函數(shù),返回1:校驗(yàn)成功、返回0:校驗(yàn)失敗
void Delay_ms(uint n)
{
unsigned char j;
while(n--)
{
for(j=0;j<125;j++);
}
}
void Delay_us(uchar n)
{
n=n/2;
while(--n);
}
/接收DHT11傳回來(lái)的數(shù)據(jù)
uchar DHT11_receive(void)
{
uchar i,Data;
for(i=0;i<8;i++)
{
U8FLAG=2; //延遲80ns
while(!DHT11_DB&&U8FLAG++); //當(dāng)DHT11_DB由低電平0拉到高電平1時(shí),跳出延遲
Delay_us(35);
Data<<=1;
if(DHT11_DB) //判斷輸出為1或0
Data|=1;
U8FLAG=2;
while(DHT11_DB&&U8FLAG++); //當(dāng)DHT11_DB由高電平1拉到低電平0時(shí),跳出延遲
}
return Data;
}
/讀取DHT11溫濕度
void DHT11_read(void)
{
DHT11_DB=0;
Delay_ms(18);
DHT11_DB=1;
Delay_us(40);
if(!DHT11_DB) //T !
{
U8FLAG=2;
while(!DHT11_DB&&U8FLAG++);
U8FLAG=2;
while(DHT11_DB&&U8FLAG++);
Humi_H=DHT11_receive(); //濕度整數(shù)
Humi_L=DHT11_receive(); //濕度小數(shù)
Temp_H=DHT11_receive(); //溫度整數(shù)
Temp_L=DHT11_receive(); //溫度小數(shù)
Check_data=DHT11_receive(); //校驗(yàn)
DHT11_DB=1; //最后拉高電平
}
}
/校驗(yàn)
bit DHT11_Check(void)
{
if((Temp_H+Temp_L+Humi_H+Humi_L)==Check_data) //判斷校驗(yàn)和是否正確
return 1;
else
return 0;
}
#endif
?I2C總線
#include <REGX52.H>
#include "I2C.h"
void I2CStart() //I2C開始
{
I2C_SCL=1;
I2C_SDA=1;
I2C_SDA=0;
I2C_SCL=0;
}
void I2CStop() //I2C結(jié)束
{
I2C_SCL=0;
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
bit I2CWrite(unsigned char dat) //I2C寫操作,dat-代寫數(shù)值,ack-返回應(yīng)答值
{
bit ack; //用來(lái)暫存應(yīng)答值
unsigned char mask; //用來(lái)暫存數(shù)據(jù)
for(mask=0x80;mask!=0;mask>>=1)
{
if((mask&dat))
I2C_SDA=1;
else
I2C_SDA=0;
I2C_SCL=1; //拉高SCL
I2C_SCL=0; //再拉低SCL,完成一個(gè)周期
}
I2C_SDA=1; //主機(jī)釋放SDA
I2C_SCL=1;
ack=I2C_SDA; //讀取SDA值,即為應(yīng)答值
I2C_SCL=0;
return (~ack); //因?yàn)樵镜腎2C是0表示應(yīng)答,1表示非應(yīng)答,所以這里取反
}
unsigned char I2CReadNAK() //I2C總線讀操作,發(fā)送非應(yīng)答信號(hào)并繼續(xù)讀下去,返回值-讀到的字節(jié)
{
unsigned char mask;
unsigned char dat; //暫存數(shù)據(jù)
I2C_SDA=1; //確保主機(jī)釋放SDA
for(mask=0x80;mask!=0;mask>>=1)
{
I2C_SCL=1;
if(I2C_SDA)
dat|=mask;
else
dat&=~mask;
I2C_SCL=0;
}
I2C_SDA=1; //拉高SDA,發(fā)送非應(yīng)答信號(hào)
I2C_SCL=1; //拉高SCL
I2C_SCL=0; //再拉低SCL完成非應(yīng)答
return dat; //返回?cái)?shù)據(jù)
}
unsigned char I2CReadACK() //I2C總線讀操作,發(fā)送應(yīng)答信號(hào)并不再讀下去,返回值-讀到的字節(jié)
{
unsigned char mask;
unsigned dat; //暫存數(shù)據(jù)
I2C_SDA=1; //確保主機(jī)釋放SDA
for(mask=0x80;mask!=0;mask>>=1)
{
I2C_SCL=1;
if(I2C_SDA)
dat|=mask;
else
dat&=~mask;
I2C_SCL=0;
}
I2C_SDA=0; //拉高SDA,發(fā)送應(yīng)答信號(hào)
I2C_SCL=1; //拉高SCL
I2C_SCL=0; //再拉低SCL完成應(yīng)答
return dat; //返回?cái)?shù)據(jù)
}
?存儲(chǔ)器AT24C02
?通過(guò)I2C總線來(lái)設(shè)置AT24C02
/**
* @brief AT24C02寫入一個(gè)字節(jié)
* @param WordAddress 要寫入字節(jié)的地址
* @param Data 要寫入的數(shù)據(jù)
* @retval 無(wú)
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
/**
* @brief AT24C02讀取一個(gè)字節(jié)
* @param WordAddress 要讀出字節(jié)的地址
* @retval 讀出的數(shù)據(jù)
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
Time0定時(shí)器
用來(lái)在設(shè)置模式是,實(shí)現(xiàn)閃爍功能
#include <REGX52.H>
/**
* @brief 定時(shí)器0初始化,1毫秒@12.000MHz
* @param 無(wú)
* @retval 無(wú)
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //設(shè)置定時(shí)器模式
TMOD |= 0x01; //設(shè)置定時(shí)器模式
TL0 = 0x18; //設(shè)置定時(shí)初值
TH0 = 0xFC; //設(shè)置定時(shí)初值
TF0 = 0; //清除TF0標(biāo)志
TR0 = 1; //定時(shí)器0開始計(jì)時(shí)
ET0=1;
EA=1;
PT0=0;
}
/*定時(shí)器中斷函數(shù)模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //設(shè)置定時(shí)初值
TH0 = 0xFC; //設(shè)置定時(shí)初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
主函數(shù)
#include <REGX52.H>
#include "DHT11.h"
#include "AT24C02.h"
#include "LCD1602.h"
#include "Timer0.h"
//燈、蜂鳴器、按鍵引腳定義
sbit led = P3^7; //超限指示燈
sbit led1 = P3^2; //正常指示燈
sbit buzz = P2^3; //蜂鳴器
sbit key_set = P1^0; //設(shè)置鍵
sbit key_jia = P1^3; //加鍵
sbit key_jian= P1^6; //減鍵
//變量定義
uchar temp_old,humi_old;//存儲(chǔ)上一次的溫度、濕度
uchar temp_up,temp_down;//存儲(chǔ)溫度上、下限值
uchar humi_up,humi_down;//存儲(chǔ)濕度上、下限值
uchar set_f; //設(shè)置選擇標(biāo)記,=0非設(shè)置,=1設(shè)置濕度上限,=2設(shè)置濕度下限
// =3設(shè)置溫度上限,=4設(shè)置溫度下限。
uchar Flash; //閃爍
//顯示固定內(nèi)容
void fix_display()
{
LCD_ShowString(1,1,"Humi:");
LCD_ShowString(2,1,"Temp:");
LCD_ShowString(1,12,"RH");
LCD_ShowChar(2,12,'C');
}
//顯示當(dāng)前測(cè)出的內(nèi)容
void now_display()
{
if(humi_old<Humi_H) //判斷濕度是否正在上升
{
LCD_ShowString(1,15,"UP"); //上升標(biāo)志UP
Delay_ms(50);
humi_old=Humi_H; //記錄此時(shí)的濕度
}
else if(humi_old>Humi_H) //判斷濕度是否正在下降
{
LCD_ShowString(1,15,"DW");
Delay_ms(50);
humi_old=Humi_H;
}
if(temp_old<Temp_H)
{
LCD_ShowString(1,15,"UP");
Delay_ms(50);
temp_old=Temp_H;
}
else if(temp_old>Temp_H)
{
LCD_ShowString(1,15,"DW");
Delay_ms(50);
temp_old=Temp_H;
}
if(temp_up<Temp_H||temp_down>Temp_H||humi_up<Humi_H||humi_down>Humi_H) //當(dāng)溫濕度超過(guò)界限時(shí),LED和蜂鳴器報(bào)警
{
buzz=0;
led=0;
led1=1;
}
else //LED1正常顯示
{
buzz=1;
led=1;
led1=0;
}
LCD_ShowNum(1,6,Humi_H,2); //顯示實(shí)測(cè)的溫濕度
LCD_ShowChar(1,8,'.');
LCD_ShowNum(1,9,Humi_L,2);
LCD_ShowNum(2,6,Temp_H,2);
LCD_ShowChar(2,8,'.');
LCD_ShowNum(2,9,Temp_L,2);
}
//顯示設(shè)置時(shí)的內(nèi)容
void set_display()
{
LCD_ShowString(1,1,"Humi:Up");
LCD_ShowString(2,1,"Temp:Up");
LCD_ShowString(1,10,"Down");
LCD_ShowString(2,10,"Down");
//利用定時(shí)器設(shè)置Flash閃爍值以達(dá)到閃爍效果
if(Flash==1&&set_f==1){LCD_ShowString(1,8," ");} //當(dāng)Flash等于1則清零,等于0則顯示
else{LCD_ShowNum(1,8,humi_up,2);} //當(dāng)set_f標(biāo)記鍵分別等于1,2,3,4時(shí),設(shè)置不同位置
if(Flash==1&&set_f==2){LCD_ShowString(1,14," ");}
else{LCD_ShowNum(1,14,humi_down,2);}
if(Flash==1&&set_f==3){LCD_ShowString(2,8," ");}
else{LCD_ShowNum(2,8,temp_up,2);}
if(Flash==1&&set_f==4){LCD_ShowString(2,14," ");}
else{LCD_ShowNum(2,14,temp_down,2);}
}
//按鍵掃描
void scan()
{
if(key_set==0) //當(dāng)設(shè)置鍵摁下時(shí)
{
Delay_ms(7);
if(key_set==0)
{
buzz=1; //蜂鳴器關(guān)閉,所有LED熄滅
led=1;
led1=1;
if(set_f==0) //進(jìn)入設(shè)置模式
{
LCD_WriteCommand(0x01); //清屏
Delay_ms(10);
}
set_f++; //每摁一次key_f設(shè)置鍵,set_f標(biāo)記鍵便加1
if(set_f==5) //當(dāng)循環(huán)4次,即摁下key_set設(shè)置鍵4次時(shí),把set_f標(biāo)記鍵置0
{
set_f=0;
AT24C02_WriteByte(0,humi_up); //把設(shè)置好的溫濕度上下限寫入AT24C02
AT24C02_WriteByte(1,humi_down);
AT24C02_WriteByte(2,temp_up);
AT24C02_WriteByte(3,temp_down);
LCD_WriteCommand(0x01); //清屏
Delay_ms(10);
fix_display(); //顯示固定內(nèi)容
}
}
while(!key_set); //當(dāng)摁下key_sey設(shè)置鍵松手時(shí),完成一次操作
}
if(key_jia==0&&set_f!=0) //加法鍵
{
Delay_ms(7);
if(set_f==1){humi_up++;}
if(set_f==2){humi_down++;}
if(set_f==3){temp_up++;}
if(set_f==4){temp_down++;}
}
if(key_jian==0&&set_f!=0) //減法鍵
{
Delay_ms(7);
if(set_f==1){humi_up--;}
if(set_f==2){humi_down--;}
if(set_f==3){temp_up--;}
if(set_f==4){temp_down--;}
}
}
void main()
{
Timer0Init(); //定時(shí)器初始化
LCD_Init(); //LCD初始化
fix_display(); //顯示固定內(nèi)容
Delay_ms(100);
AT24C02_WriteByte(0,75); //在AT24C02中設(shè)置溫濕度上下限
AT24C02_WriteByte(0,45);
AT24C02_WriteByte(0,30);
AT24C02_WriteByte(0,15);
humi_up=AT24C02_ReadByte(0); //分別賦值給humi_up,humi_down,temp_up,temp_down
humi_down=AT24C02_ReadByte(1);
temp_up=AT24C02_ReadByte(2);
temp_down=AT24C02_ReadByte(3);
while(1)
{
scan(); //按鍵掃描
if(set_f==0) //表示并未進(jìn)入設(shè)置操作
{
EA=0; //關(guān)閉中斷
DHT11_read(); //DHT11讀取溫濕度值
now_display(); //顯示實(shí)測(cè)內(nèi)容
EA=1; //打開中斷
}
else
set_display(); //顯示設(shè)置內(nèi)容
}
}
void Timer0_Routine() interrupt 1 //中斷程序
{
static unsigned int T0Count;
TL0 = 0x18; //設(shè)置初值
TH0 = 0xFC;
T0Count++;
if(T0Count>=500) //每隔500ms,即0.5秒
{
T0Count=0;
Flash=!Flash; //Flash翻轉(zhuǎn)
}
}
? ? ? ?注意:定時(shí)器中斷在實(shí)際應(yīng)用中容易出現(xiàn)溫度的讀取數(shù)據(jù)出現(xiàn)亂碼,原因在定時(shí)器的中斷打斷了溫度的傳輸?shù)却龝r(shí)間,使數(shù)據(jù)傳輸不完整。因此在讀取DHT11前使EA=0關(guān)閉中斷;讀取后EA=1打開中斷。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-421452.html
proteus仿真圖
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-421452.html
到了這里,關(guān)于基于51單片機(jī)的DHT11溫濕度檢測(cè)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!