C51单片机-示例代码成品(1)

一、独立按键和LED之间的简单联动

1.1 简单叙述

编写代码,获取开发板上独立按键状态,并控制LED亮灭。

1.2 具体实现功能

通过编写代码,控制C51单片机上面的四个按键,从左往右分别控制LED彩灯进行:

  1. 当按下一次从左往右数的第一个按键,51单片机的8个LED灯将从左往右按照一定时间间隔依次亮灯,然后在从左往右按照同样的时间间隔依次灭灯。
  2. 当按下一次从左往右数的第二个按键,51单片机的8个LED灯将以最左边为低位,进行着二进制的加一计算。
  3. 当按下一次从左往右数的第三个按键,51单片机的8个LED灯将以最左边为低位,进行着二进制的减一计算。
  4. 当按下一次从左往右的第四个按键,51单片机的8个LED灯将全部向右移位一个单位,并且第8个LED灯向右移位将会移到第1个LED灯。

1.3 实现代码

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
#include <REGX52.H>
#include <INTRINS.H>
#include <MATH.H>

// 设定按键的引脚
// 注意:此处key的印脚位置从左到右一依次是P3的1、0、2、3号引脚
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;

// 设定LED对应的引脚
sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;
sbit LED4=P2^3;
sbit LED5=P2^4;
sbit LED6=P2^5;
sbit LED7=P2^6;
sbit LED8=P2^7;

// 定义每次按下key后的状态
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0

unsigned char num;

// 设定延迟函数,延迟的时间为(10*us_10)ns
// 如果输入的值为20 000 00,则为延时20ms
void delay(unsigned int us_10){
while(us_10--);
}

// 设定的扫描按键按下的函数
unsigned char key_scan(unsigned char mode)
{
static unsigned char key=1; // 设置static的key,全局变量

if(mode)key=1;
// 设定检测按键触发模式,如果mode=1则可以长按进行多次检测,如果mode=0则只能按下进行一次检测
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) //
{
delay(2000); // 进行延迟20ms
key=0;
if(KEY1==0)
return KEY1_PRESS;
else if(KEY2==0)
return KEY2_PRESS;
else if(KEY3==0)
return KEY3_PRESS;
else if(KEY4==0)
return KEY4_PRESS;
} else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1) // 如果按键没有按下时将会重置key的值
{
key=1;
}
return KEY_UNPRESS;
}

// 设定的亮灯模式的函数
void light_way(unsigned char way){
unsigned char i;
if (way == 1) { // 方式1,进行从左到右亮灯,然后从左到右灭灯
num = 0;
P2 = ~num;
for (i = 0; i < 8; i++){
num = num << 1;
num++;
P2 = ~num;
delay(10000);
}
for (i = 0; i < 8; i++){
num = num << 1;
P2 = ~num;
delay(10000);
}
num = 0;
P2 = ~num;
} else if (way == 2) { // 方式2,进行二进制的加一
num++;
P2 = ~num;
delay(30000);
} else if (way == 3) { // 方式3,进行二进制的减一
num--;
P2 = ~num;
delay(30000);
} else if (way == 4) { // 方式四,进行LED数量不变的右移
unsigned char isIn;
isIn = num >= 128 ? 1 : 0;
num *= 2;
num += isIn; // 0000 0011 -> 1000 0001 -> 1100 0000
P2 = ~num;
delay(30000);
} else {
num = 0;
P2 = ~num;
}
}

void main()
{
while(1)
{
unsigned char key = key_scan(1);
light_way(key);
}
}

1.4 代码功能描述和解读

  1. 延时函数delay,主要是利用MCU上面晶振进行设定,如果输入为1,则延迟10us;如果输入为10,则延迟100us,如果输入为100,则延迟1ms。
  2. 按键检测函数key_scan,里面只有一个输入参数mode。关于这个输入参数,下面将会进行详细讲解:
    • 如果输入为1,那么在每次触发函数时,将会只执行if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) 里面的语句,由于里面那些语句会让key置零无效,但是我们输入的mode会使得key在执行第二个if前已经被第一个if设为有效的状态1了。这就会出现一种情况,如果一直按住一个按键不松手,如果我输入的参数mode为1,那么我这个函数就会不断返回那个按键对应的参数。
    • 如果输入为0,那么在每次触发函数时,执行了一次if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) 里面的语句后将会对key进行置零,那么我下次触发这个函数时,如果此时还是按住某个按键进行第二次触发该按键检测函数时,它就不会返回按下按键对应的参数,而是识别为未按下按键。而只有松开四个按键,即满足else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)条件时,才会将key重新设置为有效的1。
    • 简而言之,如果输入参数mode为1,此时一直长按某个按键不松手,那么按键检测函数将会一直返回 这个按键对应设置的返回值;如果输入参数mode为0,此时一直长按某个按键不松手,那么按键检测函数只会返回一次 该按键对应的返回值,只有将所有按键的状态还原到都没有被按的情况下,才能使得下次触发按键有效。
  3. 亮灯函数light_way,里面只有一个输入参数way。不同的输入会触发其他条件:
    • 如果输入参数way=1:将会对num通过移位的方法,然后再让P2取反实现灯光从左到右亮灯,然后从左到右灭灯。
    • 如果输入参数way=2:将对num进行简单的加一,使得P2像二进制递增一样移动。
    • 如果输入参数way=3:将对num进行简单的减一,使得P2像二进制递减一样移动。
    • 如果输入参数way=4:对num进行右移,并且记录右移出来最高位的数是否为1,如果为1则需要在右移后在最低位加一,从而保证右移后所亮灯数量不变。

二、流水灯与蜂鸣器的简单联动

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#include <REGX51.H>


// 定义每次按下key后的状态,对应从左到右四个独立按键
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0

// 说明蜂鸣器的引脚、四个按键的引脚
sbit Buzzer = P2^5;
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;


