基于51单片机+LCD1602的智能计算器设计(带回显、报警功能)

一,LCD1602介绍:
LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。不同厂家生产的LCD1602芯片可能有所不同,但使用方法都是一样的。为了降低成本,绝大多数制造商都直接将裸片做到板子上。

二,连接方式:
LCD1602与单片机的连接有两种方式,一种是直接控制方式,另一种是所谓的间接控制方式。它们的区别只是所用的数据线的数量不同,其他都一样。
1.直接控制方式
LCD1602的8根数据线和3根控制线E,RS和R/W与单片机相连后即可正常工作。一般应用中只须往LCD1602中写入命令和数据,因此,可将LCD1602的R/W读/写选择控制端直接接地,这样可节省1根数据线。VO引脚是液晶对比度调试端,通常连接一个10kΩ的电位器即可实现对比度的调整;也可采用将一个适当大小的电阻从该引脚接地的方法进行调整,不过电阻的大小应通过调试决定。
2.间接控制方式
间接控制方式也称为四线制工作方式,是利用HD44780所具有的4位数据总线的功能,将电路接口简化的一种方式。为了减少接线数量,只采用引脚DB4~DB7与单片机进行通信,先传数据或命令的高4位,再传低4位。采用四线并口通信,可以减少对微控制器I/O的需求,当设计产品过程中单片机的I/O资源紧张时,可以考虑使用此方法(摘自百度)

三,LCD1602引脚定义:
在这里插入图片描述
LCD1602采用标准的14脚(无背光)或16脚(带背光)接口。 LCD引脚功能引脚如下:
1 VSS 电源地
2 VDD 电源正极
3 VL 液晶显示偏压
4 RS 数据/命令选择
5 R/W 读/写选择
6 E 使能信号
7 D0 数据
8 D1 数据
9 D2 数据
10 D3 数据
11 D4 数据
14 D7 数据
12 D5 数据
13 D6 数据
15 BLA 背光源正极
16 BLK 背光源负极

各引脚的功能介绍如下:
·引脚1:VSS为地电源。
·引脚2:VDD接5V正电源。
·引脚3:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”现象,使用时可以通过一个10kQ的电位器调整其对比度。
·引脚4:RS为寄存器选择脚,高电平时选择数据寄存器、低电平时选择指令寄存器。
·引脚5:R/W为读/写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或显示地址;当RS为低电平,R/W为高电平时,可以读忙信号;当RS为高电平,R/W为低电平时,可以写入数据。
·引脚6:E端为使能端,当E端由高电平跳变为低电平时,液晶模块执行命令。
·引脚714:D0D7为8位双向数据线。
·引脚15:背光源正极。
·引脚16:背光源负极。

四,LCD1602指令介绍:
在这里插入图片描述
LCD1602液晶模块的读/写操作、显示屏和光标的操作都是通过指令编程来实现的(其中,1为高电平,0为低电平),分别如下:
(1)指令1:清屏。指令码01H,光标复位到地址00H。
(2)指令2:光标复位。光标复位到地址00H。
(3)指令3:输入方式设置。其中,I/D表示光标的移动方向,高电平右移,低电平左移;S表示显示屏上所有文字是否左移或右移,高电平表示有效,低电平表示无效。
(4)指令4:显示开关控制。其中,D用于控制整体显示的开与关,高电平表示开显示,低电平表示关显示;C用于控制光标的开与关,高电平表示有光标,低电平表示无光标;B用于控制光标是否闪烁,高电平闪烁,低电平不闪烁。
(5)指令5:光标或字符移位控制。其中,S/C表示在高电平时移动显示的文字,低电平时移动光标。
(6)指令6:功能设置命令。其中,DL表示在高电平时为4位总线,低电平时为8位总线;N表示在低电平时为单行显示,高电平时双行显示;F表示在低电平时显示5×7的点阵字符,高电平时显示5×10的点阵字符。
(7)指令7:字符发生器RAM地址设置。
(8)指令8:DDRAM地址设置。
(9)指令9:读忙信号和光标地址。其中,BF为忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平则表示不忙。
(10)指令10:写数据。
(11)指令11:读数据。

五,LCD1602操作汇总
LCD1602的基本操作分为四种:

  1. 读状态:输入RS=0,RW=1,E=高脉冲(上升沿)。输出:D0—D7为状态字。

  2. 读数据:输入RS=1,RW=1,E=高脉冲。输出:D0—D7为数据。

  3. 写命令:输入RS=0,RW=0,E=高脉冲。输出:无。

  4. 写数据:输入RS=1,RW=0,E=高脉冲。输出:无。

