DHT11传感器通过单一的数据线(通常称为信号线)与微控制器连接,通过一种简单的串行通信协议向微控制器提供温度和湿度数据。
引脚对应:
DATA--PB5
VCC-3.3V
GND-GND
定义全局变量和延时函数。
其中,Data
数组用于存储读取的温湿度信息,而Tem_DEADLINE
和Hum_DEADLINE
分别表示温度和湿度的阈值。
uint8_t Data[5]={0x00,0x00,0x00,0x00,0x00}; //Data存储读取的温湿度信息
int Tem_DEADLINE = 35;
int Hum_DEADLINE = 80;
延时函数Delay_us
采用了定时器的硬件计数器进行微秒级的延时操作。
void Delay_us(uint16_t us) { // 微秒延时函数
// 优化微秒延时的实现
// 使用定时器的硬件计数器进行延时
// 计算差值,差值越大,延时时间越长
uint16_t differ = 0xFFFF - us - 5;
// 设置TIM1计数器的起始值
__HAL_TIM_SET_COUNTER(&htim1, differ);
// 启动定时器
HAL_TIM_Base_Start(&htim1);
// 等待计数器达到设定值
while (differ < 0xFFFF - 5) {
// 查询计数器的计数值
differ = __HAL_TIM_GET_COUNTER(&htim1);
}
// 停止定时器
HAL_TIM_Base_Stop(&htim1);
}
同时,需要配置TIM1来实现微秒级的延时操作。
CUBEMX配置:
选择TIM1,后选择
接下来,是一些用于设置GPIO的函数。DHT_GPIO_SET_OUTPUT
函数将GPIO设置为输出模式,用于向DHT11发送激活信号;DHT_GPIO_SET_INPUT
函数则将GPIO设置为输入模式,用于接收DHT11传感器的信号。
void DHT_GPIO_SET_OUTPUT(void) { // 设置 GPIO 为输出模式(向 DHT11 发送激活信号)
// 初始化 GPIOB 的相关配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_5; // 使用 GPIOB 的第 5 个引脚
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 输出模式
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 设置 GPIO 速度为高速
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); // 应用 GPIO 配置
}
void DHT_GPIO_SET_INPUT(void) { // 设置 GPIO 为输入模式(DHT11 向 MCU 发送电平信号,包含温湿度信息)
// 初始化 GPIOB 的相关配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_5; // 使用 GPIOB 的第 5 个引脚
GPIO_InitStructure.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 设置 GPIO 速度为高速
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); // 应用 GPIO 配置
}
接着是读取DHT11传感器数据的核心函数DHT_Read_Byte
和DHT_Read
。其中,DHT_Read_Byte
函数用于从DHT11传感器读取一个字节的数据,而DHT_Read
函数则负责发起读取过程,并对读取到的数据进行校验。
uint8_t DHT_Read_Byte(void) { // 从 DHT11 读取一位(8字节)信号
// 用于存放 8 位数据,即 8 个单次读取的 1 位数据的组合
uint8_t ReadData = 0;
// 临时存放信号电平(0 或 1)
uint8_t temp;
// retry 用于防止卡死
uint8_t retry = 0;
uint8_t i;
for(i = 0; i < 8; i++) { // 一次温湿度信号读取八位
// 等待直到 DHT11 输出高电平
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 0 && retry < 100) {
Delay_us(1);
retry++; // retry 防止 PB5 读取不到数据卡死在这一步
}
retry = 0;
Delay_us(40); // 延时 40us
// 读取电平信号暂存 temp 内
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 1)
temp = 1;
else
temp = 0;
// 等待直到 DHT11 输出低电平,表示退出。本轮 1 bit 信号接收完毕。
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 1 && retry < 100) {
Delay_us(1);
retry++;
}
retry = 0;
// ReadData 内信号先全部左移一位,空出末尾位置,然后将 temp 写入 ReadData
ReadData <<= 1;
ReadData |= temp;
}
return ReadData;
}
/*------------------------------*/
uint8_t DHT_Read(void) {
// 用于重试计数
uint8_t retry = 0;
// 用于迭代的计数器
uint8_t i;
// 将 GPIO 设置为输出模式,向 DHT11 发送信号
DHT_GPIO_SET_OUTPUT();
// 向 DHT11 发送起始信号:先拉低电平 18ms(按时序要求)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_Delay(18);
// 拉高电平 20us
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
Delay_us(20);
// 设置 GPIO 为输入模式,准备接收 DHT11 响应并开始数据读取
DHT_GPIO_SET_INPUT();
Delay_us(20);
// 若 DHT11 发回低电平响应,说明 DHT11 有响应
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 0) {
// 接下来,DHT11 拉低电平一段时间后拉高电平一段时间
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 0 && retry < 100) {
Delay_us(1);
retry++;
}
retry = 0;
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == 1 && retry < 100) {
Delay_us(1);
retry++;
}
retry = 0;
// 一共传输 40 位,一次 DHT_Read_Byte 返回 8 位,共读取 5 次,存储在 Data[] 中。(Data[] 定义为全局)
for(i = 0; i < 5; i++) {
Data[i] = DHT_Read_Byte(); // 每次读取一字节(8位)
}
Delay_us(50);
// 说明:Data[0] 湿度, Data[2] 温度。Data[1] 和 Data[3] 分别为 0 和 2 的小数位。Data[4] 用于校验。
}
// 进行校验
uint32_t sum = Data[0] + Data[1] + Data[2] + Data[3];
if(sum == Data[4]) {
return 1; // 校验通过
} else {
return 0; // 校验失败
}
}
在DHT_Read
函数中,首先向DHT11传感器发送起始信号,并等待其响应。若收到响应,则开始读取40位数据,并存储在全局数组Data
中。最后,进行数据校验,确保数据的准确性。
在main.c
while(1)
{
DHT_Read();
OLED_ShowString(0,0,"Temperature",16,1);
OLED_ShowString(85,0,":",16,1);
OLED_ShowString(0,14,"Humidity",16,1);
OLED_ShowString(85,14,":",16,1);
if(1)//DHT_Read()
{
OLED_ShowNum(94,0,Data[2],2,16);
OLED_ShowString(110,0,".",16,1);
OLED_ShowNum(113,0,Data[3],2,16);
OLED_ShowNum(94,14,Data[0],2,16);
OLED_ShowString(110,14,".",16,1);
OLED_ShowNum(113,14,Data[1],2,16);
OLED_Refresh();
}
HAL_Delay(50);
}
OLED相关的代码在这里:【STM32】HAL库 STM32G4适配OLED屏幕_硬件SPI协议
可以直接下载OLED屏幕相关代码:STM32HAL库 STM32G4适配OLED屏幕-硬件SPI协议