第一个传感----DHT11
通过前面的学习,你已经学会了控制IO口、延时函数、串口的收发。接下来,你就可以借助以上的知识点完成自己的第一个传感器--DHT11啦!

DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。应用非常广泛,并且具有长期的稳定性。
1.引脚说明
VDD------电源 3.3-5v
GND------接地
DATA-----数据
2.数据格式
DHT11采用的是单总线进行通信,即只有一条数据线,一次传送40位的数据,高位先出。
“8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据 + 8bit 校验位”。其中,8bit的校验位是由前32位的位分别相加所得。


注:其中湿度小数部分为 0。
3.数据时序图:


注:主机从 DHT11 读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值。
4.读取步骤:(假设DATA 数据线的引脚接入到P22)
首先让接DHT11 中 P22空闲时保持一段时间的高电平越过不稳定状态。

(1)起始信号:
拉低P22端口22ms(这里只要在18ms-30ms就行),然后再拉高P22

(2)响应信号:
DHT11传感器会将P22引脚拉低83us,再拉高87us。

(3)40位数据:
接着DHT11传感器会连续发送40位数据,高位先出。
其中:54us低电平和23-27us的高电平表示位数据“1”
54us的低电平加上68-74us的高电平表示数据“0”
这里,我们如何用代码来实现这个操作呢?
我们可以在上升沿开始计数>40(最好是40-60之间),计数完成后判断此时P22的引脚是高电平还是低电平,若是高电平则为数据“1”,若低电平则为数据“0”。

(4)结束信号
将P22拉高即可,这里我们在前面使用空闲状态去代替了结束信号
(5)数据转换
我们所得到的初数据是八位的二进制,范围是0-255,为了能够在串口发送,我们需要将它转换为字符型,方便显示。

(6)校验位+串口发送
判断 8bit 校验位是否等于“8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据 ”,如果不相等则说明此时接收的数据有问题。如果没问题的话,就用之前UART发送字符串的方法发送给电脑,以供显示。

