基于51单片机的LCD1602显示温湿度

51单片机基础难点知识理解后,直接通过下面3个小项目可以更好理解51单片机。中断 定时器 串口甚至是基础的IO输入输出都有结合,再加上一些外设模块的学习(如1602显示屏,L298N电机驱动等)
项目一:秒表设计
项目二:LCD1602显示器显示内容
项目三:循迹+蓝牙小车

LCD1602显示屏

介绍

1602,顾名思义就是16(列) x 02(行)显示内容,RAM地址映射,控制器内部有80X8位的RAM缓冲区,但是一行只有16个(实际上一行有40个内容,有一部分存到缓冲区没显示出来罢了)
在这里插入图片描述在这里插入图片描述
实物图以及外形尺寸图如图所示,下面是具体的引脚定义(后面的编程要用到)
可以主要理解为由3个控制信号 RS R/W E 和8个数据位D0-D7组成
在这里插入图片描述

工作原理

1.基本读写时序图如下:
在这里插入图片描述
读操作的时候
读状态: 输入:RS = L , RW = H , E = H;
读数据: 输入:RS = H, RW = H , E = H;
在这里插入图片描述>写状态的时候
写指令 : 输入: RS = L , RW = L , D0~D7 = 指令码, E = 高脉冲(H)
写数据 : 输入 : RS = H . RW = L ,D0~D7 = s数据, E = 高脉冲(H)

关于里面的一些时序参数
在这里插入图片描述
这里面的单位都是ns级别,而单片机的指令周期为1us,具体看清时序里面的时间,方便延时以符合要求,具体的后面代码中可以参考

2.指令代码如下,表格为8位数据位
在这里插入图片描述

功能3: 置输入模式

DB7DB6DB5DB4DB3DB2DB1DB0
000001NS

N =1,读或写一个字符后地址指针+1,光标+1;
N =0,读或写一个字符后地址指针-1,光标-1;
S =1,写一个字符,整屏显示左移(N=1)或者右移(N=0)
S =0,写一个字符,没有移动功能

功能4: 显示开/关设置

DB7DB6DB5DB4DB3DB2DB1DB0
00001DCB

D =1,开显示;D =0,关显示
C =1,显示光标;C =0,不显示光标
B =1,光标闪烁;B =0,不闪烁

功能5:光标或字符移位

DB7DB6DB5DB4DB3DB2DB1DB0
0001S/CR/LXX
S/CR/L设定情况
00光标左移1格,AC值-1
01光标右移1格,AC值+1
10全部左移1格,光标不动
11全部右移1格,光标不动

功能6:置功能

DB7DB6DB5DB4DB3DB2DB1DB0
001DLNF00

DL: 0 = 数据线4位; 1 = 数据线8位
N: 0 = 显示1行; 1 = 显示两行
F: 0 = 5x7点阵; 1 = 5x10点阵

代码驱动

大部分解释完之后附上初始化的过程
在这里插入图片描述

LCD1602.c文件

#include <REG52.H>
#include "LCD1602.h"		
#include <intrins.h>	//头文件,用到nop()函数

//这里的LCD三个控制信号口根据51开发板定的,可以直接将1602显示屏插51开发板上面
sbit LCD_RS = P2^6;
sbit LCD_RW = P2^5;
sbit LCD_E  = P2^7;		//定义三个控制信号引脚的端口
#define LCD_DatePort P0		//宏定义 数据口

void LCD_Delay1ms()		//延时1ms函数
{
	unsigned char i, j;
	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}
//写指令
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS = 0;	
	LCD_RW = 0;		//写指令时 输入: RS = L , RW = L 
	
	LCD_DatePort = Command;		//数据口传入指令
	LCD_E = 1;
	LCD_Delay1ms();
	LCD_E = 0;
	LCD_Delay1ms();			//这部分根据时序图时序写
}
//写数据
void LCD_WriteDate(unsigned char Date)
{
	LCD_RS = 1;	
	LCD_RW = 0;		//写数据时 输入: RS = H , RW = L 
	
	LCD_DatePort = Date;	//数据口传入数据
	LCD_E = 1;
	LCD_Delay1ms();
	LCD_E = 0;
	LCD_Delay1ms();			//这部分根据时序图时序写
}
//LCD初始化
void LCD_Init()
{
	LCD_WriteCommand(0x38);//数据8位,显示2行,5x7点阵 具体参考指令6
	LCD_WriteCommand(0x0c);//开显示,光标关 具体参考指令4
	LCD_WriteCommand(0x06);//地址,光标移动 具体参考指令3	
	LCD_WriteCommand(0x01);//清屏
}
//LCD显示字符函数
void LCD_Showchar(unsigned char Row,unsigned	char Column,unsigned char FuHao)
{
//参数: 行  列  符号
  	SetMouse(Row,Column);
	LCD_WriteDate(FuHao);
}
//LCD显示字符串函数
void LCD_ShowString(unsigned char Row,unsigned	char Column,unsigned char *String)
{
//参数: 行 列 字符串
	unsigned char i;
	SetMouse(Row,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteDate(String[i]);
	}
}
//计算m^n函数
int LCD_Power(int x,int y)	//x^y
{
	unsigned char i;
	int ret = 1;
	for(i=0;i<y;i++)
	{
		ret = ret*x;
	}
	return ret;
}
//显示数字函数
void LCD_ShowNum(unsigned char Row,unsigned	char Column,int num,int length)
{
	SetMouse(Row,Column);
	for(;length>0;length--)
	{ 	
		LCD_WriteDate('0'+num/LCD_Power(10,(length-1))%10);
		//从高位开始逐渐显示数字的每一位,前面多一个'0'+ 代表从数字0所在的RAM存储地址算起,即让其显示数字字符
	}
}

