内容来自 《 从单片机基础到程序框架(2019版) 》 第88章至第91章

【88.1 四区一线】

提出“四区一线”理论,主要方便初学者理解单片机程序大概的“空间分区”。

“四区”代表四大主流函数,分别是:系统初始化函数,外设初始化函数,主程序的任务函数,定时中断函数。

“一线”是指“系统初始化函数”与“外设初始化函数”的“分割线”,这个“分割线”是一个delay的延时函数。

“四区一线”的布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main()
{
SystemInitial(); //“四区一线”的“第一区”
Delay(10000); //“四区一线”的“一线”
PeripheralInitial(); //“四区一线”的“第二区”

while(1) //主循环
{
LedTask(); //“四区一线”的“第三区”
KeyTask(); //“四区一线”的“第三区”
UsartTask(); //“四区一线”的“第三区”
... //凡是在主循环里的函数都是属于“第三区”
}
}

void T0_time() interrupt 1 //“四区一线”的“第四区”
{
}

“第一区”的函数SystemInitial(),是一个系统的初始化函数,专门用来初始化单片机自己的寄存器以及个别外围要求响应速度快的输出设备,防止刚上电之后,由于输出IO口电平状态不确定而导致外围设备误动作,比如驱动继电器的误动作等等。

“一线”的函数Delay(10000),是一个延时函数,为什么这里要插入一个延时函数?主要目的是为接下来的PeripheralInitial()做准备的。上电后先延时一段时间,再执行PeripheralInitial()函数,因为PeripheralInitial()函数专门用来初始化不要求上电立即处理的外设芯片和模块。比如液晶模块,AT24C02存储芯片,DS1302时钟芯片,等等。这些芯片在上电的瞬间,内部自身的复位需要一点时间,以及外部电压稳定也需要一点时间,只有过了这一点时间,这些芯片才处于正常的工作状态,这个时候单片机才能跟它正常通信,所以“一线”函数Delay(10000)的意义就在这里。

“第二区”的函数PeripheralInitial(),是一个外设的初始化函数。专门用来初始化不要求上电立即处理的外设芯片和模块。

“第三区”的函数LedTask()KeyTask()UsartTask(),等等,是一些在主循环里不断扫描的任务函数。

“第四区”的函数void T0_time() interrupt 1,是一个定时中断函数,一个系统必须标配一个定时中断函数才算完美齐全,这个中断函数提供系统的节拍时间,以及处理扫描一些跟IO口消抖动相关的函数,以及跟蜂鸣器驱动相关的函数。


【88.2 switch外加定时中断】

提出“switch外加定时中断”理论,主要方便初学者理解单片机程序大概的“逻辑框架”。

switch是一个万能语句,它外加while与for循环就可以做任何复杂的算法,比如,搜索算法,运动算法,提取关键词算法,等等。它外加定时中断,就可以搭建一个系统的基本框架。比如,做通信的程序框架,人机界面的程序框架,按键服务的程序框架,等等。switch的精髓在于“根据条件进行步骤的灵活切换”。具体内容请看本节的练习程序。


【88.3 练习例程】

根据上述的两大核心框架理论,编写1个LED灯闪烁的程序。


图88.3.1 灌入式驱动8个LED

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

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void LedTask(void);

#define BLINK_TIME_1 1000

sbit P0_0=P0^0;

volatile unsigned char vGu8TimeFlag_1=0;
volatile unsigned int vGu16TimeCnt_1=0;

void main()
{
SystemInitial(); //“四区一线”的“第一区”
Delay(10000); //“四区一线”的“一线”
PeripheralInitial(); //“四区一线”的“第二区”

while(1) { //主循环
LedTask(); //“四区一线”的“第三区”
}
}

void T0_time() interrupt 1 {//“四区一线”的“第四区”
if(1==vGu8TimeFlag_1&&vGu16TimeCnt_1>0) {
vGu16TimeCnt_1--;
}
TH0=0xfc;
TL0=0x66;
}

void SystemInitial(void) {
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
}

void Delay(unsigned long u32DelayTime) {
for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void) {
}

void LedTask(void) {
static unsigned char Su8Step=0; //加static修饰的局部变量,每次进来都会保留上一次值。

switch(Su8Step) {
case 0:
if(0==vGu16TimeCnt_1) { //时间到
P0_0=0;
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1; //重装定时的时间
vGu8TimeFlag_1=1;
Su8Step=1; //切换到下一个步骤,精髓语句!
}
break;

case 1:
if(0==vGu16TimeCnt_1) { //时间到
P0_0=1;
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1; //重装定时的时间
vGu8TimeFlag_1=1;
Su8Step=0; //返回到上一个步骤,精髓语句!
}
break;
}
}

