必备基础技能训练15 项

【基础08】外部中断的基本操作

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,实现以下功能:

1、将CT107D 上J5 处跳帽接到2~3 引脚,使S4 和S5 按键一端接地,S4 按键的另一端接到单片机的P32/INT0 引脚,S5 按键的另一端接到单片机的P33/INT1 引脚。

2、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯。

3、控制L1 指示灯循环闪烁。

4、将P32/INT0 引脚定义成外部中断,下降沿触发,使能相关中断控制位。即按下S5按键就会产生一个外部中断触发信号,进入外部中断服务函数。

5、在外部中断服务函数中,点亮L8 指示灯,延时片刻后,熄灭。

image-20230328225146398

实现代码:

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
#include "reg51.h"

sbit HC138_A = P2^5; // 74HC138的A引脚
sbit HC138_B = P2^6; // 74HC138的B引脚
sbit HC138_C = P2^7; // 74HC138的C引脚

sbit L1 = P0^0;
sbit L2 = P0^1;
sbit L3 = P0^2;
sbit L4 = P0^3;
sbit L5 = P0^4;
sbit L6 = P0^5;
sbit L7 = P0^6;
sbit L8 = P0^7;

void Delay(unsigned int t); // 延迟函数
void HC138Init(unsigned int n); // 74HC138初始化
void InterruptInit(void); // 外部中断初始化

// 08外部中断的基本操作
void main(void)
{
HC138Init(4); // 初始化74HC138
InterruptInit(); // 初始化外部中断
while (1) {
// LED1循环闪烁
L1 = 0;
Delay(1000);
L1 = 1;
Delay(1000);
}
}

void Delay(unsigned int t) // 延迟函数
{
unsigned char i, j;
while (t > 0) {
i = 2;
do {
while (--j)
;
} while (--i);
t--;
}
}

void HC138Init(unsigned int n) // 74HC138初始化
{
P2 = 0x00; // P2口输出0
if (n == 4) {
HC138_C = 1;
HC138_B = 0; // 设置74HC138为 100 4
HC138_A = 0;
} else if (n == 5) {
HC138_C = 1; // 设置74HC138为 101 5
HC138_B = 0;
HC138_A = 1;
} else if (n == 6) {
HC138_C = 1; // 设置74HC138为 110 6
HC138_B = 1;
HC138_A = 0;
} else if (n == 7) {
HC138_C = 1; // 设置74HC138为 111 7
HC138_B = 1;
HC138_A = 1;
}
}

void InterruptInit(void) // 外部中断初始化
{
IT0 = 1; // 设置外部中断0为下降沿触发
EX0 = 1; // 开启外部中断0
EA = 1; // 开启总中断
}

void Interrupt0(void) interrupt 0 // 外部中断0服务程序
{
L8 = 0;
Delay(2000);
L8 = 1;
}

【基础09】定时器实现秒闪功能

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,实现以下功能:

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯。

2、利用定时/计数器T0 的模式1 实现50ms 的间隔定时。

3、在50ms 间隔定时的基础上,每隔1 秒L1 指示灯闪烁一次,即L1 指示灯循环点亮
0.5 秒,熄灭0.5 秒。

4、每隔10 秒L8 指示灯闪烁1 次,即L1 指示灯循环点亮5 秒,熄灭5 秒。

通过STC-ISP生成中断初始化函数

再在其中开启中断:ET0、EA、PT0

实现代码:

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

sbit HC138_A = P2^5; // 74HC138的A引脚
sbit HC138_B = P2^6; // 74HC138的B引脚
sbit HC138_C = P2^7; // 74HC138的C引脚

sbit L1 = P0^0;
sbit L8 = P0^7;

void HC138Init(unsigned int n); // 74HC138初始化
void Timer0_Init(void); // 定时器0初始化
void main()
{
HC138Init(4); // 初始化74HC138
Timer0_Init(); // 初始化定时器0
while (1) {
}
}

void HC138Init(unsigned int n) // 74HC138初始化
{
P2 = 0x00; // P2口输出0
if (n == 4) {
HC138_C = 1;
HC138_B = 0; // 设置74HC138为 100 4
HC138_A = 0;
} else if (n == 5) {
HC138_C = 1; // 设置74HC138为 101 5
HC138_B = 0;
HC138_A = 1;
} else if (n == 6) {
HC138_C = 1; // 设置74HC138为 110 6
HC138_B = 1;
HC138_A = 0;
} else if (n == 7) {
HC138_C = 1; // 设置74HC138为 111 7
HC138_B = 1;
HC138_A = 1;
}
}

void Timer0_Init(void) // 50毫秒@12.000MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 设置定时器模式
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时

ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
PT0 = 0; // 定时器0为低优先级中断
}

void Timer0_Routine() interrupt 1// 定时器0中断服务程序
{
unsigned char i;
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值

i++;
if (i % 10 == 0) // 500ms
{
L1 = ~L1;
}

if (i == 100) // 5s
{
i = 0;
L8 = ~L8;
}
}

【基础10】定时器实现秒表功能

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,利用定时器T0、数码管和2 个独立按键,设计并实现一个秒表,具有启动、暂停和清零功能。

1、秒表的显示格式:分-秒-0.05 秒(即50ms)

  • 如8 分26 秒900 毫秒,显示为:08-26-18

2、独立按键S4 定义为:启动/暂停,即第1 次按下启动秒表,再次按下暂停秒表。

3、独立按键S5 定义为:清零。

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

sbit HC138_A = P2^5; // 74HC138的A引脚
sbit HC138_B = P2^6; // 74HC138的B引脚
sbit HC138_C = P2^7; // 74HC138的C引脚

sbit Key1 = P3^0; // 按键1
sbit Key2 = P3^1; // 按键2
sbit Key3 = P3^2; // 按键3
sbit Key4 = P3^3; // 按键4

unsigned char code Duanma[18] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x80, 0xc6, 0xc0, 0x86, 0x8e, 0xbf, 0x7f};

void Delay(unsigned int t); // 延迟函数
void HC138Init(unsigned int n); // 74HC138初始化
void LED_Shu(unsigned int location, unsigned int num); // 数码管显示函数
void Display(); // LED显示
void Timer0_Init(void); // 定时器0初始化

unsigned int min = 0, sec = 0, ssec = 0; // 分,秒,毫秒
unsigned int flag1 = 0, flag2 = 0;

// 10定时器实现秒表功能
void main()
{
Timer0_Init();
while (1) {
if (Key1 == 0) {
Delay(20);while (Key1 == 0);Delay(20);// 去抖

flag1 = !flag1;// 按下一次开始,再按一次暂停
}
if (Key2 == 0 && flag2 == 0) {
Delay(20);while (Key2 == 0);Delay(20);// 去抖

flag2 = !flag2;
}
Display();
}
}

void Delay(unsigned int t) // 延迟函数
{
unsigned char i, j;
while (t > 0) {
i = 2;
do {
while (--j)
;
} while (--i);
t--;
}
}

void HC138Init(unsigned int n) // 74HC138初始化
{
if (n == 4) {
HC138_C = 1;
HC138_B = 0; // 设置74HC138为 100 4
HC138_A = 0;
} else if (n == 5) {
HC138_C = 1; // 设置74HC138为 101 5
HC138_B = 0;
HC138_A = 1;
} else if (n == 6) {
HC138_C = 1; // 设置74HC138为 110 6
HC138_B = 1;
HC138_A = 0;
} else if (n == 7) {
HC138_C = 1; // 设置74HC138为 111 7
HC138_B = 1;
HC138_A = 1;
}
}

void LED_Shu(unsigned int location, unsigned int num) // 数码管显示函数
{
HC138Init(6); // 位选
P0 = 0x01 << location;
HC138Init(7); // 段选
P0 = Duanma[num];
// 消影
Delay(1);
P0 = 0xFF;
}

void Display() // LED显示
{
LED_Shu(0, min / 10);
Delay(1);
LED_Shu(1, min % 10);
Delay(1);
LED_Shu(2, 16);

LED_Shu(3, sec / 10);
Delay(1);
LED_Shu(4, sec % 10);
Delay(1);
LED_Shu(5, 16);

LED_Shu(6, ssec / 10);
Delay(1);
LED_Shu(7, ssec % 10);
Delay(1);
}

void Timer0_Init(void) // 50毫秒@12.000MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 设置定时器模式
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时

ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
PT0 = 0; // 定时器0为低优先级中断
}

void Timer0_Routine() interrupt 1 // 定时器0中断服务程序
{
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值

if (flag1 == 1 && flag2 == 0) {
ssec++;
if (ssec == 20) {
ssec = 0;
sec++;
}
if (sec >= 60) {
sec = 0;
min++;
}
if (min >= 60) {
min = 0;
}
}
if (flag2 == 1) {
min = 0, sec = 0, ssec = 0;
}
}

【基础11】利用PWM 控制灯光亮度

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,利用PWM 脉宽信号实现独立按键S7 对L1 指示灯亮度变化的控制:

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯。

2、PWM 脉宽信号的频率为100Hz。

3、L1 指示灯有4 种亮度,分别是:完全熄灭、10%的亮度、50%的亮度和90%的亮度。

4、按下S7 按键,循环切换L1 指示灯的四种亮度模式,如下图所示:

image-20230328133736650

实现代码:

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

sbit HC138_A = P2^5; // 74HC138的A引脚
sbit HC138_B = P2^6; // 74HC138的B引脚
sbit HC138_C = P2^7; // 74HC138的C引脚

sbit Key1 = P3^0; // 按键1
sbit L1 = P0^0; // LED1

void Timer0_Init(void); // 定时器0初始化函数
void HC138Init(); // 74HC138初始化函数
void Delay(unsigned int t); // 延迟函数

unsigned int PWM_flag = 0;
unsigned int flag = 0;

void main(void)
{
HC138Init();
Timer0_Init();

while (1) {
if (Key1 == 0) {
Delay(20);while (Key1 == 0);Delay(20);

if (flag == 0) {
EA = 1; // 开总中断
PWM_flag = 10;
flag = 1;
} else if (flag == 1) {
PWM_flag = 50;
flag = 2;
} else if (flag == 2) {
PWM_flag = 90;
flag = 3;
} else if (flag == 3) {
PWM_flag = 0;
EA = 0; // 开总中断
L1 = 1;
}
}
}
}

void Delay(unsigned int t) // 延迟函数
{
unsigned char i, j;
while (t > 0) {
i = 2;
do {
while (--j)
;
} while (--i);
t--;
}
}
void Timer0_Init(void) // 100微秒@12.000MHz
{
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 设置定时器模式
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时

ET0 = 1; // 允许定时器0中断
PT0 = 0; // 定时器0为低优先级中断
}

void HC138Init() // 74HC138初始化函数
{
P2 = 0x00; // P2口输出0

HC138_A = 0;
HC138_B = 0;
HC138_C = 1;
}

void Timer0_Routine() interrupt 1 // 定时器0中断服务程序
{
unsigned char count;

TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值

count++;

if (count == PWM_flag) {
L1 = 1;
} else if (count == 100) {
L1 = 0;
count = 0;
}
}

【基础12】串行接口的基本操作

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,实现以下功能:

1、初始化串口为模式1,即8 位UART 模式,波特率9600,允许接收。

2、数据发送采用查询方式,数据接收采用中断方式。

3、系统上电初始化之后,单片机向上位机发送两个字节:0x5a 和0xa5(串口助手以十六进制HEX 发送和显示)。

4、串口每成功接收到一个字节后,在该字节基础上加1,然后通过串口发送回上位机。

5、注意89C52 单片机和IAP15F2K61S2 单片机串口应用的差别,使用9600 波特率时,晶振时钟选择11.0592MHz。

image-20230331223135943

实现代码:

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
#include "reg51.h"

sfr AUXR = 0x8e; // IAP15F2K61S新增配置
unsigned char Date;

void UART_Init(); // 串口初始化函数
void Send_Data(unsigned char dat); // 发送数据

// 12串行接口的基本操作
void main(void)
{
UART_Init();
Send_Data(0X5A);
Send_Data(0XA5);
while (1) {
}
}

void UART_Init() // 串口初始化函数
{
// STC-ISP 波特率设置模块
// 11.0592MHz 9600bps 串口1 8位数据 定时器1(8位自动重载) 12T

TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 设定定时器1为8位自动重装方式
TL1 = 0xFD; // 设定定时初值
TH1 = 0xFD; // 设定定时器重装值
ET1 = 0; // 禁止定时器1中断
TR1 = 1; // 启动定时器1
EA = 1; // 开总中断
PT1 = 0; // 定时器1为低优先级中断
ES = 1; // 串口中断允许

SCON = 0x50; // 8位数据,可变波特率

AUXR = 0x00;
}
void UART_Routine(void) interrupt 4 // 串口中断函数,接受数据
{
if (RI == 1) {
Date = SBUF;
Send_Data(Date + 1);
RI = 0;
}
}

void Send_Data(unsigned char dat) // 发送数据
{
SBUF = dat;
while (TI == 0);
TI = 0;
}

【基础13】串行接口的进阶应用

新建工程,以I/O 模式编写代码,在CT107D 单片机综合训练平台上,实现以下功能:

1、初始化串口为模式1,即8 位UART 模式,波特率9600,允许接收。

2、数据发送采用查询方式,数据接收采用中断方式。

3、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯,通过串口向上位机发送字符串:“Welcome to XMF system!”,回车换行。

4、上位机通过串口发送单字节命令,控制单片机的8 个LED 灯开关,单片机响应正确的控制命令后,完成相应的灯光操作。

5、上位机通过串口发送单字节命令,读取单片机运行信息,单片机响应正确的读取命令后,向上位机返回指定的信息。

6、上位机与单片机的通信规约如下表:

image-20230328133857154

实现代码:

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
#include "reg51.h"

sfr AUXR = 0x8e; // IAP15F2K61S新增配置

sbit HC138_A = P2 ^ 5; // 74HC138的A引脚
sbit HC138_B = P2 ^ 6; // 74HC138的B引脚
sbit HC138_C = P2 ^ 7; // 74HC138的C引脚

void UART_Init(void); // 初始化串口
void UART_SentByte(unsigned char dat); // 串口发送一个字节
void UART_SentDate(unsigned char *str); // 串口发送字符串
void HC138_Init(void); // 初始化74HC138

unsigned char Data = 0x00; // 接收到的数据

// 13串行接口的进阶应用
void main(void)
{
UART_Init(); // 初始化串口
HC138_Init(); // 初始化74HC138
UART_SentDate("Welcome to XMF system!\r\n");
while (1) {
if (Data != 0x00) {
switch (Data & 0xf0) {
// 模式判断
case 0xa0: // 控制L1~L4灯
P0 = 0xff;
P0 = P0 & (~Data | 0xf0);
Data = 0x00;
break;
case 0xb0: // 控制L5~L8灯
P0 = 0xff;
P0 = P0 & ((~Data << 4) | 0x0f);
Data = 0x00;
break;
case 0xc0: // 返回状态
UART_SentDate("The System is Running...\r\n");
Data = 0x00;
break;
}
}
}
}

void UART_Init(void) // 初始化串口
{
PCON &= 0x7F; // 波特率不倍速
SCON = 0x50; // 8位数据,可变波特率
TMOD &= 0x0F; // 清除定时器1模式位
TMOD |= 0x20; // 设定定时器1为8位自动重装方式

TL1 = 0xFD; // 设定定时初值
TH1 = 0xFD; // 设定定时器重装值
ET1 = 0; // 禁止定时器1中断
TR1 = 1; // 启动定时器1
EA = 1; // 使能总中断
PT1 = 0; // 定时器1为低优先级中断
ES = 1; // 使能串口中断

AUXR = 0x00;
}
void UART_Routine(void) interrupt 4 // 串口中断函数,接受数据
{
if (RI == 1) {
Data = SBUF; // 读取数据
RI = 0;
}
}
void UART_SentByte(unsigned char dat) // 串口发送一个字节
{
SBUF = dat;
while (!TI);
TI = 0;
}
void UART_SentDate(unsigned char *str)
{
while (*str != '\0')
UART_SentByte(*str++);
}
void HC138_Init(void)
{
HC138_C = 1; // 设置74HC138为 100 4
HC138_B = 0;
HC138_A = 0;
}


【基础14】存储器映射扩展技术应用

新建工程,以MM 模式编写代码,在CT107D 单片机综合训练平台上,实现以下功能:

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯。

2、循环实现以下功能:

  • 首先,点亮指示灯低4 位,关闭高4 位,延时片刻;
  • 接着,点亮指示灯的高4 位,关闭低4 位,延时片刻;
  • 然后,关闭所有指示灯。
  • 接着,依次逐个点亮数码管的所有段码,每次只点亮一个数码管。

3、外部扩展资源的地址映射关系:

  • LED 指示灯---- 0x8000;
  • 蜂鸣器与继电器----0xa000;
  • 数码管位选----0xc000;
  • 数码管段码----0xe000

4、关于CT107D 存储器映射扩展MM 编程模式的设计提示:CT107D 平台的J13 要将1-2 脚短接,选择MM 模式。
程序中引入头文件“absacc.h”,通过XBYTE 关键字来直接操作扩展资源。

存储器映射扩展方式要占用单片机的P3.6 引脚。

IO扩展:

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
#include "reg51.h"

void Delay(unsigned int t); // 延迟函数
void Select_HC573(unsigned int i); // 选择HC573

// 14存储器映射扩展技术应用(IO扩展)
void main(void)
{
unsigned int i;
while (1) {
Select_HC573(4);
P0 = 0xf0;
Delay(1000);
P0 = 0x0f;
Delay(1000);
P0 = 0xff;

for (i = 0; i < 8; i++) {
Select_HC573(6);
P0 = 0x01 << i;
Select_HC573(7);
P0 = 0x00;
Delay(1000);
}
P0 = 0xff;
Delay(1000);
}
}

void Delay(unsigned int t) // 延迟函数
{
unsigned char i, j;
while (t > 0) {
i = 2;
do {
while (--j)
;
} while (--i);
t--;
}
}

void Select_HC573(unsigned int i) // 选择HC573
{
switch (i) {
case 4:
P2 = (P2 & 0X1f) | 0x80;
break;
case 5:
P2 = (P2 & 0X1f) | 0xa0;
break;
case 6:
P2 = (P2 & 0X1f) | 0xc0;
break;
case 7:
P2 = (P2 & 0X1f) | 0xe0;
break;
}
}


存储器扩展:

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
#include "reg51.h"
#include "absacc.h"

void Delay(unsigned int t); // 延迟函数

voidmain(void)
{
unsigned int i;
while (1) {
XBYTE[0x8000] = 0xf0;
XBYTE[0x8000] = 0xf0;
Delay(1000);
XBYTE[0x8000] = 0x0f;
Delay(1000);
XBYTE[0x8000] = 0xff;

for (i = 0; i < 8; i++) {
XBYTE[0xc000] = 0x01 << i;
XBYTE[0xe000] = 0x00;
Delay(1000);
}
XBYTE[0xe000] = 0xff;
Delay(1000);
}
}

void Delay(unsigned int t) // 延迟函数
{
unsigned char i, j;
while (t > 0) {
i = 2;
do {
while (--j)
;
} while (--i);
t--;
}
}

【基础15】工厂灯光控制系统

新建工程,分别以I/O 模式和MM 模式编写代码,在CT107D 单片机综合训练平台上,实现灯光的本地操作和远程控制,并通过串口远程读取系统的运行时间,模拟实现工厂灯光控制系统。具体功能要求如下:

1、系统上电后,关闭蜂鸣器,关闭继电器,关闭8 个LED 灯。

2、首先检测LED 指示灯,从L1 到L8 依次逐个点亮,再依次逐个熄灭;然后检测数码管,从左到右依次点亮数码管的所有段码,再依次从左到右熄灭。

3、系统从上电开始显示系统运行时间,从00 时00 分00 秒开始,显示格式:

image-20230328134034643

4、8 个LED 指示灯分为2 组:L1、L2、L3 和L4 为远程控制组,L7 和L8 为本地控制组。远程控制组的指LED 示灯由上位机通过串口发送命令控制开关,本地控制组的LED 指示灯由独立按键控制开关。按键松开有效,S5 按键控制L7 指示灯,S4 按键控制L8 指示灯。

5、串口工作在模式1,即8 位UART 模式,波特率为9600(使用9600 波特率时,晶振时钟选择11.0592MHz。如果是其他频率的系统时钟,需要降低波特率,可选择用2400,否则串口通信可能出现乱码,无法正常收发数据)。

6、上位机通过串口控制下位机的L1 到L4 指示灯和读取系统运行时间。

7、上位机和单片机的串口通信规约如下表:

image-20230328134045274

控制命令为一个字节,高4 位为命令类型,低4 位为执行参数。控制灯光开关命令中,低4 位每1 位控制一个LED 灯的开关,无返回值。读取运行时间命令中,低4 位保留,各位为0,返回3 个字节的时间数据,用16 进制的BCD 码表示,先发时,再发分,后发秒。如果系统运行的时间为12 时24 分16 秒,收到读取时间命令字后,返回:0x12 0x24 0x16。

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

sfr AUXR = 0x8e; // IAP15F2K61S新增配置

unsigned char code Duanma[18] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x80, 0xc6, 0xc0, 0x86, 0x8e, 0xbf, 0x7f};

sbit Key1 = P3^0; // 按键1
sbit Key2 = P3^1; // 按键2

sbit L7 = P0^6; // LED1
sbit L8 = P0^7; // LED1

void Delay(unsigned int t); // 延迟函数
void Select_HC138(unsigned int n); // 初始化74HC138
void LED_Shu(unsigned int location, unsigned int num); // 数码管显示函数
void Show_Time(void);//数码管显示时间
void Time0_Init(void);//定时器初始化
void UART_Init(void);//初始化串口
void check();//设备判断
void Check_Key(void);//判断按键
void UART_SendByte(unsigned char dat);//发送字节
void Check_Data(void);//判断串口数据

unsigned char hour=0,min=0,sec=0;
unsigned int flag = 0;
unsigned char Data = 0x00; // 接收到的数据


void main(){
Time0_Init();
UART_Init();
while(1){
if(flag == 0)//第一次运行,设备检验
{
check();
flag = 1;
}
Show_Time();//数码管显示时间
Check_Key();//判断按键
Check_Data();//判断串口数据
}
}

void Delay(unsigned int t) //延迟函数
{

unsigned char i, j;
while(t){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
t--;
}
}

void Select_HC138(unsigned int n)
{

switch(n){
case 4:
P2 = (P2 & 0x1f) | 0x80; //Y4输出0,选择LED控制
break;
case 5:
P2 = (P2 & 0x1f) | 0xa0; //Y5输出0,选择蜂鸣器和继电器控制
break;
case 6:
P2 = (P2 & 0x1f) | 0xc0; //Y6输出0,选择数码管位选
break;
case 7:
P2 = (P2 & 0x1f) | 0xe0; //Y7输出0,选择数码管段码
break;
case 0:
P2 = (P2 & 0x1f) | 0x00; //所有锁存器不选择
break;
}
}

void LED_Shu(unsigned int location, unsigned int num)
{
Select_HC138(6);
P0 = 0x01 << location;
Select_HC138(7);
P0 = Duanma[num];

//消影
Delay(1);
P0 = 0xFF;
}

void Show_Time()//数码管显示时间
{
LED_Shu(0, hour / 10);
Delay(1);
LED_Shu(1, hour % 10);
Delay(1);
LED_Shu(2, 16);

LED_Shu(3, min / 10);
Delay(1);
LED_Shu(4, min % 10);
Delay(1);
LED_Shu(5, 16);

LED_Shu(6, sec / 10);
Delay(1);
LED_Shu(7, sec % 10);
Delay(1);
}

void Time0_Init(void)//50毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时

ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
PT0 = 0; // 定时器0为低优先级中断
}

void check()//设备判断
{
unsigned int i;
Select_HC138(4);
for(i = 0; i < 8; i++)
{
P0 = 0xff << i;
Delay(500);
}

for(i = 0; i <= 8; i++)
{
P0 = ~(0xff << i);
Delay(500);
}

for(i = 0; i<8; i++)
{
Select_HC138(6);
P0 = 0x01 << i;
Select_HC138(7);
P0 = 0x00;
Delay(500);
}
for(i = 0; i<8; i++)
{
Select_HC138(6);
P0 = ~(0x01 << i);
Select_HC138(7);
P0 = 0xFF;
Delay(500);
}
}

void Check_Key(void)
{
if(Key1 == 0)
{
Show_Time();
while(Key1 == 0)
Show_Time();
Select_HC138(4);
L8 = ~L8;
}
if(Key2 == 0)
{
Show_Time();
while(Key2 == 0)
Show_Time();
Select_HC138(4);
L7 = ~L7;
}
}

void UART_Init(void) //[email protected]
{
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1

ES = 1;
AUXR = 0x00;
}

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

void Check_Data(void)
{
if(Data != 0x00)
{
switch(Data & 0xf0){
case 0xa0: //远程灯光控制命令
Select_HC138(4);
P0 = 0xff;
P0 = P0 & (~Data | 0xf0);
Select_HC138(0);
Data = 0x00;
break;

case 0xb0: //读取现场系统运行时间命令
UART_SendByte((hour / 10 << 4) | (hour % 10));
UART_SendByte((min / 10 << 4) | (min % 10));
UART_SendByte((sec / 10 << 4) | (sec % 10));
Data = 0x00;
break;
}
}
}




void Time0_Run(void) interrupt 1
{
unsigned char i;
TL0 = 0xB0; // 设置定时初始值
TH0 = 0x3C; // 设置定时初始值

i++;
if(i == 20)//1s
{
sec++;
i = 0;
if(sec == 60){
min++;
sec = 0;
if(min == 60){
hour++;
min = 0;
if(hour == 24)
hour = 0;
}
}
}
}

void UART_Run(void) interrupt 4
{
if (RI == 1) {
Data = SBUF;
RI = 0;
}
}