【香橙派+DHT11】香橙派(全志H616)+ DHT11温湿度传感器的驱动教程

news2024/12/23 22:36:09

目录

    • 一、前言
        • 产品概述
        • 特点
        • 数据传送逻辑
        • DHT11通信时序
    • 二、代码
        • GPIO初始化
        • 起始信号
        • 读取数据
        • 整体代码
        • 执行结果

一、前言

最近写DHT11的代码到香橙派(全志H616)上,发现网上案例基本上都是树莓派+DHT11的居多,香橙派的少,少数找得到的代码跑起来也是不稳定或者数据相对不太准确,于是这里自己写了一篇,供大家参考和批评指正

产品概述


DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,应用领域:暖通 空调;汽车;消费品;气象站;湿度调节器;除湿器;家电;医疗;自动控制
在这里插入图片描述

特点

  • 相对湿度和温度测量
  • 全部校准,数字输出
  • 长期稳定性
  • 超长的信号传输距离:20米
  • 超低能耗:休眠
  • 4 引脚安装:可以买封装好的
  • 完全互换 : 直接出结果,不用转化

数据传送逻辑

只有一根数据线DATA,主控MCU发送序列指令给DHT11模块,模块一次完整的数据传输为40bit,高位先出

数据格式

8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和

关于校验:假设接收到的40位数据为:
在这里插入图片描述

计算:
0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001(校验位)


DHT11通信时序

在这里插入图片描述

(1)主机发送起始信号
首先单片机将连接DHT11 DATA引脚的GPIO口输出低电平,且低电平保持时间不能小于18ms (t1)最大不能超多30ms,然后拉高数据线20~40us (t2) ,等待读取DHT11的响应信号。

(2)检测从机应答信号
DHT11的DATA引脚检测到外部信号有低电平(t1),并等待外部低电平信号结束(t2),延迟后DHT11的DATA引脚处于输出状态,之后DHT11开始输出80 us (t3)的低电平作为应答信号,紧接着输出80us(t4)的高电平通知主机准备接收数据。
主机的I/O此时处于输入状态,检测I/O有低电平(DHT11应答信号)后,等待80us的高电平后接受数据。

(3)数据传输
由DHT11的DATA引脚输出40位数据,采用高位优先方式(MSB),微处理器根据I/0电平的变化接收40位数据。
位数据“0”的格式为:50微秒的低电平和26-28us的高电平。
位数据“1”的格式为:50微秒的低电平加70us的高电平。


二、代码


主体代码主要是利用多线程,用户每发送一次数据读取请求,创建一个线程用于读取数据;利于提高代码的健壮性和扩展性;

同时引入一个blockFlag的标志位,避免子线程代码跑飞无法退出的问题;

并且在测试过程中发现有偶尔测试出温度明显错误的数据;考虑到可能是由于环境、传感器、延时误差等原因导致的数据不准确问题,所以程序中会将超过50°C的数据视为无效数据,自行重新测试,最多自行重试5次

GPIO初始化

因为发送给DHT11的起始信号是先拉低电平,所以拉低电平前先维持一个稳定的高电平状态

void GPIO_init(int gpio_pin)
{
    pinMode(gpio_pin, OUTPUT); // set mode to output
    digitalWrite(gpio_pin, 1); // output a high level
    delay(1000);
}

起始信号

因为DHT11的触发是单次的,即每发送一次起始信号,才会检查一次温湿度,所以发送起始信号的代码必然是要多次复用,所以这里也封装成一个函数

void DHT11_Start_Sig()
{
	pinMode(pinNumber,OUTPUT);	//让GPIO为输出模式
	digitalWrite(pinNumber,HIGH);
	digitalWrite(pinNumber,LOW);
	delay(25);	//维持25ms的低电平
	digitalWrite(pinNumber,HIGH);	//转化为高电平,等待DHT11的响应信号
	
	pinMode(pinNumber,INPUT);
	//响应信号为80us电平与80us的准备信号
	
	pullUpDnControl(pinNumber,PUD_UP);	//进行上拉,增加稳定性,非必选
	delayMicroseconds(35);	//维持35微秒
}

读取数据