DHT11温湿度模块

介绍

在这里插入图片描述
由三根线组成,VCC GND供电供地,Data为数据端口,实现数据的收发输出为单总线数字信号,温度测量范围0-50度(精度正负2度,分辨率1度),湿度测量范围为20-90%RH(精度为正负5%,分辨率1%)

工作原理

采用单总线双向串行通信协议,每次采集都要由单片机发起开始信号,然后DHT11会向单片机发送响应并开始传输40位数据帧,高位在前。
数据格式
数据格式40帧(40bit)
湿度整数部分+湿度小数部分 8bit + 8bit
温度整数部分+温度小数部分 8bit + 8bit
校验位部分 8bit
数据校验,湿度和温度的整数小数部分全部加起来判断是否与校验位部分相等,若相等,则取出数据;否之,数据重新获取
湿度小数部分和温度小数部分单片机都默认为0,即校验位只会判断两者的整数部分和
时序图
在这里插入图片描述
总线空闲状态高电平.
主机 将总线拉低,等待DHT11响应, (主机发送起始信号)
从机(DHT11)接收主机起始信号后,等待主机起始信号结束, 这个过程要求18ms以上,保证DHT11检测到起始信号
从机(DHT11)发送80us的低电平响应信号
主机延时20-40us后读取DHT11的响应信号
主机发送完开始信号,可以切换到输入模式或者接上拉电阻置高电平,

代码驱动

DHT11.c文件

#include <REG52.H>
#include "DHT11.h"

sbit DHT11_Data = P3^6;		//数据端口和IO口的连接
unsigned int rec_dat [4];	//存储温湿度高四位和低四位
unsigned char rec_dat_lcd0 [6];//湿度高四位
unsigned char rec_dat_lcd1 [6];//湿度低4位
unsigned char rec_dat_lcd2 [6];//温度高4位
unsigned char rec_dat_lcd3 [6];	//温度低4位
//微妙延时函数
void DHT11_Delayus(unsigned char us)		
{
	while(--us);
}
//毫秒延时函数
void DHT11_Delayms(unsigned int ms)
{
	unsigned int i;
	for(;ms>0;ms--)
		for(i=110;i>0;i--);
}
//主机发送起始信号
void DHT11_START() 	
{
		DHT11_Data = 1;
		DHT11_Delayus(2);		
	
		DHT11_Data = 0;	
		DHT11_Delayms(20);	

		DHT11_Data = 1;
		DHT11_Delayus(13);
		//延时都根据时序来的
}
//DHT11接收字节函数
unsigned char DHT11_REC_bit()
{
		unsigned char i,dat;
		for(i=0;i<8;i++)	//一个字节8位,循环8次
		{
			while(!DHT11_Data);	//等待高电平	
			DHT11_Delayus(8);	//等高电平稳定,根据时序来
			dat<<=1;		//数据左移						
			if(DHT11_Data == 1)
			{
				dat += 1;	//如果数据端口为高电平,直接将该位数据置1	
			}
			while(DHT11_Data == 1);	//等待电平拉低	
		}
		return dat;		//返回读取的字节
}
//DHT11接收信号函数
void DHT11_REC()
{
	unsigned int RH,RL,TH,TL,receive;
	unsigned char R_H,R_L,T_H,T_L;
	DHT11_START();
	DHT11_Data = 1;					//保持闲置状态
	if(DHT11_Data == 0)				//判断是否进入响应信号
	{
		while(DHT11_Data == 0);		//等待高电平
		DHT11_Delayus(40);//主机40us读取温湿度信号		
		R_H = DHT11_REC_bit();//接收湿度高8
		R_L = DHT11_REC_bit();//接收湿度低8
		T_H = DHT11_REC_bit();//接收温度高8
		T_L = DHT11_REC_bit();//接收温度低8
		receive = DHT11_REC_bit();//接收校验位
		DHT11_Delayus(25);//延时一下,开始后面校验	
		if((R_H+R_L+T_H+T_L)== receive)
		{
		//如果校验通过,直接将读取的值存到存储数组中去,后面就只管用数组里面的数据
				RH = R_H;
				RL = R_L;
				TH = T_H;
				TL = T_L;
		}
		rec_dat [0] = RH;
		rec_dat [1] = RL;
		rec_dat [2] = TH;
		rec_dat [3] = TL;
	}
}