5.案例工程代码:(建议用逻辑分析仪看波形)
main.c
/****************************数据类型******************************
unsigned char   uchar   8   0~255
signed   char   char    8   -128~127
unsignec int    uint    16  0~65535
signed   int    int     16  -32768~32767
******************************************************************/
#include "main.h"
/************************函数声明*************************************/
void STC8H_IOinit(void);            //STC15W单片机引脚初始化函数
/*===========================================================*/
/*函数:void STC32G_IOinit(void)
/*功能:STC18H单片机引脚初始化函数
/*说明:统一将单片机所有引脚初始化为准双向口工作模式
/*修改日期:2023.10.16
/*===========================================================*/
void STC8H_IOinit(void)
{P0M1 = 0;   P0M0 = 0;   //设置为准双向口   参考用户手册387页P1M1 = 0;   P1M0 = 0;   //设置为准双向口   参考用户手册387页P2M1 = 0;   P2M0 = 0;   //设置为准双向口   参考用户手册387页P3M1 = 0;   P3M0 = 0;   //设置为准双向口   参考用户手册387页P4M1 = 0;   P4M0 = 0;   //设置为准双向口   参考用户手册387页P5M1 = 0;   P5M0 = 0;   //设置为准双向口   参考用户手册387页P6M1 = 0;   P6M0 = 0;   //设置为准双向口   参考用户手册387页P7M1 = 0;   P7M0 = 0;   //设置为准双向口   参考用户手册387页  P0 = 0XFF;  P1 = 0XFF;  P2 = 0XFF; P3 = 0XFF;  P4 = 0XFF;  P5 = 0XFF; P6 = 0XFF;  P7 = 0XFF;                      //设置P0~P7所有I/O口为高电平
}
/****************************************************************************************************
/*函数:void main()
/*功能:40MHZ
/*说明:主函数
/*修改日期:
/****************************************************************************************************/
void main() 
{DHT11_ObjectTypedef hDHT11; STC8H_IOinit();DHT11_Init(hDHT11.rdata);while(1) {if(DHT11_Read(hDHT11.rdata)){if (hDHT11.rdata[0]+hDHT11.rdata[1]+hDHT11.rdata[2]+hDHT11.rdata[3]==hDHT11.rdata[4]) {SendString("湿度:");SendString(intToStr(hDHT11.rdata[0]));SendString(".");SendString(intToStr(hDHT11.rdata[1]));SendString("% ");SendString("温度:");SendString(intToStr(hDHT11.rdata[2]));SendString(".");SendString(intToStr(hDHT11.rdata[3]));SendString("C");}}Delay_ms(500);//40MHZ}
} 
main.h
#ifndef __MAIN_H__ #define __MAIN_H__  #include "STC8H.h" //单片机头文件(包含单片机的特殊功能寄存器的定义) #include "intrins.h"//nop头文件 #include "DHT11.h" #include "UART.h" #include "Delay.h"   typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32;  #endif
DHT11.C
#include "main.h"
void DHT11_Init(unsigned char* arr)
{   unsigned char i;for(i=0;i<5;i++){arr[i]=0;}
}
//该DHT11在40MHZ运行
unsigned char DHT11_Read(unsigned char* arr)
{unsigned char out,i=0,j=0,rdata=0;
//空闲状态P22 = 1;Delay_us(2);// 起始信号    P22 = 0;//拉低P22Delay_ms(22); //延时22ms               P22 = 1;   
// 应答信号Delay_30us(1);out=0;while(!P22)//等待传感器拉低P22{Delay_us(1);        if(out++ > 100) //while内部用于计时,如果超过100us则判断应答失败,应该重新发送起始信号return 0;}out = 0;while(P22) //等待传感器拉高P22{Delay_us(1);        if(out++>100) //while内部用于计时,如果超过100us则判断应答失败,应该重新发送起始信号return 0;}
// 读取数据for(j=0;j<5;j++)//读取5个字节{rdata=0;//暂存8位的变量for(i=0;i<8;i++)//读取8位 {      out=0;while(!P22)//等待P22拉高{Delay_us(1);        if (out++>100)//while内部用于计时,如果超过100us则判断应答失败,应该重新发送起始信号return 0;}Delay_us(40);rdata<<=1;    if(P22)//如果此时P22为高电平,则判断为数据为“1” {rdata|=0x01;}out=0;while(P22)//等待P22拉低,以读取下一个位 {Delay_us(1);        if(out++ > 100)//while内部用于计时,如果超过100us则判断应答失败,应该重新发送起始信号return 0;}}arr[j]=rdata;//将暂存的(1字节)数据分别放入数组中}return 1;
}
char* intToStr(unsigned char num) {static char str[4]; int i=3;str[i]='\0';//结尾符i--;
if (num==0) {str[i]='0';i--;} else {while (num>0) {str[i]='0'+(num%10);num/=10;i--;}}return &str[i+1];//从下标0开始
} 
DHT11.h
#ifndef __DHT11_H__
#define __DHT11_H__
typedef struct Dht11
{unsigned char rdata[5];//数据位+校验位40位数据}DHT11_ObjectTypedef;
void DHT11_Init(unsigned char* rdata);
//该DHT11在40MHZ运行
unsigned char DHT11_Read(unsigned char* rdata);
char* intToStr(unsigned char num);
#endif 
UART.C
#include "main.h"
//串口发送一个字符
void SendChar(char dat)
{unsigned char i;//起始信号P31=0;Pote(1);
//发送8bitfor(i=0; i<8; i++) {P31 = dat & 0x01;Pote(1);dat >>= 1;}//结束信号P31=1;Pote(1);
}
//串口发送字符串
void SendString(char *s)
{while (*s) {SendChar(*s);  s++;                            }
} 
UART.H
#ifndef __UART_H__ #define __UART_H__    //串口发送一个字符 void SendChar(char dat); //串口发送字符串 void SendString(char *s); #endif
Delay.c
#include "main.h"
void Pote(unsigned int time)//40mhz
{unsigned int i;unsigned int j;for(j=0;j<time;j++){for(i=0;i<462;i++){_nop_();}}
}
void Delay_ms(unsigned int time)//40mhz
{unsigned int i;unsigned int j;for(j=0;j<time;j++){for(i=0;i<4973;i++){_nop_();}}
}
void Delay_us(unsigned int time)//40mhz
{unsigned int i;unsigned int j;for(j=0;j<time;j++){for(i=0;i<1;i++){_nop_();}}
}
void Delay_30us(unsigned int time)//40mhz
{unsigned int i;unsigned int j;for(j=0;j<time;j++){for(i=0;i<107;i++){_nop_();}}
}
 
Delay.h
#ifndef __DELAY_H__ #define __DELAY_H__  void Pote(unsigned int time);//40mhz void Delay_ms(unsigned int time);//40mhz void Delay_us(unsigned int time);//40mhz void Delay_30us(unsigned int time);//40mhz  #endif 
6.结果展示
