基于51单片机的双通道DHT11温湿度显示器(LCD1602)

2023-05-16

基于STC89C51单片机的双通道DHT11实时温湿度显示系统(LCD1602)

  • 前言
    • 题目要求
    • 实现的功能
    • 思路介绍
  • 代码部分
    • 注意事项
    • 驱动部分
      • LCD1602驱动
      • 按键驱动
    • 主要模块
      • 初始化
      • DHT11驱动
      • 中断服务程序
      • 显示函数
      • 主函数
      • 数学处理函数
  • 总结

前言

这是单片机系统的课程大作业,做的有点仓促,代码其实也可以再优化一下,在这里抛砖引玉,希望能给大家带来一些思路。这也是第一次使用 Markdown编辑器 ,顺便学一下写作,淦,不说那么多废话了。

题目要求

本题目要求学生利用数字式温度和湿度采集环境信息采用I2C串口通讯技术返回单片机,并在LCD显示器实时显示,要求湿度误差不大于5%,温度误差不大于2摄氏度;要求能够显示二路的温度和湿度参数,在LCD屏幕上进行显示且刷新评率不低于0.5 Hz。系统组成包含有STC-89C52开发板,LCD1602显示屏,数字传感器DHT11。LCD显示要求可以同时显示两只传感器的任温度和湿度。

在这里插入图片描述

List item

实现的功能

前三个问题已经解决。按键切换不同状态时,后台仍在持续读取数据,最大值为开机以来的最大值,平均值为检测到近10次数据的平均值(51性能有限,其实是答主能力有限),次数过多单片机将无法显示正确数据。题目中提到使用IIC,但是答主咩用到,DHT11通过单总线通信,之后简单复制便实现了两路操作。

至于第四问,答主还没肝出来,如果在课程时间内能做出来就更新,不行的话就随缘啦~提供一个思路:LCD1602作为一个字符型LCD,没有画点函数,但内置8个自定义字符,是否可以通过自定义字符来显示曲线呢,大家有兴趣可以试一下。

  • 历次【版本优化】:
    21.10:00 添加外部中断按键消抖,优化部分代码,略微提高速度,可实现任务1.1要求
    解决了切屏后温度不实时更新的问题
    21.18:07 完成任务1.3
    21.22:00 1.2最大值部分调试成功
    22.09:26 1.2最大值可实现无缝记录,开机到显示的最大值一直有效。
    22.10:28 1.2单平均值串口测量成功
    22.11:16 1.2多平均值实时lcd、串口测量成功
    22.15:30 1.1显示实时值刷新率提高

思路介绍

本次实验中,首先是两路DHT11温湿度传感器,管脚分别接P2.0,P2.1。剩余两个管脚分别板子自带接VCC和GND。将外部中断P3.3与一个独立按键相连接。按要求插好LCD1602.工作时DHT11测量外界温度。将温度信息发送到STC-C52,STC-C52实时检测DHT11的在线状态,并使LCD1602实时显示.通过按键切换,显示两路DHT11温湿度传感器的离在线状态、实时温湿度、平均温湿度与最大温湿度。

开发板是在一块芯片中集成了CPU( 中央处理器)、RAM( 数据存储器)、ROM( 程序存储器)、定时器/ 计数器和多种功能的I/O( 输入/ 输出) 接口等一台计算机所需要的基本功能部件,从而可以完成复杂的运算、逻辑控制、通信等功能。
软件设计思路:软件程序的设计包括多个模块,包括LCD1602显示驱动模块、延时函数模块、温度模式切换显示模块中断服务函数模块两路DHT11温度检测模块、按键检测模块等。

代码部分

下面是大家喜闻乐见的代码部分

注意事项

在keil的codesize和memory model注意选择最大,否则程序可能烧不进去。
在这里插入图片描述

驱动部分

为了方便管理,我把按键和LCD驱动写了两个库文件,在实际使用中,按键扫描并没有用到,因为避免程序运行过慢,检测不到,选择使用外部中断来实现按键。

LCD1602驱动

先来个头文件:注意PIN口的定义,这里只是简单的驱动函数,大家在网上随便都能找到相似的。

#ifndef __LCD_H_
#define __LCD_H_

/**********************************
包含头文件
**********************************/
#include<reg52.h>


/**********************************
			PIN口定义
**********************************/
#define LCD1602_DB  P0     
sbit LCD1602_RS = P3^5;
sbit LCD1602_RW = P3^6;
sbit LCD1602_EN = P3^4;	 

