DHT11和DS18B20一樣,都是單總線芯片,同DHT10不同,它的四根引腳中有一條是空腳,與DS18B20相似,對時序的要求比較高,不同之處在于寫程序的時候數據的采集必須間隔1s以上,不然采集會失敗。
還有,DHT11的數據口最好要接一個上拉電阻,或者單片機內部上拉也可以。
DHT11的數據手冊網上有,上面有時序操作的詳細介紹。個人建議寫這個程序的時候要一邊寫一邊檢測(比如寫完復位子程序之后就在主函數中調用它一次,看它是否執行成功。。。),不然很可能到最后找不到錯誤出在哪里,本人就是一直寫完然后不好使,最后又重寫的!
閑話不說了,下面幫助大家分析一下DHT11的時序圖(數據手冊上有),因為DHT11對時序的要求很高,所以很可能寫完程序不好使。本人建議:延時子函數最好自己用示波器檢測一下,自己算出來的在10us下誤差會很大的。
進入正題:下面我說的話可以參照下面的程序看。
數據手冊前面的一些內容自己了解就可以了,先看數據手冊上主機復位信號和DHT11相應信號那部分。
主機先控制總線,拉低至少18ms,然后再拉高20~40us,(這時如果硬件沒有問題的話DHT11會有響應的)所以現在主機釋放總線(把DDRXN 寄存器清零),等待DHT11的響應,如果成功DHT11會產生40~50us的低電平,和40~50us的高電平。這里可以由程序完成檢測。
接下來在一次采集中,把總線一直交給DHT11,它會給主機傳送一個40位的二進制數,前0~7位是濕度的整數部分,8~15位是濕度的小數部分;16~23位是溫度的整數部分,24~31位是溫度的小數部分;最后八位是校驗位。這些數據要通過程序進行處理,轉換成溫濕度的實際值,并由顯示部分顯示出來。(本人用的是數碼管,建議用1602顯示會更方便一些)。
后面的處理部分我就不一一講解了,我在程序中是有注釋的,自己把程序加入到工程中看效果會好很多的,也可以用專門的閱讀軟件來看(source insignte),不然字體都一個顏色非常亂。
如果有不懂的地方或者程序有什么不足之處給我留言就好了,我會及時幫助大家解決的^_^!!!
程序代碼的完整版本下載地址是:http://www.zg4o1577.cn/f/dht11.rar
程序代碼的完整版本下載地址是:http://www.zg4o1577.cn/f/dht11.rar
================================================ //這里是delay.h /*************我開發板的晶振是16M的,具體的延時子函數要自己仔細寫*************/ #ifndef __DELAY_H #define __DELAY_H void delay_us(unsigned int xus); void delay_ms(unsigned int xms); #endif ================================================ //這里是delay.c #include"delay.h" #include<macros.h> //延時微妙子函數 void delay_us(unsigned int xus) { unsigned int i,j; for(i=0;i<xus;i++) { NOP();NOP();NOP();NOP();NOP();NOP(); NOP();NOP();NOP();NOP(); } } //延時毫秒子函數 void delay_ms(unsigned int xms) { unsigned int i,j; for(i=0;i<xms;i++) { for(j=0;j<2288;j++); } } ================================================ //這里是dht11.h #ifndef __DHT11_H #define __DHT11_H #ifndef __IOM128V_H #include <iom128v.h> #endif #ifndef __MACROS_H #include <macros.h> #endif #define DDR_1 DDRC|=BIT(PC0) #define DDR_0 DDRC&=~BIT(PC0) #define PORTC_1 PORTC|=BIT(PC0) #define PORTC_0 PORTC&=~BIT(PC0) #define DQ (PINC&0x01) void caiji(void); long int dht(void); void init_dht11(void); //void ceshi(void); #endif ================================================ //這里是dht11.c #include"dht11.h" unsigned char dht_data[5],a,b; unsigned int s1,s0,t1,t0,sd,wd,wsd; void caiji(void) { unsigned char i,j; //delay_ms(900); for(i=0;i<5;i++) { dht_data[i]=0x00; //數組清零 for(j=0;j<8;j++) { while(!DQ); //判斷是否為高電平 //延時50us若為高電平則為一,否則為零 delay_us(50); if(DQ) { dht_data[i]|=BIT(7-j); //保存數據 while(DQ);//低電平檢測 } } } } void init_dht11(void) { DDR_1; //設置主機輸出 PORTC_0; //總線拉低至少18ms delay_ms(20); PORTC_1; //總線由主機拉高大約30us delay_us(30); DDR_0; //主機設置為輸入,檢測從機信號 while(DQ); } long int dht(void) { init_dht11(); if(!DQ) { while(!DQ); while(DQ); //經以上兩句后開始接收信號 caiji(); DDR_1; PORTC_1; //校驗 a= ( dht_data[0]+dht_data[1]+dht_data[2]+dht_data[3] ); if(a==dht_data[4]) { s1=dht_data[0]; s0=dht_data[1]; t1=dht_data[2]; t0=dht_data[3]; } //s為濕度,t為溫度 sd=s1; sd<<=8; sd|=s0; wd=t1; wd<<=8; wd|=t0; wsd=sd<<16; wsd|=wd; } return wsd; } ================================================ //這里是xianshi.h #ifndef __XIANSHI_H #define __XIANSHI_H #ifndef __IOM128V_H #include<iom128v.h> #endif #define SCK_0 PORTB&=~(1<<PB4) #define SCK_1 PORTB|=(1<<PB4) #define LCK_0 PORTB&=~(1<<PB5) #define LCK_1 PORTB|=(1<<PB5) #define SDI_0 PORTB&=~(1<<PB6) #define SDI_1 PORTB|=(1<<PB6) void init(void); void send_595(unsigned char dat); void digitron_show(unsigned int int_part,unsigned int float_part); #endif ================================================ //這里是xianshi.c #include"xianshi.h" #ifndef __DELAY_H #include"delay.h" #endif #ifndef __DHT11_H #include"dht11.h" #endif //數碼管顯示數組定義 const unsigned char table[]= { 0x3F,// 0 0x06,// 1 0x5B,// 2 0x4F,// 3 0x66,// 4 0x6D,// 5 0x7D,// 6 0x07,// 7 0x7F,// 8 0x6F,// 9 0x3F+0x80,// 0. 0x06+0x80,// 1. 0x5B+0x80,// 2. 0x4F+0x80,// 3. 0x66+0x80,// 4. 0x6D+0x80,// 5. 0x7D+0x80,// 6. 0x07+0x80,// 7. 0x7F+0x80,// 8. 0x6F+0x80// 9. }; unsigned int s,t,st,int_part,float_part,temp,SH; //發送一字節數據到595 void send_595(unsigned char dat) { unsigned char i; LCK_0; SDI_1; SCK_0; //上面的三條語句為了初始化端口狀態 for(i=0;i<8;i++) { LCK_0;//時鐘線拉低 if(dat&0x80) SDI_1; else SDI_0; dat=dat<<1; delay_us(100); LCK_1; //時鐘線拉高將數據讀入595的移位寄存器 delay_us(100); } SCK_1; //發送數據到并行端口 SCK_0; } void show(void) { unsigned char temp_shi,temp_ge,SH_shi,SH_ge,x,y; unsigned int i; st=dht(); t=st&0x0000ffff; s=st&0xffff0000; s=s>>16; //下面為把溫度和濕度換算成十進制并且四舍五入 temp=(t>>8); temp_shi=temp/10; temp_ge=temp%10; SH=(s>>8); SH_shi=SH/10; SH_ge=SH%10; int_part=SH_shi*10+SH_ge; float_part=0; for(i=0;i<50;i++) { digitron_show(int_part,float_part); } } void digitron_show(unsigned int int_part,unsigned int float_part) { PORTA=0x01; send_595(table[float_part/10]); send_595(0x00); delay_ms(5); PORTA=0x02; send_595(table[(int_part%10)+10]); send_595(0x00); delay_ms(5); PORTA=0x04; send_595(table[int_part/10]); send_595(0x00); delay_ms(5); } ================================================ //這里是MAIN.C #include<iom128v.h> #include<macros.h> #include"delay.h" #include"dht11.h" #include"xianshi.h" #pragma interrupt_handler Timer0_COMP:16 #define uchar unsigned char uchar k=0; void init(void); void main() { init();//初始化 TCCR0=0X0F; DDRA=0XFF; TCCR0=0X0f;//CTC模式 OCR0=145;//10ms TIMSK=0X02; SEI(); while(1); } //初始化子函數 void init(void) { DDRA=0XFF; DDRB=0XFF; } void Timer0_COMP(void) { TCCR0=0X08; CLI(); k++; if(k==255) { k=0; show(); } TCCR0=0X0f;//重置初值 SEI(); } 程序到這里結束,希望大家多提寶貴意見哦!!!