主函数实现

.h文件

LCD1602.h

#ifndef _LCD1602_H_
#define _LCD1602_H_

void LCD_Delay1ms();		
void LCD_WriteCommand(unsigned char Command);	
void LCD_WriteDate(unsigned char Date);
void LCD_Init();

void SetMouse(unsigned char Row,unsigned char Column);	
void LCD_Showchar(unsigned char Row,unsigned char Column,unsigned char FuHao);	
void LCD_ShowString(unsigned char Row,unsigned	char Column,unsigned char *String);
int LCD_Power(int x,int y);	
void LCD_ShowNum(unsigned char Row,unsigned	char Column,int num,int length);

#endif

DHT11.h

#ifndef _DHT11_H_
#define _DHT11_H_

void DHT11_Delayus(unsigned char us);
void DHT11_Delayms(unsigned int ms);		
void DHT11_START();											
unsigned char DHT11_REC_bit();
void DHT11_REC();																				
#endif
主函数
#include <REG52.H>
#include "LCD1602.h"
#include "DHT11.h"
#include <stdio.h>
//DHT11文件中定义的存储数据数组		
extern unsigned int rec_dat [4];		
extern unsigned char rec_dat_lcd0 [6];
extern unsigned char rec_dat_lcd1 [6];
extern unsigned char rec_dat_lcd2 [6];
extern unsigned char rec_dat_lcd3 [6];

int main()
{
	LCD_Init();
/*在写显示字符串 显示数字函数之前,自己也可以写随便显示几个来验证函数正确性
		LCD_Showchar( 1, 1, 'Q');	
		LCD_Showchar( 1, 2, 'A');	
		LCD_ShowNum( 2,	7, 666,3);*/
			
	LCD_ShowString(1,0,"Humi:");	//在1行 0列显示字符串"Humi:"
	LCD_ShowString(2,0,"Temp:");	//在2行 0列显示字符串"Temp:"
	while(1)
	{
		DHT11_Delayms(150);
		DHT11_REC();			//不断接收数据
		
		sprintf(rec_dat_lcd0,"%d",rec_dat[0]);
		sprintf(rec_dat_lcd1,"%d",rec_dat[1]);
		sprintf(rec_dat_lcd2,"%d",rec_dat[2]);
		sprintf(rec_dat_lcd3,"%d",rec_dat[3]);		
//包含在stdio.h文件中,将数据格式强制转化成字符串形式		
		
		LCD_ShowString(1,6,rec_dat_lcd0);
		LCD_ShowString(1,8,".");
		LCD_ShowString(1,9,rec_dat_lcd1);
		LCD_ShowString(1,10," %");
//实时显示DHT11采集的温湿度,然后在LCD1602上显示了
		LCD_ShowString(2,6,rec_dat_lcd2);
		LCD_ShowString(2,8,".");
		LCD_ShowString(2,9,rec_dat_lcd3);
		LCD_ShowString(2,10," C");	
	}
}

总结

项目总结

与前面一个51单片机秒表项目相比,前面一个偏向于51单片机本身的内容(IO 中断 定时器等)。这个项目更多是偏向单片机外设的一些学习
基于51单片机温湿度显示的项目,主要是学习外设-LCD1602模块 DHT11模块。学会看外设的数据手册(里面的截图基本上都来自数据手册) 看懂时序图,会根据时序图写代码,具体可以根据时序图来验证代码,了解整个外设的工作过程

拓展应用

1.学会LCD1602之后,驱动代码基本上每次可以照搬下来,如果要显示其它内容,可以根据数据手册或者指令在.c文件中添加相关的函数
2.DHT11模块只是众多传感器的一个,基本上大部分外设模块重点就是了解大致原理,看懂时序,直接移植相关代码进行引脚修改,这部分重点仅仅是学会看时序图,根据时序图写代码,为后面学习更多外设做准备
3.也还可以添加串口初始化以及串口中断函数,在电脑串口上显示单片机从外设接收过来的内容

  • 17
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值