发新帖本帖赏金 30.00元(功能说明)我要提问
返回列表
打印
[单片机芯片]

CH32V307驱动单总线温湿度传感器DHT22

[复制链接]
890|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dql2015|  楼主 | 2022-12-13 22:14 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
#申请原创#
@21小跑堂
手头有一个DHT22温湿度传感器和CH32V307开发板,可玩性极强。DHT22是已校准的数字温湿度传感器,用于检测环境温湿度,采用DHT22(AM2302),标准单总线接口。拥有比常见的DHT11更高的精度和更大的量程。

DHT22产品主要特性如下:
工作电压:        3.3V-5.5V
湿度分辨率:        0.1%RH
湿度测量范围:        0%RH ~ 99.9%RH
湿度测量误差:        ±2%RH (25°C)
温度分辨率:        0.1°C
温度测量范围:        -40°C ~ 80°C
温度测量误差:        ±0.5℃



DHT22传感器有3个引脚,功能如下:
VCC:电源正(3.3V-5.5V)
GND:电源地
DOUT:通信端口


DHT22器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。单总线通常要求外接一个约 5.1kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。SDA 用于微处理器与 AM2302 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。具体通信时序如图所示:

DHT22通信格式说明:
起始信号: 微处理器把数据总线(SDA)拉低一段时间(至少800μs)[1],通知传感器准备数据。
响应信号: 传感器把数据总线(SDA)拉低80μs,再接高80μs以响应主机的起始信号。
数据格式: 收到主机起始信号后,传感器一次性从数据总线(SDA)串出40位数据,高位先出。
湿度: 湿度分辨率是16Bit,高位在前;传感器串出的湿度值是实际湿度值的10倍。
温度: 温度分辨率是16Bit,高位在前;传感器串出的温度值是实际温度值的10倍;温度最高位(Bit15)等于1表示负温度,温度最高位(Bit15)等
于0表示正温度;温度除了最高位(Bit14~Bit0)表示温度值。
校验位=湿度高位+湿度低位+温度高位+温度低位。

单总线通信时序
用户主机(MCU)发送一次起始信号(把数据总线 SDA 拉低至少 800µs)后, DHT22从休眠模式转换到高速模式。待主机开始信号结束后,DHT22发送响应信号,从数据总线 SDA 串行送出 40Bit的数据,先发送字节的高位;发送的数据依次为湿度高位、湿度低位、温度高位、 温度低位、校验位,发送数据结束触发一次信息采集,采集结束传感器自动转入休眠模式,直到下一次通信来临。

设计一个结构体用来存放温湿度数据:
typedef struct
{
    float hum;
    float temp;
} DHT_data;

typedef struct {
    GPIO_TypeDef *DHT_Port;
    uint16_t DHT_Pin;      
} DHT_sensor;


外设读取步骤
主机和传感器之间的通信可通过如下三个步骤完成读取数据。
步骤一:
DHT22上电后( DHT22上电后要等待 2S 以越过不稳定状态,在此期间读取设备不能发送任何指令),测试环境温湿度数据,并记录数据,此后传感器自动转入休眠状态。 DHT22的 SDA 数据线由上拉电阻拉高一直保持高电平,此时 AM2302 的 SDA 引脚处于输入状态,时刻检测外部信号。
设置CH32V307 IO为输出模式的代码如下:
static void goToOutput(DHT_sensor *sensor)
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};
   lineUp();

  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
}
设置CH32V307 IO为上拉输入模式的代码如下:
static void goToInput(DHT_sensor *sensor)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

步骤二:
微处理器的 I/O 设置为输出, 同时输出低电平,且低电平保持时间不能小于 800us,典型值是拉低 1MS,然后微处理器的 I/O 设置为输入状态,释放总线,由于上拉电阻,微处理器的 I/O 即DHT22的 SDA 数据线也随之变高,等主机释放总线后,DHT22发送响应信号,即输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据,信号传输如图所示:



步骤三:
DHT22 发送完响应后,随后由数据总线 SDA 连续串行输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据。 位数据“0”的格式为: 50 微秒的低电平加 26-28 微秒的高电平; 位数据“1”的格式为: 50 微秒的低电平加 70 微秒的高电平; 位数据“0”、位数据“1”格式信号如图 所示:



DHT22的数据总线 SDA 输出 40 位数据后,继续输出低电平 50 微秒后转为输入状态,由于上拉电阻随之变为高电平。同时 DHT22内部重测环境温湿度数据,并记录数据,测试记录结束,单片机自动进入休眠状态。单片机只有收到主机的起始信号后,才重新唤醒传感器,进入工作状态。
温湿度数据读取函数设计如下:
DHT_data DHT_getData(DHT_sensor *sensor)
{
    DHT_data data = {-128.0f, -128.0f};

    goToOutput(sensor);
    lineDown();
    Delay(18);
    lineUp();
    goToInput(sensor);

    uint16_t timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;

    while(!getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }

    uint8_t rawData[5] = {0,0,0,0,0};
    for(uint8_t a = 0; a < 5; a++)
    {
        for(uint8_t b = 7; b != 255; b--)
        {
            uint16_t hT = 0, lT = 0;
            while(!getLine() && lT != 65535) lT++;
            timeout = 0;
            while(getLine()&& hT != 65535) hT++;
            if(hT > lT) rawData[a] |= (1<<b);
        }
    }

    if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4])
    {
        data.hum = (float)(((uint16_t)rawData[0]<<8) | rawData[1])*0.1f;
        if(!(rawData[2] & (1<<7)))
        {
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*0.1f;
        }
        else
        {
            rawData[2] &= ~(1<<7);
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*-0.1f;
        }
    }

    return data;
}


DHT22传感器读单总线的流程图示意图如图所示:



主程序设计,初始化串口打印,初始化IO结构体:
int main(void)
{
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n",SystemCoreClock);

    DHT_sensor dht = {GPIOE, GPIO_Pin_6};
    while(1)
    {
      DHT_data d = DHT_getData(&dht);
      printf("Temp %2.1f°C, Hum %2.1f%%\r\n", d.temp, d.hum);
      Delay_Ms(1000);
    }

线路连接,将DHT22数据线连接到PE6管脚:



运行结果:


完整dht.h代码:
#ifndef DHT_H_
#define DHT_H_

#include "debug.h"

#define DHT_TIMEOUT                 100000

typedef struct
{
    float hum;
    float temp;
} DHT_data;

typedef struct {
    GPIO_TypeDef *DHT_Port;
    uint16_t DHT_Pin;      
} DHT_sensor;

DHT_data DHT_getData(DHT_sensor *sensor);

#endif


完整dht.c代码:
#include "dht.h"

#define lineDown()      GPIO_ResetBits(sensor->DHT_Port, sensor->DHT_Pin)
#define lineUp()        GPIO_SetBits(sensor->DHT_Port, sensor->DHT_Pin)
#define getLine()       GPIO_ReadInputDataBit(sensor->DHT_Port, sensor->DHT_Pin)
#define Delay(d)        Delay_Ms(d)

static void goToOutput(DHT_sensor *sensor)
{
   GPIO_InitTypeDef GPIO_InitStruct = {0};
   lineUp();

  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

static void goToInput(DHT_sensor *sensor)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

DHT_data DHT_getData(DHT_sensor *sensor)
{
    DHT_data data = {-128.0f, -128.0f};

    goToOutput(sensor);
    lineDown();
    Delay(18);
    lineUp();
    goToInput(sensor);

    uint16_t timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;

    while(!getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }

    uint8_t rawData[5] = {0,0,0,0,0};
    for(uint8_t a = 0; a < 5; a++)
    {
        for(uint8_t b = 7; b != 255; b--)
        {
            uint16_t hT = 0, lT = 0;
            while(!getLine() && lT != 65535) lT++;
            timeout = 0;
            while(getLine()&& hT != 65535) hT++;
            if(hT > lT) rawData[a] |= (1<<b);
        }
    }

    if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4])
    {
        data.hum = (float)(((uint16_t)rawData[0]<<8) | rawData[1])*0.1f;
        if(!(rawData[2] & (1<<7)))
        {
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*0.1f;
        }
        else
        {
            rawData[2] &= ~(1<<7);
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*-0.1f;
        }
    }

    return data;
}


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 30.00 元 2022-12-19
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2022-12-19 11:08 回复TA
DHT22是较为常用单总线(1-Wire)传感器,对速度要求较低且可节约IO资源,在实际设计中较为常用。作者使用CH32V307开发板轻松采集DHT22的温湿度并进行串口打印,整体实现效果较好,继续加油 
发新帖 本帖赏金 30.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

372

帖子

7

粉丝