// 设定的蜂鸣器音乐,简易乐谱
unsigned int code Music[] = {5024, 2014, 1014, 5024, 1012, 2012, 3012, 2012, 1012, 2012,
5024, 2014, 1014, 5024, 1012, 2012, 3012, 2012, 1012, 2012,
5024, 2014, 1014, 5024, 1012, 2012, 3012, 2012, 1012, 2012,
5024, 2014, 1014, 5024,
1024, 3022, 5022, 6024, 5024, 1024, 2022, 2022, 1024, 3024,
3022, 5022, 6022, 7022, 6022, 5022, 3022, 2022, 2022, 1022, 2024, 2024, 3024, 1024,
1024, 3022, 5022, 6024, 5024, 1024, 2022, 2022, 1024, 3024,
2024, 3024, 5024, 6024, 1014, 2014, 3014, 2014, 1014, 6024, 1014, 2014, 2014, 1014, 1014,
6024, 1014, 2014, 2014, 1014, 1014,
914, 7014, 6014, 5014, 914, 7014, 6014, 7012, 6012, 5012, 3012,
5024, 6024, 1014, 2014, 3014, 2014, 3014, 2014, 3014, 2014, 3014, 5014, 2014, 3014, 1014, 6024, 1014, 2014, 1014};



unsigned int code Music_little_star[] = {1024, 1024, 5024, 5024, 6024, 6024, 5028, 4024, 4024, 3024, 3024, 2024, 2024, 1028,
5024, 5024, 4024, 4024, 3024, 3024, 2028, 5024, 5024, 4024, 4024, 3024, 3024, 2028,
1024, 1024, 5024, 5024, 6024, 6024, 5028, 4024, 4024, 3024, 3024, 2024, 2024, 1028,
1014, 1014, 5014, 5014, 6014, 6014, 5018, 4014, 4014, 3014, 3014, 2014, 2014, 1018,
5014, 5014, 4014, 4014, 3014, 3014, 2018, 5014, 5014, 4014, 4014, 3014, 3014, 2018,
1014, 1014, 5014, 5014, 6014, 6014, 5018, 4014, 4014, 3014, 3014, 2014, 2014, 918};










/*
******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,ten_us=1时,大约延时10us,有一定的使用误差,这里测试的单片机的晶振频率为11.0592MHz
* 输 入 : ten_us
* 输 出 : 无
******************************************************************************
*/
void delay(unsigned int ten_us)
{
while(ten_us--);
}





/*
******************************************************************************
* 函 数 名 : time0_init
* 函数功能 : 定时器0中断配置函数,通过设置TH和TL即可确定定时时间,这里设定的是1ms
* 输 入 : 无
* 输 出 : 无
******************************************************************************
*/
void time0_init()
{
TMOD|=0X01;// 选择为定时器0模式,工作方式1
TH0=0XFC; // 给分别给定时器TH0和TL0赋初值,定时1ms
TL0=0x66 ;
TF0 = 0; // 清除TF0标志
ET0=1; // 打开定时器0中断允许
EA=1; // 打开总中断
TR0=1; // 打开定时器,定时器0开始计时
PT0 = 0;
}




/*
******************************************************************************
* 函 数 名 : key_scan
* 函数功能 : 检测独立按键是否按下,按下则返回对应键值
* 输 入 : mode=0:单次扫描按键;mode=1:连续扫描按键
* 输 出 : KEY1_PRESS:K1按下
KEY2_PRESS:K2按下
KEY3_PRESS:K3按下
KEY4_PRESS:K4按下
KEY_UNPRESS:未有按键按下
******************************************************************************
*/
unsigned char key_scan(unsigned char mode)
{
static unsigned char key=1; // 设置static的key,全局变量

if(mode)key=1;
// 如果mode输入1,则会让key一直保持1,这样就只会进行进入下面的if语句,不会进入else if语句
// 设定检测按键触发模式,如果mode=1则可以长按进行多次检测,如果mode=0则只能按下无论时间长短只能进行一次检测
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)){
delay(2000); // 进行延迟20ms,消抖
key=0;
if(KEY1==0)
return KEY1_PRESS; // 返回从左往右数的第一个按键的数值1
else if(KEY2==0)
return KEY2_PRESS; // 返回从左往右数的第二个按键的数值2
else if(KEY3==0)
return KEY3_PRESS; // 返回从左往右数的第三个按键的数值3
else if(KEY4==0)
return KEY4_PRESS; // 返回从左往右数的第四个按键的数值4
} else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1){
// 如果能来到这一句,说明输入的mode一定为0且key为0
// 这么就说明此时已经按下四个独立按键中的其中一个
// 如果现在按住该按键未松手,那么即使来到key_scan这个函数里面,也不会进行任何按键检测,因为此时key为0
// 只有按键松开时才会来到这个else if语句,此时将会重置key的值为1
key=1;
}
// 如果都没有触发,说明此时没有扫描到按键,返回0
return KEY_UNPRESS;
}


/*
******************************************************************************
* 函 数 名 : buzzer_time
* 函数功能 : 蜂鸣器震动函数,按照一定时间和频率震动
* 输 入 : times:需要震动的时间
delay_time: 需要震动的频率的周期
* 输 出 : 无
******************************************************************************
*/
void buzzer_time(unsigned int times, unsigned int delay_time) {
// 如果蜂鸣器需要进行500ms的1000Hz震动,则需要
// 输入times的值为5000,delay_time输入为100,因为delay函数是延迟10us的
unsigned int i;
for (i = 0; i < (times / delay_time) * 10 * 2; i++) { // 那么这里就要计时500 * 2次
Buzzer = !Buzzer;
delay(delay_time / 2); // 每隔500us翻转一次,那么周期就是1000us,即1ms
}

}


