[单片机芯片]CH32V307驱动单总线温湿度传感器DHT22

手头有一个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;

}

 ---------------------
作者:dql2015
链接:https://bbs.21ic.com/icview-3272628-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值