初识51单片机

我使用的是开拓者提供的51单片机,资料打包如下:

开拓者V2光盘资料 提取码: 85nk

STC89C51RC/RD+系列单片机简介

STC89C51RC/RD+系列单片机(包括STC89C52RC)是宏晶科技推出的新一代超强抗干扰、高速、低功耗的单片机,与传统8051系列单片机的指令和引脚完全兼容。12时钟/机器周期和6时钟/机器周期可任意选择。

命名规则

2.png

主要特性

3.png

引脚封装

4.png

产品选型表

5.png

开发板实物图

6.jpg

7.jpg

LED灯相关

LED简介

LED介绍

LED即发光二极管,它是一种能将电能转化为光能的半导体电子元件。
普通小功率LED电压范围:1.8-2.4V,电流为:20mA

如何点亮LED

LED具有单向导通性,只能往一个方向导通。正极→负极

LED模块电路

8.png

编写程序

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
/**************************************************************************************
* LED测试实验 *
实现现象:依程序设定,使LED灯正常工作
注意事项:无
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit led1=P2^0; //将单片机的P2.0端口定义为led1
sbit led2=P2^1; //将单片机的P2.1端口定义为led2
sbit led3=P2^2; //将单片机的P2.2端口定义为led3
sbit led4=P2^3; //将单片机的P2.3端口定义为led4
sbit led5=P2^4; //将单片机的P2.4端口定义为led5
sbit led6=P2^5; //将单片机的P2.5端口定义为led6
sbit led7=P2^6; //将单片机的P2.6端口定义为led7
sbit led8=P2^7; //将单片机的P2.7端口定义为led8
/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
led1=0,led4=0,led7=0,led8=0; //点亮红灯
delay(50000); //大约延时450ms
led1=1,led4=1,led7=1,led8=1; //熄灭红灯
delay(50000); //大约延时450ms

led2=0,led5=0; //点亮黄灯
delay(50000); //大约延时450ms
led2=1,led5=1; //熄灭黄灯
delay(50000); //大约延时450ms

led3=0,led6=0; //点亮绿灯
delay(50000); //大约延时450ms
led3=1,led6=1; //熄灭绿灯
delay(50000); //大约延时450ms

led1=0,led2=0,led3=0;//点亮红黄绿
delay(50000); //大约延时450ms
led1=1,led2=1,led3=1;//点亮红黄绿
delay(50000); //大约延时450ms

led4=0,led5=0,led6=0;//点亮红黄绿
delay(50000); //大约延时450ms
led4=1,led5=1,led6=1;//点亮红黄绿
delay(50000); //大约延时450ms
}
}


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
/**************************************************************************************
* LED流水灯实验 *
实现现象:下载程序后LED呈现流水灯效果进行左移右移滚动
注意事项:无
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h> //因为要用到左右移函数,所以加入这个头文件

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

#define LED P2 //将P2口定义为LED 后面就可以使用LED代替P2口

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 i;
LED=0xfe;
delay(50000); //大约延时450ms
while(1)
{
#if 1 //条件编译 条件不满足不编译//貌似这种方法无法向右移位,所以一般采用使用移位函数的方法进行移位
LED=0xfe;
for(i=0;i<8;i++)
{
LED=~(~0xfe<<i); //将0右移i位,然后将结果赋值到P2口// LED=~(~0x7f>>i);这个达不到与前面一样流水灯的效果
//总结:左移时总是移位和补零。右移时无符号数是移位和补零,此时称为逻辑右移;
//而有符号数大多数情况下是移位和补最左边的位(也就是补最高有效位),移几位就补几位,此时称为算术右移。
delay(50000); //大约延时450ms

}
#endif
//如果想要流水从第八个灯开始的话,只能使用移位函数,不然会出现连亮的现象
// LED=~(~0x7f<<i); 01111111
// 10000000 取反
// 1000000 移位
// 11000000 补最左边数的位
// 00111111 再取反
#if 0 //条件满足进行编译
for(i=0;i<7;i++) //将led左移一位 11111110
{
LED=_crol_(LED,1);//_cror_
delay(50000); //大约延时450ms
}
for(i=0;i<7;i++) //将led右移一位
{
LED=_cror_(LED,1);
delay(50000); //大约延时450ms
}
#endif
}
}

蜂鸣器报警

蜂鸣器简介

蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
(1)压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。多谐振荡器由晶体管或集成电路构成,当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
(2)电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场,振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。

如何调节蜂鸣器发声音色及声音大小?
(1)改变单片机引脚输出波形的频率,就可以调整控制蜂鸣器音调,产生各种不同音色、音调的声音。
(2)改变输出的高低电平占空比,则可以控制蜂鸣器的声音大小。

蜂鸣器模块电路

9.png

编写程序

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 "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h> //因为要用到左右移函数,所以加入这个头文件

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit beep=P1^5;

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
beep=~beep;
delay(50); //通过修改此延时时间达到不同的发声效果
}
}

数码管相关–74HC245

数码管简介

数码管是一种半导体发光器件,其基本单元是发光二极管。数码管也称为LED数码管,是由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需引出它们的各个笔划,公共电极。

LED数码管根据LED的不同接法可以分为2类:共阴数码管和共阳数码管。

如下图(b),左为共阴数码管,右为共阳数码管。

10.png

共阴数码管码表

0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d,

0 1 2 3 4 5

0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c,

6 7 8 9 A B

0x39, 0x5e, 0x79, 0x71, 0x00,

C D E F 无显示

共阳数码管码表

0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92,

0 1 2 3 4 5

0x82, 0xF8, 0x80, 0x90, 0x88, 0x83,

6 7 8 9 A B

0xC6, 0xA1, 0x86, 0x8E, 0xFF,

C D E F 无显示

数码管模块电路

11.png

程序编写

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
/**************************************************************************************
* 数码管静态显示实验 *
实现现象:下载程序后数码管静态显示我的生日。
注意事项:将LED点阵模块的J1短接片短接到VCC端
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P3^0; //38译码器控制引脚
sbit LSB=P3^1;
sbit LSC=P3^3;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值


void delay(u16 i)
{
while(i--); //
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=0;
LSB=0;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[2];
delay(50000);

LSA=1;
LSB=0;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=0;
LSB=1;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=1;
LSB=1;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[1];
delay(50000);

LSA=0;
LSB=0;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=1;
LSB=0;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[7];
delay(50000);

LSA=0;
LSB=1;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[2];
delay(50000);

LSA=1;
LSB=1;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[5];
delay(50000);


LSA=0;
LSB=0;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[2];
delay(50000);

LSA=1;
LSB=0;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=0;
LSB=1;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=1;
LSB=1;
LSC=1; //控制38译码器的Y0输出低电平
P0=smgduan[1];
delay(50000);

LSA=0;
LSB=0;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[0];
delay(50000);

LSA=1;
LSB=0;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[7];
delay(50000);

LSA=0;
LSB=1;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[2];
delay(50000);

LSA=1;
LSB=1;
LSC=0; //控制38译码器的Y0输出低电平
P0=smgduan[5];
delay(50000);
while(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
/**************************************************************************************
* 数码管动态显示实验 *
实现现象:下载程序后数码管从左至右显示0-7。
注意事项:将LED点阵模块的J1短接片短接到VCC端
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P3^0;
sbit LSB=P3^1;
sbit LSC=P3^3;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : DigDisplay
* 函数功能 : 数码管动态扫描函数,循环扫描8个数码管显示
*******************************************************************************/
void DigDisplay()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
case(4):
LSA=0;LSB=0;LSC=1; break;//显示第4位
case(5):
LSA=1;LSB=0;LSC=1; break;//显示第5位
case(6):
LSA=0;LSB=1;LSC=1; break;//显示第6位
case(7):
LSA=1;LSB=1;LSC=1; break;//显示第7位
}
P0=smgduan[i];//发送段码
delay(100); //间隔一段时间扫描 全部显示
//delay(1000); 闪烁
P0=0x00;//消隐
}
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
DigDisplay(); //数码管显示函数
}
}

