STM32實戰——DHT11溫濕度獲取并展示
介紹
DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器,包括一個電阻式感濕元件和一個NTC測溫元件,可以用來測量溫度和濕度。
硬件連線
注意
本實驗使用STM32F103C8T6芯片作為主控,使用DHT11(帶上拉電阻)模塊作為溫濕度采集裝置。
接線如下表所示:
| 名稱 | STM32 | 注釋 |
|---|---|---|
| VCC | 3.3V | 供電 3.3V |
| OUT | PB13(任意一個GPIO口即可) | 串行數據 |
| GND | GDN | 接地 |
由于使用了上拉電阻,因此STM32與DHT11的通信類似于軟件模擬IIC通信協議。使用開漏輸出模式(OD)控制PB13的高低電平。(如果不懂什么是軟件模擬IIC的話,請移步軟件I2C讀寫MPU6050
DHT11
本教程使用DHT11模塊如下圖所示:

DHT11協議
概述
STM32與 DHT11之間的通信,采用單總線數據格式,一次通信時間4ms左右。
總體通信流程為:開始信號->響應信號->數據傳輸。
STM32發送一次開始信號后,DHT11從低功耗模式轉換到高速模式,等待主機開始信號結束。DHT11發送響應信號,送出40bit的數據,并觸發一次信號采集,用戶可選擇讀取部分數據。
注:DHT11接收到開始信號觸發一次溫濕度采集,如果沒有接收到主機發送開始信號,DHT11不會主動進行溫濕度采集。采集數據后轉換到低速模式。
總體操作時序如下圖所示:

開始信號(STM32控制總線)
首先主機拉低總線至少 18ms,然后再拉高總線,延時 20~40us,取中間值 30us,此時復位信號發送完畢。
注意
是拉低總線18毫秒,不是18微秒,搞錯的話是不能正常通信的!
響應信號(DHT11控制總線)
DHT11 檢測到復位信號后,觸發一次采樣,并拉低總線 80us 表示響應信號,告訴主機數據已經準備好了;然后 DHT11 拉高總線 80us,之后開始傳輸數據。
數據傳輸(DHT11控制總線)
之后,每 1bit 數據都以 50us 低電平時隙開始。
DHT11 以高電平的長短定義數據位是 0 還是 1。當 50us 低電平時隙過后拉高總線,高電平持續 26~28us 表示數據“0”;持續 70us 表示數據“1”。如下表所示:
| 輸出 | 表示方法 |
|---|---|
| 數字0 | 50us低電平開始后,26-28us的高電平表示0 |
| 數字1 | 50us低電平開始后,70us的高電平表示1 |

數據傳輸結束
當最后一bit數據傳送完畢后,DHT11拉低總線50us,隨后釋放總線,由上拉電阻拉高進入空閑狀態。
DHT11數據格式
一次完整的數據傳輸為40bit,高位先出。數據分小數部分和整數部分,數據格式:
- 8bit濕度整數數據
- 8bit濕度小數數據
- 8bit溫度整數數據
- 8bit溫度小數數據
- 8bit校驗和
若數據傳送正確,則
校驗和數據 = “8bit 濕度整數數據 +8bit 濕度小數數據+8bit溫度整數數據 +8bit 溫度小數數據”所得結果的末8位。
STM32代碼
提示
將Tab縮進更改為2空格體驗更好。
注意
需要添加delay.h、oled.h等頭文件時,還請自行添加。
DHT11驅動代碼
頭文件
宏定義“使用引腳”和“控制IO輸出1和0的函數”
#ifndef __DHT11_H
#define __DHT11_H
// 定義引腳
#define DHT11_GPIO_Port GPIOB
#define DHT11_GPIO_Pin GPIO_Pin_13
// 定義函數
#define dht11_high GPIO_SetBits(DHT11_GPIO_Port, DHT11_GPIO_Pin)
#define dht11_low GPIO_ResetBits(DHT11_GPIO_Port, DHT11_GPIO_Pin)
#define DHT11_IN GPIO_ReadInputDataBit(DHT11_GPIO_Port, DHT11_GPIO_Pin)
void DH11_GPIO_Init(void);
uint8_t DHT11RstAndCheck(void);
uint8_t DHT11ReadByte(void);
uint8_t DHT11ReadData(uint8_t *Temp_H,uint8_t *Temp_L,uint8_t *Humi_H,uint8_t *Humi_L);
#endif
初始化
初始化DHT11使用引腳
/**
* @brief 初始化DHT11
* @param 無
* @retval 無
*/
void DH11_GPIO_Init(void)
{
// 開啟APB2中的GPIOB時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置GPIO PB12
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 開漏輸出
GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStructure);
}
復位和檢查響應
該函數將“STM32發送開始信號”與“STM32接受響應信號”放置一起
/**
* @brief 復位和檢測響應
* @param 無
* @retval 1:響應成功, 0:響應失敗
*/
uint8_t DHT11RstAndCheck(void)
{
uint8_t timer = 0; // 計數器
__set_PRIMASK(1); // 關總中斷
dht11_low; // 輸出低電平
Delay_ms(20); // 拉低至少18ms
dht11_high; // 輸出高電平
Delay_us(30); // 拉高20~40us
// 檢測是否存在第一個低電平
while (!DHT11_IN) // 等待總線拉低,DHT11會拉低40~80us作為響應信號
{
timer++; // 總線拉低時計數
Delay_us(1);
}
if (timer>100 || timer<20) // 判斷響應時間
{
__set_PRIMASK(0); // 開總中斷
return 0;
}
// 檢測是否存在第二個高電平
timer = 0; // 重置計數器
while (DHT11_IN) // 等待DHT11釋放總線,持續時間40~80us
{
timer++; // 總線拉高時計數
Delay_us(1);
}
__set_PRIMASK(0); // 開總中斷
if (timer>100 || timer<20) // 檢測響應信號之后的高電平
{
return 0;
}
// 均存在, 則返回1, 響應正常
return 1;
}
獲取一個字節數據
/**
* @brief 讀取一字節數據
* @param 無
* @retval 讀到的數據
*/
uint8_t DHT11ReadByte(void)
{
uint8_t i;
uint8_t byt = 0;
__set_PRIMASK(1); // 關總中斷
for (i=0; i<8; i++)
{
while (DHT11_IN); // 等待低電平,數據位前都有50us低電平時隙
while (!DHT11_IN); // 等待高電平,開始傳輸數據位
Delay_us(40);
byt <<= 1; // 因高位在前,所以左移byt,最低位補0
if (DHT11_IN) // 將總線電平值讀取到byt最低位中
{
byt |= 0x01;
}
}
__set_PRIMASK(0); // 開總中斷
return byt;
}
獲取DHT11全部數據
/**
* @brief 讀取數據
* @param Temp_H 溫度整數部分
* @param Temp_L 溫度小數部分
* @param Humi_H 濕度整數部分
* @param Humi_L 濕度小數部分
* @retval 0-成功,1-失敗
*/
uint8_t DHT11ReadData(uint8_t *Temp_H,uint8_t *Temp_L,uint8_t *Humi_H,uint8_t *Humi_L)
{
uint8_t sta = 0;
uint8_t i;
uint8_t buf[5];
if (DHT11RstAndCheck()) // 檢測響應信號
{
for(i=0;i<5;i++) // 讀取40位數據
{
buf[i]=DHT11ReadByte(); // 讀取1字節數據
}
if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4]) // 校驗成功
{
*Humi_H = buf[0]; // 濕度 整數部分數據
*Humi_L = buf[1]; // 濕度 小數部分數據
*Temp_H = buf[2]; // 溫度 整數部分數據
*Temp_L = buf[3]; // 溫度 小數部分數據
}
sta = 0;
}
else // 響應失敗返回-1
{
*Temp_H = 0;
*Temp_L = 0;
*Humi_H = 0;
*Humi_L = 0;
sta = 1;
}
return sta;
}
main函數
int main(void)
{
DH11_GPIO_Init(); // DHT11初始化
uint8_t Temp_H = 0;
uint8_t Temp_L = 0;
uint8_t Humi_H = 0;
uint8_t Humi_L = 0;
while (1)
{
// 獲取數據
DHT11ReadData(&Temp_H,&Temp_L,&Humi_H,&Humi_L);
// 顯示溫濕度數據
OLED_ShowNum(1,7,Temp_H,2); // 溫度 整數部分
OLED_ShowNum(1,10,Temp_L,1); // 溫度 小數部分
OLED_ShowNum(2,7,Humi_H,2); // 濕度 整數部分
OLED_ShowNum(2,10,Humi_L,1); // 濕度 小數部分
}
}
實驗效果
實驗效果如下圖所示:

參考鏈接
本實驗實現過程中參考:
DHT11詳細介紹(內含51和STM32代碼)-CSDN博客
STM32外接DHT11顯示溫濕度_stm32與dht11連接用了什么協議-CSDN博客
如果有什么問題和建議,還請讀者指出

浙公網安備 33010602011771號