一、部分代码详解
DHT11.c
一个字节是八位,因此需要循环八次,来读取一个字节
for (i=0; i<8; i++)
{
// 等待信号变为高电平
while (DHT11_DATA_IN() == Bit_RESET);
// 延迟40微秒,确保数据有效
Delay_us(40);
// 如果信号为高电平
if (DHT11_DATA_IN() == Bit_SET)
{
// 等待信号变为低电平
while(DHT11_DATA_IN() == Bit_SET);
// 设置相应的位为1
temp |= (uint8_t)(0x01 << (7 - i));
}
else
{
// 如果信号为低电平,则相应位保持为0
temp &= (uint8_t) ~ (0x01<<(7-i));
}
}
DHT11 数据位的分配遵循一个固定的格式,每次通信时,DHT11 会发送一个包含 40 位的数据包。这个数据包包含了五个字节,每个字节有 8 位。以下是具体的位分配情况:
数据包结构
- 湿度整数部分:8 位
- 湿度小数部分:8 位
- 温度整数部分:8 位
- 温度小数部分:8 位
- 校验位:8 位
详细说明
- 湿度整数部分:第一个字节表示湿度的整数部分。
- 湿度小数部分:第二个字节表示湿度的小数部分。
- 温度整数部分:第三个字节表示温度的整数部分。
- 温度小数部分:第四个字节表示温度的小数部分。
- 校验位:第五个字节是校验位,它是前面四个字节的累加和(忽略溢出)。
uint8_t Read_DHT11(DHT11_Data_TypeDef *DHT11_Data)
{
DHT11_Mode_Out_PP();
DHT11_DATA_OUT(LOW);
Delay_ms(18);
DHT11_DATA_OUT(HIGH);
Delay_us(30);
DHT11_Mode_IPU();
if(DHT11_DATA_IN() == Bit_RESET)
{
while(DHT11_DATA_IN() == Bit_RESET);
while(DHT11_DATA_IN() == Bit_SET);
DHT11_Data -> humi_int = Read_Byte();
DHT11_Data -> humi_deci = Read_Byte();
DHT11_Data -> temp_int = Read_Byte();
DHT11_Data -> temp_deci = Read_Byte();
DHT11_Data -> check_sum= Read_Byte();
DHT11_Mode_Out_PP();
DHT11_DATA_OUT(HIGH);
if (DHT11_Data -> check_sum == DHT11_Data -> humi_int + DHT11_Data -> humi_deci + DHT11_Data -> temp_int + DHT11_Data -> temp_deci)
return SUCCESS;
else
return ERROR;
}
else
{
return ERROR;
}
}
二、完整代码
DHT11.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "DHT11.h"
// 配置用于与DHT11通信的GPIO引脚
void DHT11_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 启用用于DHT11通信的GPIO外设时钟
RCC_APB2PeriphClockCmd(DHT11_Out_RCC, ENABLE);
// 设置要配置的GPIO引脚
GPIO_InitStructure.GPIO_Pin = DHT11_Out_Pin;
// 设置GPIO模式为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 设置GPIO速度为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIO
GPIO_Init(DHT11, &GPIO_InitStructure);
// 设置引脚为高电平
GPIO_SetBits(DHT11, DHT11_Out_Pin);
}
// 设置GPIO模式为上拉输入
static void DHT11_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 设置要配置的GPIO引脚
GPIO_InitStructure.GPIO_Pin = DHT11_Out_Pin;
// 设置GPIO模式为上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
// 重新初始化GPIO
GPIO_Init(DHT11, &GPIO_InitStructure);
}
// 设置GPIO模式为推挽输出
static void DHT11_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 设置要配置的GPIO引脚
GPIO_InitStructure.GPIO_Pin = DHT11_Out_Pin;
// 设置GPIO模式为推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 设置GPIO速度为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 重新初始化GPIO
GPIO_Init(DHT11, &GPIO_InitStructure);
}
// 读取一个字节的数据
static uint8_t Read_Byte(void)
{
uint8_t i, temp=0;
for (i=0; i<8; i++)
{
// 等待信号变为高电平
while (DHT11_DATA_IN() == Bit_RESET);
// 延迟40微秒,确保数据有效
Delay_us(40);
// 如果信号为高电平
if (DHT11_DATA_IN() == Bit_SET)
{
// 等待信号变为低电平
while(DHT11_DATA_IN() == Bit_SET);
// 设置相应的位为1
temp |= (uint8_t)(0x01 << (7 - i));
}
else
{
// 如果信号为低电平,则相应位保持为0
temp &= (uint8_t) ~ (0x01<<(7-i));
}
}
// 返回读取到的一个字节数据
return temp;
}
// 读取DHT11的数据
uint8_t Read_DHT11(DHT11_Data_TypeDef *DHT11_Data)
{
// 设置引脚为推挽输出
DHT11_Mode_Out_PP();
// 将引脚拉低,启动DHT11的数据传输
DHT11_DATA_OUT(LOW);
Delay_ms(18); // 持续18毫秒
// 将引脚拉高
DHT11_DATA_OUT(HIGH);
// 延迟30微秒
Delay_us(30);
// 设置引脚为上拉输入
DHT11_Mode_IPU();
// 检查是否收到了DHT11的应答信号
if(DHT11_DATA_IN() == Bit_RESET)
{
// 等待信号变为高电平
while(DHT11_DATA_IN() == Bit_RESET);
// 等待信号变为低电平
while(DHT11_DATA_IN() == Bit_SET);
// 读取湿度整数部分
DHT11_Data -> humi_int = Read_Byte();
// 读取湿度小数部分
DHT11_Data -> humi_deci = Read_Byte();
// 读取温度整数部分
DHT11_Data -> temp_int = Read_Byte();
// 读取温度小数部分
DHT11_Data -> temp_deci = Read_Byte();
// 读取校验位
DHT11_Data -> check_sum= Read_Byte();
// 设置引脚为推挽输出
DHT11_Mode_Out_PP();
// 将引脚拉高,结束本次通信
DHT11_DATA_OUT(HIGH);
// 检查校验位是否正确
if (DHT11_Data -> check_sum == DHT11_Data -> humi_int + DHT11_Data -> humi_deci + DHT11_Data -> temp_int + DHT11_Data -> temp_deci)
return SUCCESS; // 如果校验通过,返回成功
else
return ERROR; // 否则返回错误
}
else
{
return ERROR; // 如果没有收到应答信号,直接返回错误
}
}
DHT11.h
#ifndef __DHT11_H
#define __DHT11_H
// 定义用于DHT11通信的GPIO引脚
#define DHT11_Out_Pin GPIO_Pin_0
// 定义用于DHT11通信的GPIO外设时钟
#define DHT11_Out_RCC RCC_APB2Periph_GPIOA
// 定义用于DHT11通信的GPIO端口
#define DHT11 GPIOA
// 定义高低电平常量
#define HIGH 1
#define LOW 0
// 定义一个宏来设置DHT11数据引脚的电平
#define DHT11_DATA_OUT(a) if (a) \
GPIO_SetBits(DHT11, DHT11_Out_Pin);\
else \
GPIO_ResetBits(DHT11, DHT11_Out_Pin)
// 定义一个宏来读取DHT11数据引脚的状态
#define DHT11_DATA_IN() GPIO_ReadInputDataBit(DHT11, DHT11_Out_Pin)
// 定义一个结构体来存储DHT11的数据
typedef struct
{
uint8_t humi_int; // 湿度的整数部分
uint8_t humi_deci; // 湿度的小数部分
uint8_t temp_int; // 温度的整数部分
uint8_t temp_deci; // 温度的小数部分
uint8_t check_sum; // 校验和
} DHT11_Data_TypeDef;
// 配置用于与DHT11通信的GPIO引脚
void DHT11_GPIO_Config(void);
// 设置GPIO模式为上拉输入
static void DHT11_Mode_IPU(void);
// 设置GPIO模式为推挽输出
static void DHT11_Mode_Out_PP(void);
// 读取DHT11的数据
uint8_t Read_DHT11(DHT11_Data_TypeDef *DHT11_Data);
// 读取一个字节的数据
static uint8_t Read_Byte(void);
#endif // __DHT11_H
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h" // Header for delay functions
#include "OLED.h" // Header for OLED display functions
#include "DHT11.h" // Header for DHT11 sensor functions
#include "uart.h" // Header for UART communication functions
// 定义一个结构体变量来存储DHT11的数据
DHT11_Data_TypeDef DHT11_Data;
int main(void)
{
// 初始化OLED显示
OLED_Init();
// 配置用于与DHT11通信的GPIO引脚
DHT11_GPIO_Config();
// 串口初始化
Serial_Init();
// 在OLED上显示字符串 "temp:"
OLED_ShowString(1, 1, "temp:");
// 在OLED上显示字符串 "humidity:"
OLED_ShowString(2, 1, "humidity:");
while (1)
{
// 读取DHT11的数据
if(Read_DHT11(&DHT11_Data) == SUCCESS)
{
// 通过串口打印温度和湿度信息
Serial_Printf("温度:%d.%d,湿度:%d.%d",
DHT11_Data.temp_int, DHT11_Data.temp_deci,
DHT11_Data.humi_int, DHT11_Data.humi_deci);
// 延迟100毫秒
Delay_ms(100);
// 在OLED上显示湿度的整数部分
OLED_ShowNum(2, 10, DHT11_Data.humi_int, 2);
// 在OLED上显示小数点
OLED_ShowString(2, 12, ".");
// 在OLED上显示湿度的小数部分
OLED_ShowNum(2, 13, DHT11_Data.humi_deci, 2);
// 在OLED上显示温度的整数部分
OLED_ShowNum(1, 6, DHT11_Data.temp_int, 2);
// 在OLED上显示小数点
OLED_ShowString(1, 8, ".");
// 在OLED上显示温度的小数部分
OLED_ShowNum(1, 9, DHT11_Data.temp_deci, 1);
}
}
}