二、温湿度传感器DHT11测试,串口打印结果
本贴在前一贴基础上完成对温湿度传感器DHT11测试,采集温度、温度数据,并通过串口打印输出数据。
一、前提及参考说明
前一贴参考(【平头哥RVB2601开发板试用体验】一、多任务点灯RGB https://bbs.eeworld.com.cn/thread-1207638-1-1.html)
由于实验使用的DHT11温湿度传感器接口为单线双向接口,在操作期间IO需要切换方向,IO使用方法及方向控制参考了网友Lucas在平头哥网站的贴子,并根据自己的使用习惯做了适当改变。参考帖子——“使用CDK在RVB2061上编写IIC软件驱动——快来测体温队” https://occ.t-head.cn/community/post/detail?spm=a2cl5.14300636.0.0.76ae180fwgdOB6&id=3906781240955121664。
二、DHT11数字温湿度传感器相关参数、时序介绍,与RVB2601硬件连接。
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC 测温元件,并与一个高性能8 位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11 传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP 内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20 米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。
本实验采用DHT11的模块是在原DHT11传感器基础上增加了电源指示灯,单线IO已加了上拉电阻。
下面介绍DHT11的主要参数和操作时序,也可参考相关数据手册。
传感器性能参考参考下图。可以看到其性能一般,做一般家庭温湿度数据采集勉强够用。
DHT11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波。
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零.操作流程如下:
一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
通讯过程下图所示
总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。
总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
数字0信号表示方法:
数字1信号表示方法:
与传感器DHT11连接所用到RV2601的板卡的管脚有J1的13脚PA2,J3的10脚GND,J3的11脚3V3电源。
三、程序实现代码及说明。
直接上移植测试好的代码,新建头文件“dht11.h”,可看到大致使用了哪些函数、变量、数据类型。
//文件名必须保存为dht11.h
#ifndef __DHT11_H__
#define __HDT11_H__
typedef enum {
OK = 0,
ERROR1,
ERROR2,
ERROR3,
ERROR4,
ERROR5,
DEFAULT,
}dht11_state;
/*可在其他的文件引用温湿度值,湿度同理*/
extern float temp_value, humi_value;
void sda_pinmux_init(void);
void delay_ms(uint32_t);
void delay_us(uint32_t);
bool dht11_read_bit(void);
uint8_t dht11_read_byte(void);
//函数的返回值表示读取数据是否成功 OK 表示成功 ERROR 表示失败
dht11_state dht11_read_data(uint8_t *buffer);
#endif
新建“dht11.c”文件如下,可作参考。(后面也会以附件形式附上相关文件)
/*
* Copyright (C) 2015-2017 Alibaba Group Holding Limited
*/
/*********************
* INCLUDES
*********************/
#define _DEFAULT_SOURCE /* needed for usleep() */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include <aos/aos.h>
#include "app_config.h"
#include "app_init.h"
#include "csi_config.h"
#include "hw_config.h"
#include "board_config.h"
#include "drv/gpio_pin.h"
#include <drv/pin.h>
#include <drv/pwm.h>
#include <drv/tick.h>
#include "dht11.h"
#define SDA_OUT_H csi_gpio_pin_write(&sda, GPIO_PIN_HIGH)
#define SDA_OUT_L csi_gpio_pin_write(&sda, GPIO_PIN_LOW)
#define SDA_READ_IN csi_gpio_pin_read(&sda)
#define SDA_DIR_OUT csi_gpio_pin_dir(&sda, GPIO_DIRECTION_OUTPUT)
#define SDA_DIR_IN csi_gpio_pin_dir(&sda, GPIO_DIRECTION_INPUT)
#define DHT11_IN SDA_READ_IN
#define DHT11_OUT_H SDA_OUT_H
#define DHT11_OUT_L SDA_OUT_L
#define DHT11_DIR_OUT SDA_DIR_OUT
#define DHT11_DIR_IN SDA_DIR_IN
static csi_gpio_pin_t sda;
#define NUMBER 10
#define SIZE 5
/*可在其他的文件引用温湿度值,湿度同理*/
float temp_value, humi_value;
void sda_pinmux_init(void)
{
csi_pin_set_mux(PA2, PIN_FUNC_GPIO);
csi_gpio_pin_init(&sda, PA2);
csi_gpio_pin_mode(&sda, GPIO_MODE_PULLUP);
SDA_DIR_IN;
}
void delay_ms(uint32_t ms)
{
//aos_msleep(ms);
udelay(1000*ms);
}
void delay_us(uint32_t nCount)
{
udelay(nCount);
}
void dht11_reset(void)
{
// 按照DHT11手册步骤
SDA_DIR_OUT;
DHT11_OUT_L;
delay_ms(20);
DHT11_OUT_H;
SDA_DIR_IN;
delay_us(40);
}
bool dht11_read_bit(void)
{
while (DHT11_IN == GPIO_PIN_LOW);
delay_us(40);
if (DHT11_IN == GPIO_PIN_HIGH)
{
while (DHT11_IN == GPIO_PIN_HIGH);
return 1;
}
else
{
return 0;
}
}
uint8_t dht11_read_byte(void)
{
uint32_t i;
uint8_t data = 0;
for (i = 0; i < 8; i++)
{
data <<= 1;
data |= dht11_read_bit();
}
return data;
}
dht11_state dht11_read_data(uint8_t *buffer)
{
uint32_t i = 0;
uint32_t count = 0;
uint8_t checksum=0;
dht11_reset();
if (DHT11_IN != GPIO_PIN_LOW)
{
return ERROR1; //传感器无响应,退出函数
}
else
{
//检测到DHT11响应
count = 0;
while (DHT11_IN == GPIO_PIN_LOW && count++ < NUMBER)
delay_us(10);
if(count >= NUMBER) //检测计数器是否超过了设定的范围
return ERROR2; //读数据出错,退出函数
count = 0;
while (DHT11_IN == GPIO_PIN_HIGH && count++ < NUMBER)
delay_us(10);
if(count >= NUMBER) //检测计数器是否超过了设定的范围
return ERROR3; //读数据出错,退出函数
for (i = 0; i < SIZE; i++)
{
buffer = dht11_read_byte();
}
count = 0;
while (DHT11_IN == GPIO_PIN_LOW && count++ < NUMBER)
delay_us(10);
if(count >= NUMBER) //检测计数器是否超过了设定的范围
return ERROR4; //读数据出错,退出函数
checksum = buffer[0] + buffer[1] + buffer[2] + buffer[3];
if (checksum != buffer[4])
{
return ERROR5;
}
else
{
humi_value = buffer[0] + (buffer[1]%10) / 10.0;
temp_value = buffer[2] + (buffer[3]%10) / 10.0;
return OK; //正确的读出dht11 输出的数据
}
}
}
具体需要include哪些头文件,咱也不清楚,是直接照搬样例工程“ch2601_rgb_marquee_demo”中的“lec.c”,在此基础上增加了“stdint.h”、“drv/gpio_pin.h”、“dht11.h”。
宏定义IO操作,定义变量等。
使用到的IO PA2初始化,毫秒、微妙延时。
使用延时时需要注意,“udelay()”函数是在SDK库文件“tick.c”中定义的,用到了基础的“_10udelay()”,因此调用“delay_us()”时,输入参数需要是10的整数倍。
每次数据转换都需要主机发送特定的开始信号,姑且叫作复位吧,20ms(18ms)以上的低电平开始,释放总线,切换方向为输入,表示开始信号结束,延时40us用来确保传感器的响应时间,如果正常的话,此时传感器已响应;
每个位读取时传感器会把数据拉低50us,这里用死等的方式等待信号变高,之后高电平维持26到28us表示数据0,超过28us到70us之间表示数据1,这里不妨直接在40us之后判断状态是高还是低,如果是高那么意味着此位结果为1,如果为低那么结果为0;
连续8个位操作,自己然可得到一个整字节的数据。
在40us的响应时间后,总线状态如果依然为低,那么意味着传感器无响应,不能正常转换数据,程序退出此次转换,返回ERROR1标志;如果传感器已正常响应,那么程序往下走。
等待传感器将信号线拉高响应结束,如果超时还未等到高电平,则说明总线异常,程序退出,返回ERROR2;如果等到变高,则说明响应结束正常。
等待传感器由拉高变为低电平,如果超时还未等到低电平,则说明总线异常,程序退出,返回ERROR3;如果等到变低,则开始正常的40位串行数据输出。
5字节(40位)传感器数据接收。
全部40位数据接收完毕,等待传感器释放总线,如果可正常释放总线,则总线会被外部上拉电阻拉高;如果超时还未释放总线,则说明总线异常,程序退出,返回ERROR4。
检查前4字节校验和是否与第5字节数据一致,如果不一致,意味着数据有误,程序退出,返回ERROR5;检查校验成功,计算湿度、温度值。温度的小数部分可以在0到9之间变化,因此buffer[3]除以10可得到温度小数部分,先对10取模是为了保险起介,万一buffer[3]突然蹦出个超过9的数,也不至于影响温度的整数部分;实测湿度的小数部分一直为0,但算法应该是与湿度类似的。
在main函数中新建个任务来调用“dht11_read_data()”函数,实现对温湿度传感器的转换数据读取。
添加包含“dht11.h”,定义变量,声明任务函数。
前提工作都做到位了,那么“main()”函数就简单多了。开启任务“dht11_task”,在主循环中检测状态,通过串口打印温湿度数据值,每2秒打印一次数据。
为什么是2秒采集温湿度打印一次呢,因为在1秒采集时打印一次时容易出错,返回ERROR1结果。后来在dht11数据手册中找到依据。
四、实验结果与实物图片。
五、程序源代码。
(下载次数: 11, 2022-6-27 00:55 上传)
(下载次数: 9, 2022-6-27 00:55 上传)
(下载次数: 8, 2022-6-27 00:55 上传)
本帖最后由 gs001588 于 2022-6-27 01:04 编辑