LED 指示灯的基本控制
基本原理
74HC138 译码器
8 个输出为互斥的低有效输出,只有 1 路输出低电平,其他均输出高电平 
74HC573 锁存器
8 路三态输出的非反转透明锁存器 当锁存使能端 LE 为高时,这些器件的锁存对于数据是透明的(即输出同步) 当锁存使能变低时,符合建立时间和保持时间的数据会被锁存(即输入端不影响输出端) 
74HC02 或非门
当输入均为 0 时,或非门才输出 1 
点亮 LED 完整流程
要使 LED 点亮,需要锁存器为透明状态(同步输出),此时锁存器使能端 LE 应为高电平 而要使 LE 为高电平,则或非门输入均应为低电平, WR 已经接地,只需将 Y4 电平拉低即可 而要使 Y4 为低电平,则需要译码器的输入为 1 0 0
代码如下:( 8 路全点亮)
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
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void cls_buzz(void) { P2 = (P2&0x1F0xA0); P0 = 0x00; P2 &= 0x1F; }
void Delay(unsigned int t) { while(t--); while(t--); }
void LEDRunning() { HC138_C = 1; HC138_B = 0; HC138_A = 0;
P0 = 0x00; Delay(60000); Delay(60000); P0 = 0xff; Delay(60000); Delay(60000); }
void main(void) { cls_buzz(); while(1) { LEDRunning(); } }
|
练习题目
先让 8 路 LED 指示灯闪烁 3 遍然后熄灭,接着依次点亮 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
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void cls_buzz(void) { P2 = (P2&0x1F0xA0); P0 = 0x00; P2 &= 0x1F; }
void Delay(unsigned int t) { while(t--); while(t--); }
void LEDRunning() { unsigned char i; HC138_C = 1; HC138_B = 0; HC138_A = 0;
for(i=0; i<3; i++) { P0 = 0xff; Delay(60000); Delay(60000); P0 = 0x1fe; Delay(60000); Delay(60000); } for(i=1; i<=8; i++) {
P0 = 0xff << i; Delay(60000); Delay(60000); } for(i=1; i<=8; i++) { P0 = ~(0xff << i); Delay(60000); Delay(60000); } }
void main(void) { cls_buzz(); while(1) { LEDRunning(); } }
|
1
1
1
1
1
1
1
0
P0^7
P06
P0^5
P0^4
P0^3
P0^2
P0^1
P0^0
LED8
LED7
LED6
LED5
LED4
LED3
LED2
LED1
即“11111110”代表灯 LED1 亮,其他全灭,反之亦然
蜂鸣器与继电器的基本控制
基本原理
ULN2003 IC 控制器
内部主要为一个非门,会对输入值进行取反,高电平转换为低电平
启动蜂鸣器与继电器完整流程
要启动蜂鸣器,就需要 N BUZZ 引脚为低电平 对应 ULN2003 的输出引脚 N BUZZ 为低电平,因其内部为一个非门,则其输入引脚应为高电平 此时 74HC573 锁存器的使能端 LE Y5C 应为高电平以向 ULN2002 输出高电平 要使 Y5C 输出高电平,则需要或非门 74HC02 的输入端均为低电平,因 WR 脚已经为低电平,只需将 Y5 脚拉低即可 通过控制 74HC138 译码器的输入为 101 可以使 Y5 脚输出低电平
代码如下:
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
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void Delay(unsigned int t) { while(t--); while(t--); }
void HC138() {
HC138_A = 1; HC138_B = 0; HC138_C = 1; }
void InitSystem() { HC138(); P0 = 0x00; } void Relay() { HC138(); P0 = 0x10; }
void Beep() { HC138(); P0 = 0x40; }
void main(void) { InitSystem();
while(1) {
Relay(); Delay(6000); Delay(6000); Beep(); Delay(6000); Delay(6000); } }
|
练习题目
首先让 8 路 LED 指示灯闪烁 3 遍后熄灭,接着依次点亮 LED 指示灯,继电器吸合一会儿后断开,然后依次熄灭 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 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
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void Delay(unsigned int t) { while(t--); while(t--); while(t--); }
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5:
HC138_C = 1; HC138_B = 0; HC138_A = 1; break; } }
void InitSystem() { HC138(5); P0 = 0x00; }
void Relay(unsigned char s) { switch(s) { case 1: HC138(5); P0 = 0x10; break; case 0: HC138(5); P0 = 0x00; break; } }
void LED(unsigned char s) { switch(s) { case 1: HC138(4); P0 = 0x00; break; case 0: HC138(4); P0 = 0xff; break; } }
void Beep(unsigned char n) { switch(n) { case 1: HC138(5); P0 = 0x40; break; case 0: HC138(5); P0 = 0x00; break; } }
void main(void) { unsigned int i=0; InitSystem(); while(1) {
for(i=0 ;i<3 ;i++) { LED(1); Delay(9000); Delay(9000); LED(0); Delay(9000); Delay(9000); }
for(i=1 ;i<=8 ;i++) { P0 = 0xff << i; Delay(9000); Delay(9000); }
Relay(1); Delay(9000); Delay(9000); Relay(0); Delay(9000);
HC138(4); for(i=1 ;i<=8 ;i++) { P0 = ~(0xff << i); Delay(9000); Delay(9000); }
Beep(1); Delay(9000); Delay(9000); Beep(0); Delay(9000); } }
|
共阳数码管的静态显示
基本原理
7SEG-4 数码管
由段码和 com 口组成,com 口控制第几位的数码管进行显示,段码控制每一个数码管显示的内容
数码管静态显示完整流程
比赛平台使用的数码管为共阳数码管,当阴极为低电平时数码管点亮 段码控制数码管显示的具体内容,而段码由 74HC573 锁存器直接控制,需要将其使能端 Y7C 输出高电平从而使锁存器处于透明状态 数码管显示 0~F 以及 ” . ” 分别对应段码:
1 2
| unsigned char code table[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0x7f};
|
com 口控制哪几个或第几个数码管进行显示,当 com 口处于高电平时,对应数码管点亮 com 口由 74HC573 锁存器直接控制,需要将其使能端 Y6C 输出高电平从而使锁存器处于透明状态
练习题目
8 个数码管分别单独依次显示 0~9 的值,然后所有数码管一起同时显示 0~F 的值,如此往复
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
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
unsigned char code ParaCode[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0x7f};
void Delay(unsigned int t) { while(t--); while(t--); while(t--); }
void HC138(unsigned char n) { switch(n) { case 6: HC138_C = 1; HC138_B = 1; HC138_A = 0; break; case 7: HC138_C = 1; HC138_B = 1; HC138_A = 1; break; } }
void ShowAll(unsigned char dat) { HC138(6); P0 = 0xff; HC138(7); P0 = ParaCode[dat]; }
void ShowStatic(unsigned char dat, unsigned char pos) { HC138(6); P0 = 0x01 << pos; HC138(7); P0 = ParaCode[dat]; }
void main() { unsigned char i,j,k; while(1) { for(j=0; j<8; j++) { for(i=0; i<=9; i++) { ShowStatic(i,j); Delay(6000); Delay(6000); } } for(k=0; k<16; k++) { ShowAll(k); Delay(6000); Delay(6000); }
} }
|
共阳数码管的动态显示
基本原理
动态显示实质上就是轮流点亮单个数码管实现多位数码管整体显示的效果。 在轮流显示过程中,每位数码管点亮时间为 1~2s ,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位 数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的 IO 端口,而且功耗更低。 对于一组 4 位数码管来说,静态显示和动态显示都能实现同样的效果,但需要的 IO 端口是不同的。 静态显示需要 IO 端口: 8 个段码 * 4 + 4 个 COM 端 =3 6 个 IO 引脚 动态显示需要 IO 端口:8 个段码 + 4 个 COM 端 = 12 个 IO 引脚
练习题目
在 8 位数码管中,前 4 位显示年份 “2022”,接着 2 位显示分隔符 “-”,最后两位是月份,从 1 月份开始,每隔一段时间加 1 个月,到 12 月之后又从 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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| #include "reg52.h"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7; void Delay(unsigned int t); void DelayDigitalTube(unsigned int t); void Show(unsigned char dat, unsigned char pos);
unsigned char code ParaCode[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0x7f, 0xbf};
unsigned char month = 1;
void ShowYear() { Show(2,0); DelayDigitalTube(600); Show(0,1); DelayDigitalTube(600); Show(2,2); DelayDigitalTube(600); Show(2,3); DelayDigitalTube(600); Show(17,4); DelayDigitalTube(600); Show(17,5); DelayDigitalTube(600); }
void ShowMonth() { Show(month/10,6); DelayDigitalTube(600); Show(month%10,7); DelayDigitalTube(600); }
void Delay(unsigned int t) { while(t--) { ShowYear(); ShowMonth(); } }
void DelayDigitalTube(unsigned int t) { while(t--); }
void HC138(unsigned char n) { switch(n) { case 6: HC138_C = 1; HC138_B = 1; HC138_A = 0; break; case 7: HC138_C = 1; HC138_B = 1; HC138_A = 1; break; } }
void Show(unsigned char dat, unsigned char pos) { HC138(6); P0 = 0x01 << pos; HC138(7); P0 = ParaCode[dat]; }
void main() { while(1) { ShowYear(); ShowMonth(); if(month > 12) { month = 1; } Delay(100); month++; } }
|
独立按键的基本操作与扩展应用
基本原理
将 J5 处跳帽接到 2~3 引脚,使按键 S4~S7 四个按键的另一端接地成为 4 个独立键盘。 因为 4 个按键的一端均已接地,只需要读取另一端 P30~P33 是否为低电平便可确认按键是否按下。 在扫描按键的过程中,发现有按键触发信号后,先做去抖动处理,当确认按键按下时,才进行相应的功能处理。 
练习题目
按下 S7 点亮 L1 指示灯,松开按键指示灯熄灭。 S6 点亮 L2 指示灯;S5 点亮 L3 指示灯;S4 点亮 L4 指示灯。 都是松开熄灭,按键均作去抖。
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
| #include "reg52.h"
sbit S7 = P3^0; sbit S6 = P3^1; sbit S5 = P3^2; sbit S4 = P3^3;
sbit L1 = P0^0; sbit L2 = P0^1; sbit L3 = P0^2; sbit L4 = P0^3;
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void DelayKey(unsigned char t) { while(t--); }
void ScanInd() { if(S7 == 0) { DelayKey(200); if(S7 == 0) { L1 = 0; while(S7 == 0); L1 = 1; } } if(S6 == 0) { DelayKey(200); if(S6 == 0) { L2 = 0; while(S6 == 0); L2 = 1; } } if(S5 == 0) { DelayKey(200); if(S5 == 0) { L3 = 0; while(S5 == 0); L3 = 1; } } if(S4 == 0) { DelayKey(200); if(S4 == 0) { L4 = 0; while(S4 == 0); L4 = 1; } } }
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; case 6: HC138_C = 1; HC138_B = 1; HC138_A = 0; break; case 7: HC138_C = 1; HC138_B = 1; HC138_A = 1; break; } }
void main() { HC138(5); P0 = 0x00; HC138(4); P0 = 0xff; while(1) {
ScanInd(); } }
|
矩阵键盘的扫描原理与基本应用
基本原理
将 J5 处跳帽接到 1~2 引脚,使按键 S4~S7 四个按键的另一端接入 IO 口使得16个按键成为矩阵键盘。 矩阵键盘的两端均为 IO 口,一端输入一端输出。 初始时输入全部置为 1 ,扫描时输入逐行置 0 ,若在对应列的输出检测到低电平,则表明对应行列的按键被按下。
练习题目
按照自左到右自上到下的顺序,每个按键分别对应 0~F ,按下按键就将对应数字显示到数码管上。
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
| #include "STC15F2K60S2.H"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
sbit R1 = P3^0; sbit R2 = P3^1; sbit R3 = P3^2; sbit R4 = P3^3;
sbit C4 = P3^4; sbit C3 = P3^5; sbit C2 = P4^2; sbit C1 = P4^4;
unsigned char code ParaCode[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0x7f, 0xbf};
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5: HC138_C = 1; HC138_B = 0; HC138_A = 01; break; case 6: HC138_C = 1; HC138_B = 1; HC138_A = 0; break; case 7: HC138_C = 1; HC138_B = 1; HC138_A = 1; break; } }
void Display(unsigned char dat) { HC138(6); P0 = 0x01; HC138(7); P0 = ParaCode[dat]; }
void ScanKeyMulti() { R1 = 0; R2 = R3 = R4 = 1; if(C1 == 0) { while(C1 ==0) { Display(0); } } else if(C2 == 0) { while(C2 ==0) { Display(1); } } else if(C3 == 0) { while(C3 ==0) { Display(2); } } else if(C4 == 0) { while(C4 ==0) { Display(3); } }
R2 = 0; R1 = R3 = R4 = 1; if(C1 == 0) { while(C1 ==0) { Display(4); } } else if(C2 == 0) { while(C2 ==0) { Display(5); } } else if(C3 == 0) { while(C3 ==0) { Display(6); } } else if(C4 == 0) { while(C4 ==0) { Display(7); } }
R3 = 0; R2 = R1 = R4 = 1; if(C1 == 0) { while(C1 ==0) { Display(8); } } else if(C2 == 0) { while(C2 ==0) { Display(9); } } else if(C3 == 0) { while(C3 ==0) { Display(10); } } else if(C4 == 0) { while(C4 ==0) { Display(11); } }
R4 = 0; R2 = R3 = R1 = 1; if(C1 == 0) { while(C1 ==0) { Display(12); } } else if(C2 == 0) { while(C2 ==0) { Display(13); } } else if(C3 == 0) { while(C3 ==0) { Display(14); } } else if(C4 == 0) { while(C4 ==0) { Display(15); } } }
void main() { while(1) { ScanKeyMulti(); } }
|
中断系统与外部中断应用
基本原理
内核与外设之间的主要交互方式有两种:轮询和中断。 轮询的方式貌似公平,但实际工作效率很低,且不能及时响应紧急事件;中断系统使得内核具备了应对突发事件的能力。 在执行CPU当前程序时,由于系统中出现了某种急需处理的情况,CPU暂停正在执行的程序,转而去执行另外一段特殊程序来处理出现的紧急事务,处理结束后,CPU自动返回到原来暂停的程序中去继续执行。这种程序在执行过程中由于外界的原因而被中间打断的情况,称为中断。 中断服务函数:内核响应中断后执行的相应处理程序。 中断向量:中断服务程序的入口地址。每个中断源都对应 个固定的入口地址。当内核响应中断请求时,就会暂停当前的程序执行,然后跳转到该入口地址执行代码。 一般来说,51单片机有5个中断源(忽略定时/计数器2),分2个优先级,这个5个中断源按照自然优先级从高到低依次为:
优先级
中断源
0
外部中断0:INT0
1
定时/计数器0:TF0
2
外部中断1:INT1
3
定时/计数器1:TF1
4
串口中断:RI/TI

