STM32F103讀取DHT22溫溼度感測器引數

2020-10-25 08:00:38

STM32F103讀取DHT22溫溼度感測器引數

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;
}

響應超時的設定是為了便於偵錯,若各位不需要可直接刪除。

(有不足之處,還請各位老兄不吝賜教)