//void Lcd1602_Delay1ms(uint c);   //误差 0usvo
void LCD_Delay10ms(unsigned int c);
//void Read_Busy();           //忙检测函数,判断bit7是0,允许执行;1禁止
void Lcd1602_Write_Cmd(unsigned char cmd);     //写命令
void Lcd1602_Write_Data(unsigned char dat);   //写数据
void LcdSetCursor(unsigned char x,unsigned char y);  //坐标显示
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str);     //显示字符串
void InitLcd1602();              //1602初始化
					  
#endif

再来个C文件:只是简单的驱动函数,大家在网上随便都能找到相似的,也可以通过数据手册自己写。
这里有一个点,就是很多驱动都略写了LCD1602的读BUSY函数,具体原因求大佬告知。在这里为简化代码,我也忽略了。

#include <reg52.h>
#include "LCD.h"

void Lcd1602_Write_Cmd(unsigned char cmd)     //写命令
{
    //Read_Busy();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
	LCD_Delay10ms(1);
    LCD1602_EN = 1;
	LCD_Delay10ms(1);
    LCD1602_EN = 0;    
}

void Lcd1602_Write_Data(unsigned char dat)   //写数据
{
      //Read_Busy();
      LCD1602_RS = 1;
      LCD1602_RW = 0;
      LCD1602_DB = dat;
	  LCD_Delay10ms(1);
      LCD1602_EN = 1;
	  LCD_Delay10ms(1);
      LCD1602_EN = 0;
}

//指定位置开始显示数据!
void LcdSetCursor(unsigned char x,unsigned char y)  //坐标显示
{
    unsigned char addr;
    if(y == 0)
        addr = 0x00 + x;//第一行开始,x表示一行的第x个
    else
        addr = 0x40 + x;//第二行开始,x表示一行的第x个
    
    Lcd1602_Write_Cmd(addr|0x80);
}

void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str)     //显示字符串
{
    LcdSetCursor(x,y);      //当前字符的坐标
    while(*str != '\0')
    {
        Lcd1602_Write_Data(*str++);
    }
}

void InitLcd1602()              //1602初始化
{
    Lcd1602_Write_Cmd(0x38);    //打开,5*8,8位数据
    Lcd1602_Write_Cmd(0x0c);	//开显示,未添加光标闪烁
	Lcd1602_Write_Cmd(0x01);    //清屏   
	Lcd1602_Write_Cmd(0x06); 
   // Lcd1602_Write_Cmd(0x07);	//地址指针加一,右移
    
}

void LCD_Delay10ms(unsigned int c)   //误差 0us
{
    unsigned char a,b;
    for(;c>0;c--)
        for(b=38;b>0;b--)
            for(a=130;a>0;a--);
}



按键驱动

没啥好说的,虽然写了,但其实我就没咋用到。。。。。。

头文件

#ifndef __KEY_H_
#define __KEY_H_

#include<reg52.h>

#define FOSC 11059200L
#define uint unsigned int
/**********************************
			PIN口定义
**********************************/  


sbit KEY    = P3^0;//独立按键S2
sbit KEY0   = P3^1;//独立按键S3
//sbit KEY    = P3^6;//上课用单片机
//sbit KEY0   = P3^5;




void KeyScanInd();//独立按键检测
void Keymode();  //模式切换
					 
#endif

C文件:没用到就注释了哈

#include <reg52.h>
#include "key.h"
#include "LCD.h"

//uint KeyValue=0;



//void KeyScanInd()
//{
//	KEY  = 1; //判断独立按键
//	KEY0 = 1; //判断独立按键
//	
//	if(KEY != 1)
//	{
//		Delayms(5);//软件消抖
//		if( KEY!= 1)
//		{
//			KeyValue++;
//			if(KeyValue==4) KeyValue=1;
//			while(KEY != 1);//松手检测	
//		}
//	}
//	
//	if(KEY0 != 1)
//	{
//		Delayms(5);//软件消抖
//		if( KEY0!= 1)
//		{
//			KeyValue--;
//			if(KeyValue==0) KeyValue=3;
//			while(KEY0 != 1);//松手检测	
//		}
//	}
//	
//}

//void Keymode()
//{
//	if(KeyValue==1)//当前值
//		{
//			LcdShowStr(0,0,"Present value!");
//			P1 = ~P1;
//		}
//	if(KeyValue==2)//平均值
//		{
//			LcdShowStr(0,0,"Average value!");
//			P1 = ~P1;
//		}
//	if(KeyValue==3)//最大值
//		{		
//			LcdShowStr(0,0,"Maximum value!");
//			P1 = ~P1;
//			
//		}		
//}