/*
******************************************************************************
* 函 数 名 : sound
* 函数功能 : 控制蜂鸣器进行奏乐发声的函数
* 输 入 : way:一般为3位数,高2位代表音色,低位代表音调(低位值取1、2、4分别代表高音、中音、低音)
time_way: 选择一个音调发声的基本时间,简而言之就是发一个声音需要的时间,也可以通过修改它的值从而使得音乐节奏的快慢改变
* 输 出 : 无
******************************************************************************
*/
// 输入的way高位表示音调,低位表示音高
void sound(unsigned int way, unsigned char time_way) {
// 需要进行延时的时间数据
unsigned int time = 625 * time_way;
if (time_way == 9) { // 由于设置timeway最多显示9,所以只能将特殊情况进行处理
time = 10000;
} else if (time_way == 7) {
time = 7500;
}

/*
time_way一般取值和对应的延迟时间
1:62.5ms
2:125ms
4:250ms 这是正常四分之一拍的时间
6:325ms
8: 500ms
7:750ms
9:1000ms
*/

// 判断way的高位,从而得知其音色,再通过低位的判断得到音调高低,从而调整蜂鸣器震动的频率
switch(way / 10) {
// 这里case 9设置的是1的高高音,本来设计的时候只选择中低高3个音调段,但是由于本次音频中需要使用到1个高高音,所以就独立出来了
case 9: buzzer_time(time, 96 * (way % 10) / 2); break;
case 10: buzzer_time(time, 96 * (way % 10)); break; // 1
case 11: buzzer_time(time, 90 * (way % 10)); break; // #1
case 20: buzzer_time(time, 85 * (way % 10)); break; // 2
case 21: buzzer_time(time, 81 * (way % 10)); break; // #2
case 30: buzzer_time(time, 76 * (way % 10)); break; // 3
case 40: buzzer_time(time, 72 * (way % 10)); break; // 4
case 41: buzzer_time(time, 68 * (way % 10)); break; // #4
case 50: buzzer_time(time, 64 * (way % 10)); break; // 5
case 51: buzzer_time(time, 60 * (way % 10)); break; // #5
case 60: buzzer_time(time, 57 * (way % 10)); break; // 6
case 61: buzzer_time(time, 54 * (way % 10)); break; // #6
case 70: buzzer_time(time, 51 * (way % 10)); break; // 7
case 0: delay(time * 10); break; // 延时
}
}


/*
******************************************************************************
* 函 数 名 : music
* 函数功能 : 操作音乐播放的函数
* 输 入 : speed:音乐播放速率的选择,
如果输入值为2,则会以正常速度播放;
如果输入值为1,则会以较快速度播放;
如果输入值为4,则会以较慢速度播放。
* 输 出 : 无
******************************************************************************
*/
void music(unsigned char speed) {
unsigned char m;
for (m = 0; m < 99; m++) { // 简单的遍历乐谱,将乐谱里面的数据传入sound函数处理
sound(Music[m]/10, Music[m]%10/2 * speed);
}
}

void music_1(unsigned char speed) {
unsigned char m;
for (m = 0; m < 84; m++) { // 简单的遍历乐谱,将乐谱里面的数据传入sound函数处理
sound(Music_little_star[m]/10, Music_little_star[m]%10/2 * speed);
}
}

// 定义一个全局变量n,与音乐播放时LED闪烁位置有关
unsigned char n;

/*
******************************************************************************
* 函 数 名 : music_led_light
* 函数功能 : 音乐播放时LED进行闪烁的函数
* 输 入 : 无
* 输 出 : 无
******************************************************************************
*/

void music_led_light() {
// 音乐播放时进行闪烁,为了使其位置随机,设置了关于n的四阶的关系进行移位
P2 -= (0x01 << (n * n * n * n* n* 7 + n * n * n * n* 5 + n * n * n* 3)%8);
if (n % 10 == 0) { // music_led_light函数每进行10次就要重置一次P2
P2 = 0xFF;
}
n++;
}


/*
******************************************************************************
* 函 数 名 : time0
* 函数功能 : 定时器中断处理函数,每定时500ms进行一次闪烁
* 输 入 : 无
* 输 出 : 无
******************************************************************************
*/
void time0() interrupt 1 //定时器0中断函数
{
static unsigned int i;//定义静态变量i
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X66;
i++;
if(i==500)
{
i=0;
music_led_light();
}
}


/*
******************************************************************************
* 函 数 名 : light_play
* 函数功能 : 控制led进行跑马灯闪烁以及跑马灯速率
* 输 入 : way:输入1、2、3、4、5、6分别对应6种跑马灯模式,
vote: 输入1~12分别对应12档不同的速率
* 输 出 : 无
******************************************************************************
*/
void light_play(unsigned char way, unsigned char vote){
unsigned char i;
if (way == 1) { // 方式1
P2 = 0xFF;
for (i = 0; i < 8; i++){
P2 -= 0x01 << i;
delay(5000 * vote);
}
for (i = 0; i < 8; i++){
P2 += 0x01 << i;
delay(5000 * vote);
}
} else if (way == 2) { // 方式2,
P2 = 0xFF;
for (i = 0; i < 4; i++){
P2 -= (0x01 << i) + (0x80 >> i);
delay(5000 * vote);
}
for (i = 0; i < 4; i++){
P2 += (0x01 << i) + (0x80 >> i);
delay(5000 * vote);
}
} else if (way == 3) { // 方式3
for (i = 0; i < 8; i++){
P2 -= (0x07 << i) | (0xE0 >> i);
delay(5000 * vote);
P2 = 0xFF;
}

} else if (way == 4) { // 方式4
for (i = 0; i < 4; i++){
P2 = 0x55 + (0x02 << i);
delay(5000 * vote);
P2 = ~P2;
delay(5000 * vote);
P2 = P2 >> 1;
delay(5000 * vote);
P2 = ~P2;
delay(5000 * vote);
}
P2 = 0xFF;

} else if (way == 5) { // 方式5
for (i = 0; i < 8; i++){
P2 = (0x03 << i);
delay(5000 * vote);
P2 = (0xC0 >> i);
delay(5000 * vote);
}
P2 = 0xFF;
delay(5000 * vote);
P2 = 0xFF;

} else if (way == 6) { // 方式6
for (i = 0; i < 6; i++){
P2 = (0xF8 << i);
delay(5000 * vote);
}
for (i = 0; i < 6; i++){
P2 += (0x01 << i);
delay(5000 * vote);
}
for (i = 0; i < 6; i++){
P2 = (0x1F >> i);
delay(5000 * vote);
}
for (i = 0; i < 6; i++){
P2 += (0x80 >> i);
delay(5000 * vote);
}
delay(5000 * vote);
P2 = 0xFF;

} else if (way == 0) { // 方式0,先重置n,然后激活定时器,再进行音乐播放,播放结束关闭定时器
n = 0;
P2 = 0xFF;
if (vote <= 2) { // 如果跑马灯是前面的两种模式,那么将会播放音乐时闪烁
time0_init();
}
if (vote & 0x01) { // 跑马灯模式的单双数决定播放音乐的类型
music_1(vote == 3 ? 2 : 4);
} else {
music(vote == 4 ? 2 : 1);
}


P2 = 0xFF;
TR0 = 0;
n = 0;
}
}



