一、实验题目
3.7 LCD 1602显示实验
二、实验要求
1、画出实验的流程图
2、编写源程序并进行注释
3、记录实验过程
4、记录程序运行结果截图
三、实验过程及结果分析
要求利用LCD1602和16个按键实现简单的十进制数的加减乘除四则运算。其中按键KEY0-KEY9代表数字0-9,按键KEY10-KEY13分别代表运算符+、-、*、/,按键KEY15代表=,按键KEY14代表清除命令。不管什么时候按下清除按键,计算过程均将停止,两个输入变量清零,屏幕将清屏。
LCD1602第一行用于显示所输入的两个计算数以及计算符,第二行用于显示计算结果,结果允许为附属,但输入的两个输入数都必须是双字节正整数范围内的数,即0-32767.除数必须保证不为0,否则将报错。在有余数除法中,必须能同时显示商与余数。
- 在Proteus 环境下建立图1所示原理图,并将其保存为LCD1602_self.DSN 文件:
图1:实验原理图
- 编写源程序,将其保存为LCD1602_self.c。运行Keil开发环境,建立工程LCD1602_self.uV2,CPU 为AT89C51,包含启动文件STARTUP.A51。将C 语言源程序LCD1602_self.c 加入工程LCD1602_self.uV2,并设置工程LCD1602_self.uV2 属性,将其晶振频率设置为12MHz,选择输出可执行文件,仿真方式为选择硬仿真,并选择其中的“PROTEUS VSM
MONITOR 51 DRIVER”仿真器。
- 构造(Build)工程LCD1602_self.uV2。如果输入有误进行修改,直至构造正确,生成可执行程序LCD1602_self.hex 为止,为AT89C51 设置可执行程序LCD1602_self.hex。
- 运行程序,点击按键输入数据与运算符,计算,观察计算结果,并验证其是否正确,输入过程中,按“清除按键”观察结果,重新输入数据计算并验证。
1)加法:1+2=3,如图2:
图2:加法测试
2)减法:5-3=2,如图3:
图3:减法测试
3)特殊情况:6-9=-3,如图4:
图4:负数减法测试
4)乘法:4*5=20,如图5:
图5:乘法测试
5) 除法:20/5=4,如图6:
图6:除法测试
6) 带余数的除法,如图7:
图7:带余数的除法测试
7) 除法为0报错,如图8:
图8:除法为0报错
8) 清零,如图9:
图9:清零
四、实验流程图
图10:实验流程图
编程时要有一个状态变量,该变量用于记录当前是输入的哪个变量。输入第一个变量,遇到输入运算符时结束第一个变量的输入。输入第二个变量,遇到“=”号时结束第二个变量的输入,并且开始计算结果。
计算结果由于是16 进制的,要将其转换成十进制,并将该十进制的数转换成字符串后逐位显示出来。减法时要注意结果是否为负,除法时要注意除数是否为0,结果是否带有余数。另外,按键要注意去抖动处理。
采用的是逐行扫描,扫描的时候将要扫描的行置0,其余的行置1;扫描过程中,所有的列全部置1,当某一行的其中一个按键被按下,且正好扫描到这一样,那么所在的列将会被置0. 这时只需要;将行与1111相与即可判断那一列为0,即按下了哪一列。也是实验3.6中键盘扫描的基本思路。
五、实验源代码
include<reg51.h> //预处理伪指令
define uint unsigned int
define uchar unsigned char
sbit lcden=P1^5; //定义引脚E使能端,高到低液晶模块执行任务
sbit rs=P1^7;
sbit rw=P1^6; // 控制读写
sbit busy=P0^7;
char i,j,temp,num;
long a=0,b=0,c=0,d=0; //初始化a参与运算的第一个数,b参与运算的第二个数,c得数,d余数
float a_c,b_c;
uchar flag,signal; //flag表示运算符是否按下
//signal表示按下运算符的名称
uchar code table[]={
0,1,2,3,
4,5,6,7,
8,9,0x2b-0x30,0x2d-0x30,
0x2a-0x30,0x2f-0x30,0x01-0x30,0x3d-0x30}; //定义table数组
uchar code error[]="Error!"; //定义error数组,用于显示除数为0的情况
void delay(uchar z) // 设置延迟函数
{
uchar y;
for(z;z>0;z--)
for(y=0;y<110;y++);
}
void check() // 判断系统状态
{
do
{
P2=0xFF;
rs=0;
rw=1;
lcden=0;
delay(1);
lcden=1;
}
while(busy==1); //判断是否为空闲,1为忙,0为空闲
}
void lcd_wcmd(uchar com) // 写指令
{
P2=com;
rs=0;
rw=0;
lcden=0;
check();
lcden=1;
}
void lcd_wdat(uchar date) // 写数据
{
P2=date;
rs=1;
rw=0;
lcden=0;
check();
lcden=1;
}
void init() //初始化
{
num=-1;
lcden=1; // 使能信号为高电平
lcd_wcmd(0x38); // 8位,2行,5X7点阵
lcd_wcmd(0x0c); //显示开,光标关,不闪烁
lcd_wcmd(0x06); //增量方式不移位,地址自动增加
lcd_wcmd(0x80); //检测忙信号
lcd_wcmd(0x01); //清屏
i=0;
j=0;
flag=0;
signal=0;
}
void keyscan() // 键盘扫描
{
P3=0xfe; //扫描第一行,后四位是行
if(P3!=0xfe)
{
delay(20); //延时去抖动
if(P3!=0xfe) //第一行有键按下
{
temp=P3&0xf0; // 将列与1111进行与操作取列号
switch(temp) // 根据与出来的列号进行选择
{
case 0xe0:num=0;
break;
case 0xd0:num=1;
break;
case 0xb0:num=2;
break;
case 0x70:num=3;
break;
}
}
while(P3!=0xfe);
if(flag==0) // 没有按过符号键时输入继续赋给第一个数
{
a=a*10+table[num];
}
else // 如果按过符号键,则赋给第二个数
{
b=b*10+table[num];
}
i=table[num];
lcd_wdat(0x30+i); // 显示屏写入数字
}
P3=0xfd; //扫描第二行
if(P3!=0xfd)
{
delay(5);
if(P3!=0xfd) // 不等于初值,即代表该行有按键按下
{ // 第二行有数被按下
temp=P3&0xf0; // 列与1111相与,判断按下的列
switch(temp)
{
case 0xe0:num=4;
break;
case 0xd0:num=5;
break;
case 0xb0:num=6;
break;
case 0x70:num=7;
break;
}
}
while(P3!=0xfd);
if(flag==0) //没有按过符号键
{
a=a*10+table[num];
}
else //按过符号键
{
b=b*10+table[num];
}
i=table[num];
lcd_wdat(0x30+i); //写入显示屏
}
P3=0xfb; //扫描第三行
if(P3!=0xfb)
{
delay(5);
if(P3!=0xfb)
{
temp=P3&0xf0;
switch(temp)
{
case 0xe0:num=8;
break;
case 0xd0:num=9;
break;
case 0xb0:num=10;
break;
case 0x70:num=11;
break;
}
}
while(P3!=0xfb); // 第三行的话不全是数字
if(num==8||num==9) //按下的是'8','9'
{
if(flag==0) //没有按过符号键
{
a=a*10+table[num];
}
else //按过符号键
{
b=b*10+table[num];
}
}//0-9用table
else if(num==10) // 如果按下的是'+'
{
flag=1;
signal=1; // signal为1表示按下的是加号
}
else if(num==11) //如果按下的是'-'
{
flag=1;
signal=2; //2表示按下的是减号
}
i=table[num];
lcd_wdat(0x30+i);
}
P3=0xf7; // 第四行都是符号
if(P3!=0xf7)
{
delay(5);
if(P3!=0xf7)
{
temp=P3&0xf0; // 列和1111相与取列号
switch(temp)
{
case 0xe0:num=12;
break;
case 0xd0:num=13;
break;
case 0xb0:num=14;
break;
case 0x70:num=15;
break;
}
}
while(P3!=0xf7);
switch(num)
{
case 12:{lcd_wdat(0x30+table[num]); flag=1;signal=3;}
break;
case 13:{lcd_wdat(0x30+table[num]); flag=1;signal=4;} //运算符号用table
break;
case 14:{lcd_wcmd(0x01);i=0;j=0;a=0;b=0;c=0;d=0;flag=0;signal=0;} //按下的是"清零" ,0x01指令
break;
case 15:{j=1;
if(signal==1) //+号
{
lcd_wcmd(0x80+0x40); //第二行
c=a+b;
lcd_wdat(0x3d);
while(c!=0)
{
if(c/10000!=0)
{
lcd_wdat(0x30+c/10000);
c=c%10000;
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/1000!=0)
{
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/100!=0)
{
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10!=0)
{
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10==0)
{
lcd_wdat(0x30+c);
}
a=0;b=0;c=0;flag=0;signal=0;
}
}
else if(signal==2)
{
lcd_wcmd(0x80+0x40); //第二行
if(a-b>0)
c=a-b;
else
c=b-a; //c为绝对值
lcd_wdat(0x3d); //写"="
if(a-b<0)
lcd_wdat(0x2d); //小于0时为负号
while(c!=0) //若c不为0
{
if(c/10000!=0) //则先从最高位写起
{
lcd_wdat(0x30+c/10000);
c=c%10000;
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/1000!=0)
{
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/100!=0)
{
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10!=0)
{
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10==0)
{
lcd_wdat(0x30+c);
}
a=0;b=0;c=0;flag=0;signal=0;
}
}
else if(signal==3) //乘号的情况
{
lcd_wcmd(0x80+0x40);
c=a*b;
lcd_wdat(0x3d);
while(c!=0)
{
if(c/10000!=0)
{
lcd_wdat(0x30+c/10000);
c=c%10000;
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/1000!=0)
{
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/100!=0)
{
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10!=0)
{
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10==0)
{
lcd_wdat(0x30+c);
}
a=0;b=0;c=0;flag=0;signal=0;
}
}
else if(signal==4) //除号的情况
{
lcd_wcmd(0x80+0x40);
lcd_wcmd(0x06);
if(b==0)
{
i=0;
while(error[i]!='\0')
{
lcd_wdat(error[i]);
i++;
}
a=0;b=0;c=0;flag=0;signal=0;
} //被除数为0显示error
else if((a%b==0)&&(b!=0)) // 整除时输出c
{
lcd_wdat(0x3d);
c=a/b;
if(a/b<=0)
{
lcd_wdat(0x30);
}
while(c!=0)
{
if(c/10000!=0)
{
lcd_wdat(0x30+c/10000);
c=c%10000;
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/1000!=0)
{
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/100!=0)
{
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10!=0)
{
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10==0)
{
lcd_wdat(0x30+c);
}
a=0;b=0;c=0;flag=0;signal=0;
}
}
else if((a%b!=0)&&(b!=0)) //若不整除输出c```d
{
c=a/b;
d=a%b;
lcd_wdat(0x3d);
if(a/b<=0)
{
lcd_wdat(0x30);
}
while(c!=0)
{
if(c/10000!=0)
{
lcd_wdat(0x30+c/10000);
c=c%10000;
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/1000!=0)
{
lcd_wdat(0x30+c/1000);
c=c%1000;
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/100!=0)
{
lcd_wdat(0x30+c/100);
c=c%100;
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10!=0)
{
lcd_wdat(0x30+c/10);
c=c%10;
lcd_wdat(0x30+c);
}
else if(c/10==0)
{
lcd_wdat(0x30+c);
}
a=0;b=0;c=0;flag=0;signal=0;
}
lcd_wdat(0x2e);
lcd_wdat(0x2e);
lcd_wdat(0x2e);
if(d/10000!=0)
{
lcd_wdat(0x30+d/10000);
d=d%10000;
lcd_wdat(0x30+d/1000);
d=d%1000;
lcd_wdat(0x30+d/100);
d=d%100;
lcd_wdat(0x30+d/10);
d=d%10;
lcd_wdat(0x30+d);
}
else if(d/1000!=0)
{
lcd_wdat(0x30+d/1000);
d=d%1000;
lcd_wdat(0x30+d/100);
d=d%100;
lcd_wdat(0x30+d/10);
d=d%10;
lcd_wdat(0x30+d);
}
else if(d/100!=0)
{
lcd_wdat(0x30+d/100);
d=d%100;
lcd_wdat(0x30+d/10);
d=d%10;
lcd_wdat(0x30+d);
}
else if(d/10!=0)
{
lcd_wdat(0x30+d/10);
d=d%10;
lcd_wdat(0x30+d);
}
else if(d/10==0)
{
lcd_wdat(0x30+d);
}
}
}
break;
}
}
}
}
main() // 定义主程序
{
init(); //初始化子程序
while(1) //一直循环
{
keyscan(); // 键盘扫描函数扫描输入
}
}