基于STC89C51单片机的双通道DHT11实时温湿度显示系统(LCD1602)
前言
这是单片机系统的课程大作业,做的有点仓促,代码其实也可以再优化一下,在这里抛砖引玉,希望能给大家带来一些思路。这也是第一次使用 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>
/**********************************
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"
#i