独立按键

按键简介

按键是一种电子开关,使用时轻轻按下开关按钮就可使开关接

通,当松开手时,开关断开。

12.png

通常的按键所用开关为机械弹性开关,当机械触点断开 、闭合

时,电压信号如下:

13.png

独立按键模块电路

14.png

程序编写

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
/**************************************************************************************
* 独立按键实验 *
实现现象:四个独立按键分别控制一个LED的状态
注意事项:无
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit k1=P1^4; //定义P14口是k1
sbit k2=P1^6; //定义P16口是k2
sbit k3=P3^2; //定义P32口是k3
sbit k4=P1^7; //定义P17口是k4

sbit led1=P2^0; //定义P20口是led1
sbit led2=P2^1; //定义P21口是led2
sbit led3=P2^2; //定义P22口是led3
sbit led4=P2^3; //定义P23口是led4

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : keypros
* 函数功能 : 按键处理函数,判断按键K1是否按下
*******************************************************************************/
void keypros()
{
if(k1==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k1==0) //再次判断按键是否按下
{
led1=~led1; //led状态取反
}
while(!k1); //检测按键是否松开
}

if(k2==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k2==0) //再次判断按键是否按下
{
led2=~led2; //led状态取反
}
while(!k2); //检测按键是否松开
}

if(k3==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k3==0) //再次判断按键是否按下
{
led3=~led3; //led状态取反
}
while(!k3); //检测按键是否松开
}