/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/

void main(){
unsigned char vote = 1;
unsigned char way = 1;
while(1) {
unsigned char key_push = key_scan(0); // 进行按键扫描
if (key_push == 4) {
light_play(0, way); // 如果触发第四个按键,将会播放音乐,会根据way的不同来确定播放的方式
}else if (key_push != 0) {
if (key_push == 1) { // 如果触发第一个按键,将会改变跑马灯的显示方式
way = way == 6 ? 1 : way + 1;
} else if (key_push == 2){ // 如果触发第二个按键,将会改变跑马灯的速率
vote = vote == 12 ? 1 : vote + 1;
} else if (key_push == 3){ // 如果触发第三个按键,将会将跑马灯的显示方式和速率都重置为初始状态的第一档
vote = 1;
way = 1;
}
light_play(way, vote);
}
}

}

三、时钟中断与数码管显示的简单联动

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
225
#include <REGX52.H>

// 定义每次按下key后的状态,对应从左到右四个独立按键
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0

// 说明蜂鸣器的引脚、四个按键的引脚
sbit Buzzer = P2^5;
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
unsigned char digit = 0;
unsigned char count = 99;
unsigned char count_flag = 0;
unsigned int buzzer_f = 63628;
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};


void delay_10us(unsigned char ten_us)
{
while(ten_us--);
}

void light(unsigned char location, unsigned char number) {
switch(location) {
case 1: P2_4=0; P2_3=0; P2_2=0; break;
case 2: P2_4=0; P2_3=0; P2_2=1; break;
case 3: P2_4=0; P2_3=1; P2_2=0; break;
case 4: P2_4=0; P2_3=1; P2_2=1; break;
case 5: P2_4=1; P2_3=0; P2_2=0; break;
case 6: P2_4=1; P2_3=0; P2_2=1; break;
case 7: P2_4=1; P2_3=1; P2_2=0; break;
case 8: P2_4=1; P2_3=1; P2_2=1; break;
}
P0 = NixieTable[number];
delay_10us(100);
P0 = 0x00;
}



void Delay10ms(unsigned char num) //@11.0592MHz
{
unsigned char i, j;

while(num--) {
i = 18;
j = 145;
do
{
while (--j);
} while (--i);
}
}

unsigned char key_scan(unsigned char mode)
{
static unsigned char key=1; // 设置static的key,全局变量

if(mode)key=1;
// 如果mode输入1,则会让key一直保持1,这样就只会进行进入下面的if语句,不会进入else if语句
// 设定检测按键触发模式,如果mode=1则可以长按进行多次检测,如果mode=0则只能按下无论时间长短只能进行一次检测
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)){
Delay10ms(2); // 进行延迟20ms,消抖
key=0;
if(KEY1==0)
return KEY1_PRESS; // 返回从左往右数的第一个按键的数值1
else if(KEY2==0)
return KEY2_PRESS; // 返回从左往右数的第二个按键的数值2
else if(KEY3==0)
return KEY3_PRESS; // 返回从左往右数的第三个按键的数值3
else if(KEY4==0)
return KEY4_PRESS; // 返回从左往右数的第四个按键的数值4
} else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1){
// 如果能来到这一句,说明输入的mode一定为0且key为0
// 这么就说明此时已经按下四个独立按键中的其中一个
// 如果现在按住该按键未松手,那么即使来到key_scan这个函数里面,也不会进行任何按键检测,因为此时key为0
// 只有按键松开时才会来到这个else if语句,此时将会重置key的值为1
key=1;
}
// 如果都没有触发,说明此时没有扫描到按键,返回0
return KEY_UNPRESS;
}


void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}

void Timer0_Routine() interrupt 1
{
if(1) //如果不是休止符
{
/*取对应频率值的重装载值到定时器*/
TL0 = buzzer_f % 256; //设置定时初值
TH0 = buzzer_f / 256; //设置定时初值
Buzzer=!Buzzer; //翻转蜂鸣器IO口
}
}

void Timer1Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1=1;
EA=1;
PT1=0;
}

void Timer1_Routine() interrupt 3
{

static unsigned int T1Count;
TL1 = 0x18; //设置定时初值
TH1 = 0xFC; //设置定时初值
T1Count++; //T0Count计次,对中断频率进行分频

if(T1Count>=1000)//分频500次,500ms
{
T1Count=0;
count--;
}

}




void buzzer_frequency_higer(unsigned char i){
if (buzzer_f >= 65350) {
buzzer_f = 63500;
} else
buzzer_f += i*50;
}


void sec_light(unsigned int times) {
while(times--) {
P2_4=1; P2_3=1; P2_2=1;
P0 = NixieTable[count / 10];
delay_10us(100);
P0 = 0x00;

P2_4=1; P2_3=1; P2_2=0;
P0 = NixieTable[count % 10];
delay_10us(100);
P0 = 0x00;
}

}

void count_change() {
count = 9 + 5 * count_flag;
count_flag++;
if (count_flag >= 20) {
count_flag = 0;
}
}


