返回列表 发新帖我要提问本帖赏金: 30.00元(功能说明)

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

[复制链接]
 楼主| 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 位数据,高位先出。具体通信时序如图所示:
11.png
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的数据,先发送字节的高位;发送的数据依次为湿度高位、湿度低位、温度高位、 温度低位、校验位,发送数据结束触发一次信息采集,采集结束传感器自动转入休眠模式,直到下一次通信来临。

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

  6. typedef struct {
  7.     GPIO_TypeDef *DHT_Port;
  8.     uint16_t DHT_Pin;      
  9. } DHT_sensor;


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

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

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

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

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

22.png

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

33.png

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

  4.     goToOutput(sensor);
  5.     lineDown();
  6.     Delay(18);
  7.     lineUp();
  8.     goToInput(sensor);

  9.     uint16_t timeout = 0;
  10.     while(getLine())
  11.     {
  12.         timeout++;
  13.         if (timeout > DHT_TIMEOUT)
  14.         {
  15.             return data;
  16.         }
  17.     }
  18.     timeout = 0;

  19.     while(!getLine())
  20.     {
  21.         timeout++;
  22.         if (timeout > DHT_TIMEOUT)
  23.         {
  24.             return data;
  25.         }
  26.     }
  27.     timeout = 0;
  28.     while(getLine())
  29.     {
  30.         timeout++;
  31.         if (timeout > DHT_TIMEOUT)
  32.         {
  33.             return data;
  34.         }
  35.     }

  36.     uint8_t rawData[5] = {0,0,0,0,0};
  37.     for(uint8_t a = 0; a < 5; a++)
  38.     {
  39.         for(uint8_t b = 7; b != 255; b--)
  40.         {
  41.             uint16_t hT = 0, lT = 0;
  42.             while(!getLine() && lT != 65535) lT++;
  43.             timeout = 0;
  44.             while(getLine()&& hT != 65535) hT++;
  45.             if(hT > lT) rawData[a] |= (1<<b);
  46.         }
  47.     }

  48.     if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4])
  49.     {
  50.         data.hum = (float)(((uint16_t)rawData[0]<<8) | rawData[1])*0.1f;
  51.         if(!(rawData[2] & (1<<7)))
  52.         {
  53.             data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*0.1f;
  54.         }
  55.         else
  56.         {
  57.             rawData[2] &= ~(1<<7);
  58.             data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*-0.1f;
  59.         }
  60.     }

  61.     return data;
  62. }


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

44.png

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

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

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

微信图片_20221213220621.jpg

运行结果:
屏幕截图 2022-12-13 220452.png

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

  3. #include "debug.h"

  4. #define DHT_TIMEOUT                 100000

  5. typedef struct
  6. {
  7.     float hum;
  8.     float temp;
  9. } DHT_data;

  10. typedef struct {
  11.     GPIO_TypeDef *DHT_Port;
  12.     uint16_t DHT_Pin;      
  13. } DHT_sensor;

  14. DHT_data DHT_getData(DHT_sensor *sensor);

  15. #endif


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

  2. #define lineDown()      GPIO_ResetBits(sensor->DHT_Port, sensor->DHT_Pin)
  3. #define lineUp()        GPIO_SetBits(sensor->DHT_Port, sensor->DHT_Pin)
  4. #define getLine()       GPIO_ReadInputDataBit(sensor->DHT_Port, sensor->DHT_Pin)
  5. #define Delay(d)        Delay_Ms(d)

  6. static void goToOutput(DHT_sensor *sensor)
  7. {
  8.    GPIO_InitTypeDef GPIO_InitStruct = {0};
  9.    lineUp();

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

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

  15. static void goToInput(DHT_sensor *sensor)
  16. {
  17.   GPIO_InitTypeDef GPIO_InitStruct = {0};

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

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

  26.     goToOutput(sensor);
  27.     lineDown();
  28.     Delay(18);
  29.     lineUp();
  30.     goToInput(sensor);

  31.     uint16_t timeout = 0;
  32.     while(getLine())
  33.     {
  34.         timeout++;
  35.         if (timeout > DHT_TIMEOUT)
  36.         {
  37.             return data;
  38.         }
  39.     }
  40.     timeout = 0;

  41.     while(!getLine())
  42.     {
  43.         timeout++;
  44.         if (timeout > DHT_TIMEOUT)
  45.         {
  46.             return data;
  47.         }
  48.     }
  49.     timeout = 0;
  50.     while(getLine())
  51.     {
  52.         timeout++;
  53.         if (timeout > DHT_TIMEOUT)
  54.         {
  55.             return data;
  56.         }
  57.     }

  58.     uint8_t rawData[5] = {0,0,0,0,0};
  59.     for(uint8_t a = 0; a < 5; a++)
  60.     {
  61.         for(uint8_t b = 7; b != 255; b--)
  62.         {
  63.             uint16_t hT = 0, lT = 0;
  64.             while(!getLine() && lT != 65535) lT++;
  65.             timeout = 0;
  66.             while(getLine()&& hT != 65535) hT++;
  67.             if(hT > lT) rawData[a] |= (1<<b);
  68.         }
  69.     }

  70.     if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4])
  71.     {
  72.         data.hum = (float)(((uint16_t)rawData[0]<<8) | rawData[1])*0.1f;
  73.         if(!(rawData[2] & (1<<7)))
  74.         {
  75.             data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*0.1f;
  76.         }
  77.         else
  78.         {
  79.             rawData[2] &= ~(1<<7);
  80.             data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*-0.1f;
  81.         }
  82.     }

  83.     return data;
  84. }


打赏榜单

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

评论

DHT22是较为常用单总线(1-Wire)传感器,对速度要求较低且可节约IO资源,在实际设计中较为常用。作者使用CH32V307开发板轻松采集DHT22的温湿度并进行串口打印,整体实现效果较好,继续加油  发表于 2022-12-19 11:08
您需要登录后才可以回帖 登录 | 注册

本版积分规则

104

主题

384

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部

104

主题

384

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部