【89.1 跑马灯的三种境界。】

跑马灯也称为流水灯,排列的几个LED依次循环的点亮和熄灭,给人“跑动起来”的感觉,故称为“跑马灯”。实现跑马灯的效果,编程上有三种思路,分别代表了跑马灯的三种境界,分别是:移位阻塞,移位非阻塞,状态切换非阻塞。

图89.1.1 灌入式驱动8个LED

本节用的是8个LED灯依次挨个熄灭点亮,如上图所示。

【89.2 移位阻塞】

移位阻塞,“移位”用的是C语言的左移或者右移语句,“阻塞”用的是delay延时。代码如下:

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

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void LedTask(void);

void main() {
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1) {
LedTask();
}
}

void T0_time() interrupt 1 {
TH0=0xfc;
TL0=0x66;
}

void SystemInitial(void) {
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
}

void Delay(unsigned long u32DelayTime) {
for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void) {
}

//跑马灯的任务程序
void LedTask(void) {
static unsigned char Su8Data=0x01; //加static修饰的局部变量,每次进来都会保留上一次值。
static unsigned char Su8Cnt=0; //加static修饰的局部变量,每次进来都会保留上一次值。

P0=Su8Data; //Su8Data的8个位代表8个LED的状态,0为点亮,1为熄灭。
Delay(10000) ; //阻塞延时

Su8Data=Su8Data<<1; //左移一位
Su8Cnt++; //计数器累加1

if(Su8Cnt>=8) { //移位大于等于8次后,重新赋初值
Su8Cnt=0;
Su8Data=0x01; //重新赋初值,继续下一次循环移动
}
}

分析总结: 这是第1种境界的跑马灯,这种思路虽然实现了跑马灯的效果,但是因为“阻塞延时”,整个程序显得僵硬机械,缺乏多任务并行的框架。

【89.3 移位非阻塞】

移位非阻塞,“移位”用的是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
#include "REG52.H"

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void LedTask(void);

#define BLINK_TIME_1 1000

volatile unsigned char vGu8TimeFlag_1=0;
volatile unsigned int vGu16TimeCnt_1=0;

void main() {
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1) {
LedTask();
}
}

void T0_time() interrupt 1 {
if(1==vGu8TimeFlag_1 && vGu16TimeCnt_1>0) {//软件定时器
vGu16TimeCnt_1--;
}
TH0=0xfc;
TL0=0x66;
}

void SystemInitial(void) {
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
}

void Delay(unsigned long u32DelayTime) {
for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void) {
}

//跑马灯的任务程序
void LedTask(void) {
static unsigned char Su8Data=0x01; //加static修饰的局部变量,每次进来都会保留上一次值。
static unsigned char Su8Cnt=0; //加static修饰的局部变量,每次进来都会保留上一次值。

if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1; //重装定时的时间
vGu8TimeFlag_1=1;

P0=Su8Data; //Su8Data的8个位代表8个LED的状态,0为点亮,1为熄灭。
Su8Data=Su8Data<<1; //左移一位
Su8Cnt++; //计数器累加1

if(Su8Cnt>=8) { //移位大于等于8次后,重新赋初值
Su8Cnt=0;
Su8Data=0x01; //重新赋初值,继续下一次循环移动
}
}
}

分析总结: 这是第2种境界的跑马灯,这种思路虽然实现了跑马灯的效果,也用到了多任务并行处理的基本元素“软件定时器”,但是因为还停留在“移位”语句的阶段,此时的程序并没有超越跑马灯本身,跑马灯还是跑马灯,处于“看山还是山”的境界。

【89.4 状态切换非阻塞】

状态切换非阻塞,“状态切换”用的是switch语句中根据特定条件进行步骤切换,“非阻塞”用的是定时中断衍生出来的软件定时器。代码如下:

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

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void LedTask(void);

#define BLINK_TIME_1 1000

sbit P0_0=P0^0;
sbit P0_1=P0^1;
sbit P0_2=P0^2;
sbit P0_3=P0^3;
sbit P0_4=P0^4;
sbit P0_5=P0^5;
sbit P0_6=P0^6;
sbit P0_7=P0^7;

volatile unsigned char vGu8TimeFlag_1=0;
volatile unsigned int vGu16TimeCnt_1=0;

void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
LedTask();
}
}