void display_time() {
if (count > 5 && count < 100) {
sec_light(1);
} else if (count == 0) {
unsigned int buzzer_f_1 = buzzer_f;
buzzer_f = 65100;
Timer0Init();
sec_light(300);
TR0 = 0;
Delay10ms(12);
buzzer_f = buzzer_f_1;
}
else if (count <= 5){
Timer0Init();
sec_light(300);
TR0 = 0;
Delay10ms(12);
} else {
count_change();
}
}



void main() {
while(1) {
unsigned char i = 1;
unsigned char key = key_scan(1);
display_time();
if (key) {
if (key == 1) Timer1Init();
else if (key == 2) {
count_change();
} else if (key == 4) {
TR1 = 0;
}
else {
count--;
if (key == 3) buzzer_frequency_higer(2);
Timer0Init();
Delay10ms(5);
TR0 = 0;
}

}
}
}

四、矩阵键盘与蜂鸣器的简单联动

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
#include <REGX51.H>



#define KEY_MATRIX_PORT P1 //使用宏定义矩阵按键控制口
#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0

// 说明蜂鸣器的引脚、四个按键的引脚
sbit Buzzer = P2^5;
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;

unsigned int buzzer_f = 63628;
unsigned int code pitch[3][8] = {{63628, 63835, 64021, 64103, 64160, 64400, 64528},
{64580, 64684, 64777, 64820, 64898, 64968, 65030},
{65058, 65110, 65157, 65178, 65217, 65252, 65283}};


unsigned int code music[8][13] = {{8, 234, 232, 232, 244, 254, 236, 222, 228},
{8, 214, 212, 212, 224, 234, 236, 172, 176},
{6, 164, 234, 228, 164, 234, 228},
{5, 164, 234, 226, 212, 219},
{6, 232, 222, 257, 242, 234, 220},
{7, 252, 242, 234, 242, 256, 234, 227},
{9, 282, 212, 164, 234, 226, 212, 154, 222, 210},
{12, 242, 232, 242, 232, 218, 242, 232, 242, 232, 216, 222, 219}
};

void delay_10us(unsigned int ten_us)
{
while(ten_us--);
}


void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}

void Timer0_Routine() interrupt 1
{
if(1) //如果不是休止符
{
/*取对应频率值的重装载值到定时器*/
TL0 = buzzer_f % 256; //设置定时初值
TH0 = buzzer_f / 256; //设置定时初值
Buzzer=!Buzzer; //翻转蜂鸣器IO口
}
}


unsigned char key_matrix_flip_scan(void)
{
static unsigned char key_value=0;

KEY_MATRIX_PORT=0x0f;//给所有行赋值0,列全为1
if(KEY_MATRIX_PORT!=0x0f)//判断按键是否按下
{
delay_10us(1000);//消抖
if(KEY_MATRIX_PORT!=0x0f)
{
//测试列
KEY_MATRIX_PORT=0x0f;
switch(KEY_MATRIX_PORT)//保存行为0,按键按下后的列值
{
case 0x07: key_value=1;break;
case 0x0b: key_value=2;break;
case 0x0d: key_value=3;break;
case 0x0e: key_value=4;break;
}
//测试行
KEY_MATRIX_PORT=0xf0;
switch(KEY_MATRIX_PORT)//保存列为0,按键按下后的键值
{
case 0x70: key_value=key_value;break;
case 0xb0: key_value=key_value+4;break;
case 0xd0: key_value=key_value+8;break;
case 0xe0: key_value=key_value+12;break;
}
while(KEY_MATRIX_PORT!=0xf0);//等待按键松开
}
}
else
key_value=0;

return key_value;
}


void Delay10ms(unsigned char num) //@11.0592MHz
{
unsigned char i, j;

while(num--) {
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
}

unsigned char key_scan(unsigned char mode)
{
static unsigned char key=1; // 设置static的key,全局变量

if(mode)key=1;
// 如果mode输入1,则会让key一直保持1,这样就只会进行进入下面的if语句,不会进入else if语句
// 设定检测按键触发模式,如果mode=1则可以长按进行多次检测,如果mode=0则只能按下无论时间长短只能进行一次检测
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)){
delay_10us(2000); // 进行延迟20ms,消抖
key=0;
if(KEY1==0)
return KEY1_PRESS; // 返回从左往右数的第一个按键的数值1
else if(KEY2==0)
return KEY2_PRESS; // 返回从左往右数的第二个按键的数值2
else if(KEY3==0)
return KEY3_PRESS; // 返回从左往右数的第三个按键的数值3
else if(KEY4==0)
return KEY4_PRESS; // 返回从左往右数的第四个按键的数值4
} else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1){
// 如果能来到这一句,说明输入的mode一定为0且key为0
// 这么就说明此时已经按下四个独立按键中的其中一个
// 如果现在按住该按键未松手,那么即使来到key_scan这个函数里面,也不会进行任何按键检测,因为此时key为0
// 只有按键松开时才会来到这个else if语句,此时将会重置key的值为1
key=1;
}
// 如果都没有触发,说明此时没有扫描到按键,返回0
return KEY_UNPRESS;
}


void pitch_change(unsigned char height, unsigned char p) {
buzzer_f = pitch[height - 1][p - 1];
}

void music_play(unsigned char height, unsigned char p, unsigned char delay_time) {
if (p == 8) {
Delay10ms(delay_time);
} else {
pitch_change(height, p);
Timer0Init();
Delay10ms(delay_time);
TR0 = 0;
}
}


void music_play_top(unsigned char section) {
unsigned char i;
for (i = 1 ; i <= music[section][0]; i++) {
unsigned char height = music[section][i] / 100;
unsigned char p = music[section][i] % 100 / 10;
unsigned char delay_time = music[section][i] % 10;
switch(delay_time) {
case 2: delay_time = 3; break;
case 4: delay_time = 6; break;
case 6: delay_time = 9; break;
case 8: delay_time = 12; break;
case 7: delay_time = 18; break;
case 9: delay_time = 24; break;
case 0: delay_time = 15; break;
}
music_play(height, p, delay_time);
}


}