if(k4==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k4==0) //再次判断按键是否按下
{
led4=~led4; //led状态取反
}
while(!k4); //检测按键是否松开
}
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
keypros(); //按键处理函数
}
}

矩阵按键(4X4)

4X4矩阵按键简介

​ 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键
排列成矩阵形式。在矩阵式键盘中,每条水平线和垂直线在交叉处
不直接连通,而是通过一个按键加以连接。

矩阵按键识别方法:
(1)逐行扫描
可以通过高四位轮流输出低电平来对矩阵键盘进行逐行
扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,
然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
(2)行列扫描
可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位
不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键
按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的
值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。

4X4矩阵按键模块电路

15.png

编写程序

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
/**************************************************************************************
* 矩阵按键实验 *
实现现象:下载程序后数码管显示0,按下矩阵按键上的按键显示对应的数字
S1-S4:0-3
S5-S8:4-7
S9-S12:8-B
S13-S16:C-F
注意事项:
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

#define GPIO_DIG P0
#define GPIO_KEY P1

sbit LSA=P3^0;
sbit LSB=P3^1;
sbit LSC=P3^3;

u8 KeyValue; //用来存放读取到的键值


u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : KEY_Scan
* 函数功能 : 检测按键是否按下并读取键值
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void KEY_Scan(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测,防止占用CPU
{
delay(1000);
a++;
}
}
}
}


/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=0; //给一个数码管提供位选
LSB=0;
LSC=0;
while(1)
{
KEY_Scan(); //按键判断函数
GPIO_DIG=smgduan[KeyValue]; //
}
}

点阵(8X8)–74HC595

8X8LED点阵简介

LED点阵是由发光二极管排列组成的显示器件,在我们日常生活的电器中随处可见,被广泛应用于汽车报站器,广告屏等。

16.png

8*8LED点阵内部结构:

17.png

8X8LED点阵模块电路

18.png

编写程序

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
/**************************************************************************************
* 8*8点阵--点亮一个点实验 *
实现现象:下载程序后点阵从左上角第一个点亮,沿一条对角线等比例点亮,直至全亮

注意事项:一定要将LED点阵模块上的J1短接片短接到GND端
数码管的段选和点阵列共用P0口,而38译码器默认是Y7输出,即数码管最后一个会点亮
***************************************************************************************/

#include "reg51.h" //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h>

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;