练习题目
首先将 J5 处的跳帽接到 2~3 引脚,即 S5 按键接到 P32/INT0, S4 按键接到 P33/INT1 。定义一个 Working() 函数, 使 L1 指示灯不断闪烁。将 P32 引脚定义成外部中断功能,按 S5 按键就会产生外部中断触发信号,在中断响应函数中,点亮 L8 指示灯, 延时一段较长的时间后熄灭,该功能用两种方式实现: 1-直接在中断服务函 数中延时。 2-在中断服务函数中标志变量,在外部执行延时。
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
| #include "STC15F2K60S2.H"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; } }
void InitSystem() { HC138(4); P0 = 0xff; HC138(5); P0 = 0x00; }
void Delay(unsigned int t) { while(t--); while(t--); while(t--); }
void Working() { HC138(4); P0 = 0xfe; Delay(9000); P0 = 0xff; Delay(9000); }
void InitINT0() { IT0 = 1; EX0 = 1; EA = 1; }
unsigned char InterruptStat = 0; void ServiceINT0() interrupt 0 { InterruptStat = 1; }
void InterruptProgram() { if(InterruptStat == 1) { HC138(4); P0 = 0x7f; Delay(999999); Delay(999999); Delay(999999); Delay(999999); Delay(999999); Delay(999999); P0 = 0xff; } InterruptStat = 0; }
void main() { InitSystem(); InitINT0(); while(1) { Working(); InterruptProgram(); } }
|
定时计数器的基本原理与应用
基本原理
【蓝桥杯单片机08】定时器的基本原理与应用 - - 21ic电子技术开发论坛
定时/计数器的编程思路 在定时/计数器的程序设计中,通常有两个函数:初始化函数和中断服务函数。 在初始化函数中,一般需要进行以下几个配置: <1> 配置工作模式,即对TMOD寄存器编程。 <2> 计算技术初值,即对THx和TLx寄存器进行赋值。 <3> 使能定时/计数器中断,即ET0或ET1置1。 <4> 打开总中断,即EA =1。 <5> 启动定时器,即TR0或TR1置1。 在中断服务函数中,一般需要进行以下的编程: <1> 如果不是自动重装模式,需要对THx和TLx重新赋值。 <2> 进行间隔定时到达的逻辑处理(越少越好)。 其程序框架和代码编写基本上差不多:
练习题目
利用单片机的定时/计数器 T0 的模式 1 实现间隔定时,每隔 1 秒 L1 指示灯闪烁一下,也就是点亮 0.5 秒,熄灭 0.5 秒; 每隔 10 秒 L8 指示灯闪烁一下,即点亮 5 秒,熄灭 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 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
| #include "STC15F2K60S2.H"
sbit L1 = P0^0; sbit L8 = P0^7;
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 HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; } }
void Delay(unsigned int t) { while(t--); while(t--); while(t--); }
void InitSystem() { HC138(4); P0 = 0xff; HC138(5); P0 = 0x00; }
void InitTimer0() { TMOD = 0x01; TH0 = (65535 - 50000) / 256; TL0 = (65535 - 50000) % 256;
EA = 1; ET0 = 1; TR0 = 1; }
unsigned int count = 0; void ServiceTimer0() interrupt 1 { HC138(4); count++; if(count % 10 == 0) { L1 = ~L1; } if(count == 100) { L8 = ~L8; count = 0; } }
void main() { InitSystem(); Delay(99999); InitTimer0(); while(1) { HC138(4); L2 = 1; L3 = 1; L4 = 1; L5 = 1; L6 = 1; L7 = 1; } }
|
定时器的进阶综合案例解析
利用定时器 T0 、数码管模块和 2 个独立按键(J5 的 2~3 短接),设计一个秒表,具有清零、暂停、启动功能。 1、显示格式为:分 - 秒 - 0.05 秒(即 50ms) 08-26-18 表示:8 分 26 秒 900 毫秒 2、独立按键 S4 为:暂停/启动;独立按键 S5 为:清零。按键均为按下有效
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
| #include "STC15F2K60S2.H"
sbit S4 = P3^3; sbit S5 = P3^2;
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
unsigned char min = 0; unsigned char sec = 0; unsigned char ms = 0;
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break; case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; case 6: HC138_C = 1; HC138_B = 1; HC138_A = 0; break; case 7: HC138_C = 1; HC138_B = 1; HC138_A = 1; break; } }
void InitSystem() { HC138(4); P0 = 0xff; HC138(5); P0 = 0x00; }
unsigned char code ParaCode[]= {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0x7f, 0xbf};
void DelayDitialTube(unsigned char t) { while(t--); }
void DisplayBit(unsigned char dat, unsigned char pos) { HC138(6); P0 = 0x01 << pos; HC138(7); P0 = dat; }
void DisPlayTime() { DisplayBit(ParaCode[ms%10],7); DelayDitialTube(500); DisplayBit(ParaCode[ms/10],6); DelayDitialTube(500);
DisplayBit(ParaCode[17],5); DelayDitialTube(500);
DisplayBit(ParaCode[sec%10],4); DelayDitialTube(500); DisplayBit(ParaCode[sec/10],3); DelayDitialTube(500);
DisplayBit(ParaCode[17],2); DelayDitialTube(500);
DisplayBit(ParaCode[min%10],1); DelayDitialTube(500); DisplayBit(ParaCode[min/10],0); DelayDitialTube(500); }
void InitTimer0() { TMOD = 0x01; TH0 = (65535 - 50000) / 256; TL0 = (65535 - 50000) % 256;
EA = 1; ET0 = 1; TR0 = 1; }
void ServicesTimer0() interrupt 1 { ms++; if(ms == 20) { sec++; ms = 0; if(sec == 60) { min++; sec = 0; if(min == 99) { min = 0; } } } }
void DelayKey(unsigned char t) { while(t--); }
void ScanKey() { if(S4 == 0) { DelayKey(100); if(S4 == 0) { TR0 = ~TR0; while(S4 == 0) { DisPlayTime(); } } } if(S5 == 0) { DelayKey(100); if(S5 == 0) { ms = 0; sec = 0; min = 0; while(S5 == 0) { DisPlayTime(); } } } }
void main() { InitSystem(); InitTimer0(); while(1) { DisPlayTime(); ScanKey(); } }
|
PWM 脉宽调制信号的发生与控制
基本原理
基础是通过定时器进行中断控制,若题目要求为 100Hz ,则根据频率与周期的反比关系,一个周期应为 1/100 秒,即 10ms 而定时器的计时单位为微秒 us,故周期应换算为 10‘000us 在这 10’000us 的周期中,通过调控高电平持续的时间比例即占空比来控制设备
练习题目
利用 PWM 脉宽信号实现独立按键 S7 对 L1 指示灯亮度变化的控制。 具体要求如下:
- PWM 脉宽信号的频率为 100Hz
- 系统上电后 L1 指示灯处在熄灭状态
- L1 指示灯有 4 种亮度模式,分别是完全熄灭、10% 的亮度、50% 的亮度和 90% 的亮度。
- 按下 S7 键,循环切换 L1 指示灯的四种亮度模式。
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
| #include "STC15F2K60S2.H"
sbit S7 = P3^0;
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break;
case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; } }
void InitSystem() { HC138(4); P0 = 0xff; HC138(5); P0 = 0x00; }
void InitTimer0() { TMOD = 0x01; TH0 = (65535 - 100) / 256; TL0 = (65535 - 100) % 256;
EA = 1; ET0 = 1; }
unsigned char KeyStat = 0; unsigned char PWM = 0; unsigned char count = 0; void ServiceTimer0() interrupt 1 { TH0 = (65535 - 100) / 256; TL0 = (65535 - 100) % 256;
count++; if(count == PWM) {
P0 = 0xff; } else if(count == 100) {
P0 = 0xfe; count = 0; } }
void DelayKey(unsigned int t) { while(t--); }
void ScanKey() { if(S7 == 0) { DelayKey(200); if(S7 == 0) {
switch(KeyStat) {
case 0: P0 = 0xfe; TR0 = 1; PWM = 10; KeyStat = 1; break;
case 1: PWM = 50; KeyStat = 2; break;
case 2: PWM = 90; KeyStat = 3; break;
case 3: P0 = 0xff; TR0 = 0; KeyStat = 0; break; } while(S7 == 0); } } }
void main() { InitSystem(); HC138(4); P0 = 0xff; InitTimer0(); while(1) { ScanKey(); } }
|
串口通信的基本原理与应用
基本原理
串口通信概述
微控制器与外部设备的数据通信,根据连线结构和传送方式的不同,可以分为两种:并行通信和串行通信。 并行通信:指数据的各位同时发送或接收,每个数据位使用一条导线。 串行通信:指数据一位接一位地顺序发送或接收。 串行通信有 SPI、IC、UART 等多种,最常见最通用的是指 UART,大多数情况下,串口通信指的就是 UART。 串行通信的制式有:单工、半双工、全双工三种。 RS485 总线是半双工的通信制式。 串行通信的主要方式有两种:同步和异步 同步串行通信:需要使用同一个时钟,以数据块为单位传送数据。 异步串行通信:每个设备都有自己的时钟信号,通信中双方的波特率要保 持一致,以字符为单位进行数据帧传送,一次传送一个帧。
UART 口的数据发送与接收
串行口中有两个缓冲寄存器 SBUF,一个是发送寄存器,一个是接收寄存器,在物理结构上是完全独立的。它们都是字节寻址的寄存器,字节地址均为 99H 。 这个重叠的地址靠读写指令区分: 串行发送时,CPU 向 SBUF 写入数据,此时99H表示发送缓存 SBUF 。 串行接收时,CPU 从 SBUF 读出数据,此时 99H 表示接收缓存 SBUF 。 数据发送,把数据扔进 SBUF 后,内核会自动将数据发送出去, 内容发生完成后,会将 TI 标志位置 1 。 发送数据程序:SBUF=数据/变量;
如:SBUF=0x58; 数据接收,内核从串口接收到一个完整的数据后,会将 RI 标志位置 1,用户用 SBUF 直接读取即可。 接收数据程序:变量=SBUF;
如:dat=SBUF;
串口控制寄存器 SCON