六,时钟频率:
在Proteus里面,LCD1602的默认时钟是250KHz 。
在这里插入图片描述
七,操作时序:
根据操作时序,给相应IO相应的电平即可。
1,写操作
在这里插入图片描述
在这里插入图片描述
2,读操作
在这里插入图片描述
在这里插入图片描述
八,功能简介:
利用51单片机、LCD1602、蜂鸣器,矩阵键盘等构成硬件系统,独立按键开启和关闭计算器,矩阵键盘进行相应的计算。此计算器可以进行多位数的加减乘除。

九,程序源码:

LCD1602 C文件

#include "lcd1602.h"
#include <intrins.h>

sbit RS = P2^3;   //定义端口 
sbit RW = P2^4;
sbit EN = P2^5;

#define RS_CLR RS=0 
#define RS_SET RS=1

#define RW_CLR RW=0 
#define RW_SET RW=1 

#define EN_CLR EN=0
#define EN_SET EN=1

#define DataPort P0


/*
*	函数名称: LCD uS延时函数
*	参数:	  延时数
*	返回值:  无
*/
void delay_us(uchar t)
{   
	while(--t);
}


/*
*	函数名称: LCD mS延时函数
*	参数:	  延时数
*	返回值:  无
*/
void DelayMs(uchar t)
{
     
	while(t--)
	{
		 delay_us(245);
		 delay_us(245);
	}
}
         
/*
*	函数名称:  判忙函数
*	参数:	  无
*	返回值:  无
*/
bit LCD_Check_Busy(void) 
{ 
	DataPort= 0xFF; 
	RS_CLR; 
	RW_SET; 
	EN_CLR; 
	_nop_(); 
	EN_SET;
	return (bit)(DataPort & 0x80);
}

/*
*	函数名称: 写入命令函数
*	参数:	  命令
*	返回值:  无
*/
void LCD_Write_Com(uchar com) 
{  
//	while(LCD_Check_Busy()); //忙则等待
	DelayMs(5);
	RS_CLR; 
	RW_CLR; 
	EN_CLR;
	DataPort= com; 
	EN_SET; 	
	_nop_(); 
	EN_CLR;
}

/*
*	函数名称:  写入数据函数
*	参数:	  数据
*	返回值:  无
*/
void LCD_Write_Data(uchar Data) 
{ 
//	while(LCD_Check_Busy()); //忙则等待
	DelayMs(5);
	RS_SET; 
	RW_CLR;
	EN_CLR; 
	DataPort= Data; 
	EN_SET; 	
	_nop_();
	EN_CLR;
}

/*
*	函数名称: 清屏函数
*	参数:	  无
*	返回值:  无
*/
void LCD_Clear(void) 
{ 
	LCD_Write_Com(0x01); 
	DelayMs(5);
}

/*
*	函数名称:写入字符串函数
*	参数:	  坐标,待写字符串
*	返回值:  无
*/
void LCD_Write_String(uchar x,uchar y,uchar *s) 
{     
	if (y == 0) 
	{     
		LCD_Write_Com(0x80 + x);     //表示第一行
	}
	else 
	{      
		LCD_Write_Com(0xC0 + x);      //表示第二行
	}        
	while (*s) 
	{     
		LCD_Write_Data(*s);     
		s ++;     
	}
}

/*
*	函数名称:Calc写入字符串函数
*	参数:	  坐标,待写字符串
*	返回值:  无
*/
void LCD_Calc_String(uchar posx,uchar posy,uchar *str)
{
	LCD_Write_Com(0x04);
	if (posy == 0) 
	{     
		LCD_Write_Com(0x80 + posx);     
	}    
	else 
	{     
		LCD_Write_Com(0xC0 + posx);     
	} 

	while (*str=='\0') 
	{     
		LCD_Write_Data(*str);     
		str ++;     
	}       	 	
}

/*
*	函数名称:写入字符函数
*	参数:	  坐标,待写字符
*	返回值:  无
*/
void LCD_Write_Char(uchar x,uchar y,uchar Data) 
{     
	if (y == 0) 
	{     
		LCD_Write_Com(0x80 + x);     
	}    
	else 
	{     
		LCD_Write_Com(0xC0 + x);     
	}        
	LCD_Write_Data( Data);  
}

/*
*	函数名称:初始化函数
*	参数:	  坐标,待写字符
*	返回值:  无
*/
void LCD_Init(void) 
{
	LCD_Write_Com(0x38);    
	DelayMs(5); 
	LCD_Write_Com(0x38); 
	DelayMs(5); 
	LCD_Write_Com(0x38); 
	DelayMs(5); 
	LCD_Write_Com(0x38);  
	LCD_Write_Com(0x08);    
	LCD_Write_Com(0x01);  
	LCD_Write_Com(0x06);    
	DelayMs(5); 
	LCD_Write_Com(0x0F);    

}

LCD1602 头文件

