1、背景介绍
stm32单片机要通过dht11模块,获取温湿度数据,改模块支持单总线通信,使用一根数据线,按照模块厂商规定通信时序,即可获取是温湿度数据,我们打算2s 采集一次温湿度数据,然后通过串口输出到上位机
2、具体步骤如下
- 1、初始化GPIO口映射到DHT11的数据引脚
- 2、设置引脚的工作模式(输入浮空:用于读取dht11发送过来的数据 | 开漏输出:用于输出0/1电平信号)
- 3、发送启动信号,DHT11检测到启动信号后,发送一个应答信号,然后就开始传输40bit的数据、
- 4、按照数据的高低电平时序,开始读取,
- byte1-2:表示湿度高8位 + 低8位
- byte3-4:表示温度高8位 + 低8位
- byte5:表示byte1 + … + byte4 校验和 (这里的校验和只存储低8bit)
通讯时序图:
数据位时序图
3、代码
#include "TEMP.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
u8 buffer[5] = {0,0,0,0,0};
// 设置IO为输出模式
void DHT11_IO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
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_50MHz;//输出50MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG,&GPIO_InitStructure);
}
// 设置IO为输入模式
void DHT11_IO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;//浮空输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出50MHz
GPIO_Init(GPIOG,&GPIO_InitStructure);
}
// 复位DHT11
void DHT11_Reset(void){
DHT11_IO_OUT();
DHT11_DQ_OUT = 0; //拉低DQ
delay_ms(20);
DHT11_DQ_OUT = 1;
delay_us(40);
}
void DHT11_Mode_Init(void){
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//使能GPIOG时钟;
}
// 初始化DHT11的io DQ、 同时检测dht11的存在
u8 DHT11_Start_CollectData (void){
u8 result = 0;
// 主机发送检测信号给dht11
DHT11_Reset();
// 等待dht11 响应MCU的呼叫
result = DHT11_Response_CheckWait();
return result;
}
//等待DHT11的回应
// 返回1:未检测到DHT11存在
// 返回0:检测到DHT11
u8 DHT11_Response_CheckWait(void){
u8 retry = 0;
// 主机设置为输入模式,准备接收dht11的响应
DHT11_IO_IN();
delay_us(20);
while(!DHT11_DQ_IN && retry < 100) {
delay_us(1);
retry++;
}
retry=0;
delay_us(30);
if(retry >= 100) return 1;
else retry = 0;
while(DHT11_DQ_IN && retry < 100) {
// 跳出这里,说明现在dht11 开始响应数据给mcu
delay_us(1);
retry++;
}
if(retry >= 100) return 1;
// 开始读取dht11 发送给mcu 的40bit数据
return 0;
}
// 读取所有温湿度数据(温度2byte + 湿度2byte + 1byte校验和)
u8 DHT11_ReadData(){
u8 i;
u32 sum = 0;
// 发送采集信号
if(DHT11_Start_CollectData() == 1){
return 0;
}
// 连续读取5个字节
// byte1~2:湿度高8bit、低8bit
// byte3~4:温度高8bit、低8bit
// bity5:byte1 ~ byte4 校验和
for(i = 0 ;i<5;i++){
buffer[i] = DHT11_ReadByte();
}
sum = (u8)(buffer[0] + buffer[1] + buffer[2] + buffer[3]);
if(sum == buffer[4]){
return 1;
}else {
return 0;
}
}
// 读取1byte dht11 发过给MCU的数据
u8 DHT11_ReadByte(void){
u8 temp;
u8 i;
u8 readByteData;
u8 retry;
for(i = 0 ;i < 8; i++){
while(!DHT11_DQ_IN && retry < 100){
delay_us(1);
retry++;
}
retry = 0;
// 此时dht11数据位变高,下面是判断高低电平的核心
delay_us(30);
temp = 0;
if(DHT11_DQ_IN == 1){
temp = 1;
while(DHT11_DQ_IN && retry < 100) {
delay_us(1);
retry++;
}
retry = 0;
} else {
temp = 0;
}
readByteData<<=1;
readByteData |= temp;
}
return readByteData;
}
头文件代码:
#ifndef __TEMP_H
#define __TEMP_H
#include "sys.h"
#define DHT11_DQ_OUT PGout(9) //数据端口 PG9 用去驱动IO口输出高低电平
#define DHT11_DQ_IN PGin(9) //数据端口 PG9 用于读取io的高低电平
// 设置数据IO口的工作模式为:输入/输出
void DHT11_IO_OUT(void);
void DHT11_IO_IN(void);
u8 DHT11_ReadData(void);
u8 DHT11_ReadByte(void);
u8 DHT11_Response_CheckWait(void);
void DHT11_Reset(void);
void DHT11_Mode_Init(void);
u8 DHT11_Start_CollectData(void);
#endif
应用层代码:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "temp.h"
extern u8 buffer[5];
int main(void)
{
u8 t=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
LED_Init(); //初始化LED
DHT11_Mode_Init();
while(1)
{
delay_ms(10);
t++;
if(t==200)
{
if(DHT11_ReadData() == 1){
printf("read temperature & humidity success !\r\n");
printf("湿度:%d ,温度:%d ℃\r\n",buffer[0],buffer[2]);
}
LED0=!LED0;
}
}
}
4、实验结果:
上位机串口工具输出如下
小结
1、由于设计的问题,每次采集数据的时候,模块内部存储的是上次采集的数据,故而,我们需要采集2次,才能确保获取到最新准确的数据
2、每次采集前,需要检测一下模块是否能正常响应,然后再开始采集数据,否则程序直接返回、、
3、