51 单片机实验(C 语言)

  1. 1. 实验 1 利用数码管显示数字
  2. 2. 实验 2 数码管显示脉冲的时间(计数器)
  3. 3. 实验 3 检测按键次数(计数器)
  4. 4. 实验 4 按键的短按长按
  5. 5. 实验 4% 按键设置倒计时
  6. 6. 实验 5 串口通信
  7. 7. 实验 6 检测脉冲 串口显示
  8. 8. 实验 6% 电子琴
  9. 9. 实验 7 串口收数据 倒计数亮灯
  10. 10. 实验 8 电子表

这学期我开了 51 单片机的课,之后会将我的一些作业发自己网站上,希望能有用。我使用的开发板核心是 STC12C5A16S2,理论上以下代码是适用于 8051 内核单片机。

实验 1 利用数码管显示数字

本实验利用“共阳数码管”显示一串数字,利用中断方式每隔 1 秒更换一个数字。

代码如下,对其中的一些语句做出了标注。

说明:我所用的 STC 实验板中,p21 是“4 位共阳数码管”的第一位数字的控制引脚,P0 是数码管的公共极。

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
// 数码管显示数字
//#include"head.h"
#include<reg51.h>
sbit P21=P2^1;
int i=0;
int j=0;
//int led[11]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};// 共阴 0~9
int led[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 共阳 0~9
int num[13]={1,2,3,4,5,6,7,8,9};
int counter=0;// 更换的数字
void main()
{
P21=0;

TMOD=0x01;//50ms
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值

EA=1;// 中断总开关
ET0=1;//T0 中断子开关
TF0=0;//T0 溢出标志置 0
TR0=1;// 打开计时器
while(1);
}
void Timer0() interrupt 1
{
// 清除中断标志
TH0=0x3c;
TL0=0xb0;
// 进行 1s 的计数
counter++;
if(counter==20)
{
counter=0;
i=num[j++];
P0=led[i];
if(j>=9) j=0;
}
}

实验 2 数码管显示脉冲的时间(计数器)

本实验利用“共阳数码管”显示测量脉冲的时间(ms),而这个脉冲由按键产生。

代码如下,对其中的一些语句做出了标注。

说明:我所用的 STC 实验板中,p21-p24 是“4 位共阳数码管”的第一位数字的控制引脚,P0 是数码管的公共极;p32 是按键引脚,按下是高电平。

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
#include<reg51.h>
sbit p21=P2^1;sbit p22=P2^2;sbit p23=P2^3;sbit p24=P2^4;
sbit p32=P3^2;
unsigned char led[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 共阳 0~9
unsigned int n=0,s=0,i=0,qian=0,bai=0,shi=0,ge=0;
void delay(int z)// 延时函数
{
int i,j;
for(i=z;i>0;i--)
for(j=0;j<921;j++);
}
void main()
{
TMOD=0x01;
TH0=0x00;// 高八位时间初值
TL0=0x00;// 低八位时间初值

EA=1;// 中断总开关
ET0=1;//T0 定时器中断子开关

while(p32==1){delay(20);}
TR0=1; // 打开计时器
while(p32==0){delay(20);} // 等待电平由低变高
while(p32==1)
{
s=(n*65536+TH0*256+TL0)/1000;
qian=s/1000%10;
bai=s/100%10;
shi=s/10%10;
ge=s%10;

switch(i)
{
case 0:P0=0xff;p21=0;p22=1;p23=1;p24=1;P0=led[qian];i++;break;
case 1:P0=0xff;p21=1;p22=0;p23=1;p24=1;P0=led[bai];i++;break;
case 2:P0=0xff;p21=1;p22=1;p23=0;p24=1;P0=led[shi];i++;break;
case 3:P0=0xff;p21=1;p22=1;p23=1;p24=0;P0=led[ge];i=0;break;
default:break;
}

} // 等待电平由高变低
TR0=0; // 关闭计时器
while(1)
{
switch(i) // 同上
{
case 0:P0=0xff;p21=0;p22=1;p23=1;p24=1;P0=led[qian];i++;break;
case 1:P0=0xff;p21=1;p22=0;p23=1;p24=1;P0=led[bai];i++;break;
case 2:P0=0xff;p21=1;p22=1;p23=0;p24=0;P0=led[shi];i++;break;
case 3:P0=0xff;p21=1;p22=1;p23=1;p24=1;P0=led[ge];i=0;break;
default:break;
}
}
}
void Timer0() interrupt 1 using 0
{
n++;
TH0=0x00;// 高八位时间初值
TL0=0x00;// 低八位时间初值
}

实验 3 检测按键次数(计数器)

本实验利用“共阳数码管”显示测量脉冲的次数,脉冲由按键产生。

说明:我所用的 STC 实验板中,p21-p24 是“4 位共阳数码管”的第一位数字的控制引脚,P0 是数码管的公共极;p34 是按键引脚,按下是高电平。

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
//P32 按键外部中断启动计时
// 检测 p34 在 1s 内共按的次数,显示在数码管上
#include"reg51.h"
sbit p21=P2^1;sbit p22=P2^2;sbit p23=P2^3;sbit p24=P2^4;
sbit p32=P3^2;sbit p33=P3^3;sbit p34=P3^4;sbit p35=P3^5;
sbit p37=P3^7;
unsigned char led[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 共阳 0~9
int n=0;
int num=0;

void main()
{
TMOD=0x51;//0101 0001 T1 计数 T0 计时
TH1=0;
TL1=0;
TH0=0x3c;//50ms
TL0=0xb0;

ET0=1;// 中断允许
ET1=1;// 中断允许
TF0=0;// 溢出标志位清 0
EA=1;

while(INT0==1);// 未按 p32

TR0=1;// 打开 T0
TR1=1;// 打开 T1
while(1)
{
num=TH1*256+TL1;
p21=0;
P0=led[num];
}
}
void Timer0() interrupt 1 using 0
{
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
n++;
if(n==20)
{
TR1=0;// 关闭 T1
}
}

实验 4 按键的短按长按

实验目的:体会外部中断的边沿触发和电平触发。

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
// 实验 4 按键的短按长按
//T0 外部中断 p32 长按计时 数码管快加 1
//T1 外部中断 p33 短按 数码管慢加 1
#include<reg51.h>
sbit p21=P2^1;
sbit p22=P2^2;
sbit p23=P2^3;
sbit p24=P2^4;
int num=0,n=0;
unsigned char i=0,ge=0,shi=0,bai=0,qian=0;
unsigned char led[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 共阳 0~9
void main()
{
TMOD=0x01;
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
IT0=0;// 外部中断电平触发 // 长按
IT1=1;// 外部中断边沿触发 // 短按
ET0=1;// 定时器 0 中断允许位 //T0 定时器中断子开关
ET1=1;
EX0=1;// 外部中断 INT0 开关
EX1=1;
PT0=1;// 定时器提升优先级
//PT1=1;
//PX0=1;// 外部中断提高优先级
//PX1=1;
EA=1;

while(1)
{
qian=num/1000%10;
bai=num/100%10;
shi=num/10%10;
ge=num%10;

switch(i)
{
case 0:P0=0xff;p21=0;p22=1;p23=1;p24=1;P0=led[qian];i++;break;
case 1:P0=0xff;p21=1;p22=0;p23=1;p24=1;P0=led[bai];i++;break;
case 2:P0=0xff;p21=1;p22=1;p23=0;p24=1;P0=led[shi];i++;break;
case 3:P0=0xff;p21=1;p22=1;p23=1;p24=0;P0=led[ge];i=0;break;
default:break;
}
}
}
void IN0() interrupt 0
{
TR0=1;
}
void Timer0() interrupt 1
{
n++;
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
if(n==4) // 计时 200ms, 实现快加 1
{
num++;
n=0;
}
if(INT0==1)
{
TR0=0;
}
}
void IN1() interrupt 2
{
num++;
}

实验 4% 按键设置倒计时

实验目的:设计一个可以自定时间的倒计时器。

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
// 实验 4% 利用按键设置倒计时
// 按键计数设置时间,外部中断实现按下开始倒计时,倒计时完毕小灯点亮或蜂鸣器响
//T0 外部中断设置时间,T1 外部中断开始倒计时
//T0 作为计时器
#include<reg51.h>
sbit p21=P2^1;
sbit p22=P2^2;
sbit p23=P2^3;
sbit p24=P2^4;
sbit p37=P3^7;// 蜂鸣器
int Time=0,n=0;
unsigned char i=0,ge=0,shi=0,bai=0,qian=0;
unsigned char led[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 共阳 0~9
void delay(int z)// 延时函数
{
int i,j;
for(i=z;i>0;i--)
for(j=0;j<921;j++);
}
void main()
{
TMOD=0x01;
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
IT0=1;
IT1=1;// 外部中断边沿触发 // 短按
ET0=1;// 定时器 0 中断允许位 //T0 定时器中断子开关
ET1=1;
EX0=1;// 外部中断 INT0 开关
EX1=1;
EA=1;

while(1)
{
qian=Time/1000%10;
bai=Time/100%10;
shi=Time/10%10;
ge=Time%10;

switch(i)
{
case 0:P0=0xff;p21=0;p22=1;p23=1;p24=1;P0=led[qian];i++;break;
case 1:P0=0xff;p21=1;p22=0;p23=1;p24=1;P0=led[bai];i++;break;
case 2:P0=0xff;p21=1;p22=1;p23=0;p24=1;P0=led[shi];i++;break;
case 3:P0=0xff;p21=1;p22=1;p23=1;p24=0;P0=led[ge];i=0;break;
default:break;
}
}
}
void IN0() interrupt 0
{
Time++;
}
void Timer0() interrupt 1
{
n++;
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
if(n==20) // 计时 200ms, 实现快加 1
{
Time--;
n=0;
}
if(Time==0)
{
p37=1;
delay(200);
p37=0;
delay(20000);
}
}
void IN1() interrupt 2
{
TR0=1;
}

实验 5 串口通信

实验目的:初步接触串口通信,单片机接收电脑发来的数据,用数码管显示,并发送回电脑。

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
// 单片机接收电脑发来的数据,用数码管显示,再发送回电脑
#include<reg51.h>
sbit p21=P2^1;
sbit p22=P2^2;
sbit p23=P2^3;
sbit p24=P2^4;
unsigned char led[12]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; // 共阳 0~9
unsigned int num=0,i=0;
unsigned char ch[2]= {0};
void main() {
SCON=0X50;// 选择方式 1;允许接受数据;
TMOD=0X20;// 选 T1 为方式 2:自动重装的 8 位计时器
TH1=TL1=0XF3;//f=2400~12MHz (9600~B11.0592-0xFD)

TR1=1;// 打开计时器
ES=1;// 串口中断允许
EA=1;// 中断总开关
while(1) {
num=SBUF;
ch[0]=num/16;
ch[1]=num%16;

switch(i) {
case 0:
P0=0xff;
p21=0;
p22=1;
p23=1;
p24=1;
P0=led[ch[0]];
i++;
break;
case 1:
P0=0xff;
p21=1;
p22=0;
p23=1;
p24=1;
P0=led[ch[1]];
i=0;
break;
default:
break;
}
}
}

void serial() interrupt 4 { // 串行口中断
if(TI==1) { // 发送
TI=0;
}
if(RI==1) { // 接收
RI=0;
SBUF=SBUF;
}
}

实验 6 检测脉冲 串口显示

实验目的:实验 2 和 5 的结合。

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
// 单片机检测 高电平时间或低电平时间或周期时间
// 结果发送电脑串口显示
// T0 用作计时器
#include<reg51.h>
#include<stdio.h>
sbit p21=P2^1;
sbit p32=P3^2;
unsigned char led[12]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; // 共阳 0~9
unsigned int n=0,s=0;
void delay(int z) { // 延时函数
int i,j;
for(i=z; i>0; i--)
for(j=0; j<921; j++);
}
void main() {
SCON=0X50;// 选择方式 1;允许接受数据;
TMOD=0X21;// 选 T1 为方式 2:自动重装的 8 位计时器 T0 做计时器,测量按键(脉冲)一个周期
//TMOD=0X29;//T0 做计时器,测量按键 p32 高电平
TH0=0x00;// 高八位时间初值
TL0=0x00;// 低八位时间初值
TH1=TL1=0XF3;//f=2400~12MHz (9600~B11.0592-0xFD)

EA=1;// 中断总开关
ET0=1;//T0 中断子开关
TR1=1;// 打开 T1 计时器
ES=1;// 串口中断允许

while(p32==1);
TR0=1; // 打开计时器
while(p32==0) {
delay(5); // 等待电平由低变高
}
while(p32==1); // 等待电平由高变低
TR0=0; // 关闭计时器


s=((n*65536+TH0*256+TL0)/1000000)%10;
p21=0;
P0=led[s];
SBUF=s;
while(1);
}
void Timer0() interrupt 1 {
n++;
TH0=0x00;// 高八位时间初值
TL0=0x00;// 低八位时间初值
}

void serial() interrupt 4 { // 串行口中断
if(TI==1) { // 发送
TI=0;
}
if(RI==1) { // 接收
RI=0;
}
}

实验 6% 电子琴

实验目的:利用中断模拟出蜂鸣器的不同音调的声音。

说明:我所用的 STC 实验板中,使用 12MHz 晶振,p37 是蜂鸣器控制引脚。

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
// 单片机  电子琴
//T1 做串口定时器
//T0 做蜂鸣器的定时中断
#include<reg51.h>
#include<stdio.h>
sbit p37=P3^7;
int receive=0;
unsigned int n=0,s=0;
unsigned char T0RH = 0xFF; //T0 重载值的高字节
unsigned char T0RL = 0x00; //T0 重载值的低字节
unsigned int code NoteReload[] = { // 中音 1-7 和高音 1-7 对应的定时器重载值
65536 - (12000000/12) / (523*2), // 中音 1
65536 - (12000000/12) / (587*2), //2
65536 - (12000000/12) / (659*2), //3
65536 - (12000000/12) / (698*2), //4
65536 - (12000000/12) / (784*2), //5
65536 - (12000000/12) / (880*2), //6
65536 - (12000000/12) / (988*2), //7
65536 - (12000000/12) / (1047*2), // 高音 1
65536 - (12000000/12) / (1175*2), //2
65536 - (12000000/12) / (1319*2), //3
65536 - (12000000/12) / (1397*2), //4
65536 - (12000000/12) / (1568*2), //5
65536 - (12000000/12) / (1760*2), //6
65536 - (12000000/12) / (1976*2), //7
};
void delay(int z) { // 延时函数
int i,j;
for(i=z; i>0; i--)
for(j=0; j<921; j++);
}
void main() {
SCON=0X50;// 选择方式 1;允许接受数据;
TMOD=0X21;// 选 T1 为方式 2:自动重装的 8 位计时器 T0 做计时器,测量按键(脉冲)一个周期
//TMOD=0X29;//T0 做计时器,测量按键 p32 高电平
TH0 = T0RH;
TL0 = T0RL;
TH1=TL1=0XF3;//f=2400~12MHz (9600~B11.0592-0xFD)

EA=1;// 中断总开关
ET0=1;//T0 中断子开关
TR0=1;
TR1=1;// 打开 T1 计时器
ES=1;// 串口中断允许


while(1) {
T0RH = NoteReload[receive] >> 8;
T0RL = NoteReload[receive];
}
}
void Timer0() interrupt 1 {
p37 = ~p37;
TH0 = T0RH;
TL0 = T0RL;

}

void serial() interrupt 4 { // 串行口中断
if(TI==1) { // 发送
TI=0;
}
if(RI==1) { // 接收
RI=0;
receive=SBUF;
}
}

以下是我找的一个例程,可以播放出《两只老虎》。

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
/********* 两只老虎 例程 ***********/
/*** 摘自《手把手教你学 51 单片机 -C 语言版》*/
/* 由金沙滩工作室宋雪松老师等人员编著、由清华大学出版社出版的单片机学习教材 */
#include <reg52.h>
sbit BUZZ = P3^7; // 蜂鸣器控制引脚
unsigned int code NoteFrequ[] = { // 中音 1-7 和高音 1-7 对应频率列表
523, 587, 659, 698, 784, 880, 988, // 中音 1-7
1047, 1175, 1319, 1397, 1568, 1760, 1976 // 高音 1-7
};
unsigned int code NoteReload[] = { // 中音 1-7 和高音 1-7 对应的定时器重载值
65536 - (12000000/12) / (523*2), // 中音 1
65536 - (12000000/12) / (587*2), //2
65536 - (12000000/12) / (659*2), //3
65536 - (12000000/12) / (698*2), //4
65536 - (12000000/12) / (784*2), //5
65536 - (12000000/12) / (880*2), //6
65536 - (12000000/12) / (988*2), //7
65536 - (12000000/12) / (1047*2), // 高音 1
65536 - (12000000/12) / (1175*2), //2
65536 - (12000000/12) / (1319*2), //3
65536 - (12000000/12) / (1397*2), //4
65536 - (12000000/12) / (1568*2), //5
65536 - (12000000/12) / (1760*2), //6
65536 - (12000000/12) / (1976*2), //7
};
bit enable = 1; // 蜂鸣器发声使能标志
bit tmrflag = 0; // 定时器中断完成标志
unsigned char T0RH = 0xFF; //T0 重载值的高字节
unsigned char T0RL = 0x00; //T0 重载值的低字节
void PlayTwoTiger();
void main() {
unsigned int i;
EA = 1; // 使能全局中断
TMOD = 0x01; // 配置 T0 工作在模式 1
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1; // 使能 T0 中断
TR0 = 1; // 启动 T0
while (1) {
PlayTwoTiger(); // 播放乐曲 -- 两支老虎
for (i=0; i<40000; i++); // 停止一段时间
}
}
// 两支老虎乐曲播放函数
void PlayTwoTiger() {
unsigned char beat; // 当前节拍索引
unsigned char note; // 当前节拍对应的音符
unsigned int time = 0; // 当前节拍计时
unsigned int beatTime = 0; // 当前节拍总时间
unsigned int soundTime = 0; // 当前节拍需发声时间
// 两只老虎音符表
unsigned char code TwoTigerNote[] = {
1, 2, 3, 1, 1, 2, 3, 1, 3, 4, 5, 3, 4, 5,
5,6, 5,4, 3, 1, 5,6, 5,4, 3, 1, 1, 5, 1, 1, 5, 1,
};
// 两只老虎节拍表,4 表示一拍,1 就是 1/4 拍,8 就是 2 拍
unsigned char code TwoTigerBeat[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 8,
3,1, 3,1, 4, 4, 3,1, 3,1, 4, 4, 4, 4, 8, 4, 4, 8,
};
for (beat=0; beat<sizeof(TwoTigerNote); ) { // 用节拍索引作为循环变量
while (!tmrflag); // 每次定时器中断完成后,检测并处理节拍
tmrflag = 0;
if (time == 0) { // 当前节拍播完则启动一个新节拍
note = TwoTigerNote[beat] - 1;
T0RH = NoteReload[note] >> 8;
T0RL = NoteReload[note];
// 计算节拍总时间,右移 2 位相当于除 4,移位代替除法可以加快执行速度
beatTime = (TwoTigerBeat[beat] * NoteFrequ[note]) >> 2;
// 计算发声时间,为总时间的 0.75,移位原理同上
soundTime = beatTime - (beatTime >> 2);
enable = 1; // 指示蜂鸣器开始发声
time++;
} else { // 当前节拍未播完则处理当前节拍
if (time >= beatTime) { // 当前持续时间到达节拍总时间时归零,
// 并递增节拍索引,以准备启动新节拍
time = 0;
beat++;
} else { // 当前持续时间未达到总时间时,
time++; // 累加时间计数
if (time == soundTime) { // 到达发声时间后,指示关闭蜂鸣器,
// 插入 0.25* 总时间的静音间隔,
enable = 0; // 用以区分连续的两个节拍
}
}
}
}
}
// T0 中断服务函数,用于控制蜂鸣器发声
void InterruptTimer0() interrupt 1 {
TH0 = T0RH; // 重新加载重载值
TL0 = T0RL;
tmrflag = 1;
if (enable) // 使能时反转蜂鸣器控制电平
BUZZ = ~BUZZ;
else // 未使能时关闭蜂鸣器
BUZZ = 1;
}

实验 7 串口收数据 倒计数亮灯

实验目的:实验 6 拓展。

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
// 实验 7 串口收数据 倒数亮灯
// 单片机接受数字(c 串口)
// T0 用作外部中断
#include<reg51.h>
#include<stdio.h>
sbit p20=P2^0;
sbit p21=P2^1;
sbit p32=P3^2;
unsigned char led[12]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; // 共阳 0~9
int num=0;
bit flag=0;
void main() {
SCON=0X50;// 选择方式 1;允许接受数据;
TMOD=0X21;
TH1=TL1=0XF3;//f=2400~12MHz (9600~B11.0592-0xFD)

IT0=1;// 外部中断边沿触发 // 短按
ET0=1;// 定时器 0 中断允许位 //T0 定时器中断子开关
EX0=1;// 外部中断 INT0 开关

TR1=1;// 打开 T1 计时器
ES=1;// 串口中断允许
EA=1;// 中断总开关
while(1) {
p21=0;
P0=led[num];

if(num<=0 && flag==1) {
p20=0;
P0=0;
}
}
}
void IN0() interrupt 0 {
num--;
}
void serial() interrupt 4 { // 串行口中断
if(RI==1) { // 接收
RI=0;
num=SBUF;
flag=1;
TR1=0;// 关闭 T1 计时器
}
}

实验 8 电子表

实验目的:使用 4 个数码管的前两位表示小时,后两位表示分钟。使用 3 个按键对电子表进行调节,其中 p32 更换调节位,p33 加 1,p34 减 1。

难点:按键消抖;时间的进位(小时最大 24,分钟最大 60)。

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
#include<reg51.h>
#include<intrins.h>
// 实验 8 电子表
//T0 作 1s 计时
sbit p21=P2^1;
sbit p22=P2^2;
sbit p23=P2^3;
sbit p24=P2^4;
sbit p32=P3^2;// 选择键
sbit p33=P3^3;// 增加键 +1
sbit p34=P3^4;// 减小键 -1
int hour=0,minute=0;// 前小时,后分钟
int num[4]= {0}; // 前两位小时,后两位分钟
unsigned char led[12]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; // 共阳 0~9
int counter=0;
int i=0;
int t=20;
int flag_set=0;

void delay(int z) { // 延时函数
int i,j;
//for(i=z;i>0;i--)
// for(j=0;j<123;j++);
for(i=z*1000; i>0; i--) _nop_(); //1us
}

void key() {
switch(flag_set) {
case 0:// 调整 小时 十位
if(p33==0) {
delay(t);
if(p33==0) {
hour+=10;
if(hour>24) hour%=10;
}
while(p33==0);
}
if(p34==0) {
delay(t);
if(p34==0) {
hour-=10;
if(hour<0) hour=0;
}
while(p34==0);
}
break;
case 1:// 调整 小时 个位
if(p33==0) {
delay(t);
if(p33==0) {
hour++;
if(hour==24) hour=0;
}
while(p33==0);
}
if(p34==0) {
delay(t);
if(p34==0) {
hour--;
if(hour<0) hour=23;
}
while(p34==0);
}
break;
case 2:// 调整 分钟 十位
if(p33==0) {
delay(t);
if(p33==0) {
minute+=10;
if(minute>60) minute%=10;
}
while(p33==0);
}
if(p34==0) {
delay(t);
if(p34==0) {
minute-=10;
if(minute<0) minute+=60;
}
while(p34==0);
}
break;
case 3:// 调整 分钟 个位
if(p33==0) {
delay(t);
if(p33==0) {
minute++;
if(minute==60) minute=0;
}
while(p33==0);
}
if(p34==0) {
delay(t);
if(p34==0) {
minute--;
if(minute<0) minute=59;
}
while(p34==0);
}
break;
default:
break;
}
}
void main() {
TMOD=0x01;
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值

ET0=1;//T0 中断子开关
TF0=0;//T0 溢出标志置 0
TR0=1;// 打开计时器
EA=1;// 中断总开关

while(1) {
if(p32==0) {
delay(t);
if(p32==0) {
flag_set++;
if(flag_set==4) flag_set=0;
}
while(p32==0);
}

key();

num[0]=hour/10;
num[1]=hour%10;
num[2]=minute/10;
num[3]=minute%10;
switch(i)
{
case 0:P0=0xff;p21=0;p22=1;p23=1;p24=1;P0=led[qian];i++;break;
case 1:P0=0xff;p21=1;p22=0;p23=1;p24=1;P0=led[bai];i++;break;
case 2:P0=0xff;p21=1;p22=1;p23=0;p24=1;P0=led[shi];i++;break;
case 3:P0=0xff;p21=1;p22=1;p23=1;p24=0;P0=led[ge];i=0;break;
default:break;
}
}
}
void numr0() interrupt 1 {
TH0=0x3c;// 高八位时间初值
TL0=0xb0;// 低八位时间初值
counter++;
if(counter==20*60) { //1min 计时
counter=0;
minute++;
if(minute==60) minute=0,hour++;
if(hour==24) minute=0,hour=0;
}
}