#ifndef __LCD1602_H
#define __LCD1602_H

#include "reg51.h"

#define uint unsigned int 
#define uchar unsigned char

void LCD_Write_Com(uchar com); 
void LCD_Write_Data(uchar Data);
void LCD_Clear(void);
void LCD_Write_String(uchar x,uchar y,uchar *s);
void LCD_Write_Char(uchar x,uchar y,uchar Data);
void LCD_Init(void);
void LCD_Calc_String(uchar posx,uchar posy,uchar *str);
void DelayMs(uchar t);
void delay_us(uchar t);
#endif

主函数:

#include "reg51.h"
#include "lcd1602.h"
#include <intrins.h>

#define uchar unsigned char 
#define uint  unsigned int 

sbit BEEP=P3^6;
sbit LED=P3^5;
sbit DISPLAY=P3^2;

/***********************************函数声明区**********************************/
void ButtonScan(void);
void ALL_Init(void);
void delay_ms(uint i);
void ECHO_Result(void);
void Compare_Result(uchar result);
/***********************************函数声明区END**********************************/
#define UNSEAL 0
uchar code dat1[]={0,1,2,3,4,5,6,7,8,9,0x2b-0x30,0x2d-0x30,0x2a-0x30,0x2f-0x30,0x3d-0x30,0x43-0x30};

//回显数据容量根据宏定义修改	
#define VOLUME 2
typedef struct _CALC
{
	uchar symbol;
	uchar symbolFlag;
	uchar keyValue;
	uchar num;
	uchar count;
	uchar n;
	uchar pointBit;
	uchar buttonFlag;
	uchar time;
	uchar displayFlag;
	uchar str[10];
	float saveDat[VOLUME][VOLUME];
	long dat[3];
}CALC;

CALC calc;

#if  UNSEAL
void ECHO_Result(void)
{
	uchar j=0;
	for(j=0;j<calc.count;j++)
	{
		LCD_Clear();
		LCD_Write_String(0,0," ECHO Results: ");
		LCD_Write_String(0,1,(uchar *)&calc.saveDat[j]);
		delay_ms(1000);		
	}

}
#endif
/*
*	函数名称:延时函数
*	参数:	  延时数
*	返回值:  无
*/
void delay_ms(uint i)
{
	uint j=0;
	uint n=0;						  
	for(j=0;j<i;j++)
		for(n=0;n<=118;n++);	
}

/*
*	函数名称:按键扫描处理函数
*	参数:	  无
*	返回值:  无
*/
void ButtonScan(void)
{
	P1=0xfe;   
	if(P1!=0xfe)
	{
		delay_ms(10);
		if(P1!=0xfe)
		{
			calc.buttonFlag=1;
			calc.keyValue=P1&0xf0;
			switch(calc.keyValue)
			{
				case 0xe0: calc.num=7;break;	  
				case 0xd0: calc.num=8;break;	 
				case 0xb0: calc.num=9;break;   	  
				case 0x70: calc.num=13;break;   
			}
		}
		while(P1!=0xfe);
		LCD_Write_Data(0x30+dat1[calc.num]);
	}

	P1=0xfd;				
	if(P1!=0xfd)
	{
		delay_ms(10);
		if(P1!=0xfd)
		{	
			calc.buttonFlag=1;
			calc.keyValue=P1&0xf0;
			switch(calc.keyValue)
			{
				case 0xe0: calc.num=4;break;	 
				case 0xd0: calc.num=5;break;	  
				case 0xb0: calc.num=6;break;	
				case 0x70: calc.num=12;break;
			}	
		}
		while(P1!=0xfd);
		LCD_Write_Data(0x30+dat1[calc.num]);	
	 }

	P1=0xfb;		
	if(P1!=0xfb)
	{
		delay_ms(10);
		if(P1!=0xfb)
		{
			calc.buttonFlag=1;
			calc.keyValue=P1&0xf0;
			switch(calc.keyValue)
			{
				case 0xe0: calc.num=1;break;	  
				case 0xd0: calc.num=2;break;	  
				case 0xb0: calc.num=3;break;   
				case 0x70: calc.num=11;break;   
			}	
		}
		while(P1!=0xfb);
		LCD_Write_Data(0x30+dat1[calc.num]);	
	}
		
	P1=0xf7;		
	if(P1!=0xf7)
	{
		delay_ms(10);
		if(P1!=0xf7)
		{
			calc.buttonFlag=1;
			calc.keyValue=P1&0xf0;
			switch(calc.keyValue)
			{
				case 0xe0: calc.num=15;break;  
				case 0xd0: calc.num=0;break;   
				case 0xb0: calc.num=14;break;  
				case 0x70: calc.num=10;break; 
			}	
		}
		while(P1!=0xf7);
		LCD_Write_Data(0x30+dat1[calc.num]);	  
	}
	if(calc.buttonFlag==1)
	{
		LED=0;
	}
	if(calc.buttonFlag==1)
	{
		switch(calc.num)
		{
			case 0: 
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
				break;
			case 1:
				if(calc.symbolFlag==0)	
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 2:
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 3:
				if(calc.symbolFlag==0)	
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 4:
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 5:
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 6:
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 7:
				if(calc.symbolFlag==0)	 
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 8:
				if(calc.symbolFlag==0)	
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
			case 9:
				if(calc.symbolFlag==0)	
				{
					calc.dat[0]=calc.dat[0]*10+dat1[calc.num];	
				}
				else
				{
					calc.dat[1]=calc.dat[1]*10+dat1[calc.num];
				}
			break;
	
			case 10:
			{
				calc.symbolFlag=1;
				calc.symbol=1;    
			}
			break;
			case 11:
			{
				calc.symbolFlag=1;
				calc.symbol=2;
			}
			break;
			case 12:
			{
				calc.symbolFlag=1;
				calc.symbol=3;
			}
			break;
			case 13:
			{
				calc.symbolFlag=1;
				calc.symbol=4;
			}
			break;
			case 15:					
				calc.dat[0]=0;
				calc.dat[1]=0;
				calc.symbolFlag=0;
				calc.symbol=0;
				LCD_Clear();	
				LCD_Write_String(0,0," My Calculate");
				LCD_Write_Com(0x0F);
				LCD_Write_Com(0xc0);
				LED=1;
				BEEP=1;
				TR0=0;
				TR1=0;
			break;
	
			case 14:
			//	LCD_Clear();	//清屏指令
				BEEP=1;
				TR0=1;
				LCD_Write_Com(0x0C);
				LCD_Write_Com(0x4f+0x80);
				LCD_Write_Com(0x04);
				//Omit here
				LCD_Write_Data(0);
				calc.dat[0]=0;
				calc.dat[1]=0;
				calc.symbolFlag=0;
				calc.count++;
				calc.symbol=0;
			break;
	   }
	}
	calc.buttonFlag=0;
}