void main()
{
unsigned char key=0;
unsigned char Key = 0;
unsigned char i = 0;
unsigned char delay_time = 5;
unsigned char height = 2;


while(1)
{
key=key_matrix_flip_scan();
Key = key_scan(1);
if(key!=0){
delay_time = Key == 1 ? 10 : Key == 4 ? 3 : 5;
height = Key == 2 ? 1 : Key == 3 ? 3 : 2;
if (key <= 7) {
music_play(height, key, delay_time);
} else if (key > 8) {
music_play_top(key - 9);
}
}
}
}

五、LCD1602与矩阵键盘的简单联动——简易计算器

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
#include <REGX51.H>
typedef unsigned int uint; //定于无符号int
typedef unsigned char uchar; //定义无符号char
/*****************************定义引脚**********************************/
sbit rw=P2^5;
sbit rs=P2^6;
sbit e=P2^7;
/*****************************定义变量**********************************/
uchar fuhao,flag,j,k,num;
uchar ERROR[]=" ERROR!";//除数为0时,显示
uchar wel[]=" Welcome!";//初始化时显示
int a,b;
long c,x;
uchar cal_mode = 0;
uchar ans_mode = 0;
/*****************************延时函数**********************************/
void delay(unsigned int us_10){
while(us_10--);
}
/*****************************LCD写命令————位置**********************************/
void lcdwrc(uint c)
{
delay(100);
rs=0;
rw=0;
e=0;
P0=c;
e=1;
delay(10000);
e=0;
}

/*****************************LCD写数据————内容**********************************/
void lcdwrd(uint dat) //设置LCD写数据
{
delay(10000);
rs=1;
rw=0;
e=0;
P0=dat;
e=1;
delay(10000);
e=0;
rs=0;
}
/*****************************初始化LCD**********************************/
void lcdinit() //初始化LCD
{
delay(15000);
lcdwrc(0x38);//写指令38H
delay(5000);
lcdwrc(0x38);//显示模式设置
delay(5000);
lcdwrc(0x38);
delay(5000);
lcdwrc(0x38);//功能设定指令
lcdwrc(0x08);
lcdwrc(0x01);//清屏
lcdwrc(0x06);//显示光标移动设置
lcdwrc(0x0c); //显示开关控制指令
num=0;//用于判定按键是否为“=”或者“归零”时的变量
fuhao=0;//用于判定是否为运算符号
flag=0;//用于判定运算方式时的变量
a=0;//计算的第一个数
b=0;//计算的后一个数
c=0;//计算结果
x=0;//判定小数点的变量,暂存c值
k=0;//判定小数点的变量,小数点后非零数个数
j=0;//显示ERROR时的循环用变量
}

//void show(int show_data){
// uchar the_data;
// if (show_data < 10) {
// lcdwrd(0x30+show_data);
// return;
// }
// the_data = show_data % 10;
// show(show_data / 10);
// lcdwrd(0x30+the_data);
//}

void show(int show_data) {
uchar buffer[6];
char i = 0;
for (i = 0; i < 6; i++) {
buffer[i] = show_data % 10;
show_data /= 10;
}
do{
i--;
}while (buffer[i] == 0);
for (;i >= 0;i--) {
lcdwrd(0x30 + buffer[i]);
}
return;

}


/*****************************计算器函数**********************************/
void KeyDown()//计算器函数
{
k=0;
P1=0x0f; //0000 11111,行都为低电平0,列都为高电平
if(P1!=0x0f)//P1不为0x0f,则判定为有按键被按下
{
delay(10000);//消抖
if(P1!=0x0f)//延时后,再次判断
{
P1=0x0f;//0000 11111,行都为低电平0,列都为高电平
switch(P1)//
{
/********************第一列有按键按下*********************/
case 0x07://0000 0111 第一列有按键被按下
{
P1=0xf0;//列都为低电平0,行都为高电平1
switch(P1)//判断哪一行的按键被按下
{
case 0x70: num = 1/*S1*/;k=1;break; //0111 0000第一列第一行 数字7
case 0xb0: num = 5/*S5*/;k=4;break; //1011 0000第一列第二行 数字4
case 0xd0: num = 9/*S9*/;k=7;break; //1101 0000第一列第三行 数字1
case 0xe0: num = 13/*S13*/;break; //1110 0000第一列第四行 归零
}

if(num!=13)//被按下的按键不是归零键
{
if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b
a=a*10+k;
else
b=b*10+k;
}
lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k

if(num==13)//按下的按键是清零键
{
ans_mode = 0;
cal_mode = 0;
lcdwrc(0x01); //清屏指令
lcdinit();//重新初始化LCD1602
}
}while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断


/********************第二列有按键按下*********************/
case 0x0b: //0000 1011 第二列有按键被按下
{
P1=0xf0;//行都为高电平1,列都为低电平0
switch(P1)//判断哪一行有按键被按下
{
case 0x70: k=2;break;//第二列第一行 数字8
case 0xb0: k=5;break;//第二列第二行 数字5
case 0xd0: k=8;break;//第二列第三行 数字2
case 0xe0: k=0;break;//第二列第四行 数字0
}

if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b
a=a*10+k;
else
b=b*10+k;
lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k
} while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断


/********************第三列有按键按下*********************/
case 0x0d: //0000 1101 第三列有按键被按下
{
P1=0xf0;//行都为高电平1,列都为低电平0
switch(P1)//判断哪一行有按键被按下
{
case 0x70: num=3;k=3;break;//第三列第一行 数字9
case 0xb0: num=7;k=6;break;//第三列第二行 数字6
case 0xd0: num=11;k=9;break;//第三列第三行 数字3
case 0xe0: num=15;break;//第三列第四行 “=”键
}
if(num!=15)//按下的键不为“=”键
{
if(fuhao==0)//fuhao==0,表示第一个数a,fuhao==1,表示第二个数b
a=a*10+k;
else
b=b*10+k;
lcdwrd(0x30+k);//显示按下的数,0x30为ASCII码中数字组的开头,0x3k:显示k,即按下数字k
}

/************"="键被按下************/
if(num==15)//按下的键为=键
{

switch(flag)//判断进行哪种运算
{
/*******加法运算*********/
case 1://加法运算
{
c=a+b;//计算结果
x=a+b;
lcdwrc(0x4f/*第二行末尾位置*/+0x80);//光标置于第二行末尾
lcdwrc(0x04);//设置显示方式:显示后指针减一,即前移一位

if(c==0)//若结果为0
lcdwrd(0x30);//显示0
while(c!=0)
{
lcdwrd(0x30+c%10);//从后向前显示最后一位
c=c/10;//去掉最后一位后再循环显示
}
lcdwrd(0x3d);//显示完计算结果后,显示“=”
}break;

/*******减法运算*********/
case 2://减法运算
{
if(a>b){//大数减小数
c=a-b;
x=a-b;
}
else{//小数减大数
c=b-a;//计算两数相减的绝对值
x=b-a;
}
lcdwrc(0x4f+0x80);//将光标置于第二行末尾
lcdwrc(0x04);//设置显示方式:显示后指针减一

if(c==0)//若结果为零
lcdwrd(0x30);//显示0
while(c!=0)
{
lcdwrd(0x30+c%10);//从后向前显示最后一位
c=c/10;//去掉最后一位后再循环显示
}
if(a<b)//若结果为负数
lcdwrd(0x2d);//ASCII中的“-”,显示负号
lcdwrd(0x3d);//显示=
}break;

/*******乘法运算*********/
case 3://乘法运算
{
c=a*b; //计算结果
x=a*b;
lcdwrc(0x4f+0x80);//光标置于第二行末尾
lcdwrc(0x04);//显示设置:显示后指针减一
if(c==0)//若结果为0
lcdwrd(0x30);//显示0
while(c!=0)
{
lcdwrd(0x30+c%10);//从后向前显示最后一位
c=c/10;//去掉最后一位后再循环显示
}
lcdwrd(0x3d);//显示=
}break;

/*******除法运算*********/
case 4: //除法运算
{
if(b==0)//若除数为0
{
lcdwrc(0x01);//清屏
for(j=0;j<11;j++)
{
lcdwrd(ERROR[j]);//显示语句:ERROR!
}
delay(100000);//延时
lcdinit();//重新初始化LCD1602
}

else
{
c=(long)(((float)a/b)*1000);//将结果放大10000倍,即:若无法整除,保留4位小数
x = c;
lcdwrc(0x4f+0x80);//光标置于第二行末尾
lcdwrc(0x04);//显示设置:显示后指针减一
while(c!=0)//一位一位显示
{
k++;
lcdwrd(0x30+c%10);//显示结果的最后一位在0x4f的位置 //0 00 000 0000
c=c/10;//取前面的结果数据
if(x>0&&x<=9)//0.0001到0.0009
{
if(k==1)//只有小数点后最后一位 x.000x
{
lcdwrd(0x30);//0
lcdwrd(0x30);//0
lcdwrd(0x30);//0
lcdwrd(0x2e);//.
k=0;
}
}
if(x>9&&x<=99)//0.0010到0.0099
{
if(k==2)//只有小数点后最后两位 x.00xx
{
lcdwrd(0x30);//0
lcdwrd(0x30);//0
lcdwrd(0x2e);//.
k=0;
}
}
if(x>99&&x<=999)//0.0100到0.0999
{
if(k==3)//只有小数点后最后三位 x.0xxx
{
lcdwrd(0x30);//0
lcdwrd(0x2e);//.
k=0;
}
}
else if(k==4)//小数点后四位都非零 x.xxxx
{
lcdwrd(0x2e);//.
}
}
if(x<10000)//若结果小于1,在个位补0
lcdwrd(0x30);//个位补0
lcdwrd(0x3d);//显示=
k=0;//将k定回0用于下次计算
x /= 1000;
}
} break;
}
cal_mode = 0;
ans_mode = 1;
}
}
while(P1!=0xf0);break;//当有按键被按下时,结束此次判断,进行下一次判断


/********************第四列有按键按下*********************/
case 0x0e://0000 1110 第四列有按键被按下
{
if (ans_mode == 1) {
fuhao = 1;
a = x;
lcdwrc(0x01); //清屏指令
show(x);
b = 0;
c = 0;
cal_mode = 0;
}
if (cal_mode == 0) {
fuhao=1;//有符号键被按下
P1=0xf0;//1111 0000
switch(P1)
{
case 0x70:/*0111 0000*/flag=1;lcdwrd(0x2b);cal_mode = 1;break;//“/”第四列第一行
case 0xb0:/*1011 0000*/flag=2;lcdwrd(0x2d);cal_mode = 1;break;//“*”第四列第二行
case 0xd0:/*1101 0000*/flag=3;lcdwrd(0x2a);cal_mode = 1;break;//“-”第四列第三行
case 0xe0:/*1110 0000*/flag=4;lcdwrd(0xfd);cal_mode = 1;break;//“+”第四列第四行
}
cal_mode = 1;
} else {
break;
}
}
while(P1!=0xf0);break;
}
}
}
}
/*****************************主函数**********************************/
void main()
{
uchar x;
lcdinit();//LCD1602初始化
for(x=0;x<12;x++)
lcdwrd(wel[x]);//显示欢迎语句:Welcome
delay(100000);
lcdwrc(0x01);//LCd1602清屏
while(1)
{
KeyDown();//按键判断函数
}
}

六、通过UART串口进行电脑端到单片机端的通信

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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include<REGX51.H>

unsigned char Sec;
unsigned char strIndex;
unsigned char getStr[20];
unsigned char way = 1;
unsigned char vote = 1;
unsigned char *whatWay = "Now the way is ";
unsigned char *whatVote = "Now the vote is ";


void delay(unsigned int ten_us)
{
while(ten_us--);
}

void UART_Init(void) //4800bps@11.0592MHz
{
PCON |= 0x80; //使能波特率倍速位SOMD
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA = 1;
ES = 1;
}

void UART_SendByte(unsigned char byte) {
SBUF = byte;
while(TI == 0);
TI = 0;
}

void UART_SendStringLine(unsigned char *str) {
unsigned char i = 0;
while(*str!='\0'){
SBUF = *str;
while(TI == 0);
TI = 0;
str++;
i++;
}
i = 36 * (i / 36 + 1) - i;
while (i != 0) {
UART_SendByte(' ');
i--;
}
}

void UART_SendStringAndNumLine(unsigned char *str, unsigned char num) {
unsigned char i = 0;
while(*str!='\0'){
SBUF = *str;
while(TI == 0);
TI = 0;
str++;
i++;
}
if (num > 10) {
UART_SendByte(48 + num / 10);
UART_SendByte(48 + num % 10);
i++;
}else {
UART_SendByte(48 + num);
}
i++;
i = 36 * (i / 36 + 1) - i;
while (i != 0) {
UART_SendByte(' ');
i--;
}
}



void light_play(unsigned char way, unsigned char vote){
unsigned char i;
if (way == 1) { // 方式1
P2 = 0xFF;
for (i = 0; i < 8; i++){
P2 -= 0x01 << i;
delay(1000 * vote);
}
for (i = 0; i < 8; i++){
P2 += 0x01 << i;
delay(1000 * vote);
}
} else if (way == 2) { // 方式2
P2 = 0xFF;
for (i = 0; i < 4; i++){
P2 -= (0x01 << i) + (0x80 >> i);
delay(1000 * vote);
}
for (i = 0; i < 4; i++){
P2 += (0x01 << i) + (0x80 >> i);
delay(1000 * vote);
}
} else if (way == 3) { // 方式3
for (i = 0; i < 8; i++){
P2 -= (0x07 << i) | (0xE0 >> i);
delay(1000 * vote);
P2 = 0xFF;
}
} else if (way == 4) { // 方式4
for (i = 0; i < 4; i++){
P2 = 0x55 + (0x02 << i);
delay(1000 * vote);
P2 = ~P2;
delay(1000 * vote);
P2 = P2 >> 1;
delay(1000 * vote);
P2 = ~P2;
delay(1000 * vote);
}
P2 = 0xFF;

} else if (way == 5) { // 方式5
for (i = 0; i < 8; i++){
P2 = (0x03 << i);
delay(1000 * vote);
P2 = (0xC0 >> i);
delay(1000 * vote);
}
P2 = 0xFF;
delay(1000 * vote);
P2 = 0xFF;

} else if (way == 6) { // 方式6
for (i = 0; i < 6; i++){
P2 = (0xF8 << i);
delay(1000 * vote);
}
for (i = 0; i < 6; i++){
P2 += (0x01 << i);
delay(1000 * vote);
}
for (i = 0; i < 6; i++){
P2 = (0x1F >> i);
delay(1000 * vote);
}
for (i = 0; i < 6; i++){
P2 += (0x80 >> i);
delay(1000 * vote);
}
delay(1000 * vote);
P2 = 0xFF;

}
}



unsigned char StrEqual(unsigned char *str) {
unsigned char i = 0;
while(*str!='\0'){
if (*str != getStr[i]) {
return 0;
}
str++;
i++;
}
return 1;
}


void UART_Get() {
char num;
EA = 0;
if (StrEqual("show;") == 1) {
UART_SendStringLine("Show light!");
light_play(way, vote);
} else if (StrEqual("way add;") == 1) {
way = way == 6 ? 0 : way + 1;
UART_SendStringLine("Light way change!");
light_play(way, vote);
} else if (StrEqual("vote add;") == 1) {
vote = vote == 60 ? 0 : vote + 1;
UART_SendStringLine("Light vote change!");
light_play(way, vote);
} else if (StrEqual("what way;") == 1) {
UART_SendStringAndNumLine(whatWay, way);
} else if (StrEqual("what vote;") == 1) {
UART_SendStringAndNumLine(whatVote, vote);
} else if (StrEqual("show all;") == 1) {
UART_SendStringAndNumLine(whatWay, way);
UART_SendStringAndNumLine(whatVote, vote);
light_play(way, vote);
} else if (StrEqual("set way ") == 1) {
num = getStr[8] - 48;
if (num <= 0 || num > 6 || getStr[9] != ';') UART_SendStringLine("WAY SET ERROR!");
else {
way = num;
UART_SendStringAndNumLine(whatWay, way);
light_play(way, vote);
}
} else if (StrEqual("set vote ") == 1) {
if (getStr[10] == ';') {
num = getStr[9] - 48;
} else {
num = (getStr[9] - 48) * 10 + (getStr[10] - 48);
}
if (num <= 0 || num > 60 || !(getStr[10] == ';' || getStr[11] == ';')) UART_SendStringLine("VOTE SET ERROR!");
else {
vote = num;
UART_SendStringAndNumLine(whatVote, vote);
light_play(way, vote);
}
}
else {
UART_SendStringLine("INPUT ERROR!");
}
strIndex = 0;
EA = 1;
}


void UART_Routine() interrupt 4
{
if (RI == 1) { // 如果是电脑接送中断
if (SBUF == ';') {
getStr[strIndex] = SBUF;
RI = 0;
UART_Get();
return;
}
getStr[strIndex] = SBUF;
RI = 0; // 内部对RI软件复位
strIndex++;
if (strIndex == 20) {
UART_SendStringLine("INPUT TO MANY!");
strIndex = 0;
}
}

}



void main() {
UART_Init();
while(1) {

}
}
  • 命令:show;way add;vote add;what way;what vote;show all;set way n;、 `set vote n;``。

七、其他模块化设计

由于模块化设计涉及到多个模块,所以将会分别用多个文章进行记录,防止出现阅读错误的情况。