/*******************************************************************************
* 函数名 : HC595SendByte(u8 dat)
* 函数功能 : 向74HC595发送一个字节的数据
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void HC595SendByte(u8 dat)
{
u8 a;
SRCLK=0;
RCLK=0;
for(a=0;a<8;a++)
{
SER=dat>>7;
dat<<=1;

SRCLK=1;
_nop_();
_nop_();
SRCLK=0;
}

RCLK=1;
_nop_();
_nop_();
RCLK=0;
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
HC595SendByte(0x80); //74HC595控制点阵行
P0=0x7f; //P0控制点阵列
delay(50000);
HC595SendByte(0XC0);
P0=0X3f;
delay(50000);
HC595SendByte(0XE0);
P0=0X1f;
delay(50000);
HC595SendByte(0XF0);
P0=0X0f;
delay(50000);
HC595SendByte(0XF8);
P0=0X07;
delay(50000);
HC595SendByte(0XFC);
P0=0X03;
delay(50000);
HC595SendByte(0XFE);
P0=0X01;
delay(50000);
HC595SendByte(0XFF);
P0=0X00;
delay(50000);
}
}

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
/**************************************************************************************
* 8*8点阵--显示数字实验 *
实现现象:下载程序后点阵上显示数字0(或1)

注意事项:一定要将LED点阵模块上的J1短接片短接到GND端
***************************************************************************************/

#include "reg51.h" //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h>

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;

u8 ledduan0[]={0X00,0X00,0X3E,0X41,0X41,0X41,0X3E,0X00};
u8 ledduan1[]={0X00,0X00,0X24,0X44,0XFC,0X04,0X04,0X00};
u8 ledwei[]={0X7f,0Xbf,0Xdf,0Xef,0Xf7,0Xfb,0Xfd,0Xfe};

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函数名 : HC595SendByte(u8 dat)
* 函数功能 : 向74HC595发送一个字节的数据
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void HC595SendByte(u8 dat)
{
u8 a;
SRCLK=0;
RCLK=0;
for(a=0;a<8;a++)
{
SER=dat>>7;
dat<<=1;

SRCLK=1;
_nop_();
_nop_();
SRCLK=0;
}

RCLK=1;
_nop_();
_nop_();
RCLK=0;
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 i,j;
while(1)
{
P0=0x7f;
for(i=0;i<8;i++)
{
P0=ledwei[i];
HC595SendByte(ledduan0[i]);
delay(100);
HC595SendByte(0X00);
} //显示数字0
// for(j=0;j<8;j++)
// {
// P0=ledwei[j];
// HC595SendByte(ledduan1[j]);
// delay(100);
// HC595SendByte(0X00);
// } //显示数字1
//
}
}

直流电机控制–TC1508S

直流电机简介

直流电机是指能将直流电能转换成机械能的旋转电机。

相关参数:

轴长:8mm

轴径:2mm

电压:1-6v

参考电流:0.35-0.4A

3v转速:17000-18000转每分钟

直流电机驱动模块电路

19.png

编写程序

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
/**************************************************************************************
* 直流电机控制实验 *
实现现象:下载程序后,将直流电机引脚两端连接在电机驱动模块的A-和A+上,
通过按键K1控制直流电机开启和停止
注意事项:
***************************************************************************************/

#include "reg51.h" //此文件中定义了单片机的一些特殊功能寄存器
#include <intrins.h>

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit motoa=P1^0; //OUTA输出
sbit motob=P1^1; //OUTB输出
sbit k1=P3^0; //按键K1

u8 mode=0;
/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}


/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
while(1)
{
if(k1==0) //判断K1是否按下
{
delay(1000); //消抖
if(k1==0) //再次判断K1是否按下
{
mode++;
if(mode==2)mode=0;
}
while(!k1);
}
if(mode==0) //停止
{
motoa=0;
motob=0;
}
else if(mode==1) //开启
{
motoa=0;
motob=1;
}

}
}

中断系统

中断简介

20.png

中断技术不仅解决了快速主机与慢速I/O设备的数据传送问题,而且还具有如下优点:

①分时操作。CPU可以分时为多个I/O设备服务,提高了计算机的利用率;

②实时响应。CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;

③可靠性高。CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高。

中断系统结构

89C51的中断系统有5个中断源 ,2个优先级,可实现二级中断嵌套。

21.png

①INT0(P3.2),可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测

到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置1,向CPU申请中断。

②INT1(P3.3),可由IT1(TCON.2)选择其为低电平有效还是下降沿有效。当CPU检测

到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)置1,向CPU申请中断。

