基于STC89C51单片机的双通道DHT11实时温湿度显示系统(LCD1602)
- 前言
-
- 代码部分
- 注意事项
- 驱动部分
-
- 主要模块
- 初始化
- DHT11驱动
- 中断服务程序
- 显示函数
- 主函数
- 数学处理函数
- 总结
前言
这是单片机系统的课程大作业,做的有点仓促,代码其实也可以再优化一下,在这里抛砖引玉,希望能给大家带来一些思路。这也是第一次使用 Markdown编辑器 ,顺便学一下写作,淦,不说那么多废话了。
题目要求
本题目要求学生利用数字式温度和湿度采集环境信息采用I2C串口通讯技术返回单片机,并在LCD显示器实时显示,要求湿度误差不大于5%,温度误差不大于2摄氏度;要求能够显示二路的温度和湿度参数,在LCD屏幕上进行显示且刷新评率不低于0.5 Hz。系统组成包含有STC-89C52开发板,LCD1602显示屏,数字传感器DHT11。LCD显示要求可以同时显示两只传感器的任温度和湿度。
实现的功能
前三个问题已经解决。按键切换不同状态时,后台仍在持续读取数据,最大值为开机以来的最大值,平均值为检测到近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>
#define LCD1602_DB P0
sbit LCD1602_RS = P3^5;
sbit LCD1602_RW = P3^6;
sbit LCD1602_EN = P3^4;
void LCD_Delay10ms(unsigned int c);
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();
#endif
再来个C文件:只是简单的驱动函数,大家在网上随便都能找到相似的,也可以通过数据手册自己写。
这里有一个点,就是很多驱动都略写了LCD1602的读BUSY函数,具体原因求大佬告知。在这里为简化代码,我也忽略了。
#include <reg52.h>
#include "LCD.h"
void Lcd1602_Write_Cmd(unsigned char cmd)
{
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)
{
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;
else
addr = 0x40 + 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()
{
Lcd1602_Write_Cmd(0x38);
Lcd1602_Write_Cmd(0x0c);
Lcd1602_Write_Cmd(0x01);
Lcd1602_Write_Cmd(0x06);
}
void LCD_Delay10ms(unsigned int c)
{
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
sbit KEY = P3^0;
sbit KEY0 = P3^1;
void KeyScanInd();
void Keymode();
#endif
C文件:没用到就注释了哈
#include <reg52.h>
#include "key.h"
#include "LCD.h"
我使用到的按键是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;
}
}
主要模块
初始化
淦定义了好多变量,大家可以看一下代码的注释
这里有一点,在编写到最后,因为多定义两个数组,结果烧录进去程序就崩了,最后只好把一个二维数组改成一维的,放弃储存一些数据。
#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
sbit Temp_data =P2^0;
sbit Temp_data1=P2^1;
sbit flag = P3^3;
uchar modeflag = 0;
uchar status1 = 0;
uchar status = 0;
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没毛病这种错误一定不会出现。。。
void DHT11_start()
{
Temp_data=1;
DHT11_delay_us(2);
Temp_data=0;
DHT11_delay_ms(20);
Temp_data=1;
DHT11_delay_us(13);
}
void DHT11_start1()
{
Temp_data1=1;
DHT11_delay_us(2);
Temp_data1=0;
DHT11_delay_ms(20);
Temp_data1=1;
DHT11_delay_us(13);
}
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;
}
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;
}
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;
while(Temp_data==0);
DHT11_delay_us(40);
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;
}
rec_dat[0]=RH;
rec_dat[1]=RL;
rec_dat[2]=TH;
rec_dat[3]=TL;
}
else
{
status = 1;
}
}
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;
while(Temp_data1==0);
DHT11_delay_us(40);
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;
}
rec_dat[4]=RH1;
rec_dat[5]=RL1;
rec_dat[6]=TH1;
rec_dat[7]=TL1;
}
else
{
status1 = 1;
}
}
void DHT11_delay_us(unsigned char n)
{
while(--n);
}
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)
{
SCON=0x40;
REN=1;
TMOD=0x20;
TH1=0xFd,TL1=0xFd;
TI=1;
TR1=1;
}
void int1Init()
{
EA = 1;
EX1 = 1;
IT1 = 1;
}
void int1() interrupt 2
{
if(flag != 1)
{
Delayms(5);
if( flag!= 1)
{
P1 = ~P1;
modeflag++;
while(flag != 1);
}
}
}
显示函数
最占地方的一部分了。。。。。这里把各种状态显示到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);
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();
sprintf(dat_max_lcd0,"%.1f",dat_manage[0]);
sprintf(dat_max_lcd1,"%.1f",dat_manage[1]);
sprintf(dat_max_lcd2,"%.1f",dat_manage[2]);
sprintf(dat_max_lcd3,"%.1f",dat_manage[3]);
LcdShowStr(2,0,dat_max_lcd0);
LcdShowStr(6,0,"%");
LcdShowStr(2,1,dat_max_lcd1);
LcdShowStr(6,1,"C");
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("Humi2:%d.%d ", rec_dat[4],rec_dat[5]);
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);
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]);
LcdShowStr(2,0,dat_max_lcd0);
LcdShowStr(6,0,"%");
LcdShowStr(2,1,dat_max_lcd1);
LcdShowStr(6,1,"C");
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);
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]);
LcdShowStr(2,0,dat_max_lcd0);
LcdShowStr(6,0,"%");
LcdShowStr(2,1,dat_max_lcd1);
LcdShowStr(6,1,"C");
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)
{
KeymodeINTER();
DHT11_delay_ms(100);
DHT11_receive();
DHT11_delay_ms(100);
DHT11_receive1();
}
}
数学处理函数
当时求平均值愁死我了,淦,捡起我大一零碎的C语言知识,在VSCODE跑了几遍求平均值才又移植到keil这儿。
求平均值借鉴了一个大佬的:ADC采样算法----递推平均值采样法
void manage_math()
{
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];
}
}
总结
大体就是这样,希望给大家带来帮助,需要资料私信即可~
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)