與DS18B20一樣DHT11也是采用單總線,但所不同的是DHT11可同時實作溫度和濕度的檢測,在我們的產品中經常使用它來檢測環境的溫濕度資訊,這一篇我們將設計并封裝DHT11的驅動程式,以方便重復使用,
1、功能概述
DHT11數字溫濕度傳感器是一款含有已校準數字信號輸出的溫濕度復合傳感器,它應用專用的數字模塊采集技術和溫濕度傳感技術,確保產品具有極高的可靠性與卓越的長期穩定性,
1.1、硬體描述
傳感器包括一個電阻式感濕元件和一個NTC測溫元件,并與一個高性能8位單片機相連接,因此該產品具有品質卓越、超快回應、抗干擾能力強、性價比極高等優點,每個DHT11傳感器都在極為精確的濕度校驗室中進行校準,校準系數以程式的形式儲存在OTP記憶體中,傳感器內部在檢測信號的處理程序中要呼叫這些校準系數,單線制串行介面,使系統集成變得簡易快捷,超小的體積、極低的功耗,信號傳輸距離可達20米以上,使其成為各類應用甚至最為苛刻的應用場合的最佳選擇,產品為4針單排引腳封裝,

DHT11的供電電壓為 3-5.5V,傳感器上電后,要等待 1s 以越過不穩定狀態在此期間無需發送任何指令,電源引腳(VDD,GND)之間可增加一個100nF 的電容,用以去耦濾波,
1.2、通訊介面
DHT11傳感器單總線通訊建議連接線長度短于20米時用5K上拉電阻,大于20米時根據實際情況使用合適的上拉電阻,連線圖如下:

DATA用于微處理器與DHT11之間的通訊和同步,采用單總線資料格式,一次通訊時間4ms左右,資料分小數部分和整數部分,具體格式在下面說明,當前小數部分用于以后擴展,現讀出為零,一次完整的資料傳輸為40bit,高位在前,資料格式:8bit濕度整數資料+8bit濕度小數資料+8bi溫度整數資料+8bit溫度小數資料+8bit校驗和,

其中,在資料傳送正確時,校驗和資料等于“8bit濕度整數資料+8bit濕度小數資料+8bi溫度整數資料+8bit溫度小數資料”所得結果的末8位,
用戶MCU發送一次開始信號后,DHT11從低功耗模式轉換到高速模式,等待主機開始信號結束后,DHT11發送回應信號,送出40bit的資料,并觸發一次信號采集,用戶可選擇讀取部分資料,通訊程序如下圖所示:

從模式下,DHT11接收到開始信號觸發一次溫濕度采集,如果沒有接收到主機發送開始信號,DHT11不會主動進行溫濕度采集,采集資料后轉換到低速模式,
2、驅動設計與實作
我們已經了解了DHT11溫濕度傳感器的相關資訊,接下來我們將設計并實作DHT11溫濕度傳感器的驅動程式,
2.1、物件定義
在使用一個物件之前我們需要獲得這個物件,同樣的我們想要操作DHT11溫濕度傳感器就需要先定義DHT11溫濕度傳感器的物件,
2.1.1、物件的抽象
我們要得到DHT11溫濕度傳感器物件,需要先分析其基本特性,一般來說,一個物件至少包含兩方面的特性:屬性與操作,接下來我們就來從這兩個方面思考一下DHT11溫濕度傳感器的物件,
先來考慮屬性,作為屬性肯定是用于標識或記錄物件特征的東西,我們來考慮DHT11溫濕度傳感器物件屬性,DHT11溫濕度傳感器并沒有標識設備區別的特性,只有溫度和濕度資訊可以表示當前的作業狀態我們將其作為屬性,
接著我們還需要考慮DHT11溫濕度傳感器物件的操作問題,我們知道DHT11溫濕度傳感器采用的是單總線,單總線就需要控制總線的輸入輸出方向,而且這對這條總線在不同的輸入輸出方向,我們需要讀資料和寫資料,而這些操作都依賴于硬體平臺,所以我們將他們定義為DHT11溫濕度傳感器物件的操作,處于時序控制的需要,我們需要延時操作函式,而在不同的軟硬體平臺延時操作會有差異,我們也將其作為物件的操作,
根據上述我們對DHT11溫濕度傳感器的分析,我們可以定義DHT11溫濕度傳感器的物件型別如下:
/* 定義DHT11物件型別 */
typedef struct Dht11Object {
float temperature; //溫度值
float humidity; //濕度值
uint8_t (*SetPinOutValue)(DhtPinValueType setValue);//設定DHT11引腳的輸出值
uint8_t (*ReadPinBit)(void);//讀取引腳電平
void (*SetPinDirection)(DHT11IOModeType mode);//設定引腳的輸入輸出方向
void (*Delayms)(volatile uint32_t nTime); /*實作ms延時操作*/
void (*Delayus)(volatile uint32_t nTime); /*實作us延時操作*/
}Dht11ObjectType;
2.1.2、物件初始化
我們知道,一個物件僅作宣告是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮DHT11溫濕度傳感器物件的初始化函式,一般來說,初始化函式需要處理幾個方面的問題,一是檢查輸入引數是否合理;二是為物件的屬性賦初值;三是對物件作必要的初始化配置,據此我們設計DHT11溫濕度傳感器物件的初始化函式如下:
/*DHT11初始化操作*/
DHT11ErrorType InitializeDHT11(Dht11ObjectType *dht, //需要初始化物件
Dht11SetPinOutValueType setPinStatus,//設定總線輸出值
Dht11ReadPinBitType getPinStatus, //讀取總線輸入值
Dht11SetPinModeType mode, //配置總線的輸入輸出模式
Dht11DelayType delayms, //毫秒延時
Dht11DelayType delayus //微秒延時
)
{
if((dht==NULL)||(setPinStatus==NULL)||(getPinStatus==NULL)||(mode==NULL)||(delayms==NULL)||(delayus==NULL))
{
return DHT11_InitError;
}
dht->SetPinOutValue=setPinStatus;
dht->ReadPinBit=getPinStatus;
dht->SetPinMode=mode;
dht->Delayms=delayms;
dht->Delayus=delayus;
dht->humidity=0.0;
dht->temperature=0.0;
ResetDHT11(dht);
return CheckDHT11Status(dht);
}
2.2、物件操作
我們已經完成了DHT11溫濕度傳感器物件型別的定義和物件初始化函式的設計,但我們的主要目標是獲取物件的資訊,接下來我們還要實作面向DHT11溫濕度傳感器的各類操作,
2.2.1、啟動資料通訊
DHT11溫濕度傳感器上電后,總線空閑狀態為高電平,主機把總線拉低等待DHT11回應,主機把總線拉低必須大于18毫秒,保證DHT11能檢測到起始信號,DHT11接收到主機的開始信號后,等待主機開始信號結束,然后發送80us低電平回應信號,主機發送開始信號結束后,延時等待20-40us后, 讀取DHT11的回應信號,主機發送開始信號后,可以切換到輸入模式,或者輸出高電平均可, 總線由上拉電阻拉高,啟動資料通訊的時序圖如下:

/*復位DHT11,開始通訊*/
static void ResetDHT11(Dht11ObjectType *dht)
{
dht->SetPinMode(DHT11_Out); //設定為輸出方式
dht->SetPinOutValue(DHT11_Reset); //將引腳點位拉低
dht->Delayms(20); //拉低至少18ms
dht->SetPinOutValue(DHT11_Set); //拉高
dht->Delayus(30); //主機拉高20至40us
}
DHT11傳感器的DATA引腳檢測到外部信號有低電平時,等待外部信號低電平結束,延遲后DHT11的 DATA引腳處于輸出狀態,輸出80微秒的低電平作為應答信號,緊接著輸出 80 微秒的高電平通知外設準備接收資料,微處理器的 I/O 此時處于輸入狀態,檢測到 I/O 有低電平(DHT11 回應信號)后,等待80微秒的高電平后的資料接收,
/*等待DHT11的回應,回傳1:未檢測到DHT11的存在;回傳0:存在*/
static DHT11ErrorType CheckDHT11Status(Dht11ObjectType *dht)
{
uint8_t retry=0;
dht->SetPinMode(DHT11_In); //設定為輸入方式
while(dht->ReadPinBit()&&(retry<100))
{
retry++;
dht->Delayus(1);
}
if(retry>=100)
{
return DHT11_None;
}
retry=0;
while(!dht->ReadPinBit()&&(retry<100))
{
retry++;
dht->Delayus(1);
}
if(retry>=100)
{
return DHT11_None;
}
return DHT11_NoError;
}
2.2.2、讀取資料位
當主機變為輸入模式后,檢測到總線為低電平,說明DHT11發送回應信號,DHT11發送回應信號后,再把總線拉高80us,準備發送資料,每一bit資料都以50us低電平時隙開始,高電平的長短定了資料位是“0”還是“1”,表示0”和“1”的時序圖如下所示:


/*從DHT11讀取一個位,回傳值:1/0*/
static uint8_t ReadBitFromDHT11(Dht11ObjectType *dht)
{
uint8_t retry=0;
/*等待變為低電平*/
while(dht->ReadPinBit()&&(retry<100))
{
retry++;
dht->Delayus(1);
}
retry=0;
/*等待變高電平*/
while(!dht->ReadPinBit()&&(retry<100))
{
retry++;
dht->Delayus(1);
}
dht->Delayus(40); //延時判斷此位是0還是1
return dht->ReadPinBit();
}
當最后一bit資料傳送完畢后,DHT11拉低總線50us,隨后總線由上拉電阻拉高進入空閑狀態,
3、驅動的使用
我們已經實作了DHT11溫濕度傳感器的驅動,接下來將以此驅動為基礎設計了簡單的測驗應用,
3.1、宣告并初始化物件
使用基于物件的操作我們需要先得到這個物件,所以我們先要使用前面定義的DHT11溫濕度傳感器物件型別宣告一個DHT11溫濕度傳感器物件變數,具體操作格式如下:
Dht11ObjectType dht;
宣告了這個物件變數并不能立即使用,我們還需要使用驅動中定義的初始化函式對這個變數進行初始化,這個初始化函式所需要的輸入引數如下:
Dht11ObjectType *dht,需要初始化物件
Dht11SetPinOutValueType setPinStatus,設定總線輸出值
Dht11ReadPinBitType getPinStatus,讀取總線輸入值
Dht11SetPinModeType mode,配置總線的輸入輸出模式
Dht11DelayType delayms,毫秒延時
Dht11DelayType delayus,微秒延時
對于這些引數,物件變數我們已經定義了,剩下的輸入引數就是我們操作中需要的函式,這幾個函式需要我們在應用中定義,并將函式指標作為引數,這幾個函式的型別如下:
typedef uint8_t (*Dht11SetPinOutValueType)(DhtPinValueType setValue);//設定DHT11引腳的輸出值
typedef uint8_t (*Dht11ReadPinBitType)(void);//讀取引腳電平
typedef void (*Dht11SetPinModeType)(DHT11IOModeType mode);//設定引腳的輸入輸出方向
typedef void (*Dht11DelayType)(volatile uint32_t nTime); /*實作ms延時操作*/
對于這幾個函式我們根據樣式定義就可以了,具體的操作可能與使用的硬體平臺有關系,我們在STM32F4及HAL庫環境下使用,具體函式定義如下:
//設定DHT11引腳的輸出值
uint8_t Dht11SetPinOutValue(DhtPinValueType setValue)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,(GPIO_PinState)setValue);
}
//讀取引腳電平
uint8_t Dht11ReadPinBit(void)
{
return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_11);
}
//設定引腳的輸入輸出方向
void Dht11SetPinMode(DHT11IOModeType mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_11;
if(mode==DHT11_In)
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else
{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
}
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
對于延時函式我們可以采用各種方法實作,我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函式,微秒延時函式則使用我們自己定義的,于是我們可以呼叫初始化函式如下:
InitializeDHT11(&dht,Dht11SetPinOutValue,Dht11ReadPinBit,Dht11SetPinMode,HAL_Delay,Delayus);
3.2、基于物件進行操作
我們定義了物件變數并使用初始化函式給其作了初始化,接著我們就來考慮操作這一物件獲取我們想要的資料,我們在驅動中已經將獲取資料并轉換為轉換值的比例值,接下來我們使用這一驅動開發我們的應用實體,
/*獲取資料值*/
void GetMeasureDataFromDHT11(void)
{
float temperature; //溫度值
float humidity; //濕度值
GetProcessValueFromDHT11(&dht);
temperature=dht.temperature;
humidity=dht.humidity;
}
4、應用總結
我們已經實作了DHT11溫濕度傳感器的驅動,并在此基礎上設計了簡單的驗證應用,經過測驗,利用驅動我們成功的讀取了溫濕度資料,
根據資料手冊的要求,DHT11溫濕度傳感器上電后要等待1S以越過不穩定狀態在此期間不能發送任何指令,
單總線資料傳輸時,會改變總線的輸入輸出方向,在我們的應用中,我們修改了對應GPIO引腳的輸入輸出模式,事實上如果我們在STM32中使用時,我們可將該引腳配置為開漏輸出模式,加上總線的上拉電阻,可以在不修改GPIO的輸入輸出模式的情況下實作讀寫,
原始碼下載:https://github.com/foxclever/ExPeriphDriver
歡迎關注:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/182900.html
標籤:其他
上一篇:藍牙電話協議HFP(Hands-Free Profile) 獲取本地號碼/獲取語音信箱號碼/獲取通話時號碼(CLCC)
下一篇:【單片機學習】第一課:課程介紹