③T0(P3.4),TF0(TCON.5),片内定时/计数器T0溢出中断请求标志。当定时/计

数器T0发生溢出时,置位TF0,并向CPU申请中断。

④T1(P3.5),TF1(TCON.7),片内定时/计数器T1溢出中断请求标志。当定时/计

数器T1发生溢出时,置位TF1,并向CPU申请中断。

⑤RI(SCON.0)或TI(SCON.1),串行口中断请求标志。当串行口接收完一帧串行

数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。

22.png

中断寄存器

定时器控制寄存器TCON

22-1.png

IT0:外部中断0触发方式控制位。当IT0=0时,为电平触发方式。当IT0=1时,为边沿触发方式(下降沿有效)。

IE0:外部中断0中断请求标志位。

IT1:外部中断1触发方式控制位。

IE1:外部中断1中断请求标志位。

TF0:定时/计数器T0溢出中断请求标志位。

TF1:定时/计数器T1溢出中断请求标志位。

中断允许寄存器IE

22-2.png

EX0:外部中断0允许位;

ET0:定时/计数器T0中断允许位;

EX1:外部中断0允许位;

ET1:定时/计数器T1中断允许位;

ES: 串行口中断允许位;

EA :CPU中断允许(总允许)位

中断优先级寄存器IP

22-3.pngPT2:定时器/计数器2中断优先级控制位(仅8052);1:高优先级,0:低优先级

PS:串行口优先级控制位;1:高优先级,0:低优先级

PT0/PT1:定时器/计数器0/1中断优先级控制位;1:高优先级,0:低优先级

PX0/PX1:外部中断管0/1中断优先级控制位;1:高优先级,0:低优先级

中断优先级

同一优先级中的中断申请不止一个时,则有中断优先权排队问题。

同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级

决定。

23.png

*中断执行原则:

①CPU同时接收到几个中断时,首先响应优先级别最高的中断请求。

②正在进行的中断过程不能被新的同级或低优先级的中断请求所中

断。

③正在进行的低优先级中断服务,能被高优先级中断请求所中断。

中断的使用

中断响应条件

①中断源要有中断请求

②中断源的中断允许位为1

③打开总中断(即EA=1)

外部中断