我使用到的按键是P3^3,通过外部中断1触发(不好意思忘更新了),这部分直接写到主要模块了。

//键盘函数中断版
void KeymodeINTER()
{
	switch(modeflag)
	{
		case 0:  displaySTATUS();
				 Lcd1602_Write_Cmd(0x01);  //清屏
		break;
		//当前值
		case 1: 			 
				displayNOW();  				 	         
				Lcd1602_Write_Cmd(0x01);    //清屏	
		break;
				 
		//平均值
		case 2:   				
				displayAVE();
				Lcd1602_Write_Cmd(0x01);    //清屏 
				 
		break;
		//最大值
		case 3:   
				 	         
				 displayMAX();	
				 Lcd1602_Write_Cmd(0x01);    //清屏	
		break;
		
		case 4:  
				 displayBight();	
				 Lcd1602_Write_Cmd(0x01);    //清屏	
		break;
		
		case 5:  
				 displayBight1();
				 modeflag = 0;	
				 Lcd1602_Write_Cmd(0x01);    //清屏	
		break;
	}	
}

主要模块

初始化

淦定义了好多变量,大家可以看一下代码的注释
这里有一点,在编写到最后,因为多定义两个数组,结果烧录进去程序就崩了,最后只好把一个二维数组改成一维的,放弃储存一些数据。

/*********************************************************************************
* 【编写时间】: 2021年3月22日
* 【作    者】: 手动打码,滑稽
* 【版    本】: 1.7
* 【编译环境】: Keil μVisio5	
* 【程序功能】: 			   			            			    
* 【版本更新】: 21.10:00 添加外部中断按键消抖,优化部分代码,略微提高速度,可实现功能1.1要求
				 解决了切屏后温度不实时更新的问题
				 21.18:07 完成任务1.3
				 21.22:00 1.2最大值部分调试成功
				 22.9:26   1.2最大值可实现无缝记录,开机到显示的最大值一直有效。
				 22.10:28  1.2单平均值串口测量成功
				 22.11:16  1.2多平均值实时lcd、串口测量成功
				 22.15:30  1.1显示实时值刷新率提高
* 【预期改动】:1.使用定时器0按键消抖,使用定时器1定时发送串口数据(可拓展为1.2平均值问题)--------失败
				2.不用数组储存数据,直接累加!!!解决问题1.2----------成功

**********************************************************************************/

#include <reg52.h>
#include <intrins.h>
#include <math.h>
#include <stdio.h>
#include "LCD.h"
#include "key.h"
#include <string.h>

#define uchar unsigned char
#define uint unsigned int

#define N 5 //平均值计算的组数
/*******************************************************************************
* 实验名			   :温度显示程序
* 使用的IO	           : P2^0;P2^1;				 :
*******************************************************************************/

sbit Temp_data =P2^0; //
sbit Temp_data1=P2^1;
sbit flag   = P3^3;//中断启动位

uchar modeflag = 0;//键盘模式切换

uchar status1 = 0;//DHT11状态
uchar status  = 0;//DHT11状态

//函数定义
void Delayms(unsigned int ms);

void DHT11_delay_us(unsigned char n);
void DHT11_delay_ms(unsigned int z);

void DHT11_start();

uchar DHT11_rec_byte();
uchar DHT11_rec_byte1();

void DHT11_receive();
void DHT11_receive1();
void InitUART(void);

void displaySTATUS();
void displayNOW();
void displayAVE();
void displayMAX();

void manage_math();
void max_math();
void avr_math();

unsigned int rec_dat[8]={0,0,0,0,0,0,0,0};//温度初始数组


double dat_manage[4]={ 0, 0, 0, 0};//实时数据储存数组
double dat_max[4]={0,0,0,0};//最大值储存数组
double dat_avr[4]={0,0,0,0};//平均值储存数组

double S[4] = {0,0,0,0};      //累加和
double C[4] = {0,0,0,0};      //本次采样值
double A[4] = {0,0,0,0};      //平均值

//第一路传感器
unsigned char rec_dat_lcd0[6];
unsigned char rec_dat_lcd1[6];
unsigned char rec_dat_lcd2[6];
unsigned char rec_dat_lcd3[6];

//第二路传感器
unsigned char rec_dat_lcd4[6];
unsigned char rec_dat_lcd5[6];
unsigned char rec_dat_lcd6[6];
unsigned char rec_dat_lcd7[6];

//最大值平均值字符串中转函数
unsigned char dat_max_lcd0[6];
unsigned char dat_max_lcd1[6];
unsigned char dat_max_lcd2[6];
unsigned char dat_max_lcd3[6];


DHT11驱动

我们将DHT11接受到的数据储存在rec_dat[ ]数组中,并记录判断位,为第三问求在线掉线错误做准备。 我将错误设置成校验位与传输数据不符,当然DHT11没毛病这种错误一定不会出现。。。

//DHT11起始信号1

void DHT11_start()	
{
	Temp_data=1;
	
	DHT11_delay_us(2);
	
	Temp_data=0;
	
	DHT11_delay_ms(20);
	
	Temp_data=1;
	
	DHT11_delay_us(13);
	

}

//DHT11起始信号2

void DHT11_start1()	
{
	Temp_data1=1;//
	
	DHT11_delay_us(2);
	
	Temp_data1=0;//
	
	DHT11_delay_ms(20);
	
	Temp_data1=1;//
	
	DHT11_delay_us(13);
	

}
//接收一个字节通道1


unsigned char DHT11_rec_byte()
{
	
	unsigned char i,dat;

	
	for(i=0;i<8;i++)
	{
			while(!Temp_data);
		  DHT11_delay_us(8);
			dat<<=1;
			if(Temp_data==1)
			{
				dat+=1;
			}
			while(Temp_data);
	}

	return dat;

	
}
//接收一个字节通道2


unsigned char DHT11_rec_byte1()
{
	
	unsigned char i,dat1;
	
	for(i=0;i<8;i++)
	{
			while(!Temp_data1);
			DHT11_delay_us(8);
			dat1<<=1;
			if(Temp_data1==1)
			{
				dat1+=1;
			}
			while(Temp_data1);
	}

	return dat1;
	
}

//接收温湿度数据通道1
void DHT11_receive()
{
	unsigned int R_H,R_L,T_H,T_L;
	unsigned char RH,RL,TH,TL,revise;
	
	DHT11_start();
	Temp_data=1;
	
	
	if(Temp_data==0)
	{
					
		status=Temp_data;//此时为0,准备好嘞		
		while(Temp_data==0);   //等待拉高     
        DHT11_delay_us(40);  //拉高后延时80us
		
        R_H=DHT11_rec_byte();    //接收湿度高八位  
        R_L=DHT11_rec_byte();    //接收湿度低八位  
        T_H=DHT11_rec_byte();    //接收温度高八位  
        T_L=DHT11_rec_byte();    //接收温度低八位
        revise=DHT11_rec_byte(); //接收校正位

        DHT11_delay_us(25);    //结束

        if((R_H+R_L+T_H+T_L)==revise)      //校正
        {
            RH=R_H;
            RL=R_L;
            TH=T_H;
            TL=T_L;
	
        } 
		else
		{
			status = 2;//此时为2,数据有误
		}
        /*数据处理,方便显示*/
        rec_dat[0]=RH;
        rec_dat[1]=RL;
        rec_dat[2]=TH;
        rec_dat[3]=TL;

	}
		else
	{
		status = 1;//此时为1,DHT11断线
	}
}

//接收温湿度数据通道2
void DHT11_receive1()
{
	uint R_H1,R_L1,T_H1,T_L1;
	uchar RH1,RL1,TH1,TL1,revise1;
	
	DHT11_start1();
	
	Temp_data1=1;	
	
	
	if(Temp_data1==0)
	{
		status1=Temp_data1;//此时为0,准备好嘞
		
		while(Temp_data1==0);   //等待拉高     
        DHT11_delay_us(40);  //拉高后延时80us
		
        R_H1=DHT11_rec_byte1();    //接收湿度高八位  
        R_L1=DHT11_rec_byte1();    //接收湿度低八位  
        T_H1=DHT11_rec_byte1();    //接收温度高八位  
        T_L1=DHT11_rec_byte1();    //接收温度低八位
        revise1=DHT11_rec_byte1(); //接收校正位

        DHT11_delay_us(25);    //结束

        if((R_H1+R_L1+T_H1+T_L1)==revise1)      //校正
        {
            RH1=R_H1;
            RL1=R_L1;
            TH1=T_H1;
            TL1=T_L1;

        } 
		else
		{
			status1 = 2;//此时为2,数据有误
		}

		
        /*数据处理*/
        rec_dat[4]=RH1;
        rec_dat[5]=RL1;
        rec_dat[6]=TH1;
        rec_dat[7]=TL1;

	}
	else
	{
		status1 = 1;//此时为1,DHT11断线
	}
}

//延时us   --2*n+5us
void DHT11_delay_us(unsigned char n)
{
    while(--n);
}

//延时ms
void DHT11_delay_ms(unsigned int z)
{
   unsigned int i,j;
   for(i=z;i>0;i--)
      for(j=110;j>0;j--);
}

中断服务程序

这里启用了串口通信来实时打印温度值,在程序中可能有的注释了,大家如果想用取消注释就可以,打开串口助手起飞~
这里也定义了键盘中断函数,可怜我的key.c没用上呜呜呜(开个玩笑)。

void InitUART(void)//使用定时器1作为串口波特率发生器
{

	SCON=0x40;					//串口通信工作方式1
	REN=1;						//允许接收
	TMOD=0x20;					//定时器1的工作方式2
	TH1=0xFd,TL1=0xFd;		
	TI=1;                       //这里一定要注意
	TR1=1;	

}
//外部中断1初始化--键盘使用p3.3qingxiang
void int1Init()
{
	EA = 1;	//开总中断
	EX1 = 1;//开外部中断1
	IT1 = 1;//外部中断1下降沿触发	
}
//外部中断1中断服务程序 切换显示模式
void int1() interrupt 2
{
	if(flag != 1)
	{
		Delayms(5);//软件消抖
		if( flag!= 1)
		{
			P1 = ~P1;
			modeflag++;
			while(flag != 1);//松手检测	
		}
	}
	
//	P1 = ~P1;
//	modeflag++;
}  


显示函数

最占地方的一部分了。。。。。这里把各种状态显示到lcd上。
sprintf函数是转字符串,毕竟lcd1602的字符库一个一个敲地址也有点费事鸭。

/***********************************显示函数********************/

void displaySTATUS()
{
	LcdShowStr(0,0,"1-DHT:");
	LcdShowStr(0,1,"2-DHT:");
	
	while(1)
	{
			//解决切屏后温度不实时更新的问题
			DHT11_delay_ms(150);
			DHT11_receive();		
			DHT11_delay_ms(150);
			DHT11_receive1();
									

			switch(status1)
		{
			case 0:  LcdShowStr(7,1,"ALREADY");

			break;
			
			case 1:  
					LcdShowStr(7,1,"WAITING");

			break;
					 
			case 2:   
					LcdShowStr(7,1,"ERROR!");				 
			break;
			
		}	
		
				switch(status)
		{
			case 0:  LcdShowStr(7,0,"ALREADY");

			break;
			
			case 1:  
					LcdShowStr(7,0,"WAITING");

			break;
					 
			case 2:   
					LcdShowStr(7,0,"ERROR!!!");				 
			break;
			
		}
		
		if(modeflag != 0)	break;
	}
		
}

void displayNOW()
{
	InitLcd1602(); 
	LcdShowStr(0,0,"Present value!");
	DHT11_delay_ms(500);//只是为了好看
	Lcd1602_Write_Cmd(0x01);    //清屏 
	
	//	//LED显示静态变量
	LcdShowStr(0,0,"H:");
	LcdShowStr(0,1,"T:");
	LcdShowStr(8,0,"H':");
	LcdShowStr(8,1,"T':");
	
	while(1)
	{
		//解决切屏后温度不实时更新的问题
		DHT11_delay_ms(150);
		DHT11_receive();		
		DHT11_delay_ms(150);
		DHT11_receive1();
	
		manage_math();//

		//通道1数据	
		sprintf(dat_max_lcd0,"%.1f",dat_manage[0]);
		sprintf(dat_max_lcd1,"%.1f",dat_manage[1]);
		//通道2数据
		sprintf(dat_max_lcd2,"%.1f",dat_manage[2]);
		sprintf(dat_max_lcd3,"%.1f",dat_manage[3]);		
		//*********通道1
		//湿度
		LcdShowStr(2,0,dat_max_lcd0);
		LcdShowStr(6,0,"%");
				
		//温度
		LcdShowStr(2,1,dat_max_lcd1);
		LcdShowStr(6,1,"C");
		
		//*********通道2
		//湿度
		LcdShowStr(11,0,dat_max_lcd2);
		LcdShowStr(15,0,"%");
		
		//温度
		LcdShowStr(11,1,dat_max_lcd3);
	    LcdShowStr(15,1,"C");
	
		max_math();//保证之前的最大值被记录
		avr_math();//保证之前的平均值被记录
		
//串口助手打印温度		
		printf("Humi1:%d.%d ",    rec_dat[0],rec_dat[1]);
		//printf("Temp1:%d.%d °C\n",rec_dat[2],rec_dat[3]);
		printf("Humi2:%d.%d ",  rec_dat[4],rec_dat[5]);
		//printf("Temp2:%d.%d °C\n",rec_dat[6],rec_dat[7]);		
		
		printf("avrHumi1:%.1f \n", dat_avr[0]);	
		printf("avrHumi2:%.1f \n", dat_avr[3]);	
		printf("A:%.1f \n", A[1]);	
		printf("C:%.1f \n", C[1]);	
		printf("S:%.1f \n", S[1]);	
		printf("/***********/\n");			
		
		
		if(modeflag != 1)	break;
	}

}

