实验具体内容

在 Proteus 环境下搭建如下图所示的电路图:

img

控制要求:

本实验利用 LCD1602 和 16 个按键实现简单的十进制数加减乘除四则混合运算。其中按键 KEY0-KEY9 分别代表了数字 0-9,按键 10-13 分别代表了运算符“+,-,*,/”,按键“15”代表“=”,按键 14 代表清除命令,以便进行下一次的输入和计算。不管什么时候按下清除按键计算过程停止,两个输入变量都将清 0,屏幕将清屏。

LCD1602 的第一行用于显示所输入的两个计算数以及计算符,第二行用于显示计算结果。结果允许为负数,但输入的两个输入数都必须是双字节正整数范围内的数,即:0-32767之间。除数必须保证不为 0,否则将报错。除数时必须能同时显示商与余数。

编程思路

按键的扫描与识别可以参考“3.4.1 示例实验”中的方法。LCD1602 的显示控制可以

参考“3.7.2 LCD1602 示例实验”。

编成时要有一个状态变量,该变量用于记录当前是输入的哪个变量。输入第一个变量,遇到输入运算符时结束第一个变量的输入。输入第二个变量,遇到“=”号时结束第二个变量的输入,并且开始计算结果。

计算结果由于是 16 进制的,要将其转换成十进制,并将该十进制的数转换成字符串后逐位显示出来。减法时要注意结果是否为负,除法时要注意除数是否为 0,结果是否带有余数。

另外,按键要注意去抖动处理。

流程图

image-20220212125219750

实验过程

① 根据上述实验内容,参考 1.2.2,在 Proteus 环境下建立图 3.22 所示原理图,并将其保存为 LCD1602_self.DSN 文件。

② 根据控制要求和编程思路画出流程图,并编写源程序,将其保存为 LCD1602_self.c。

③ 运行 Keil uVision2 开发环境,按照 1.1.3 节介绍的方法建立工程LCD1602_self.uV2,CPU 为 AT89C51,包含启动文件 STARTUP.A51。

④ 按照 1.2.2 第(6)节介绍的方法将 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。

⑦ 运行程序,点击按键输入数据与运算符,计算,观察计算结果,并验证其是否正确。

⑧ 输入过程中,按“清除按键”观察结果,重新输入数据计算并验证。

实验截图:

加法:

img

减法:

img

乘法:

img

除法:

img

带余数除法:

img

除数为0报错:

img

实验源程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#include <reg51.h> 
#include <stdio.h>
#include <string.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar code KEY_TABLE[]={ 0x11,0x21,0x41,0x81,0x12,0x22,0x42,0x82,0x14,
0x24,0x44,0x84,0x18,0x28,0x48,0x88}; //按键键值表
uchar code line_data[] ={'0','1','2','3','4','5','6','7','8','9',
'+','-','*','/','C','='}; //要显示的字符
int code num_data[] = {0,1,2,3,4,5,6,7,8,9};    //按钮对应的具体数值
sbit LCD_RS=P1^7; //定义控制引脚
sbit LCD_RW=P1^6;
sbit LCD_EN=P1^5;
uchar flag  = 0;
int sum1,sum2,sum3,nums;        //sum1为第一个数,sum2为第2个数,sum3为计算结果
uchar result[16];
uchar operator;                //记录运算符状态   0为+ ,1为- ,2为* ,3位/  
/***************************延时子程序**********************/
void delay_ms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}

/*******************短延时子程序,作键盘去抖动功能****************/
void delays()
{
uint n=10000;while(n--);
}
/*************************LCD 忙检查子程序**********************/
bit lcd_busy()
{
bit check;
LCD_RS=0;
LCD_RW=1;
LCD_EN=1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
check=(bit)(P2&0x80);
LCD_EN=0;
return check;
}
/************************写命令子程序**********************/
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS=0;
LCD_RW=0;
LCD_EN=0;
_nop_();
_nop_();
P2=cmd;
_nop_();
_nop_();
_nop_();
_nop_();
LCD_EN=1;
_nop_();
_nop_();
_nop_();
_nop_();
LCD_EN=0;
}
/***************************清除子程序**********************/
 void clr()
{
sum1 = 0;
sum2 = 0;
sum3 = 0;
nums = 0;
flag = 0;
memset(result, 0, sizeof(result));
lcd_wcmd(0x01); //清屏指令 DB7-DB0 部分为 01H
lcd_wcmd(0x000x80); //显示位置为第1行第0列
delay_ms(2); //清屏指令需要 1.64ms 以上
}