void ALL_Init(void)
{
	LCD_Init();
	calc.keyValue=0;
	calc.num=0;
	calc.symbolFlag=0;
	calc.symbol=0;
	calc.dat[0]=0;
	calc.dat[1]=0;
	calc.dat[2]=0;
	calc.count=0;
	calc.displayFlag=0;
	calc.n=0;
	calc.time=0;
	LED=1;
	IT0=1;
	EX0=1;
	IT1=1;
	EX1=1;
	PX1=1;
	BEEP=1;
	TMOD =0X11;
	ET0=1;
	ET1=1;
	EA=1;
	TR0=0;
	TR1=0;
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;
	TH1=(65535-500)/256;
	TL1=(65535-500)%256;
	LCD_Write_String(0,0," ? Calc Whether");
    LCD_Write_String(0,1," or not to open");	
	while(0==calc.displayFlag);
	LCD_Clear();
	LCD_Write_String(0,0," My Calculate");
	LCD_Write_Com(0xc0);
}

int main(void)
{
	ALL_Init();
	while(1)
	{
		if(calc.displayFlag==1)
			ButtonScan();
		else
			for(;;);	
	}
	return 0;
}

void inter0(void)interrupt 0 using 0
{
	LCD_Clear();
	LCD_Write_String(0,0," My Calculate");
	LCD_Write_String(0,1," OPEN ");
	calc.displayFlag=1;
}

void inter1(void)interrupt 1 using 1
{
	TH0=(65535-50000)/256;
	TL0=(65535-50000)%256;
	BEEP=!BEEP;
}

void inter2(void)interrupt 2 using 3
{
	LCD_Clear();
	LCD_Write_Com(0x0F);
	TR0=0;
	TR1=0;
	LCD_Write_String(0,1," My Calculate");
	LCD_Write_String(0,0," CLOSE ");
	calc.displayFlag=0;
}

void inter3(void)interrupt 3 using 2
{
	TH1=(65535-500)/256;
	TL1=(65535-500)%256;
	BEEP=!BEEP;
}

十,Proteus仿真图:
1,上电显示:
在这里插入图片描述
2,执行加法仿真效果图:
在这里插入图片描述
3,执行减法仿真效果图:
在这里插入图片描述
3,执行乘法仿真效果图:
在这里插入图片描述
4,执行除法仿真效果图:
在这里插入图片描述
5,当除数为零时,蜂鸣报警,效果图如下:
在这里插入图片描述
在这里插入图片描述

发布了26 篇原创文章 · 获赞 23 · 访问量 5744

猜你喜欢

转载自blog.csdn.net/liuxianfei0810/article/details/105350074