参考——温湿度传感器DHT11驱动_STM32

设备: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;
//    }
//}
相关推荐
长弓聊编程6 分钟前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.13 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
暮色_年华27 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子36 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
日晨难再40 分钟前
嵌入式:STM32的启动(Startup)文件解析
stm32·单片机·嵌入式硬件
我们的五年1 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
yufengxinpian1 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
做人不要太理性1 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.2 小时前
2、桥接模式
c++·桥接模式
chnming19872 小时前
STL关联式容器之map
开发语言·c++