void* readSensorData(void *arg)
{
    uint8 crc; 	
    uint8 i;	
	int attempt = 5;	//调用一次最多尝试测5次
	
	while(attempt)
	{
		databuf = 0;	//清空数据存储buf
		crc = 0;		//清空校验位数据存储buf
		DHT11_Start_Sig();	
        
	    if(digitalRead(pinNumber)==0) //检测DHT11是否应答,应答则继续下一步
	    {
	      while(!digitalRead(pinNumber)); //wait to high
          
          //读取4个数据,合计32位
	      for(i=0;i<32;i++)
	      {
		       while(digitalRead(pinNumber)); //data clock start
		       while(!digitalRead(pinNumber)); //data start
	           delayMicroseconds(HIGH_TIME);	//如果32微秒后,仍然检测到是高电平,则该数据位为1
	           databuf*=2;		//移位到buf的更高位
	           if(digitalRead(pinNumber)==1) //1
	            {
	                databuf++;
	            }
	      }
          //读取校验位
	      for(i=0;i<8;i++)
	      {
			  	while(digitalRead(pinNumber)); //data clock start
			    while(!digitalRead(pinNumber)); //data start
		        delayMicroseconds(HIGH_TIME);
		        crc*=2;  
		        if(digitalRead(pinNumber)==1) //1
		        {
		           crc++;
		        }
	      }
          
		  //用于校验数据的准确性,当温度大于50时,视为无效数据
	  	  if(((databuf>>8)&0xff) > 50)
		  {
			 attempt--;
			 delay(500);	//不加这段延迟,下一次传感器来不及响应
			 continue;
		  }	
		  else
		  {
            //打印数据
		  	printf("Congratulations ! Sensor data read ok!\n");
	       	printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff); 
	       	printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
			blockFlag = 0;	//用来避免程序有时候跑飞,卡在此函数中,无法跳出
			return (void*)1;
		  }
	   }
	   else		//dht not answer
	   {
	   		blockFlag = 0;
	   		printf("Sorry! Sensor dosent ans!\n");
	        return (void*)0;
	   }
	}
    
    //如果代码执行到这里,则证明尝试读取了5次数据,都是不准确的数据
	blockFlag = 0;
	printf("get data fail\n");
	return (void*)2;
}

整体代码

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "contrlDevices.h"

typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;
 
#define HIGH_TIME 32
 
int pinNumber = 6;  //use gpio1 to read data
uint32 databuf;
int blockFlag;


void GPIO_init(int gpio_pin)
{
    pinMode(gpio_pin, OUTPUT); // set mode to output
    digitalWrite(gpio_pin, 1); // output a high level
    delay(1000);
    //return;
}


void DHT11_Start_Sig()
{
	pinMode(pinNumber,OUTPUT);
	digitalWrite(pinNumber,HIGH);
	digitalWrite(pinNumber,LOW);
	delay(25);
	digitalWrite(pinNumber,HIGH);
	
	pinMode(pinNumber,INPUT);
	//响应信号为80us电平与80us的准备信号
	
	pullUpDnControl(pinNumber,PUD_UP);
	delayMicroseconds(35);
}
 
 
void* readSensorData(void *arg)
{
    uint8 crc; 	
    uint8 i;	
	int attempt = 5;	//调用一次最多尝试测5次
	
	while(attempt)
	{
		databuf = 0;	//清空数据存储buf
		crc = 0;		//清空校验位数据存储buf
		DHT11_Start_Sig();	
        
	    if(digitalRead(pinNumber)==0) //检测DHT11是否应答,应答则继续下一步
	    {
	      while(!digitalRead(pinNumber)); //wait to high
          
          //读取4个数据,合计32位
	      for(i=0;i<32;i++)
	      {
		       while(digitalRead(pinNumber)); //data clock start
		       while(!digitalRead(pinNumber)); //data start
	           delayMicroseconds(HIGH_TIME);	//如果32微秒后,仍然检测到是高电平,则该数据位为1
	           databuf*=2;		//移位到buf的更高位
	           if(digitalRead(pinNumber)==1) //1
	            {
	                databuf++;
	            }
	      }
          //读取校验位
	      for(i=0;i<8;i++)
	      {
			  	while(digitalRead(pinNumber)); //data clock start
			    while(!digitalRead(pinNumber)); //data start
		        delayMicroseconds(HIGH_TIME);
		        crc*=2;  
		        if(digitalRead(pinNumber)==1) //1
		        {
		           crc++;
		        }
	      }
          
		  //用于校验数据的准确性,当温度大于50时,视为无效数据
	  	  if(((databuf>>8)&0xff) > 50)
		  {
			 attempt--;
			 delay(500);	//不加这段延迟,下一次传感器来不及响应
			 continue;
		  }	
		  else
		  {
            //打印数据
		  	printf("Congratulations ! Sensor data read ok!\n");
	       	printf("RH:%lu.%lu\n",(databuf>>24)&0xff,(databuf>>16)&0xff); 
	       	printf("TMP:%lu.%lu\n",(databuf>>8)&0xff,databuf&0xff);
			blockFlag = 0;	//用来避免程序有时候跑飞,卡在此函数中,无法跳出
			return (void*)1;
		  }
	   }
	   else		//dht not answer
	   {
	   		blockFlag = 0;
	   		printf("Sorry! Sensor dosent ans!\n");
	        return (void*)0;
	   }
	}
    
    //如果代码执行到这里,则证明尝试读取了5次数据,都是不准确的数据
	blockFlag = 0;
	printf("get data fail\n");
	return (void*)2;
}
 
