基于STC89C单片机双通道51DHT11实时温湿度显示系统(LCD1602)
- 前言
-
- 题目要求
- 实现的功能
- 思路介绍
- 代码部分
-
- 注意事项
- 驱动部分
-
- LCD1602驱动
- 按键驱动
- 主要模块
-
- 初始化
- DHT11驱动
- 中断服务程序
- 显示函数
- 主函数
- 数学处理函数
- 总结
前言
这是单片机系统的课程作业,有点匆忙。事实上,代码可以再次优化。我希望它能给你带来一些想法。这也是第一次使用 ,顺便学写作,甘,别说那么多废话。
题目要求
本课题要求学生利用数字温湿度收集环境信息I2C串口通信技术返回单片机LCD实时显示显示器要求湿度误差不大于5%,温度误差不大于2摄氏度;在LCD显示在屏幕上,刷新评率不低于0.5 Hz。包括系统组成STC-89C52开发板,LCD1602显示屏,数字传感器DHT11。LCD两个传感器的温度和湿度可以同时显示。
实现的功能
前三个问题已经解决了。当按钮切换到不同的状态时,,最大值为,检测到平均值平均值(51性能有限,其实答题能力有限),单片机过多次无法显示正确数据。标题中提到的使用IIC,但答主用,DHT11通过单总线通信,然后简单复制实现两个操作。
至于第四个问题,答案还没有出来。如果能在课程时间内更新,如果不行,就会随缘。~提供一个想法:LCD1602作为字符型LCD,没有画点函数,但内置8个自定义字符可以通过自定义字符显示曲线吗?如果你感兴趣,你可以试试。
- 以往版本优化: 21.10:00 添加外部中断按钮抖动,优化部分代码,稍微提高速度.1要求 解决了切割屏幕后温度不实时更新的问题 21.18:07 完成任务1.3 21.22:00 1.最大值部分调试成功 22.09:26 1.2最大值可实现无缝记录,从启动到显示的最大值始终有效。 22.10:28 1.成功测量单平均值22串 22.11:16 1.2多平均值实时lcd、串口测量成功 22.15:30 1.1显示实时值刷新率提高
思路介绍
在这个实验中,首先是两条路DHT11温湿度传感器,管脚分别连接P2.0,P2.1。剩下的两个管脚分别由板子连接VCC和GND。将外部中断P3.连接到一个独立的按钮。按要求插好LCD1602.工作时DHT11测量外部温度。发送温度信息STC-C52,STC-C52实时检测DHT11的在线状态,使LCD1602实时显示.通过按钮切换显示两条路DHT离线状态、实时温湿度、平均温湿度和最大温湿度为11温湿度传感器。
开发板集成在芯片中CPU( 中央处理器),RAM( 数据存储器),ROM( 程序存储器)、定时器/ 计数器及多种功能I/O( 输入/ 输出) 计算机所需的基本功能部件,如接口,可以完成复杂的操作、逻辑控制、通信等功能。 软件设计理念:软件程序的设计包括多个模块,包括LCD1602显示驱动模块、延迟函数模块、、、按键检测模块等。
代码部分
以下是大家喜欢看到的代码部分
注意事项
驱动部分
为了方便管理,我把按钮和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文件:只是简单的驱动函数,大家在网上随便都能找到相似的,也可以通过数据手册自己写。 这里有一个点,就是很多驱动都,具体原因求大佬告知。在这里为简化代码,我也忽略了。
#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