外部中断0–控制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
/**************************************************************************************
* 外部中断0--控制LED实验 *
实现现象:下载程序后按下K3按键可以对D1小灯状态取反。
注意事项:无。
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit k3=P3^2; //定义按键K3
sbit led=P2^0; //定义P20口是led

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : Int1Init()
* 函数功能 : 设置外部中断1
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Int0Init()
{
//设置INT0
IT0=1;//跳变沿出发方式(下降沿)
EX0=1;//打开INT0的中断允许。
EA=1;//打开总中断
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
Int0Init(); // 设置外部中断0
while(1);
}

/*******************************************************************************
* 函 数 名 : Int0() interrupt 0
* 函数功能 : 外部中断0的中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/

void Int0() interrupt 0 //外部中断0的中断函数
{
delay(1000); //延时消抖
if(k3==0)
{
led=~led;
}
}

外部中断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
/**************************************************************************************
* 外部中断1--控制蜂鸣器实验 *
实现现象:下载程序后按下K4按键可以控制蜂鸣器发声。
注意事项:无。
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit k4=P3^3; //定义按键K4
sbit beep=P1^5; //定义P15口是beep

u8 beep_flag=0; //蜂鸣器报警标志位

/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}

/*******************************************************************************
* 函 数 名 : Int1Init()
* 函数功能 : 设置外部中断1
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Int1Init()
{
//设置INT1
IT1=1;//跳变沿出发方式(下降沿)
EX1=1;//打开INT1的中断允许。
EA=1;//打开总中断
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
Int1Init(); // 设置外部中断1
while(1)
{
if(beep_flag==1)
{
beep=!beep;
delay(10);
}
}
}

/*******************************************************************************
* 函 数 名 : Int1() interrupt 2
* 函数功能 : 外部中断1的中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/

void Int1() interrupt 2 //外部中断1的中断函数
{
delay(1000); //延时消抖
if(k4==0)
{
beep_flag=!beep_flag;
}
}

定时器和计数器

定时器/计数器简介

定时器/计数器(Timer/Counter,简称T/C)是单片机中最基本的接口之一。即可以定时又可以计数。常用于计数、延时、测量周期/脉宽/频率、提供定时脉冲信号等。

8051系列单片机至少含有2个16位的T/C,STC89C51/52含有3个T/C。其中2个为基本定时器/计数器T/C0和T/C1,另一个是T/C2。可通过软件配置为定时器功能或者计数器功能。

定时/计数器的工作原理: 定时/计数器实质上是一个加1计数器。当工作在定时器模式时,对振荡源12分频的脉冲计数,即每个机器周期计数值加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零。当工作在计数器模式时,计数脉冲来至外部脉冲输入引脚P3.4(T0)和P3.5(T1),当T0或T1脚上负跳变时计数值加1 ,识别管脚负跳变需要2个机器周期,即24个振荡周期。所以T0或T1脚输入的可计数的外部脉冲的最高频率为1/24fosc,当晶振12MHZ时,最高计数频率为500KHz,高于此频率将计数出错。

定时器/计数器内部结构:

24.png

定时器/计数器寄存器

控制寄存器TCON

25.png

TF1:T1溢出中断请求标志位。

TR1:T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。

TF0:T0溢出中断请求标志位。

TR0:T0运行控制位。

工作方式寄存器TMOD

26.png

27.png

定时/计数器2控制寄存器T2CON

28.png定时器/计数器工作方式

通过工作方式寄存器TMOD的M0和M1组合,形成了定时器/计数器的4种不同工作方式。T/C0和T/C1都可以工作在方式0、方式1、方式2工作,而方式3只有T/C0才能工作。

方式0

方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。

29.png

方式1

方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器。

30.png

方式2

方式2为自动重装初值的8位计数方式。

31.png

方式3

方式3只适用于定时/计数器T0,工作方式3将T0分成为两个独立的8位计数器TL0和TH0。定时器T1处于方式3时相当于TR1=0,停止计数。

32.png

定时器/计数器使用方法

①对TMOD赋值,以确定T0和T1的工作方式。

②计算初值,并将其写入TH0、TL0或TH1、TL1。

③中断方式时,打开定时器中断(ET0/ET1)及总中断EA。

④使TR0或TR1置位,启动定时/计数器定时或计数

定时器/计数器初值计算:

假如要定时1ms,定时器初值是多少?

计数脉冲个数N=1ms/1us=1000个

定时器初值X=2^16-1000=65536-1000=64536=0XFC18

定时器中断

定时器0中断–控制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
/**************************************************************************************
* 定时器0中断--控制LED实验 *
实现现象:下载程序后D1小灯循环点亮1秒,熄灭1秒。使用单片机内部定时器可以实现准确延时。
注意事项:无。
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit led=P2^0; //定义P20口是led


/*******************************************************************************
* 函 数 名 : Timer0Init
* 函数功能 : 定时器0初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0Init()
{
TMOD&=0XFC; //使定时器模式清零,11111100,无论初始为多少,后面一律变成??????00,在自行写入定时器工作方式
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。

TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
Timer0Init(); //定时器0初始化
while(1);
}

/*******************************************************************************
* 函 数 名 : void Timer0() interrupt 1
* 函数功能 : 定时器0中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}

定时器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
/**************************************************************************************
* 定时器1中断--控制数码管实验 *
实现现象:下载程序后数码管间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。
注意事项:将LED点阵模块的J1短接片短接到VCC端
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P3^0;
sbit LSB=P3^1;
sbit LSC=P3^3;

u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 n=0;
/*******************************************************************************
* 函 数 名 : Timer1Init
* 函数功能 : 定时器1初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。

TH1=0XFC; //给定时器赋初值,定时1ms
TL1=0X18;
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=0;
LSB=0;
LSC=0;
Timer1Init(); //定时器1初始化
while(1);
}

/*******************************************************************************
* 函 数 名 : void Timer1() interrupt 3
* 函数功能 : 定时器1中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1() interrupt 3
{
static u16 i;
TH1=0XFC; //给定时器赋初值,定时1ms
TL1=0X18;
i++;
if(i==1000)
{
i=0;
P0=smgduan[n++];
if(n==16)n=0;
}
}

简易交通灯

本节所要实现的功能是:系统运行时,横向通行绿灯亮10秒,然后黄灯亮3秒,纵向通行绿灯亮10秒,黄灯亮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
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
/**************************************************************************************
* 简易交通灯实验 *
实现现象:下载程序后,数码管显示交通灯时间,注意:由于交通灯控制口和数码管位选共用了3个引脚,所以
不能显示多位数字,如果强制显示,将会干扰交通灯LED状态。
注意事项:将LED点阵模块的J1短接片短接到VCC端
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;

sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

sbit rd1=P2^0; //D1 红色LED
sbit yd1=P2^1; //D2 黄色LED
sbit gd1=P2^2; //D3 绿色LED

sbit rd2=P2^3; //D4 红色LED
sbit yd2=P2^4; //D5 黄色LED
sbit gd2=P2^5; //D6 绿色LED


u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
char sec1=0,sec2=0,sec3=0,sec4=0;
u8 sec1flag=1,sec2flag,sec3flag,sec4flag;

/*******************************************************************************
* 函 数 名 : Timer1Init
* 函数功能 : 定时器1初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。

TH1=0XFC; //给定时器赋初值,定时1ms
TL1=0X18;
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=1;
LSB=1;
LSC=1;
Timer1Init(); //定时器1初始化
while(1)
{
if(sec1<10&&sec1flag==1) //横向通行绿灯亮10秒
{
rd1=0; //纵向禁止通行 红灯亮 黄灯灭 绿灯灭
yd1=1;
gd1=1;

rd2=1; //横向通行 红灯灭 黄灯灭 绿灯亮
yd2=1;
gd2=0;
P0=smgduan[9-sec1];
}
if(sec2<4&&sec2flag==1) //横向通行黄灯亮3秒
{
rd1=0; //纵向禁止通行 红灯亮 黄灯灭 绿灯灭
yd1=1;
gd1=1;

rd2=1; //横向通行 红灯灭 黄灯亮 绿灯灭
yd2=0;
gd2=1;
P0=smgduan[3-sec2];
}
if(sec3<10&&sec3flag==1) //纵向通行绿灯亮10秒
{
rd1=1; //纵向通行 红灯灭 黄灯灭 绿灯亮
yd1=1;
gd1=0;

rd2=0; //横向禁止通行 红灯亮 黄灯灭 绿灯灭
yd2=1;
gd2=1;
P0=smgduan[10-sec3];
}
if(sec4<4&&sec4flag==1) //纵向通行黄灯亮3秒
{
rd1=1; //纵向通行 红灯灭 黄灯亮 绿灯灭
yd1=0;
gd1=1;

rd2=0; //横向禁止通行 红灯亮 黄灯灭 绿灯灭
yd2=1;
gd2=1;
P0=smgduan[3-sec4];
}

}
}

/*******************************************************************************
* 函 数 名 : void Timer1() interrupt 3
* 函数功能 : 定时器1中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer1() interrupt 3
{
static u16 i;
TH1=0XFC; //给定时器赋初值,定时1ms
TL1=0X18;
i++;
if(i==1000)
{
i=0;
if(sec1flag==1)
{
sec1++;
if(sec1>9)
{
sec1=0;
sec1flag=0;
sec2flag=1;
}
}
if(sec2flag==1)
{
sec2++;
if(sec2>3)
{
sec2=0;
sec2flag=0;
sec3flag=1;
}
}
if(sec3flag==1)
{
sec3++;
if(sec3>9)
{
sec3=0;
sec3flag=0;
sec4flag=1;
}
}
if(sec4flag==1)
{
sec4++;
if(sec4>3)
{
sec4=0;
sec4flag=0;
sec1flag=1;
}
}
}
}

串口通信

通信的概念

通信的方式可以分为多种,按照数据传送方式可分为串行通信和并行通信。按照通信的数据同步方式,可分为异步通信和同步通信。按照数据的传输方向又可分为单工、半双工和全双工通信。

串行通信与并行通信
串行通信

串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。

33.png

特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。

并行通信

并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位、16位、32位等数据一起传输。

34.png

特点:控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难,抗干扰能力差。

异步通信与同步通信
异步通信

异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。

35.png

特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高。

同步通信

同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。

36.png

单工、半双工与全双工通信
单工通信

单工是指数据传输仅能沿一个方向,不能实现反向传输。

半双工通信

半双工是指数据传输可以沿两个方向,但需要分时进行。

全双工通信

全双工是指数据可以同时进行双向传输。

通信速率

衡量通信性能的一个非常重要的参数就是通信速率,通常以比特率(Bitrate)来表示。比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240个/秒 = 2400 bps

串口通信简介

串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线等,按位进行传输数据的一种通信方式,属于串行通信方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。

RS232的通信协议比较简单,通常遵循96-N-8-1格式。

串口内部结构

37.png

串口相关寄存器

串口控制寄存器SCON

38.png

SM0和SM1为工作方式选择位:

39.png

SM2:多机通信控制位,主要用于方式2和方式3。当SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走)。当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。

REN:允许串行接收位。由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收。

TB8:在方式2或方式3中,是发送数据的第9位,可以用软件规定其作用。可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位。在方式0和方式1中,该位未用到。

RB8:在方式2或方式3中,是接收到数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位。在方式1时,若SM2=0,则RB8是接收到的停止位。

TI:发送中断标志位。在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。

RI:接收中断标志位。在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。

电源控制寄存器PCON

40.png

SMOD:波特率倍增位。在串口方式1、方式2、方式3时,波特

率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0。

串口工作方式

方式0

方式0时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口。数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。发送和接收均为8位数据,低位在先,高位在后。波特率固定为fosc/12。

①方式0输出

41.png

②方式0输入

42.png

方式1

方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如下所示。其中1位起始位,8位数据位,1位停止位。

43.png

①方式1输出

44.png

②方式1输入

45.png

用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断。

方式2和方式3

方式2或方式3时为11位数据的异步通信口。TXD为数据发送引脚,
RXD为数据接收引脚 。

46.png

①方式2、方式3输出

47.png

发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚。每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出。 第一次移位时,停止位“1”移入输出移位寄存器的第9位上 ,以后每次移位,左边都移入0。当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断。

②方式2、方式3输入

48.png

接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF和RB8(接收数据的第9位),置RI=1,向CPU请求中断。如果条件不满足,则数据丢失,且不置位RI,继续搜索RXD引脚的负跳变。

串口的使用

(1)如何计算波特率

方式0的波特率 = fosc/12

方式2的波特率 =(2SMOD/64)· fosc

方式1的波特率 =(2SMOD/32)·(T1溢出率)

方式3的波特率 =(2SMOD/32)·(T1溢出率)

T1 溢出率 = fosc /{12×[256 -(TH1)]}

49.png

(2)串口初始化步骤

①确定T1的工作方式(TMOD寄存器);

②确定串口工作方式(SCON寄存器);

③计算T1的初值(设定波特率),装载TH1、TL1;

④启动T1(TCON中的TR1位);

⑤如果使用中断,需开启串口中断控制位(IE寄存器)。

串口模块电路

50.png

编写程序

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
/**************************************************************************************
* 串口通信实验 *
实现现象:下载程序后打开串口调试助手,将波特率设置为4800,选择发送的数据就可以显示
在串口助手上。
注意事项: 串口调试助手操作步骤见实验内现象截图。
***************************************************************************************/

#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器

typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;


/*******************************************************************************
* 函数名 :UsartInit()
* 函数功能 :设置串口
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void UsartInit()
{
SCON=0X50; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X80; //波特率加倍
TH1=0XF3; //计数器初始值设置,注意波特率是4800的
TL1=0XF3;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
UsartInit(); // 串口初始化
while(1);
}

/*******************************************************************************
* 函数名 : Usart() interrupt 4
* 函数功能 : 串口通信中断函数
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Usart() interrupt 4
{
u8 receiveData;
receiveData=SBUF;//出去接收到的数据
RI = 0;//清除接收中断标志位
SBUF=receiveData;//将接收到的数据放入到发送寄存器
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}