必备基础技能训练 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

实现代码:

c
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

实现代码:

c
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、按键均为按下有效。

注意一下临界条件判断

实现代码:

c
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

实现代码:

c
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

实现代码:

c
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

实现代码:

c
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 扩展:

c
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;
}
}


存储器扩展:

c
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。

c
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;
}
}