int main ()
{

	pthread_t tid;
	int waitTimes = 10;
	char cmd[5] = {'\0'};
	
    if (wiringPiSetup() == -1) 
    {
    	printf("Setup wiringPi failed!");
    	return 1;
  	}
  	printf("Enter OS-------\n");
  
    while(1)
    {
     	waitTimes = 10;		
        blockFlag = 1;
        delay(1000);
        printf("input y\n");
        scanf("%s",cmd);
        getchar();
        if(strcmp(cmd,"y") == 0)
        {
            //创建一个线程用于读取传感器数据;
            //严谨来说此处的tid并发时有bug;读者可以自行优化,可以用互斥锁或者tid设为数组等都行
            //就当作留给读者的一个小作业吧
            if (pthread_create(&tid, NULL, readSensorData, NULL) != 0)
            {
                printf("thread create fail!\n");
                return -1;
            }
            
            //等待数据读取线程10s钟,如果10后blockFlag未置0,则说明读数据时跑飞卡住了
            while(waitTimes && blockFlag)
            {
                delay(1000);
                waitTimes--;
            }
            //强行结束跑飞的线程
            if(blockFlag == 1)
            {
                pthread_cancel(tid);
                printf("线程超时退出.....\n");
            }
        }
        else
        {
            printf("go on\n");
            continue;
        }
    }
    return 0;
}


执行结果

在这里插入图片描述


参考文章:
STM32一线协议-DHT11温湿度传感器采样实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux进程通信:无名管道

进程通信目的&#xff1a; &#xff08;1&#xff09;数据传输&#xff1a;进程间数据传输&#xff1b; &#xff08;2&#xff09;通知事件&#xff1a;一个进程向另一个或一组进程发送消息&#xff0c;通知某个事件的发生&#xff08;如子进程终止时需通知父进程&#xff0…

使用Lim测试平台快速完成批量造数

一、前言 在我们平时的测试过程中&#xff0c;经常遇到需要造大量数据的情况&#xff0c;比如&#xff1a;测试分页功能&#xff0c;性能压测、数据准备等。 这时我们可以通过写脚本或jmeter这类的工具来实现&#xff0c;但这些方式在团队协作的时候并不友好&#xff0c;当别人…

MySQL 函数和运算符

文章目录 一、MySQL 函数二、MySQL 运算符总结 一、MySQL 函数 MySQL 有很多内置的函数&#xff0c;以下列出了这些函数的说明。 二、MySQL 运算符 算术运算符 比较运算符 逻辑运算符 位运算符算术运算符 MySQL 支持的算术运算符包括: 除法运算和模运算中&#xf…

(2)elasticsearch环境搭建(win10)

安装jdk 版本选择&#xff1a;最好是java 8、java11或者java14 jdk兼容性&#xff1a;https://www.elastic.co/cn/support/matrix#matrix_jvm 操作系统兼容性&#xff1a;https://www.elastic.co/cn/support/matrix 自身兼容性&#xff1a;https://www.elastic.co/cn/suppo…

Linux下版本控制器(SVN) -服务器端环境搭建步骤

文章目录 进阶知识-Linux下版本控制器(SVN)4、服务器端环境搭建步骤4.1 安装服务器端程序4.2 验证是否安装成功4.3 创建并配置版本库4.4 配置 SVN对应的服务4.5 启动 SVN服务 本人其他相关文章链接 进阶知识-Linux下版本控制器(SVN) 4、服务器端环境搭建步骤 4.1 安装服务器端…

AD7356_SPI驱动程序设计_5MSPS_Verilog

好久没动手了&#xff0c;使用Verilog编写一个AD7356的SPI驱动程序。 AD7356是一个双通道、12位、低功耗的ADC。最高采样速率可达5MSPS&#xff0c;全功率输入带宽为110MHz。AD7356的引脚图如下。 SPI的时序图如下&#xff0c;为了使单通道的采样速率达到最高的5MSPS&#xff…

mac m1的docker nacos2.0.3访问宿主机器的mysql

一、环境介绍&#xff1a; 我的mac系统&#xff1a;m1芯片 macOs Ventura 13.1 我的docker版本&#xff1a;v20.10.8 本机msyql&#xff1a;8.0.25 端口&#xff1a;3306。 mysql版本查询脚本&#xff1a;select version() from dual; 二、准备安装nacos2.0.3 m1版本的镜像 …

Vue+Echarts 项目演练(中)后台数据接口的创建

全局引用Echarts与axios 后台接口创建express路由 api接口数据创建 全局引用Echarts与axios vue3.0的挂载方式&#xff1a;使用Provide/Inject依赖注入&#xff0c;将替代vue2中在原型链上挂载一些属性在app.vue中使用provider来给后代们提供数据 <script> import { p…

