一、参数
通信协议:单总线
测量范围:20-90%,0-50℃
精度:±5%,±2℃
二、串行接口(单线双向)
DATA 数据引脚用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零。
操作流程如下:一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
用户MCU发送一次开始信号后, DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后, DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下, DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号, DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
通信可以分为三个过程:
1.通信的开始
2.通信的内容
3.通信的结束
时序图
(1)PG9引脚变为输出模式,PG9引脚变为低电平,持续至少18s,推荐20us,PG9引脚变为高电平,持续20us-40us,推荐30us
(2)PG9引脚变为输入模式检查DFT11的响应信号,低电平持续80us,高电平持续80us,也就是说DFT11有响应,如果没有响应,就一直为高电平。
数字0信号表示
PG9引脚保持输入模式,识别低电平50us,高电平26-28us,就为bit0
数字1信号表示
PG9引脚保持输入模式,识别低电平50us,高电平70us,就为bit1
通信的结束
PG9引脚输入模式识别低电平50us,PG9引脚输出模式输出高电平
三、代码
1.配置引脚的输入输出模式,记得在打开时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
void dht11_outputmode(void)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
void dht11_inputmode(void)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
2.编写
(1)开始信号函数,
(2)读取一个字节函数,
(3)读取所有数据函数,用一个指针来保存数据
开始信号的函数
int dht11_start(void)
{
uint32_t i;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
//PG9变为输出模式
dht11_outputmode();
//PG9引脚输出低电平
PGout(9) = 0;
//延时20ms
delay_ms(20);
//PG9引脚输出高电平
PGout(9) = 1;
//延时30us
delay_us(30);
//切换到输入模式,检查DHT11温湿度模块是否有响应
dht11_inputmode();
//检测是否有低电平
i=0;
while(i<100)
{
if(PGin(9)==0)//如果变为高电平,则说明读取成功跳出while
break;
delay_us(1);
i++;
}
//设置100,是为了证明已经超过80us时间,超时返回1
if(i >=100)
return 1;
//检测是否有高电平
i=0;
while(i<100)
{
if(PGin(9))
break;
delay_us(1);
i++;
}
//超时
if(i >=100)
return 1;
//没有错误,正确的返回
return 0;
}
读取一个字节的数据,并返回
uint8_t dht11_read_byte(void)
{
uint8_t d=0;//读取一个字节,新建一个变量进行保存
uint32_t i=0;//控制八次循环读取八位数据
//等待高电平持续完毕
while(PGin(9));//在读取一个位之前,需要等高电平变为低电平,!!!这是易漏点
for(i=0; i<8; i++)
{
//检测低电平
while(PGin(9)==0);//因为数据1和数据0前半部分都是低电平,可以一直等待,等待变为高电平
//延时40us
delay_us(40);//如果超过28us后还是高电平就是数据1,结合时序图来看
//判断PG9引脚的电平,若为高电平就是bit1
if(PGin(9))
{
/*数据是从高往低获取的,获取的是第7位的数据,
所以要把这个数据从第0位送到第7位,采用左移的方法最为简单,
如果是高电平则将第七位置1,直到这一位数据发送完,电平才翻转,进行下一位的传输,持续八次
*/
d|=1<<(7-i);
//等待高电平持续完毕
while(PGin(9));//!!!这里也是易漏点
}
}
return d;
}
下面把整个通信过程进行组合:
uint32_t dht11_read_data(uint8_t *pbuf)
{
uint32_t i=0;
uint8_t check_sum=0;
//建立启动信号
while(dht11_start()==1);
//连续读取5个字节
for(i=0; i<5; i++)
{
pbuf[i] = dht11_read_byte();
}
//计算校验和
check_sum = pbuf[0]+pbuf[1]+pbuf[2]+pbuf[3];
//检验和判断
if(check_sum != pbuf[4])
return 1;//不正确返回1
//结束部分
//PG9变为输出模式
dht11_outputmode();
//PG9引脚输出高电平,这两句可以不加,因为默认上拉电阻会自动上拉总线
PGout(9) = 1;
return 0;//正确返回0
}
注意需要将system_stm32f4xx.c中的PLL_M 修改正确
以及stm32f4xx.h中的HSE_VALUE修改正确,外部晶振的值
在主函数部分需要注意的几个点
创建一个数组用于保存变量的值
uint8_t dht11_data[4]={
0};
if(dht11_read_data(dht_data)==0)
{
printf("temp=%d.%d\r\n",dht_data[2],dht_data[3]);
printf("humi=%d.%d\r\n",dht_data[0],dht_data[1]);
printf("\r\n");
}
如果出现没有数值的情况,可以使用Printf在校验和那里查看是否输出正确。