void displayAVE()
{
	LcdShowStr(0,0,"Average value!"); 
	DHT11_delay_ms(500);
	Lcd1602_Write_Cmd(0x01);    //清屏  
	
	//	//LED显示静态变量
	LcdShowStr(0,0,"H:");
	LcdShowStr(0,1,"T:");
	LcdShowStr(8,0,"H':");
	LcdShowStr(8,1,"T':");
	while(1)
	{
		//解决切屏后温度不实时更新的问题
		DHT11_delay_ms(150);
		DHT11_receive();		
		DHT11_delay_ms(150);
		DHT11_receive1();
		
		manage_math();
		avr_math();
		
		sprintf(dat_max_lcd0,"%.1f",dat_avr[0]);
		sprintf(dat_max_lcd1,"%.1f",dat_avr[1]);
		sprintf(dat_max_lcd2,"%.1f",dat_avr[2]);
		sprintf(dat_max_lcd3,"%.1f",dat_avr[3]);
		//*********通道1
		//湿度
		LcdShowStr(2,0,dat_max_lcd0);
		LcdShowStr(6,0,"%");
				
//		//温度
		LcdShowStr(2,1,dat_max_lcd1);
		LcdShowStr(6,1,"C");
		
		//*********通道2
		//湿度
		LcdShowStr(11,0,dat_max_lcd2);
		LcdShowStr(15,0,"%");
		
		//温度
		LcdShowStr(11,1,dat_max_lcd3);
	    LcdShowStr(15,1,"C");	

		printf("avrHumi:%.1f ",  dat_avr[0]);
		printf("avrTemp:%.1f °C\n",dat_avr[1]);
	
		if(modeflag != 2)	break;
	
	}
}

void displayMAX()
{
	
	LcdShowStr(0,0,"Maximum value!");			 
	DHT11_delay_ms(500);
	Lcd1602_Write_Cmd(0x01);    //清屏  
	//	//LED显示静态变量
	LcdShowStr(0,0,"H:");
	LcdShowStr(0,1,"T:");
	LcdShowStr(8,0,"H':");
	LcdShowStr(8,1,"T':");
	
	while(1)
	{
		//解决切屏后温度不实时更新的问题
		DHT11_delay_ms(150);
		DHT11_receive();		
		DHT11_delay_ms(150);
		DHT11_receive1();
		
		max_math();
		
		sprintf(dat_max_lcd0,"%.1f",dat_max[0]);
		sprintf(dat_max_lcd1,"%.1f",dat_max[1]);
		sprintf(dat_max_lcd2,"%.1f",dat_max[2]);
		sprintf(dat_max_lcd3,"%.1f",dat_max[3]);
		//*********通道1
		//湿度
		LcdShowStr(2,0,dat_max_lcd0);
		LcdShowStr(6,0,"%");
				
//		//温度
		LcdShowStr(2,1,dat_max_lcd1);
		LcdShowStr(6,1,"C");
		
		//*********通道2
		//湿度
		LcdShowStr(11,0,dat_max_lcd2);
		LcdShowStr(15,0,"%");
		
		//温度
		LcdShowStr(11,1,dat_max_lcd3);
	    LcdShowStr(15,1,"C");	

		printf("maxHumi:%.1f ",  dat_max[0]);
		printf("maxTemp:%.1f °C\n",dat_max[1]);

		if(modeflag != 3)	break;
	
	}
}

主函数

他来了他来了!主函数他来了!
哈哈哈哈哈其实主函数只是简单的初始化一下~

