DHT11數(shù)字溫濕度傳感器是一款含有已校準(zhǔn)數(shù)字信號輸出的溫濕度復(fù)合傳感器。其成本低、長期穩(wěn)定、可以測量相對濕度和溫度測量,并可以只使用一根數(shù)據(jù)線進(jìn)行溫濕度采集。
1.模塊來源
模塊實(shí)物展示:
資料下載鏈接:https://pan.baidu.com/s/1HQEL699-Yl5Jh3Hp87_FlQ
資料提取碼:2sgq
模塊的廠家資料下載見百度網(wǎng)盤鏈接
工作電壓:3-5.5V
工作電流:1MA
測量分辨率:8 bit
濕度量程: 20 - 90 %RH
濕度精度:±5 %RH
溫度量程: 0 - 50 ℃
溫度精度:±2 ℃
通信協(xié)議:單總線
3.移植過程
我們的目標(biāo)是在立創(chuàng)·地文星·CW32F030C8T6開發(fā)板上實(shí)現(xiàn)讀取溫濕度的功能。首先要獲取資料,查看數(shù)據(jù)手冊應(yīng)如何實(shí)現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。
3.1查看資料
DHT11使用的是單總線通信,即發(fā)送數(shù)據(jù)與接收數(shù)據(jù)都在一根數(shù)據(jù)線上,通過規(guī)定的時序進(jìn)行控制。
從左向右看,時序一開始,主機(jī)信號就保持著高電平,所以引腳初始化完畢時,及時給引腳輸出高電平。因?yàn)?模塊的數(shù)據(jù)線要求空閑時,要保持高電平狀態(tài)。(其實(shí)模塊上已經(jīng)接了上拉電阻,使數(shù)據(jù)線一直保持高電平)
根據(jù)時序圖可以知道,主機(jī)(開發(fā)板)發(fā)送一次開始信號,待主機(jī)開始信號結(jié)束后,DHT11 發(fā)送響應(yīng)信號,送出 溫濕度數(shù)據(jù),并觸發(fā)一次數(shù)據(jù)采集給下一次數(shù)據(jù)讀取作準(zhǔn)備。因此完成一次數(shù)據(jù)讀取需要進(jìn)行起始信號、響應(yīng)信號、數(shù)據(jù)接收、結(jié)束信號。
讀取數(shù)據(jù)步驟:
起始信號:主機(jī)(開發(fā)板)接入數(shù)據(jù)線的I/O輸出低電平,且低電平保持時間不能小于 18ms
DATA_GPIO_OUT(0); //數(shù)據(jù)線輸出低電平 delay_1ms(19); //起始信號保持時間19ms DATA_GPIO_OUT(1); //主機(jī)釋放總線 delay_uus( 20 ); //拉高等待
2.響應(yīng)信號:等待模塊的響應(yīng)信號到來。將數(shù)據(jù)線改為輸入模式,如果接入到低電平,說明接收到模塊的響應(yīng)。
DHT11_GPIO_Mode_IN();//數(shù)據(jù)線轉(zhuǎn)為輸入模式 //如果前面沒有錯誤,則模塊會發(fā)出低電平的應(yīng)答信號, //所以直接等待DHT11拉高,83us timeout = 5000; while( (! DATA_GPIO_IN ) && ( timeout >0 ) ) { timeout--; //等待高電平的到來 } //模塊當(dāng)前處于拉高準(zhǔn)備輸出數(shù)據(jù), //所以直接等待DHT11拉低,87us timeout = 5000;//設(shè)置超時時間 while( DATA_GPIO_IN && ( timeout >0 ) ) { timeout-- ; //等待低電平的到來 }
3.數(shù)據(jù)傳輸:主機(jī)接收模塊發(fā)送的40位數(shù)據(jù),其中,位數(shù)據(jù) ‘0’ 表示54us的低電平,27us的高電平;位數(shù)據(jù) ‘1’ 表示54us的低電平,74us的高電平。兩個格式的分辨主要是高電平的輸出時長不同。
#define CHECK_TIME 28 //超過0值的高電平時間 for(i=0;i40;i++)//循環(huán)接收40位數(shù)據(jù) { timeout = 5000; //等待低電平過去 while( ( !DATA_GPIO_IN ) && (timeout > 0) ) timeout--; //54us delay_uus(CHECK_TIME);//等待超過位數(shù)據(jù)0值的高電平時間 if ( DATA_GPIO_IN )//如果還是高電平,說明是1值 { val=(val<1)+1; } else //如果是低電平,說明是0值 { val<=1; } timeout = 5000; //如果當(dāng)前還是高電平,等待高電平過去,準(zhǔn)備接收下一位數(shù)據(jù) while( DATA_GPIO_IN && (timeout > 0) ) timeout-- ; }
4.結(jié)束信號:模塊的數(shù)據(jù)線輸出 40 位數(shù)據(jù)后,是以低電平結(jié)束,它會繼續(xù)輸出低電平 54 微秒后轉(zhuǎn)為輸入狀態(tài),主機(jī)需要轉(zhuǎn)為輸出狀態(tài),輸出高電平釋放總線。
DHT11_GPIO_Mode_OUT();//轉(zhuǎn)為輸出模式 DATA_GPIO_OUT(1);//主機(jī)釋放總線
數(shù)據(jù)接收完成,但是這40位數(shù)據(jù)要如何轉(zhuǎn)化為溫濕度數(shù)據(jù)?并如何保證傳輸?shù)臄?shù)據(jù)沒有錯誤?
DHT11模塊一次完整的數(shù)據(jù)傳輸為40bit,高位先出。數(shù)據(jù)格式:
8bit濕度整數(shù)數(shù)據(jù) + 8bit濕度小數(shù)數(shù)據(jù) + 8bi溫度整數(shù)數(shù)據(jù) + 8bit溫度小數(shù)數(shù)據(jù) + 8bit校驗(yàn)和
注意
濕度小數(shù)部分?jǐn)?shù)據(jù)一直為0。
數(shù)據(jù)傳送正確時,校驗(yàn)和數(shù)據(jù)等于“8bit濕度整數(shù)數(shù)據(jù)+8bit濕度小數(shù)數(shù)據(jù) +8bi溫度整數(shù)數(shù)據(jù)+8bit溫度小數(shù)數(shù)據(jù)”所得結(jié)果的末8位。舉幾個例子。
示例一:接收的40位數(shù)據(jù)分別為:
0011 0101 | 0000 0000 | 0001 1000 | 0000 0100 | 0101 0001 |
濕度高8位 | 濕度低8位 | 溫度高8位 | 溫度低8位 | 校驗(yàn)位 |
校驗(yàn)和為 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,與接收的數(shù)據(jù)一致
濕度為 0011 0101 + 0000 0000 = 35 + 0 = 35%RH
溫度為 0001 1000 0000 0100 = 24 + 4 = 24.4℃
示例二:接收的40位數(shù)據(jù)分別為:
0011 0101 | 0000 0000 | 0001 1000 | 0000 0100 | 0100 1001 |
濕度高8位 | 濕度低8位 | 溫度高8位 | 溫度低8位 | 校驗(yàn)位 |
校驗(yàn)和為 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,與接收的數(shù)據(jù)不一致 計算的數(shù)據(jù)為0101 0001,接收的數(shù)據(jù)為0100 1001,兩者不一致說明數(shù)據(jù)不準(zhǔn)確,丟棄這次數(shù)據(jù),重新接收。
以下為數(shù)據(jù)處理的實(shí)現(xiàn)代碼:
//val為接收到的40位數(shù)據(jù)。 // 濕高8 + 濕低8 + 溫高8 + 溫低8 verify_num = (val>>32) + (val>>24) + (val>>16) + (val>>8); //計算的校驗(yàn)和 與 接收的校驗(yàn)和 的差為0說明一致,不為0說明不一致 //(val&0xff)是因?yàn)関al的大小為64位,我們只需要val的最后8位校驗(yàn)和 verify_num = verify_num - (val&0xff); //進(jìn)行校驗(yàn) if( verify_num )//如果不為0,說明校驗(yàn)失敗 { // 校驗(yàn)錯誤 return 0; } else //校驗(yàn)成功 { //數(shù)據(jù)處理 humidity = (val>>32)&0xff; //濕度前8位(小數(shù)點(diǎn)前數(shù)據(jù)) small_point = (val>>24)&0x00ff; //濕度后8位(小數(shù)點(diǎn)后數(shù)據(jù)) small_point = small_point * 0.1; //換算為小數(shù)點(diǎn) humidity = humidity + small_point; //小數(shù)前+小數(shù)后 printf("濕度:%.2frn",humidity); temperature = (val>>16)&0x0000ff; //溫度前8位(小數(shù)點(diǎn)前數(shù)據(jù)) small_point = (val>>8)&0x000000ff; //溫度后8位(小數(shù)點(diǎn)后數(shù)據(jù)) small_point = small_point * 0.1; //換算為小數(shù)點(diǎn) temperature = temperature + small_point;//小數(shù)前+小數(shù)后 printf("溫度:%.2frn",temperature); return val>>8; //返回未處理的數(shù)據(jù) }
3.2引腳選擇
該模塊有3個引腳,具體引腳連接見 表 各引腳連接。
3.3移植至工程
工程模板下載請查看入門手冊百度鏈接
然后我們打開空白工程,新建兩個文件dht11.c和dht11.h
在文件dht11.c中,編寫如下代碼。
/* * Change Logs: * Date Author Notes * 2024-06-19 LCKFB-LP first version */ #include "dht11.h" #include "stdio.h" float temperature = 0; float humidity = 0; /****************************************************************** * 函 數(shù) 名 稱:DHT11_GPIO_Init * 函 數(shù) 說 明:DHT11溫濕度傳感器初始化 * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:無 * 作 者:LC * 備 注:無 ******************************************************************/ void DHT11_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體 RCC_DHT11_GPIO_ENABLE(); // 使能GPIO時鐘 GPIO_InitStruct.Pins = GPIO_DHT11; // GPIO引腳 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽輸出 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 輸出速度高 GPIO_Init(PORT_DHT11, &GPIO_InitStruct); // 初始化 DATA_GPIO_OUT(1); } /****************************************************************** * 函 數(shù) 名 稱:DHT11_GPIO_Mode_OUT * 函 數(shù) 說 明:配置DHT11的數(shù)據(jù)引腳為輸出模式 * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:無 * 作 者:LC * 備 注:無 ******************************************************************/ void DHT11_GPIO_Mode_OUT(void) { GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體 GPIO_InitStruct.Pins = GPIO_DHT11; // GPIO引腳 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽輸出 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 輸出速度高 GPIO_Init(PORT_DHT11, &GPIO_InitStruct); // 初始化 } /****************************************************************** * 函 數(shù) 名 稱:DHT11_GPIO_Mode_IN * 函 數(shù) 說 明:配置DHT11的數(shù)據(jù)引腳為輸入模式 * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:無 * 作 者:LC * 備 注:無 ******************************************************************/ void DHT11_GPIO_Mode_IN(void) { GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體 GPIO_InitStruct.Pins = GPIO_DHT11; // GPIO引腳 GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP; // 上拉輸入 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 輸出速度高 GPIO_Init(PORT_DHT11, &GPIO_InitStruct); // 初始化 } /****************************************************************** * 函 數(shù) 名 稱:DHT11_Read_Data * 函 數(shù) 說 明:根據(jù)時序讀取溫濕度數(shù)據(jù) * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:0=數(shù)據(jù)校驗(yàn)失敗 其他=溫濕度未處理的數(shù)據(jù) * 作 者:LC * 備 注:無 ******************************************************************/ unsigned int DHT11_Read_Data(void) { int i; long long val=0; int timeout=0; float small_point=0; unsigned char verify_num = 0;//驗(yàn)證值 DATA_GPIO_OUT(0);//數(shù)據(jù)線輸出低電平 delay_ms(19); //起始信號保持時間19ms DATA_GPIO_OUT(1);//主機(jī)釋放總線 delay_us( 20 );//拉高等待 DHT11_GPIO_Mode_IN();//數(shù)據(jù)線轉(zhuǎn)為輸入模式 //如果前面沒有錯誤,則模塊會發(fā)出低電平的應(yīng)答信號,所以直接等待DHT11拉高,80us timeout = 5000; while( (! DATA_GPIO_IN ) && ( timeout >0 ) )timeout--; //等待高電平的到來 //模塊當(dāng)前處于拉高準(zhǔn)備輸出數(shù)據(jù),所以直接等待DHT11拉低,80us timeout = 5000;//設(shè)置超時時間 //DATA_GPIO_IN=0時,while條件不成立退出while 說明接收到響應(yīng)信號 //當(dāng)timeout<=0時,while條件不成立退出while 說明超時 while( DATA_GPIO_IN && ( timeout >0 ) )timeout-- ; //等待低電平的到來 #define CHECK_TIME 28 //實(shí)測發(fā)現(xiàn)超過0值的高電平時間 for(i=0;i40;i++)//循環(huán)接收40位數(shù)據(jù) { timeout = 5000; while( ( !DATA_GPIO_IN ) && (timeout > 0) ) timeout--; //等待低電平過去 delay_us(CHECK_TIME);//超過0值的高電平時間 if ( DATA_GPIO_IN )//如果還是高電平,說明是1值 { val=(val<1)+1; } else //如果是低電平,說明是0值 { val<=1; } timeout = 5000; while( DATA_GPIO_IN && (timeout > 0) ) timeout-- ; //如果還是高電平 } DHT11_GPIO_Mode_OUT();//轉(zhuǎn)為輸出模式 DATA_GPIO_OUT(1);//主機(jī)釋放總線 // 濕高8 + 濕低8 + 溫高8 + 溫低8 verify_num = (val>>32) + (val>>24) + (val>>16) + (val>>8); //計算的校驗(yàn)和 與 接收的校驗(yàn)和 的差為0說明一致,不為0說明不一致 verify_num = verify_num - (val&0xff); //進(jìn)行校驗(yàn) if( verify_num ) { // 校驗(yàn)錯誤 return 0; } else //校驗(yàn)成功 { //數(shù)據(jù)處理 humidity = (val>>32)&0xff;//濕度前8位(小數(shù)點(diǎn)前數(shù)據(jù)) small_point = (val>>24)&0x00ff;//濕度后8位(小數(shù)點(diǎn)后數(shù)據(jù)) small_point = small_point * 0.1;//換算為小數(shù)點(diǎn) humidity = humidity + small_point;//小數(shù)前+小數(shù)后 // printf("濕度:%.2frn",humidity); temperature = (val>>16)&0x0000ff;//溫度前8位(小數(shù)點(diǎn)前數(shù)據(jù)) small_point = (val>>8)&0x000000ff;//溫度后8位(小數(shù)點(diǎn)后數(shù)據(jù)) small_point = small_point * 0.1;//換算為小數(shù)點(diǎn) temperature = temperature + small_point;//小數(shù)前+小數(shù)后 // printf("溫度:%.2frn",temperature); return val>>8; //返回未處理的數(shù)據(jù) } } /****************************************************************** * 函 數(shù) 名 稱:Get_temperature * 函 數(shù) 說 明:獲取溫度數(shù)據(jù) * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:溫度值 * 作 者:LC * 備 注:使用前必須先調(diào)用 DHT11_Read_Data 讀取有數(shù)據(jù) ******************************************************************/ float Get_temperature(void) { return temperature; } /****************************************************************** * 函 數(shù) 名 稱:Get_humidity * 函 數(shù) 說 明:獲取濕度數(shù)據(jù) * 函 數(shù) 形 參:無 * 函 數(shù) 返 回:濕度值 * 作 者:LC * 備 注:使用前必須先調(diào)用 DHT11_Read_Data 讀取有數(shù)據(jù) ******************************************************************/ float Get_humidity(void) { return humidity; }
在文件dht11.h中,編寫如下代碼。
/* * Change Logs: * Date Author Notes * 2024-06-19 LCKFB-LP first version */ #ifndef _BSP_DHT11_H_ #define _BSP_DHT11_H_ #include "board.h" /**************引腳修改此處****************/ #define RCC_DHT11_GPIO_ENABLE() __RCC_GPIOB_CLK_ENABLE() #define PORT_DHT11 CW_GPIOB #define GPIO_DHT11 GPIO_PIN_0 //設(shè)置DHT11輸出高或低電平 #define DATA_GPIO_OUT(x) GPIO_WritePin(PORT_DHT11, GPIO_DHT11, x ? GPIO_Pin_SET : GPIO_Pin_RESET) //獲取DHT11數(shù)據(jù)引腳高低電平狀態(tài) #define DATA_GPIO_IN GPIO_ReadPin(PORT_DHT11, GPIO_DHT11) extern float temperature; extern float humidity; void DHT11_GPIO_Init(void);//引腳初始化 unsigned int DHT11_Read_Data(void);//讀取模塊數(shù)據(jù) float Get_temperature(void);//返回讀取模塊后的溫度數(shù)據(jù) float Get_humidity(void);//返回讀取模塊后的濕度數(shù)據(jù) #endif
4.移植驗(yàn)證
在自己工程中的main主函數(shù)中,編寫如下。
/* * Change Logs: * Date Author Notes * 2024-06-19 LCKFB-LP first version */ #include "board.h" #include "stdio.h" #include "bsp_uart.h" #include "dht11.h" int32_t main(void) { board_init(); // 開發(fā)板初始化 uart1_init(115200); // 串口1波特率115200 DHT11_GPIO_Init(); //DHT11引腳初始化 delay_ms(1000); printf("DHT11 demo startrn"); while(1) { //讀取模塊數(shù)據(jù) DHT11_Read_Data(); //顯示讀取后的溫度數(shù)據(jù) printf("temperature = %.2frn", Get_temperature() ); //顯示讀取后的濕度數(shù)據(jù) printf("humidity = %.2frn", Get_humidity() ); delay_ms(1000); } }
上電效果:
模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/10WX784WnNQeMiwbLH8Yt7g?pwd=LCKF
提取碼:LCKF
審核編輯 黃宇
-
數(shù)字信號
+關(guān)注
關(guān)注
2文章
943瀏覽量
47492 -
溫濕度傳感器
+關(guān)注
關(guān)注
5文章
571瀏覽量
35630 -
DHT11
+關(guān)注
關(guān)注
19文章
276瀏覽量
57515 -
CW32
+關(guān)注
關(guān)注
1文章
174瀏覽量
550
發(fā)布評論請先 登錄
相關(guān)推薦
評論