1、首先通過DHT22的說明書獲取操作流程 (覺得文字太多可直接向下翻,有完整.c和.h )
通訊啟動規則:
它有一條通訊匯流排,空閒時匯流排為高電平,傳送一次開始訊號後,DHT22從低功耗模式轉換到高速模式,通訊開始時主機(MCU)拉低匯流排500us後釋放匯流排,延時20-40us後主機開始檢測從機(DHT22)的響應訊號。 從機的響應訊號是一個80us左右的低電平,隨後從機在拉高匯流排80us左右代表即將進入資料傳送。
資料傳送規則:
首先傳送50us左右的低電平時隙,它代表資料位的起始,其後的高電平的長度決定資料位所代表的數值,70us的高電平代表1,26-28us的高電平代表0。 共40bit資料,當最後一Bit資料傳送完畢後,從機將再次拉低匯流排50us左右。
下面進行分析:
(1)只有一條資料匯流排,所以埠既做輸入也做輸出,功能有拉高、拉低和讀取
//其中CRH用來設定高8-15引腳,CRL用來設定0-7引腳,這裡是PA0,選擇CRL暫存器
#define DHT22_IO_IN() {DHT22_PORT->CRL&=0XFFFFFFF0;DHT22_PORT->CRL|=8<<0;}//設為輸入
#define DHT22_IO_OUT() {DHT22_PORT->CRL&=0XFFFFFFF0;DHT22_PORT->CRL|=3<<0;}//設為輸出
#define DHT22_IO_HIGH() GPIO_SetBits(DHT22_PORT,DHT22_PIN); //輸出1,拉高
#define DHT22_IO_LOW() GPIO_ResetBits(DHT22_PORT,DHT22_PIN); //輸出0,拉低
#define DHT22_IO_READ (GPIO_ReadInputDataBit(DHT22_PORT,DHT22_PIN)) //讀埠值
(2)空閒為高,初始化埠時讓埠輸出高電平。
void DHT22_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DHT22_PORT_CLK, ENABLE); //使能PORTA口時鐘
GPIO_InitStructure.GPIO_Pin = DHT22_PIN; //PORTA0 推輓輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT22_PORT, &GPIO_InitStructure);
DHT22_IO_HIGH(); //輸出1
}
(3)開始訊號,拉低500us,再拉高等待20到40us,等待感測器響應。
static void DHT22_Awake(void)//喚醒
{
DHT22_IO_OUT();//設為輸出
DHT22_IO_LOW();//拉低500us,啟用感測器
delay_us(500);
DHT22_IO_HIGH();//拉高,釋放匯流排
delay_us(30);
DHT22_IO_IN();//等待20到40us設為輸入,等待響應
}
(4)感測器響應,先拉低80us,再拉高80us
while(DHT22_IO_READ);//檢測響應訊號,感測器拉低80us
while((!DHT22_IO_READ));//感測器將匯流排拉高80us,標誌馬上進入資料傳輸
(5)感測器傳送40bit的資料,根據規則進行讀取
for(i = 0;i < 40;i ++)//獲取40bit資料
{
while(DHT22_IO_READ);//等待50us低電平間隙到來
while(!DHT22_IO_READ);//間隙結束,等待資料高電平到來
//高電平保持26~28us表示低電平,保持70us表示高電平。
delay_us(30);//這裡等待30us,越過28us(但不能大於28+50),直接判斷是不是1
if(DHT22_IO_READ)//讀取當前匯流排的狀態,是不是為1
{
//為真則:
data <<= 1;//右移1位空出末位
data |= 1;//把1添入末位
}
else
//為假則:
data <<= 1;右移1位空出末位,末位就是0
}
(6)最後依舊釋放匯流排,以便下次資料獲取
DHT22_IO_OUT();//設為輸出
DHT22_IO_HIGH();//拉高,釋放匯流排
2、完整.c和.h
dht22.h
#ifndef __DHT22_H
#include "common.h"
#define DHT22_PORT GPIOA
#define DHT22_PIN GPIO_Pin_0
#define DHT22_PORT_CLK RCC_APB2Periph_GPIOA
//其中CRH用來設定高8-15引腳,CRL用來設定0-7引腳,這裡是PA0,選擇CRL暫存器
#define DHT22_IO_IN() {DHT22_PORT->CRL&=0XFFFFFFF0;DHT22_PORT->CRL|=8<<0;}//設為輸入
#define DHT22_IO_OUT() {DHT22_PORT->CRL&=0XFFFFFFF0;DHT22_PORT->CRL|=3<<0;}//設為輸出
#define DHT22_IO_HIGH() GPIO_SetBits(DHT22_PORT,DHT22_PIN); //輸出1
#define DHT22_IO_LOW() GPIO_ResetBits(DHT22_PORT,DHT22_PIN); //輸出0
#define DHT22_IO_READ (GPIO_ReadInputDataBit(DHT22_PORT,DHT22_PIN)) //讀埠值
void DHT22_Init(void);
u8 DHT22_GetValue(int* temp,u16* humi);
#endif
dht22.c
#include "dht22.h"
/*************************
*作 用:初始化感測器引腳
*參 數:無
*返回值:無
**************************/
void DHT22_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DHT22_PORT_CLK, ENABLE); //使能PORTA口時鐘
GPIO_InitStructure.GPIO_Pin = DHT22_PIN; //PORTA0 推輓輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT22_PORT, &GPIO_InitStructure);
DHT22_IO_HIGH(); //輸出1
}
/**************************
*作 用:喚醒感測器
*參 數:無
*返回值:無
***************************/
static void DHT22_Awake(void)
{
DHT22_IO_OUT();//設為輸出
DHT22_IO_LOW();//拉低500us,啟用感測器
delay_us(500);
DHT22_IO_HIGH();//拉高,釋放匯流排
delay_us(30);
DHT22_IO_IN();//等待20到40us設為輸入,等待響應
}
/************************************************
*作 用:與DHT感測器通訊,獲取資料
*參 數:無
*返回值:返回獲取的40位資料,小於0xff表示失敗
*************************************************/
static uint64_t DHT22_ReadData(void)
{
u8 waitTime = 0;//響應等待時間
u16 i = 0;//迴圈變數
uint64_t data = 0;//資料儲存變數
DHT22_Awake();//感測器喚醒
waitTime = 100;//響應超時時間設定
while(DHT22_IO_READ)//檢測響應訊號,感測器拉低80us
{
if((waitTime--) > 0)
delay_us(1);
else
return 1;
}
waitTime = 100;//響應超時時間設定
while((!DHT22_IO_READ))//感測器將匯流排拉高80us,標誌馬上進入資料傳輸
{
if((waitTime--) > 0)
delay_us(1);
else
return 2;
}
for(i = 0;i < 40;i ++)//獲取40bit資料
{
waitTime = 100;//響應超時時間設定
while(DHT22_IO_READ)//等待50us低電平間隙到來
{
if((waitTime--) > 0)
delay_us(1);
else
return 3;
}
waitTime = 50;//響應超時時間設定
while(!DHT22_IO_READ)//間隙結束,等待資料高電平到來
{
if((waitTime--) > 0)
delay_us(1);
else
return 3;
}
//高電平保持26~28us表示低電平,保持70us表示高電平。
delay_us(30);//這裡等待30us,越過28us(但不能大於28+50),直接判斷是不是1
if(DHT22_IO_READ)//讀取當前匯流排的狀態
{
data <<= 1;//右移1位空出末位
data |= 1;//把1添入末位
}
else
data <<= 1;右移1位空出末位,末位就是0
}
DHT22_IO_OUT();//設為輸出
DHT22_IO_HIGH();//拉高,釋放匯流排
return data;
}
/*****************************************
*作 用:獲取溫溼度感測器的檢測值
*參 數:temp:用於獲取溫度值的變數地址
humi:用於獲取溼度值得變數地址
*返回值:返回0成功,非0獲取失敗
******************************************/
u8 DHT22_GetValue(int* temp,u16* humi)
{
uint64_t data1 = 0;
data1 = DHT22_ReadData();
if(data1 < 0xff)//判斷資料接收是否成功,大於0xff代表成功
{
return (u8)data1;//
}
//去掉後24位元資料保留16位元溼度資料
*humi = (u16)(data1 >> 24) & 0x00ffff;
//去掉前16位元溼度資料和後8位元校驗資料,保留16位元溫度資料
*temp = (u16)(data1 >> 8) & 0x00ffff;
return 0;
}
響應超時的設定是為了便於偵錯,若各位不需要可直接刪除。
(有不足之處,還請各位老兄不吝賜教)