void T0_time() interrupt 1 {
if(1==vGu8TimeFlag_1 && vGu16TimeCnt_1>0) { //软件定时器
vGu16TimeCnt_1--;
}
TH0=0xfc;
TL0=0x66;
}

void SystemInitial(void) {
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
}

void Delay(unsigned long u32DelayTime) {
for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void) {
}

//跑马灯的任务程序
void LedTask(void) {
static unsigned char Su8Step=0; //加static修饰的局部变量,每次进来都会保留上一次值。

switch(Su8Step) {
case 0:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1; //重装定时的时间
vGu8TimeFlag_1=1;
P0_0=1; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=1; //切换到下一个步骤,精髓语句!
}
break;
case 1:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=1; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=2; //切换到下一个步骤,精髓语句!
}
break;
case 2:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=1; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=3; //切换到下一个步骤,精髓语句!
}
break;
case 3:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=1; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=4; //切换到下一个步骤,精髓语句!
}
break;
case 4:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=1; P0_5=0; P0_6=0; P0_7=0;
Su8Step=5; //切换到下一个步骤,精髓语句!
}
break;
case 5:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=1; P0_6=0; P0_7=0;
Su8Step=6; //切换到下一个步骤,精髓语句!
}
break;
case 6:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=1; P0_7=0;
Su8Step=7; //切换到下一个步骤,精髓语句!
}
break;
case 7:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0;
vGu16TimeCnt_1=BLINK_TIME_1;
vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=1;
Su8Step=0; //返回到第0个步骤重新开始往下走,精髓语句!
}
break;
}
}

分析总结: 这是第3种境界的跑马灯,很多初学者咋看此程序,表示不理解,人家一条赋值语句就解决8个LED一次性显示的问题,你非要拆分成8条按位赋值的语句,人家只用一个判断就实现了LED灯移动显示的功能,你非要整出8个步骤的切换,况且,整个程序的代码量明显增加了很多,这个程序好在哪?其实,我这么做是用心良苦呀。这个程序的代码量虽然增多了,但是仔细一看,并没有影响运行的效率。之所以把8个LED灯拆分成一个一个的LED灯单独赋值显示,是因为,在我眼里,这个8个LED灯代表的不仅仅是LED灯,而是8个输出信号!这8个输出信号未来驱动的可能是不同的继电器,气缸,电机,大炮,导弹,以及它们的各种千变万化的组合逻辑,拆分之后程序框架就有了无限可能的扩展性。之所以整出8个步骤的切换,也是同样的道理,为了增加程序框架无限可能的扩展性。这个程序虽然表面看起来繁琐,但是仔细一看它是“多而不乱”,非常富有“队形感”。因此可以这么说,这个看似繁琐的跑马灯程序,其实背后蕴藏了编程界的大智慧,它已经突破了“看山还是山”的境界。

【90.1 多任务并行处理】

两路速度不同的跑马灯,代表了两路独立运行的任务,单片机如何“并行”处理这两路任务,就涉及到“多任务并行处理的编程思路”。

上图90.1.1 灌入式驱动8个LED 第1路跑马灯

上图90.1.2 灌入式驱动4个LED 新增加的第2路跑马灯

如上图,本节特别值得一提的是,新增加的第2路跑马灯用的是4个LED,这4个LED的驱动IO口是“散装的”,因为,前面3个是P1口的(P1.4,P1.5,P1.6),最后1个是P3口的(P3.3),这种情况下,肯定用不了“移位”的处理思路,只能用跑马灯第3种境界里所介绍的“状态切换非阻塞”思路,可见,“IO口拆分”和“switch状态切换”又一次充分体现了它们“程序框架万能扩展”的优越性。代码如下:

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

void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void Led_1_Task(void);
void Led_2_Task(void);

//控制第1路跑马灯的速度,数值越大“跑动”越慢。
#define BLINK_TIME_1 1000
//控制第2路跑马灯的速度,数值越大“跑动”越慢。
#define BLINK_TIME_2 200

sbit P0_0=P0^0;
sbit P0_1=P0^1;
sbit P0_2=P0^2;
sbit P0_3=P0^3;
sbit P0_4=P0^4;
sbit P0_5=P0^5;
sbit P0_6=P0^6;
sbit P0_7=P0^7;

sbit P1_4=P1^4;
sbit P1_5=P1^5;
sbit P1_6=P1^6;
sbit P3_3=P3^3;

