【Proteus仿真】4X4矩阵按键扫描组成的简易加法器

【Proteus仿真】4X4矩阵按键扫描组成的简易加法器


VSM Studio程序代码

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
/\* Main.c file generated by New Project wizard
\*简易加法计算器
\* Created: 周二 6月 14 2022
\* Processor: AT89C52
\* Compiler: Keil for 8051
\*/

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit KEY_IN_1 = P2^4;
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0;
unsigned char code LedChar[] = { //数码管显示字符转换表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6]={0xff,0xff,0xff,0xff,0xff,0xff}; //数码管显示缓冲区
unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表
{ 0x31, 0x32, 0x33, 0x26 }, //数字键 1、数字键 2、数字键 3、向上键
{ 0x34, 0x35, 0x36, 0x25 }, //数字键 4、数字键 5、数字键 6、向左键
{ 0x37, 0x38, 0x39, 0x28 }, //数字键 7、数字键 8、数字键 9、向下键
{ 0x30, 0x1B, 0x0D, 0x27 } //数字键 0、ESC 键、 回车键、 向右键
};
unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};


unsigned char THR0,TLR0;
void KeyDriver();
void ConfigTimer(unsigned long ms);
void main()
{
EA = 1; //使能总中断
ENLED = 0;
ADDR3 = 1;
ConfigTimer(1); //计算定时1ms,需要赋的初值,存在在 THR0,TLR0中
TMOD = TMOD & 0xF0;
TMOD = TMOD | 0x01; //设置定时器模式1 ,不影响高四位
TH0 = THR0; //定时器赋初值
TL0 = TLR0;
ET0= 1; //使能T0中断
TR0 = 1; //启动定时器T0
LedBuff[0] = LedChar[0]; //数码管默认显示0
while(1)
{
KeyDriver();
}
}
void ConfigTimer(unsigned long ms)
{
unsigned long temp;
temp = 65536 - ms\*11059200/1000/12; //ms最大定时71ms
THR0 = (unsigned char)(temp>>8); //取计数值高八位 ,计数值不会超过65535,最多占用16位。
TLR0 = (char)temp; //取计数值低八位
}
void ShowNumber(unsigned long num)
{
signed char i;
unsigned char buf[6]; //将num六位数字按顺序放入buf数组中
for(i=0;i<=5;i++)
{
buf[i]=num%10;
num = num/10;
}
for(i=5;i>=1;i--) //从高位到低位依次扫描,直到某一位不为0
{
if(buf[i]==0)
{
LedBuff[i]=0xff; //如果高位为0,则关闭此对应数码管显示,否则跳出,保留buf[]中不为0的下角标在i中
}
else
{
break;
}
}
for(;i>=0;i--)
{
LedBuff[i]=LedChar[buf[i]]; //将各位数字取出,转换成数码管显示字符放入公有数组变量LedBuff[6]中
}
}
void KeyAction(unsigned char keycode)
{
static unsigned long result = 0;
static unsigned long addend = 0;
if((keycode>=0x30)&&(keycode<=0x39)) //输入的是数字
{
addend = (addend\*10)+(keycode-0x30); //将原有数字顶上去
ShowNumber(addend);
}
else if(keycode == 0x26) //上键,执行加法操作
{
result += addend; //将上一个数字addend存在result中,清空addend,等待下一次数字
addend = 0;
ShowNumber(result);
}
else if(keycode == 0x0D) //回车键,作用和上键等同
{
result += addend; //将上一个数字addend存在result中,清空addend,等待下一次数字
addend = 0;
ShowNumber(result);
}
else if(keycode == 0x1B) //esc键,清零
{
addend = 0;
result = 0;
ShowNumber(addend);
}
}
void KeyDriver()
{
unsigned char i,j;
static unsigned char backup[4][4]={ //一定定义成静态,否则bug
{1,1,1,1}, {1,1,1,1},{1,1,1,1},{1,1,1,1}
};
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
if(KeySta[i][j]!=backup[i][j]) //当前按键状态和之前按键状态不一致,证明按键已经按下或者抬起
{
if(KeySta[i][j]==0) //如果当前按键状态为0,说明按键按下;在此我们只对按键按下动作响应
{
KeyAction(KeyCodeMap[i][j]); //将对应的按键转换成标准键盘码传入KeyAction中,根据传入的键盘码执行相应动作
}
backup[i][j]=KeySta[i][j]; //保存当前按键状态
}
}
}
}



void LedScan()
{
static unsigned char i = 0;
P0=0xff;
switch(i)
{
case 0: ADDR2=0;ADDR1=0;ADDR0=0;P0=LedBuff[i];i++;break;
case 1: ADDR2=0;ADDR1=0;ADDR0=1;P0=LedBuff[i];i++;break;
case 2: ADDR2=0;ADDR1=1;ADDR0=0;P0=LedBuff[i];i++;break;
case 3: ADDR2=0;ADDR1=1;ADDR0=1;P0=LedBuff[i];i++;break;
case 4: ADDR2=1;ADDR1=0;ADDR0=0;P0=LedBuff[i];i++;break;
case 5: ADDR2=1;ADDR1=0;ADDR0=1;P0=LedBuff[i];i=0;break;
default:break;
}
}
void KeyScan()
{
static unsigned char keyout = 0;
unsigned char i;
static unsigned char keybuf[4][4]={
{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff},{0xff,0xff,0xff,0xff}
};
keybuf[keyout][0] = (keybuf[keyout][0]<<1)| KEY_IN_1;
keybuf[keyout][1] = (keybuf[keyout][1]<<1)| KEY_IN_2;
keybuf[keyout][2] = (keybuf[keyout][2]<<1)| KEY_IN_3;
keybuf[keyout][3] = (keybuf[keyout][3]<<1)| KEY_IN_4;
for(i=0;i<=3;i++)
{
if((keybuf[keyout][i]&0x0f)==0x00)
{
KeySta[keyout][i]=0;
}
else if((keybuf[keyout][i]&0x0f)==0x0f)
{
KeySta[keyout][i]=1;
}
else
{}
}
keyout++;
if(keyout>=4)
{keyout=0;}
switch(keyout) //扫描下一行
{
case 0: KEY_OUT_4=1;KEY_OUT_1=0;break;
case 1: KEY_OUT_1=1;KEY_OUT_2=0;break;
case 2: KEY_OUT_2=1;KEY_OUT_3=0;break;
case 3: KEY_OUT_3=1;KEY_OUT_4=0;break;
default:break;
}
}
void InterruptTimer0() interrupt 1
{
TH0=THR0;
TL0=TLR0;
LedScan();
KeyScan();
}

  • VSM Studio界面
    在这里插入图片描述

程序源码和仿真资源

本实验基于Proteus8.12平台,本项目另外包含Keil工程。

1
2
3
链接:https://pan.baidu.com/s/1o64EZGZlfyirrx1ib13mBQ 
提取码:0aho


乡村四月 宋朝·翁卷
绿遍山原白满川,子规声里雨如烟。
乡村四月闲人少,才了蚕桑又插田。