//主函数
void main()
{
	InitUART();
	InitLcd1602();
	int1Init();

	while(1)
	{
		//KeyScanInd();		
		KeymodeINTER();//进入该函数后会一直循环,下列函数功能失效
		
		DHT11_delay_ms(100);
		DHT11_receive();		
		DHT11_delay_ms(100);
		DHT11_receive1();
				
//      //串口助手打印温度		
//		printf("Humi:%d.%d ",  rec_dat[0],rec_dat[1]);
//		printf("Temp:%d.%d °C\n",rec_dat[2],rec_dat[3]);
//		printf("Humi:%d.%d ",  rec_dat[4],rec_dat[5]);
//		printf("Temp:%d.%d °C\n",rec_dat[6],rec_dat[7]);
						
	}
}

数学处理函数

当时求平均值愁死我了,淦,捡起我大一零碎的C语言知识,在VSCODE跑了几遍求平均值才又移植到keil这儿。
求平均值借鉴了一个大佬的:ADC采样算法----递推平均值采样法

/*******************************数学处理部分**********************/

void manage_math()
{
//	int i,j;
	
	//第一路温湿度
	 dat_manage[0]=rec_dat[0]+rec_dat[1]*0.1;
	 dat_manage[1]=rec_dat[2]+rec_dat[3]*0.1;
	//第二路温湿度
	 dat_manage[2]=rec_dat[4]+rec_dat[5]*0.1;
	 dat_manage[3]=rec_dat[6]+rec_dat[7]*0.1;
	
	
}

void max_math()//最大值处理函数
{
	uint n;

	manage_math();
	
	for(n=0;n<4;n++) //取最大值
	{   
		if ( dat_manage[n] >  dat_max[n] )
			 dat_max[n]=dat_manage[n] ;

	}           
}


void avr_math() //平均值处理函数
{
	static  uchar count = 0;	
	uchar i;
	
	manage_math();
	
	for(i=0;i<4;i++)
	{
		C[i] = dat_manage[i];
		if( count == 0 )
		{
			A[i] = C[i];
			S[i] = A[i] * N;
			count = 1;
		}
		S[i] = S[i] - A[i] + C[i];        //加上本次采样值,减去上次平均值
		A[i] = S[i] / N;                  //计算本次平均值
		count = 0;
		dat_avr[i]=A[i];
	}

}

总结

大体就是这样,希望给大家带来帮助,需要资料私信即可~

主程序 中断 显示 DHT11 按键扫描,触发中断 循环 切换显示模式. 按键中断, 当前在线状态 实时两路温湿度 实时平均温湿度 实时最大温湿度 传输温湿度数据 传输温湿度数据 传输温湿度数据 主程序 中断 显示 DHT11
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于51单片机的双通道DHT11温湿度显示器(LCD1602) 的相关文章

随机推荐

  • 30 秒速成好莱坞黑客 -- 在 Linux 终端中伪造好莱坞黑客屏幕

    这是一个假好莱坞黑客的视频 xff1a 视频效果 xff1a https www douyin com video 7053016921830116623 简介 xff1a 这个小工具将 Linux 终端变成好莱坞风格的实时黑客场景 该工具
  • linux/mm/memory.c/try_to_share()

    try to share checks the page at address 34 address 34 in the task 34 p 34 to see if it exists and if it is clean If so s
  • 通过 Telnet 在 Linux 终端中观看ASCII 星球大战

    打开终端并使用以下命令安装 telnet xff1a span class token function sudo span span class token function apt span span class token funct
  • 追逐鼠标光标的好奇小猫咪

    Oneko 是一个有趣的命令 xff0c 它将常规光标变成鼠标 xff0c 并创建一只好奇的小猫 xff0c 一旦移动光标 xff0c 小猫就会追逐它 它不仅限于终端 当猫追逐光标时 xff0c 还可以继续工作 使用以下命令安装 Oneko
  • Linux 中的旧扣簧键盘(Bucklespring 键盘)声音

    Ubuntu 可以使用 Snap 轻松安装它 运行以下命令 xff1a span class token function sudo span snap span class token function install span buck
  • PDF已加密,可以打开但是不能编辑不允许打印的解决办法

    文章目录 文档打开口令和许可口令解除加密方法一 FreeMyPdf xff1a http freemypdf com http freemypdf com 方法二 绿色小软件 xff1a PDF Password Remover 文档打开口
  • 如何在 Ubuntu 和其他 Linux 发行版中查看 MAC 地址

    文章目录 使用命令行查找MAC地址获取蓝牙的MAC地址 使用 GUI 获取 MAC 地址 在学习网络或对其进行故障排除时 xff0c 需要知道 MAC 地址 一台计算机可以有多个 MAC 地址 因为MAC地址是联网的核心部分 xff0c 每
  • 卸载流氓透明APP,帮老人清理手机弹窗广告

    症状 xff1a 只要解锁手机 xff0c 就时不时弹出广告视频 xff0c 声音还开到最大 xff0c 无法关闭 尤其是 xff0c 每点开一个app xff0c 都会弹出广告视频 xff0c 接打电话也不例外 xff0c 导致无法正常通
  • 无法翻译此网页的解决办法 - GoogleTranslateIpCheck

    2022年9月末 xff0c 网页翻译工具谷歌翻译停止了中国区服务 GitHub 地址 xff1a https github com Ponderfly GoogleTranslateIpCheck 我用的是win10系统 xff0c 所以
  • 树莓派4B(aarch64,arm64)bullseye安装Qt5、GStreamer

    简介 本文描述树莓派 4B Raspberry Pi OS xff08 64位 xff09 完成启动后 xff0c 进行的第一个编译安装任务 xff1a Qt5 43 GStreamer xff0c 同时也是OpenCV编译安装前的准备工作
  • 51单片机:蜂鸣器

    小白向单片机笔记二 xff1a 蜂鸣器 前言一 蜂鸣器原理二 蜂鸣器原理图1 蜂鸣器代码2 代码解释1 为什么蜂鸣器响 xff0c 和LED6闪烁中有两个delay 函数 xff1f 2 为什么BEEP 61 1是关闭LED6 而i 61
  • 多目标跟踪算法中之图匹配——匈牙利算法和KM算法详解

    目录 一 匈牙利算法1 算法背景及思想2 最大匹配3 最优匹配 完美匹配4 增广路径5 代码实现6 匈牙利算法总结6 1 深度优先6 2 广度优先 二 KM算法思想及局限性代码示例1 定义KM方法类2 定义权重数值 xff0c 执行主函数
  • linux/mm/memory.c/int share_page(unsigned long address)

    share page tries to find a process that could share a page with the current one Address is the address of the wanted pag
  • sumo教程 Hello World

    sumo教程 Hello World 注意事项 确保安装的sumo版本至少为1 4 0 以便能够使用本教程中显示的所有功能 要求 sumo gui和netedit的版本大于等于1 4 0 介绍 这个教程服务于第一次使用sumo的人员 我们准
  • 教程——OSMWebWizard

    教程 OSMWebWizard 有关事宜 osm web wizard提供一种最简单的方式开始sumo 基于一种openstreetmap的摘抄 你可以配置随机流量需求和可视化一种场景在sumo gui 这个教程可以指导你一步步从选择地图类
  • sumo-绕圈行驶

    绕圈行驶 在本教程中 xff0c 我们将使用 netedit 构建一个简单的圆形网络 xff0c 并使用重路由器使车辆绕圈行驶 所有定义文件都可以在 lt SUMO HOME gt docs tutorial circles 目录中找到 本
  • 教程——sumolympics

    SUMOlympics 本教程针对不同的交通模式设置了比赛 xff08 集体 100 米冲刺 xff09 您将学习如何在 netedit 中创建特殊车道和 xff08 非常简单的 xff09 红绿灯 xff0c 使用不同的车辆类别来定义车辆
  • sumo 教程——高速公路

    本教程涵盖了在多车道高速公路上创建异构流以及修改和保存视图设置的非常基本的元素 修建高速公路 编辑几何点 打开 netedit 创建一个虚构的高速公路 我们一开始就保持这个非常简单 xff0c 并考虑一个没有入口或出口的路段 按e进入边创建
  • sumo教程——Manhattan

    介绍 本教程介绍了如何在 SUMO 中构建曼哈顿移动模型 在这个模型中 xff0c 固定数量的车辆在曼哈顿电网网络上随机行驶 所有文件也可以在 lt SUMO HOME gt docs tutorial manhattan 目录中找到 创建
  • 基于51单片机的双通道DHT11温湿度显示器(LCD1602)

    基于STC89C51单片机的双通道DHT11实时温湿度显示系统 xff08 LCD1602 xff09 前言题目要求实现的功能思路介绍 代码部分注意事项驱动部分LCD1602驱动按键驱动 主要模块初始化DHT11驱动中断服务程序显示函数主函