/**************************写数据子程序**********************/
void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
_nop_();
_nop_();
P2=dat;
_nop_();
_nop_();
   _nop_();
   _nop_();
LCD_EN=1;
_nop_();
_nop_();
   _nop_();
   _nop_();
LCD_EN=0;
}

/*************************初始化子程序**********************/
void lcd_init()
{
delay_ms(15); //等待 LCD 电源稳定
lcd_wcmd(0x38); //功能设定指令中 DL=1,N=1,F=0,8 位数据宽度,16*2 显示,
//5*7 点阵字符
delay_ms(5);
lcd_wcmd(0x0f); //显示开关控制指令中 D=1,C=0,B=0,显示开,开光标,闪烁
delay_ms(5);
lcd_wcmd(0x06); //进入模式设置指令中 I/D=1,S=0,地址自动增加
delay_ms(5);
lcd_wcmd(0x01); //清除 LCD 显示内容,清屏指令 DB7-DB0 部分为 01H
delay_ms(5);
}


/**************************主程序******************************/
void main()
{
uchar i,temp,key,str,num,result2[4];
uint remainder;
lcd_init(); //初始化
clr(); //清屏
delay_ms(5);
while(1)
{
   P3=0xf0; //行置为 0,列置为 1,读列值
   if(P3!=0xf0)
      {
       delays(); //去抖动
       P3=0xf0;
       if(P3!=0xf0)
          {
           temp=P3;
           P3=0x0f;
           key=tempP3;   //按位取或
           key=0xff-key; //按位取反
           while(1)
          {
               P3=0xf0;
               if(P3==0xf0) break;      //等待按键释放
          }  
           for (i=0;i<16;i++)
              {
               if(key==KEY_TABLE[i])
                  {
                    num=i;
                    break;
                  }
              }
           str=line_data[i];
           if(i<10)    
          {   nums=num_data[i];
               if(flag == 0)    sum1= sum1*0x000A+nums;
               if(flag == 1)    sum2= sum2*0x000A+nums;
          }
           
           if(i==10)   {operator = 0;    flag = 1;}       //转为状态1,输入第2个数
           if(i==11)   {operator = 1;    flag = 1;}
           if(i==12)   {operator = 2;    flag = 1;}
           if(i==13)   {operator = 3;    flag = 1;}
           if(i==14)   clr();
           if(i==15)  
          {  
               switch(operator)
              {
                   case 0: sum3 = sum1+sum2;
                           break;
                   case 1: sum3 = sum1-sum2;
                           break;
                   case 2: sum3 = sum1*sum2;
                           break;
                   case 3: if(sum2==0)
                          {
                               strcpy(result,"Error!");
                               break;
                          }
                           sum3 = sum1/sum2;
                           remainder = sum1%sum2;
                           break;
              }
               flag=2;                 //转为状态2,方便在lcd第2行输出结果
          }
           if((flag==0flag==1)&&str!='C')    lcd_wdat(str);      //'C'代表清除指令,不输出
           if(flag==2)    
          {  
               
               lcd_wcmd(0x400x80);    //设置显示位置为第2行第0列
               if(strcmp(result,"Error!"))
              {
               sprintf(result,"%d",sum3);
               if(operator==3&&(remainder!=0))
              {
                   strcat(result,"...");
                   sprintf(result2,"%d",remainder);
strcat(result,result2);
              }
               i=0;
               lcd_wdat(str);
               while(result[i]!='\0')
              {
                   lcd_wdat(result[i]);
                   i++;
              }
              }
               else
              {
                   i=0;
                   while(result[i]!='\0')
              {
                   lcd_wdat(result[i]);
                   i++;
              }
              }
               
          }
          }
      }
}

}