组态王与FX3U之间无线通讯(485BD口)

设备与设备之间互相通信&#xff0c;需要一座桥梁把二者连接起来&#xff0c;那就是传输通路与通信协议。 传输通路由传输介质与传输接口组成&#xff0c;传输介质可分为有线和无线传输介质两大类。传输接口即通信模块。传输接口常见的有三种RS-232、RS-485和RS-422通信模块。…

干货|英国专利申请

英国是世界上公认的一个实施现代专利制度的国家。英国1624年颁布的《垄断法案》被认为是世界上一部具有现代意义的专利法&#xff0c;并成为现代专利保护制度系的起点。英国现行的专利法于1977年颁布&#xff0c;并于1978年生效。英国的专利制度在保护发明创造&#xff0c;促进…

21.Java网络通信

目录 1. Java基本介绍 2. JDK下载安装及其环境配置 3. 一个简单的java程序 4. Eclipse基本使用、数据类型、运算符 5. 控制语句&#xff08;if、switch、for、while、foreach&#xff09; 6. Java数组 7. Java字符串对象(String|StringBuffer|StringBuilder|StringJoiner…

Asp.NET CORE实验室信息管理系统源码,支持LIS独立部署,Docker部署

技术架构&#xff1a;Asp.NET CORE 3.1 MVC SQLserver Redis等 基于B/S架构的实验室管理系统源码&#xff0c;整个系统的运行基于WEB层面&#xff0c;只需要在对应的工作台安装一个浏览器软件有外网即可访问。全套系统采用云部署模式&#xff0c;部署一套可支持多家医院检验科…

Java ---Object根类

(一&#xff09;定义 官方说法&#xff1a; 在Java中&#xff0c; Object 类是所有类的父类&#xff0c;即Java 的所有类都继承了 Object&#xff0c;子类可以使用 Object 的所有方法。 注意&#xff1a; Object类型的变量与除Object以外的任意引用数据类型的对象都多态引用 所…

youdiancms 9.5.0 版本 SQL注入(vulfocus复现)

启动服务 从上述信息&#xff0c;可以看到&#xff0c;web服务的80端口被映射到62461端口&#xff0c;并且该服务对外开启了3306端口并映射到20130 端口。并且给了数据库的相关信息。 配置web数据库 登陆后台&#xff0c;后台如下&#xff1a; 探测注入点 上述提到注入点是M…

OpenGL教程之 纹理练习

网址 LearnOpenGL 练习一 修改片段着色器&#xff0c;仅让笑脸图案朝另一个方向看。  解析&#xff1a;朝向另一个方向很简单&#xff0c;即让上文中的图片进行左右对称变换即可&#xff0c;即将片段着色器中笑脸的纹理坐标从textCord转换为vec2( 1 - textCord.x, text.y …

【淘宝】商品列表页数据采集+商品销量数据采集代码

采集场景 在淘宝首页&#xff08;https://s.taobao.com/&#xff09;输入关键词搜索&#xff0c;采集搜索后得到的商品列表页数据。示例中关键词为【耐克】&#xff0c;可根据需求进行更换&#xff0c;同时支持自动批量输入多个关键词。 采集字段 采集字段包括关键字文本值…

Nginx和tomcat反向代理(动静分离)

正向代理&#xff1a; 当用户想访问某一网址时&#xff0c;用户先访问代理服务器&#xff0c;然后由代理服务器向目标网址发送请求最终将数据返回代理服务器&#xff0c;最后代理服务器将数据返回给用户这一过程我们称之为正向代理。 反向代理&#xff1a;基本流程是与正向代理…

【react全家桶学习】如何创建一个react组件(超详)

前提是你安装了react脚手架&#xff0c;不会的看这里&#xff0c;然后再进行创建哦~ 【react全家桶学习】初始化react脚手架及项目结构讲解_suohs Blog的博客-CSDN博客 目录 问题1&#xff1a;如何创建一个简单的hello组件&#xff1f; 问题2&#xff0c;如果组件特别多怎么…

庚顿数据正式发布军工版实时数据库庚金3.0,鼎力支撑中国国防数字化

庚金实时数据库管理系统是北京庚顿数据科技有限公司旗下自主知识产权的军工级产品&#xff0c;可有效满足特种行业自主产权、高性能、高安全、高稳定等高端需求&#xff0c;轻松实现海量实时数据高频采集、海量存储等应用场景&#xff0c;切实保障了客户生产活动的稳定运行。庚…

c/c++:数组做函数参数,传入函数的首地址,相当于传址,指针做函数返回值,数组止做c语言中函数的返回值

c/c:数组做函数参数&#xff0c;传入函数的首地址&#xff0c;相当于传址&#xff0c;指针做函数返回值&#xff0c;数组禁止做c语言中函数的返回值 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0…