volatile unsigned char vGu8TimeFlag_1=0;
volatile unsigned int vGu16TimeCnt_1=0;
volatile unsigned char vGu8TimeFlag_2=0;
volatile unsigned int vGu16TimeCnt_2=0;

void main() {
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1) {
Led_1_Task(); //第1路跑马灯
Led_2_Task(); //第2路跑马灯
}
}

void T0_time() interrupt 1 {
if(1==vGu8TimeFlag_1 && vGu16TimeCnt_1>0) { //软件定时器1
vGu16TimeCnt_1--;
}
if(1==vGu8TimeFlag_2 && vGu16TimeCnt_2>0) { //软件定时器2
vGu16TimeCnt_2--;
}
TH0=0xfc;
TL0=0x66;
}

void SystemInitial(void) {
TMOD=0x01;
TH0=0xfc;
TL0=0x66;
EA=1;
ET0=1;
TR0=1;
}

void Delay(unsigned long u32DelayTime) {
for(;u32DelayTime>0;u32DelayTime--);
}

void PeripheralInitial(void) {
}

//第1路跑马灯
void Led_1_Task(void) {
static unsigned char Su8Step=0; //加static修饰的局部变量,每次进来都会保留上一次值。
switch(Su8Step) {
case 0:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=1; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=1; //切换到下一个步骤,精髓语句!
}
break;
case 1:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=1; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=2; //切换到下一个步骤,精髓语句!
}
break;
case 2:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=1; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=3; //切换到下一个步骤,精髓语句!
}
break;
case 3:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=1; P0_4=0; P0_5=0; P0_6=0; P0_7=0;
Su8Step=4; //切换到下一个步骤,精髓语句!
}
break;
case 4:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=1; P0_5=0; P0_6=0; P0_7=0;
Su8Step=5; //切换到下一个步骤,精髓语句!
}
break;
case 5:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=1; P0_6=0; P0_7=0;
Su8Step=6; //切换到下一个步骤,精髓语句!
}
break;
case 6:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=1; P0_7=0;
Su8Step=7; //切换到下一个步骤,精髓语句!
}
break;
case 7:
if(0==vGu16TimeCnt_1) { //时间到
vGu8TimeFlag_1=0; vGu16TimeCnt_1=BLINK_TIME_1; vGu8TimeFlag_1=1;
P0_0=0; P0_1=0; P0_2=0; P0_3=0; P0_4=0; P0_5=0; P0_6=0; P0_7=1;
Su8Step=0; //返回到第0个步骤重新开始往下走,精髓语句!
}
break;
}
}

//第2路跑马灯
void Led_2_Task(void) {
/*
疑点讲解(1):
这里第2路跑马灯的“Su8Step”与第1路跑马灯的“Su8Step”虽然同名,
但是,因为它们是静态的局部变量,在两个不同的函数内部,是两个不同的变量,
这两个变量所分配的RAM内存地址是不一样的,因此,它们虽然同名,但是不矛盾不冲突。
*/
static unsigned char Su8Step=0; //加static修饰的局部变量,每次进来都会保留上一次值。

switch(Su8Step) {
case 0:
if(0==vGu16TimeCnt_2) { //时间到
vGu8TimeFlag_2=0;
vGu16TimeCnt_2=BLINK_TIME_2; //重装定时的时间
vGu8TimeFlag_2=1;
P1_4=1; P1_5=0; P1_6=0; P3_3=0;
Su8Step=1; //切换到下一个步骤,精髓语句!
}
break;
case 1:
if(0==vGu16TimeCnt_2) { //时间到
vGu8TimeFlag_2=0;
vGu16TimeCnt_2=BLINK_TIME_2;
vGu8TimeFlag_2=1;
P1_4=0; P1_5=1; P1_6=0; P3_3=0;
Su8Step=2; //切换到下一个步骤,精髓语句!
}
break;
case 2:
if(0==vGu16TimeCnt_2) { //时间到
vGu8TimeFlag_2=0;
vGu16TimeCnt_2=BLINK_TIME_2;
vGu8TimeFlag_2=1;
P1_4=0; P1_5=0; P1_6=1; P3_3=0;
Su8Step=3; //切换到下一个步骤,精髓语句!
}
break;
case 3:
if(0==vGu16TimeCnt_2) { //时间到
vGu8TimeFlag_2=0;
vGu16TimeCnt_2=BLINK_TIME_2;
vGu8TimeFlag_2=1;
P1_4=0; P1_5=0; P1_6=0; P3_3=1;
Su8Step=0; //返回到第0个步骤重新开始往下走,精髓语句!
}
break;
}
}