- 异步8位UART并且允许接收:
SCON=0x50;
- 对于IAP15F2K61S2单片机,你还要对辅助寄存器 AUXR(0x8e) 进行设置。
练习题目
利用单片机的串行接口与上位机建立传输信道进行数据的收发。采用 8 位的 UART 模式,即模式 1,波特率为 9600 BPS。 数据发送采用查询方式,数据接收采用中断方式。 系统上电初始化之后,单片机向上位机发送两个字节:0x51 和 0xa5, 然后等待接收上位机的数据,每接收到一个字节后,在该字节的基础上加 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 77 78 79
| #include "STC15F2K60S2.H"
sbit HC138_A = P2^5; sbit HC138_B = P2^6; sbit HC138_C = P2^7;
void Send(unsigned char dat);
void HC138(unsigned char n) { switch(n) { case 4: HC138_C = 1; HC138_B = 0; HC138_A = 0; break;
case 5: HC138_C = 1; HC138_B = 0; HC138_A = 1; break; } }
void InitSystem() { HC138(4); P0 = 0xff; HC138(5); P0 = 0x00; }
void InitUart() { TMOD = 0x20; TH1 = 0xfd; TL1 = 0xfd; TR1 = 1;
SCON = 0x50; AUXR = 0x00;
ES = 1; EA = 1; }
unsigned char dat; void ServiceUart() interrupt 4 { if(RI == 1) { RI = 0; dat = SBUF; Send(dat + 1); } }
void Send(unsigned char dat) { SBUF = dat; while(TI == 0); TI = 0; }
void main() { InitSystem(); InitUart(); Send(0x5a); Send(0xa5); while(1) {
} }
|
串口通信进阶应用案例解析
利用单片机的串行接口与上位机建 立传数据输信道。 采用 8 位的 UART 模式,即模式 1,波特率为 9600BPS 。 数据发送采用查询方式,数据接收采用中断方式。
- 系统上电初始化之后,关闭蜂鸣器和继电器等无关设备,并向上位机发 送字符串:“Welcome to XMF system!”,回车换行。
- 上位机通过串口发送单字节命令可以控制下位机的 8 个 LED 灯开关。
- 上位机通过串口发送单字节命令可以读取下位机运行信息。
- 通信规约如下表:
