设备:stm32f407ZGT6
环境:FreeRTOS HAL
到网上找DHT11 的驱动,但是都无法使用。原因是RTOS 环境中,由于多线程 ,使用循环计数阻塞式的delay_us 延时函数就没那么准,且不同设备中delay_us的计数值不一样。而DHT11对时序要求得又十分严格,这就会导致读取数据异常,甚至无法读取。
虽然考虑过使用其他方法达成更精准的微秒级延时,但是对于单总线传输终归小题大做了。于是下面就简单的使用计数值来模拟延时,不需要考虑延时多少微秒,只需要比较高电平持续时间的相对长短。
cpp
/**********************************************************************************************************
函数名称:us延时函数,最小延时为1us
输入参数:时间
输出参数:无
**********************************************************************************************************/
void delay_us(unsigned int time)
{
unsigned short i = 0;
while (time--)
{
i = 8;
while (i--)
;
}
}
需要注意的是,下面代码使用的是C++,如果你使用的是C,那么用相应功能的代码替换掉即可。bool 类型需要包含stdbool头文件
DHT11.h
头文件里未被注释的接口,决定了哪些函数需要重点关注
cpp
#ifndef DHT11_H
#define DHT11_H
void DHT11_Init();
//bool DHT11_Read_Data(float &temp,float &humi);
bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi);//自定义的快速读取函数
#endif//DHT11_H
DHT11.cpp
里面的这个std_delay_us 是用于调试中用的,在**void DHT11_Rst()**函数里,你把延时换成你自带的即可,这个不需要多精准。
下面的函数是与时序图直接对应的,连续读取温度时,最好间隔1s以上
同时说明一下,下面将作为反面例子!!,找了许久才发现是数组越界引发的硬件中断,下面代码忘了对timeBufIndex清零。后面我会发一个简化版
cpp
#include "DHT11.h"
#include "cmsis_os2.h"
#include "stm32f4xx_hal.h"
#define DHT11_Pin GPIO_PIN_6
#define DHT11_GPIO_Port GPIOE
#define DHT11_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()
#define DHT11_Read() (DHT11_GPIO_Port->IDR & DHT11_Pin) /*HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin)*/
#define DHT11_Write(x) HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, (GPIO_PinState) (x))
#define DHT11_High() HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_SET)
#define DHT11_Low() HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)
static GPIO_InitTypeDef GPIO_InitStruct = {0};
static uint16_t std_delay_80us=875;//事先测试过
static uint16_t std_delay_50us=566;
static uint16_t time_count;
/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
void std_delay_us(uint8_t us)
{
// uint16_t count = std_delay_80us * us / 80;//测试得到的
uint16_t count =11 * us ;
for (uint16_t i = 0; i < count; ++i);
}
/**函数*/
void DHT11_Input();
void DHT11_Output();
void DHT11_Rst();
void DHT11_Init()
{
DHT11_GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = DHT11_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
// GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
DHT11_High();
}
void DHT11_Input()
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}
void DHT11_Output()
{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}
void DHT11_Rst()
{
DHT11_Output();
DHT11_Low();
osDelay(25);//根据时序图可知,需要至少拉低18ms
DHT11_High();
std_delay_us(20);//20-40us
}
void DHT11_Check()
{
DHT11_Input();
for (time_count =0;DHT11_Read();++time_count);/* 等待DHT11数据线拉低 */
for (time_count =0;!DHT11_Read();++time_count); /* 等待DHT11数据线拉高 *///持续80us
std_delay_80us = time_count;//获取80us需要的计数值
}
//从DHT11读取一个位
//返回值:1/0
uint8_t DHT11_Read_Bit()
{
for (time_count =0;DHT11_Read();++time_count);//等待低电平
//变低了说明上一次数据位读取结束
for (time_count =0;!DHT11_Read();++time_count); //等待变高电平
// std_delay_50us = time_count;//测试用的
//变高了说明数据位读取开始
/**开始读数据*/
//低电平:26-28us
//高电平:70us
std_delay_us(40);// 等待40us,如果延时准确的话
if (DHT11_Read())
return 1;
else
return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
uint8_t DHT11_Read_Byte()
{
uint8_t dat = 0;
for (uint8_t i = 0; i < 8; i++)
{
dat <<= 1;
dat |= DHT11_Read_Bit();
}
return dat;
}
//uint16_t timeBuf[40];
//uint8_t timeBufIndex=0;
//uint16_t timeMax=0;
//uint16_t timeMin=10000;
//uint8_t DHT11_Read_Byte_Fast()
//{
// uint8_t dat=0;
// for (uint8_t i = 0; i < 8; i++)
// {
// for (time_count =0;DHT11_Read();++time_count);//等待低电平
// //变低了说明上一次数据位读取结束
//
// for (time_count =0;!DHT11_Read();++time_count); //等待变高电平
// // std_delay_50us = time_count;//测试用的
// //变高了说明数据位读取开始
//
// /**开始读数据*/
// //低电平:26-28us 高电平:70us
// for (time_count =0;DHT11_Read();++time_count);//等待低电平
// timeBuf[timeBufIndex++]=time_count;
// // std_delay_us(30);// 等待40us,如果延时准确的话
// dat <<= 1;
// dat |= DHT11_Read();
// }
// return dat;
//}
uint16_t timeBuf[40];//存储计数值
uint8_t timeBufIndex=0;//存储计数值索引
uint16_t timeMax=0;
uint16_t timeMin=0xFFFF;
void DHT11_Read_Byte_Fast_Pro()
{
for (uint8_t i = 0; i < 8; i++)
{
for (time_count =0;DHT11_Read();++time_count);//等待低电平
//变低了说明上一次数据位读取结束
for (time_count =0;!DHT11_Read();++time_count); //等待变高电平
//变高了说明数据位读取开始
/**开始读数据*/
//低电平:26-28us 高电平:70us
for (time_count =0;DHT11_Read();++time_count);//等待低电平
timeBuf[timeBufIndex++]=time_count;//存储计数值
}
}
bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
uint8_t buf[5];
DHT11_Rst();
DHT11_Check();
for (unsigned char & i : buf)//读取40位数据
{
DHT11_Read_Byte_Fast_Pro();
}
for (unsigned short i : timeBuf)
{
if(i>timeMax)
{
timeMax=i;
}
else if(i<timeMin)
{
timeMin=i;
}
}
/**把计数值转为二进制数据*/
uint16_t timeMed=(timeMax+timeMin)>>1;//整除2,取中位数
uint8_t data;//临时数据
bool tempBin;//临时二进制数据
for (int i = 0; i < 5; ++i)
{
data=0;//重置
for (int j = 0; j < 8; ++j)
{
data<<=1;
//比较计数值,读取二进制数据
if(timeBuf[i*8+j]>timeMed)
{
tempBin= true;
}
else
{
tempBin= false;
}
data|=tempBin;
}
buf[i]=data;//存储数据
}
/**检验**/
if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
humi = (float) (buf[0] + buf[1] * 0.1);
temp = (float) (buf[2] + buf[3] * 0.1);
return true;
}
else
{
return false;
}
}
//bool DHT11_Read_Data(float &temp, float &humi)
//{
// uint8_t buf[5];
// DHT11_Rst();
// DHT11_Check();
// for (unsigned char & i : buf)//读取40位数据
// {
// i = DHT11_Read_Byte_Fast();
// }
//
// for (unsigned short i : timeBuf)
// {
// if(i>timeMax)
// {
// timeMax=i;
// }
// else if(i<timeMin)
// {
// timeMin=i;
// }
// }
//
// uint16_t timeMed=(timeMax+timeMin)>>1;
// uint8_t data;
// uint8_t tempBin;
// uint8_t tempBuf[5];
// for (int i = 0; i < 5; ++i)
// {
// data=0;
// for (int j = 0; j < 8; ++j)
// {
// data<<=1;
// if(timeBuf[i*8+j]>timeMed)
// {
// tempBin=1;
// }
// else
// {
// tempBin=0;
// }
// data|=tempBin;
// }
// tempBuf[i]=data;
// }
//
// //检验
// if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
// {
// humi = (float) (buf[0] + buf[1] * 0.1);
// temp = (float) (buf[2] + buf[3] * 0.1);
// return true;
// }
// else
// {
// return false;
// }
//}
修正版。里面有三个版本,没被注释的是特化版本,其下愈为通用
cpp
#include "DHT11.h"
#include "cmsis_os2.h"
#include "delay.h"
#include "stm32f4xx_hal.h"
#define DHT11_Pin GPIO_PIN_6
#define DHT11_Pin_Location 6
#define DHT11_GPIO_Port GPIOE
#define DHT11_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()
#define DHT11_MAX_DELAY_COUNT 7000//防止卡死
#define DHT11_Read() (DHT11_GPIO_Port->IDR & DHT11_Pin) /*HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin)*/
#define DHT11_High() DHT11_GPIO_Port->ODR |= (0x01 << DHT11_Pin_Location)
#define DHT11_Low() DHT11_GPIO_Port->ODR &= ~(0x01 << DHT11_Pin_Location) /*HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)*/
#define DHT11_IN() \
{ \
DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
DHT11_GPIO_Port->MODER |= 0 << 2 * DHT11_Pin_Location; \
}
#define DHT11_OUT() \
{ \
DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
DHT11_GPIO_Port->MODER |= 1 << 2 * DHT11_Pin_Location; \
}
//static uint16_t std_delay_80us = 875;//事先测试过
//static uint16_t std_delay_50us = 566;
/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
//void std_delay_us(uint8_t us)
//{
// // uint16_t count = std_delay_80us * us / 80;//测试得到的
// uint16_t count = 11 * us;
// for (uint16_t i = 0; i < count; ++i)
// ;
//}
inline void std_delay_25us()
{
for (uint16_t i = 0; i < 273; ++i)
;
}
/**函数*/
inline void DHT11_Rst();
void DHT11_Init()
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
DHT11_GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = DHT11_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
DHT11_High();
}
inline void DHT11_Rst()
{
DHT11_OUT();
DHT11_Low();
osDelay(30);//根据时序图可知,需要至少拉低18ms
DHT11_High();
std_delay_25us();//20-40us
}
inline void DHT11_Check()
{
DHT11_IN();
while (DHT11_Read())
;
while (!DHT11_Read())
;
}
bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
static uint8_t buf[5];
DHT11_Rst(); // 设置输出模式
DHT11_Check();// 设置输入模式
for (unsigned char & i : buf)// 读取40位数据
{
uint8_t data=0;
for (uint8_t j = 0; j < 8; j++)
{
data<<=1;
while (DHT11_Read()) {} // 等待低电平
while (!DHT11_Read()) {}// 等待变高电平
// 开始读数据
uint16_t time_count;
for (time_count = 0; DHT11_Read(); ++time_count) {}
data|=time_count>>10;// 由于事先已经知道一个为1194,一个为406左右
}
i = data;// 存储数据
}
if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
humi = (buf[0]*10 + buf[1]) / 10.0f;
temp = (buf[2]*10 + buf[3] ) / 10.0f;
return true;
}
else
{
return false;
}
}
/********************下面为次优级优化********************/
全局变量
//static uint8_t timeBuf[40];// 存储计数值
//static uint8_t timeBufIndex = 0;
//
//void DHT11_Read_Byte_Fast_Pro()
//{
// for (uint8_t i = 0; i < 8; i++)
// {
// while (DHT11_Read()) {} // 等待低电平
// while (!DHT11_Read()) {}// 等待变高电平
//
// // 开始读数据
// uint16_t time_count;
// for (time_count = 0; DHT11_Read(); ++time_count) {}
// timeBuf[timeBufIndex++] = time_count>>4;// 存储计数值,由于事先已经知道一个为875,一个为275左右,所以除以16
// }
//}
//
//bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
//{
// static uint8_t buf[5];
//
// DHT11_Rst(); // 设置输出模式
// DHT11_Check();// 设置输入模式
//
// timeBufIndex = 0; // 重置计数值索引
// for (unsigned char &i: buf)// 读取40位数据
// {
// DHT11_Read_Byte_Fast_Pro();
// }
//
// uint16_t timeMax = 0;
// uint16_t timeMin = 0xFFFF;
// for (unsigned short i: timeBuf)
// {
// if (i > timeMax) timeMax = i;
// if (i < timeMin) timeMin = i;
// }
//
// uint16_t timeMed = (timeMax + timeMin) >> 1;// 取中位数
// for (uint8_t i = 0; i < 5; ++i)
// {
// uint8_t data = 0;
// for (uint8_t j = 0; j < 8; j++)
// {
// data <<= 1;
// data |= (timeBuf[i * 8 + j] > timeMed);
// }
// buf[i] = data;// 存储数据
// }
//
// if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
// {
// humi = (buf[0]*10 + buf[1]) / 10.0f;
// temp = (buf[2]*10 + buf[3] ) / 10.0f;
// return true;
// }
// else
// {
// return false;
// }
//}
//
//static uint16_t timeBuf[40];//存储计数值
//static uint8_t timeBufIndex = 0;
//void DHT11_Read_Byte_Fast_Pro()
//{
// for (uint8_t i = 0; i < 8; i++)
// {
// while (DHT11_Read())
// ;//等待低电平
// //变低了说明上一次数据位读取结束
//
// while (!DHT11_Read())
// ;//等待变高电平
// //变高了说明数据位读取开始
//
// /**开始读数据*/
// //低电平:26-28us 高电平:70us
// uint16_t time_count;
// for (time_count = 0; DHT11_Read(); ++time_count)
// ; //等待低电平
// timeBuf[timeBufIndex++] = time_count;//存储计数值//存储计数值
// }
//}
//
//
//bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
//{
// static uint8_t buf[5];
//
// DHT11_Rst(); //在里面设置了输出模式
// DHT11_Check(); //在里面设置了输入模式
// // return false;//如果超时,则退出
// timeBufIndex = 0; //存储计数值索引
// for (unsigned char &i: buf)//读取40位数据
// {
// DHT11_Read_Byte_Fast_Pro();
// }
//
// uint16_t timeMax = 0;
// uint16_t timeMin = 0xFFFF;
// for (unsigned short i: timeBuf)
// {
// if (i > timeMax)
// {
// timeMax = i;
// }
// else if(i<timeMin)
// {
// timeMin = i;
// }
// }
//
// /**把计数值转为二进制数据*/
// uint8_t data; //临时数据
// uint16_t timeMed = (timeMax + timeMin) >> 1;//整除2,取中位数
// bool tempBin;
// for (uint8_t i = 0; i < 5; ++i)
// {
// data = 0;
// for (uint8_t j = 0; j < 8; j++)
// {
// data <<= 1;
// //比较计数值,读取二进制数据
// if (timeBuf[i * 8 + j] > timeMed)
// {
// tempBin = true;
// }
// else
// {
// tempBin = false;
// }
// data |= tempBin;
// }
// buf[i] = data;//存储数据
// }
//
// /**检验**/
// if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
// {
// humi = (float) (buf[0] + buf[1] * 0.1);
// temp = (float) (buf[2] + buf[3] * 0.1);
// return true;
// }
// else
// {
// return false;
// }
//}
/********************下面为原版优化********************/
//static uint16_t timeBuf[40];//存储计数值
//
//void DHT11_Read_Byte_Fast_Pro()
//{
// static uint8_t timeBufIndex = 0;//存储计数值索引
// for (uint8_t i = 0; i < 8; i++)
// {
// while (DHT11_Read())
// ;//等待低电平
// //变低了说明上一次数据位读取结束
//
// while (!DHT11_Read())
// ;//等待变高电平
// //变高了说明数据位读取开始
//
// /**开始读数据*/
// //低电平:26-28us 高电平:70us
// uint16_t time_count;
// for (time_count = 0; DHT11_Read(); ++time_count)
// ; //等待低电平
// timeBuf[timeBufIndex++] = time_count;//存储计数值
// }
//}
//
//
//bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
//{
// static uint8_t buf[5];
// static uint16_t timeMax = 0;
// static uint16_t timeMin = 0xFFFF;
//
// DHT11_Rst(); //在里面设置了输出模式
// DHT11_Check(); //在里面设置了输入模式
// // return false;//如果超时,则退出
// for (unsigned char &i: buf)//读取40位数据
// {
// DHT11_Read_Byte_Fast_Pro();
// }
//
// for (unsigned short i: timeBuf)
// {
// if (i > timeMax)
// {
// timeMax = i;
// }
// else
// {
// timeMin = i;
// }
// }
// std_delay_25us();
// std_delay_25us();
// DHT11_OUT();
// DHT11_High();
//
//
// /**把计数值转为二进制数据*/
// uint16_t timeMed = (timeMax + timeMin) >> 1;//整除2,取中位数
// uint8_t data; //临时数据
// bool tempBin; //临时二进制数据
// for (int i = 0; i < 5; ++i)
// {
// data = 0;//重置
// for (int j = 0; j < 8; ++j)
// {
// data <<= 1;
// //比较计数值,读取二进制数据
// if (timeBuf[i * 8 + j] > timeMed)
// {
// tempBin = true;
// }
// else
// {
// tempBin = false;
// }
// data |= tempBin;
// }
// buf[i] = data;//存储数据
// }
//
// /**检验**/
// if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
// {
// humi = (float) (buf[0] + buf[1] * 0.1);
// temp = (float) (buf[2] + buf[3] * 0.1);
// return true;
// }
// else
// {
// return false;
// }
//}