一、前言
最近寫DHT11的代碼到香橙派(全志H616)上,發(fā)現(xiàn)網(wǎng)上案例基本上都是樹莓派+DHT11的居多,香橙派的少,少數(shù)找得到的代碼跑起來也是不穩(wěn)定或者數(shù)據(jù)相對不太準(zhǔn)確,于是這里自己寫了一篇,供大家參考和批評指正
產(chǎn)品概述
DHT11數(shù)字溫濕度傳感器是一款含有已校準(zhǔn)數(shù)字信號輸出的溫濕度復(fù)合傳感器,應(yīng)用領(lǐng)域:暖通 空調(diào);汽車;消費(fèi)品;氣象站;濕度調(diào)節(jié)器;除濕器;家電;醫(yī)療;自動(dòng)控制
特點(diǎn)
- 相對濕度和溫度測量
- 全部校準(zhǔn),數(shù)字輸出
- 長期穩(wěn)定性
- 超長的信號傳輸距離:20米
- 超低能耗:休眠
- 4 引腳安裝:可以買封裝好的
- 完全互換 : 直接出結(jié)果,不用轉(zhuǎn)化
數(shù)據(jù)傳送邏輯
只有一根數(shù)據(jù)線DATA,主控MCU發(fā)送序列指令給DHT11模塊,模塊一次完整的數(shù)據(jù)傳輸為40bit,高位先出
數(shù)據(jù)格式
8bit濕度整數(shù)數(shù)據(jù)+8bit濕度小數(shù)數(shù)據(jù)+8bi溫度整數(shù)數(shù)據(jù)+8bit溫度小數(shù)數(shù)據(jù)+8bit校驗(yàn)和
關(guān)于校驗(yàn):假設(shè)接收到的40位數(shù)據(jù)為:
計(jì)算:
0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001(校驗(yàn)位)
DHT11通信時(shí)序
(1)主機(jī)發(fā)送起始信號
首先單片機(jī)將連接DHT11 DATA引腳的GPIO口輸出低電平,且低電平保持時(shí)間不能小于18ms (t1)最大不能超多30ms,然后拉高數(shù)據(jù)線20~40us (t2) ,等待讀取DHT11的響應(yīng)信號。
(2)檢測從機(jī)應(yīng)答信號
DHT11的DATA引腳檢測到外部信號有低電平(t1),并等待外部低電平信號結(jié)束(t2),延遲后DHT11的DATA引腳處于輸出狀態(tài),之后DHT11開始輸出80 us (t3)的低電平作為應(yīng)答信號,緊接著輸出80us(t4)的高電平通知主機(jī)準(zhǔn)備接收數(shù)據(jù)。
主機(jī)的I/O此時(shí)處于輸入狀態(tài),檢測I/O有低電平(DHT11應(yīng)答信號)后,等待80us的高電平后接受數(shù)據(jù)。
(3)數(shù)據(jù)傳輸
由DHT11的DATA引腳輸出40位數(shù)據(jù),采用高位優(yōu)先方式(MSB),微處理器根據(jù)I/0電平的變化接收40位數(shù)據(jù)。
位數(shù)據(jù)“0”的格式為:50微秒的低電平和26-28us的高電平。
位數(shù)據(jù)“1”的格式為:50微秒的低電平加70us的高電平。
二、代碼
主體代碼主要是利用多線程,用戶每發(fā)送一次數(shù)據(jù)讀取請求,創(chuàng)建一個(gè)線程用于讀取數(shù)據(jù);利于提高代碼的健壯性和擴(kuò)展性;
同時(shí)引入一個(gè)blockFlag的標(biāo)志位,避免子線程代碼跑飛無法退出的問題;
并且在測試過程中發(fā)現(xiàn)有偶爾測試出溫度明顯錯(cuò)誤的數(shù)據(jù);考慮到可能是由于環(huán)境、傳感器、延時(shí)誤差等原因?qū)е碌臄?shù)據(jù)不準(zhǔn)確問題,所以程序中會(huì)將超過50°C的數(shù)據(jù)視為無效數(shù)據(jù),自行重新測試,最多自行重試5次
GPIO初始化
因?yàn)榘l(fā)送給DHT11的起始信號是先拉低電平,所以拉低電平前先維持一個(gè)穩(wěn)定的高電平狀態(tài)
void GPIO_init(int gpio_pin)
{
pinMode(gpio_pin, OUTPUT); // set mode to output
digitalWrite(gpio_pin, 1); // output a high level
delay(1000);
}
起始信號
因?yàn)镈HT11的觸發(fā)是單次的,即每發(fā)送一次起始信號,才會(huì)檢查一次溫濕度,所以發(fā)送起始信號的代碼必然是要多次復(fù)用,所以這里也封裝成一個(gè)函數(shù)文章來源:http://www.zghlxwxcb.cn/news/detail-668280.html
void DHT11_Start_Sig()
{
pinMode(pinNumber,OUTPUT); //讓GPIO為輸出模式
digitalWrite(pinNumber,HIGH);
digitalWrite(pinNumber,LOW);
delay(25); //維持25ms的低電平
digitalWrite(pinNumber,HIGH); //轉(zhuǎn)化為高電平,等待DHT11的響應(yīng)信號
pinMode(pinNumber,INPUT);
//響應(yīng)信號為80us電平與80us的準(zhǔn)備信號
pullUpDnControl(pinNumber,PUD_UP); //進(jìn)行上拉,增加穩(wěn)定性,非必選
delayMicroseconds(35); //維持35微秒
}
讀取數(shù)據(jù)
void* readSensorData(void *arg)
{
uint8 crc;
uint8 i;
int attempt = 5; //調(diào)用一次最多嘗試測5次
while(attempt)
{
databuf = 0; //清空數(shù)據(jù)存儲buf
crc = 0; //清空校驗(yàn)位數(shù)據(jù)存儲buf
DHT11_Start_Sig();
if(digitalRead(pinNumber)==0) //檢測DHT11是否應(yīng)答,應(yīng)答則繼續(xù)下一步
{
while(!digitalRead(pinNumber)); //wait to high
//讀取4個(gè)數(shù)據(jù),合計(jì)32位
for(i=0;i<32;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME); //如果32微秒后,仍然檢測到是高電平,則該數(shù)據(jù)位為1
databuf*=2; //移位到buf的更高位
if(digitalRead(pinNumber)==1) //1
{
databuf++;
}
}
//讀取校驗(yàn)位
for(i=0;i<8;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
crc*=2;
if(digitalRead(pinNumber)==1) //1
{
crc++;
}
}
//用于校驗(yàn)數(shù)據(jù)的準(zhǔn)確性,當(dāng)溫度大于50時(shí),視為無效數(shù)據(jù)
if(((databuf>>8)&0xff) > 50)
{
attempt--;
delay(500); //不加這段延遲,下一次傳感器來不及響應(yīng)
continue;
}
else
{
//打印數(shù)據(jù)
printf("Congratulations ! Sensor data read ok!\n");
printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff);
printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
blockFlag = 0; //用來避免程序有時(shí)候跑飛,卡在此函數(shù)中,無法跳出
return (void*)1;
}
}
else //dht not answer
{
blockFlag = 0;
printf("Sorry! Sensor dosent ans!\n");
return (void*)0;
}
}
//如果代碼執(zhí)行到這里,則證明嘗試讀取了5次數(shù)據(jù),都是不準(zhǔn)確的數(shù)據(jù)
blockFlag = 0;
printf("get data fail\n");
return (void*)2;
}
整體代碼
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "contrlDevices.h"
typedef unsigned char uint8;
typedef unsigned int uint16;
typedef unsigned long uint32;
#define HIGH_TIME 32
int pinNumber = 6; //use gpio1 to read data
uint32 databuf;
int blockFlag;
void GPIO_init(int gpio_pin)
{
pinMode(gpio_pin, OUTPUT); // set mode to output
digitalWrite(gpio_pin, 1); // output a high level
delay(1000);
//return;
}
void DHT11_Start_Sig()
{
pinMode(pinNumber,OUTPUT);
digitalWrite(pinNumber,HIGH);
digitalWrite(pinNumber,LOW);
delay(25);
digitalWrite(pinNumber,HIGH);
pinMode(pinNumber,INPUT);
//響應(yīng)信號為80us電平與80us的準(zhǔn)備信號
pullUpDnControl(pinNumber,PUD_UP);
delayMicroseconds(35);
}
void* readSensorData(void *arg)
{
uint8 crc;
uint8 i;
int attempt = 5; //調(diào)用一次最多嘗試測5次
while(attempt)
{
databuf = 0; //清空數(shù)據(jù)存儲buf
crc = 0; //清空校驗(yàn)位數(shù)據(jù)存儲buf
DHT11_Start_Sig();
if(digitalRead(pinNumber)==0) //檢測DHT11是否應(yīng)答,應(yīng)答則繼續(xù)下一步
{
while(!digitalRead(pinNumber)); //wait to high
//讀取4個(gè)數(shù)據(jù),合計(jì)32位
for(i=0;i<32;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME); //如果32微秒后,仍然檢測到是高電平,則該數(shù)據(jù)位為1
databuf*=2; //移位到buf的更高位
if(digitalRead(pinNumber)==1) //1
{
databuf++;
}
}
//讀取校驗(yàn)位
for(i=0;i<8;i++)
{
while(digitalRead(pinNumber)); //data clock start
while(!digitalRead(pinNumber)); //data start
delayMicroseconds(HIGH_TIME);
crc*=2;
if(digitalRead(pinNumber)==1) //1
{
crc++;
}
}
//用于校驗(yàn)數(shù)據(jù)的準(zhǔn)確性,當(dāng)溫度大于50時(shí),視為無效數(shù)據(jù)
if(((databuf>>8)&0xff) > 50)
{
attempt--;
delay(500); //不加這段延遲,下一次傳感器來不及響應(yīng)
continue;
}
else
{
//打印數(shù)據(jù)
printf("Congratulations ! Sensor data read ok!\n");
printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff);
printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
blockFlag = 0; //用來避免程序有時(shí)候跑飛,卡在此函數(shù)中,無法跳出
return (void*)1;
}
}
else //dht not answer
{
blockFlag = 0;
printf("Sorry! Sensor dosent ans!\n");
return (void*)0;
}
}
//如果代碼執(zhí)行到這里,則證明嘗試讀取了5次數(shù)據(jù),都是不準(zhǔn)確的數(shù)據(jù)
blockFlag = 0;
printf("get data fail\n");
return (void*)2;
}
int main ()
{
pthread_t tid;
int waitTimes = 10;
char cmd[5] = {'\0'};
if (wiringPiSetup() == -1)
{
printf("Setup wiringPi failed!");
return 1;
}
printf("Enter OS-------\n");
while(1)
{
waitTimes = 10;
blockFlag = 1;
delay(1000);
printf("input y\n");
scanf("%s",cmd);
getchar();
if(strcmp(cmd,"y") == 0)
{
//創(chuàng)建一個(gè)線程用于讀取傳感器數(shù)據(jù);
//嚴(yán)謹(jǐn)來說此處的tid并發(fā)時(shí)有bug;讀者可以自行優(yōu)化,可以用互斥鎖或者tid設(shè)為數(shù)組等都行
//就當(dāng)作留給讀者的一個(gè)小作業(yè)吧
if (pthread_create(&tid, NULL, readSensorData, NULL) != 0)
{
printf("thread create fail!\n");
return -1;
}
//等待數(shù)據(jù)讀取線程10s鐘,如果10后blockFlag未置0,則說明讀數(shù)據(jù)時(shí)跑飛卡住了
while(waitTimes && blockFlag)
{
delay(1000);
waitTimes--;
}
//強(qiáng)行結(jié)束跑飛的線程
if(blockFlag == 1)
{
pthread_cancel(tid);
printf("線程超時(shí)退出.....\n");
}
}
else
{
printf("go on\n");
continue;
}
}
return 0;
}
執(zhí)行結(jié)果
參考文章:
STM32一線協(xié)議-DHT11溫濕度傳感器采樣實(shí)現(xiàn)文章來源地址http://www.zghlxwxcb.cn/news/detail-668280.html
到了這里,關(guān)于【香橙派+DHT11】香橙派(全志H616)+ DHT11溫濕度傳感器的驅(qū)動(dòng)教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!