前段時間開發遠端醫療系統(Dr.Cloud)就曾用到過溫濕度傳感器,不過當時考慮到內建難度,選了一個RS485接口的傳感器,該傳感器實作了Modbus Rtu Slave的功能,隻要客戶程式實作Modbus Rtu Client即可讀出溫濕度資料。是友善了開發,不過價格不菲,要價要150元左右。
網友fangyuan推薦了一款僅7元的DHT11溫濕度傳感器,剛開始以為是TTL電平的序列槽通信,後來一研究,原來通過一根資料線的雙向通信,并且對時序要求很嚴格,這東西也許用單片做更容易,用STM32來做,反而有老虎吃天,無從下口之感。想來想去,最理想的方式也許需要深入研究時鐘的輸入捕獲相關知識了。
不過直到三個多月之後的今天,才有時間和精力去研究相關内容。閑言少叙,先看一下DHT11的典型應用電路(見下圖)。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLzhTZThTO4EzMykDOyEzXw8CX48CXxEDMxAjMvw1ckF2bsBXdvwFdl5mLuR2cj5Set1yZtl2Lc9CX6MHc0RHaiojIsJye.gif)
通信接口為串行接口(單線雙向),通信過程如下:
數字0和數字1的信号表示方法如下(有點類似紅外遙控器的編碼了):
一次完整的資料傳輸為40bit,高位先出。
資料格式:8bit濕度整數資料+8bit濕度小數資料
+8bi溫度整數資料+8bit溫度小數資料
+8bit校驗和
其工作模式為:使用者MCU發送一次開始信号後,DHT11從低功耗模式轉換到高速模式,等待主機開始信号結束後,DHT11發送響應信号,送出40bit的資料,并觸發一次信号采集,使用者可選擇讀取部分資料.從模式下,DHT11接收到開始信号觸發一次溫濕度采集,如果沒有接收到主機發送開始信号,DHT11不會主動進行溫濕度采集.采集資料後轉換到低速模式。
單片機讀取資料的一般思路就是,延時一定的時間,去讀Data資料線的電平是高是低,進而判斷資料是0還是1。不過這樣做,對延時精度要求很高,否則很容易把起始位當成資料位來判斷了。
但是在STM32系統,由于存在多任務和中斷程式,如果用傳統的延時方法,是很難滿足多任務下資料讀取需求的,是以我最直接的思路還是,通過時鐘的輸入捕獲功能,擷取Data資料線上所有上升沿和下降沿的精确時間,有了這個資料,就可以非常精确的判斷出數字0和數字1了。
設定時鐘的代碼如下:
//72M/(71+1)=1M 1us一次滴答
CPU_TIMER_Initialize(timer,0xFFFF,71,TIMER_ISR,NULL);
STM32F10x_TIMER &tim=STM32F10x::TIMER(timer);
//TIM3_CH3
tim.CCMR[1] = 0x01 | 0x30; //輸入模式,濾波為8 映射到TI3上
tim.CCER = 0x0200; //下降沿有效
tim.DIER = 0x0008; //中斷
//tim.CCER |= 0x0100; //捕獲使能
//TIM3_CH4
tim.CCMR[1] |= 0x0200 | 0x3000; //輸入模式,濾波為8 映射到TI3上
tim.CCER |= 0x0000; //上升沿有效
tim.DIER |= 0x0010;
//tim.CCER |= 0x1000; //捕獲使能
中斷TIMER_ISR函數裡,記下中斷發生時刻的計數器的值,根據這些值,就可以換算為實際的溫濕度資料了,相關代碼如下:
STM32F10x_TIMER &tim=STM32F10x::TIMER(timer);
time_index = 0;
HT_Flag = FALSE;
tim.CCER |= 0x0100; //捕獲使能
tim.CCER |= 0x1000; //捕獲使能
Sleep(10000); //延時 10ms 最長 80+80+40*(50+70) = 4960us = 4.96ms
tim.CCER &= ~0x0100; //捕獲禁止
tim.CCER &= ~0x1000; //捕獲禁止
tim.CR1 = 0x0; //禁止計數
if(time_index>8)
{
UINT32 index=0;
UINT32 data[128];
for(int i=4;i<time_index;i+=2)
{
time_data[index++]=time_data[i];
}
if(index==40)
for(int i=0;i<5;i++)
{
HT_data[i]=0;
for(int j=0;j<8;j++)
{
HT_data[i]|= time_data[i*8+j] > 40 ? (1 <<(7-j)) :0;
}
}
HT_Flag = (((HT_data[0]+HT_data[1]+HT_data[2]+HT_data[3]) & 0xFF) == HT_data[4]);
}
}
接線圖和運作後的結果如下圖所示,可以看出溫濕度已經源源不斷的被讀出來了。
是以,我打算專門寫一篇這種接口封裝的博文,以期應用開發的朋友,也可以開發出這種對時序要求嚴格的應用來。