第九章 独立按键
按键的作用相当于一个开关,按下时接通(或断开),松开后断开(或接通)。实物图、原理图、封装
9.2 需求描述
通过 SW1、SW2、SW3、SW4 四个独立按键分别控制 LED1、LED2、LED3、LED4
的亮灭,具体要求是,按一下(按下并松开)SW,LED 点亮,再按一下 SW,LED 熄灭。
9.3 硬件设计
9.3.1 实现思路
为实现上述需求,需要设法令单片机感知到按键被按下,也就是说在按键被按下时,
需要向单片机发送一个信号,当单片机收到该信号后,再执行控制 LED 的逻辑即可。
由于 51 单片机的 GPIO 引脚的默认均为高电平,因此只需将按键的一侧接入单片机的
某个 GPIO 引脚,另一侧接地。这样一来,当按键按下时,引脚直接接地,就相当于向单
片机发送了一个低电平信号。

9.3.2 硬件原理图

9.4 软件设计
9.4.1 初步实现
按照原理图,SW1 按下时,P4.2 引脚会被拉低;SW2 按下时,P4.3 引脚会被拉低;
SW3 被按下时,P3.2 引脚会被拉低;SW4 按下时,P3.3 引脚会被拉低。
因此只需检测上述引脚是否变为低电平即可,若检测到变为低电平,就执行控制 LED
的逻辑。需要注意的是,按键的检测需要持续进行,所以需要不停的检查上述引脚是否变
为低电平。
9.4.2 按键单次触发
当前代码的逻辑是,只要按键处在按下的状态,就触发控制 LED 的逻辑。为实现单次
触发,可以在按键按下后,等待按键抬起,并在抬起的一刻,执行控制 LED 的逻辑。
9.4.3 按键消抖
由于按键的抖动,单片机引脚的信号切换并不像我们想象的那样干脆,实际情况如下
图所示

9.4.4 规范代码
按照前文的编码规范,我们将检测按键的代码单独抽取到 Int 层,具体代码如下。
(1)Int_Key.h
cs
#ifndef __INT_KEY_H__
#define __INT_KEY_H__
#include <STC89C5xRC.H>
#include "Util.h"
/**
* @brief 检测 SW1 按键是否按下
*
* @return bit 是或否
*/
bit Int_Key_IsSW1Pressed();
/**
* @brief 检测 SW2 按键是否按下
*
* @return bit 是或否
*/
bit Int_Key_IsSW2Pressed();
/**
* @brief 检测 SW3 按键是否按下
*
* @return bit 是或否
*/
bit Int_Key_IsSW3Pressed();
/**
* @brief 检测 SW4 按键是否按下
*
* @return bit 是或否
*/
bit Int_Key_IsSW4Pressed();
#endif /* __INT_KEY_H__ */
(2)Int_Key.c
cs
#include "Int_Key.h"
#include <STC89C5xRC.H>
#include "Com_Util.h"
#define SW1 P42
#define SW2 P43
#define SW3 P32
#define SW4 P33
bit Int_Key_IsSW1Pressed()
{
if (SW1 == 0) {
Com_Util_Delay1ms(10);
if (SW1 == 0) {
while (SW1 == 0);
return 1;
}
}
return 0;
}
bit Int_Key_IsSW2Pressed()
{
if (SW2 == 0) {
Com_Util_Delay1ms(10);
if (SW2 == 0) {
while (SW2 == 0);
return 1;
}
}
return 0;
}
bit Int_Key_IsSW3Pressed()
{
if (SW3 == 0) {
Com_Util_Delay1ms(10);
if (SW3 == 0) {
while (SW3 == 0);
return 1;
}
}
return 0;
}
bit Int_Key_IsSW4Pressed()
{
if (SW4 == 0) {
Com_Util_Delay1ms(10);
if (SW4 == 0) {
while (SW4 == 0);
return 1;
}
}
return 0;
}
(3)Main.c
cs
#include <STC89C5xRC.H>
#include "Int_Key.h"
#define LED1 P00
#define LED2 P01
#define LED3 P02
#define LED4 P03
void main()
{
while (1) {
if (Int_Key_IsSW1Pressed()) {
LED1 = ~LED1;
}
if (Int_Key_IsSW2Pressed()) {
LED2 = ~LED2;
}
if (Int_Key_IsSW3Pressed()) {
LED3 = ~LED3;
}
if (Int_Key_IsSW4Pressed()) {
LED4 = ~LED4;
}
}
}
第 10 章 矩阵按键
按下按键矩阵中的 SW5 到 SW20 按键后,数码管显示对应的按键编号。
10.2 硬件设计
10.2.1 实现思路
由于按键矩阵中共有 4x4=16 个按键,如果每个按键都接入一个 GPIO 引脚,势必会造
成引脚的浪费,为了节省引脚,我们同样可以借用动态扫描的思想,具体逻辑如下。
10.2.2 51 单片机引脚内部结构
在上面的演示中,我们应注意的一个问题是:当高电平引脚和低电平引脚短接时,结
果是高电平引脚被拉低,而不是低电平引脚被拉高。这实际上是由 51 单片机引脚的内部结
构决定的,其简化结构如下图所示。

上图的工作原理是:当端口锁存器为 0 时,mos 管导通,此时引脚直接接地,输出低
电平;当端口锁存器为 1 时,mos 管关闭,引脚靠内部的弱上拉电阻(弱的意思是电流很
微弱,可进一步理解为上拉电阻的阻值很大)拉置高电平。
根据上图,可以得出结论,当引脚输出高电平时,很容易被被外部装置拉低(因为上
拉电阻分压多),而当引脚输出低电平时,却很难被拉高(因为引脚接地)。这就是常说的
弱上拉+强下拉模式。
所以在本节矩阵按键的例子中,当我们按下按键短接高低引脚,最终结果是高电平引
脚被拉低。
10.2.3 硬件原理图

10.3 软件设计
(1)Int_DigitalTube.h 和 Int_DigitalTube.c
数码管项目中的 Int_DigitalTube.h 和 Int_DigitalTube.c 复制到当前项目的 Int 目录。
(2)Int_MatrixKey.h
在项目的 Int 目录下创建 Int_MatrixKey.h,写入以下内容。
cs
#ifndef __INT_KEYMATRIX_H__
#define __INT_KEYMATRIX_H__
#include"Com_Util.h"
u8 Int_KetMatrix_CheckSW();
#endif /* __INT_KEYMATRIX_H__ */
(3)Int_MatrixKey.c
在项目的 Int 目录下创建 Int_MatrixKey.c,写入以下内容。
cs
#include "Int_KeyMatrix.h"
#include<STC89C5xRC.H>
u8 Int_KetMatrix_CheckSW()
{
u8 i,j;
u8 lines[4]={0xFE,0xFD,0xFB,0xF7};
u8 columns[4]={0x10,0x20,0x40,0x80};
for (i = 0; i < 4; i++)
{
P2=lines[i];
for ( j = 0; j< 4; j++)
{
if((P2 & columns[j])==0)
{
Com_Util_Delay1ms(10);
if((P2 & columns[j])==0)
{
while((P2 & columns[j])==0);
return j+5+i*4;
}
}
}
}
return 0;
}
(4)main.c
cs
#include"Int_KeyMatrix.h"
#include"Int_DigitalTube.h"
void main()
{
u8 key;
Int_DigitalTube_Init();
while(1){
key=Int_KetMatrix_CheckSW(); //检测按键
if(key){
Int_DigitalTube_DisplayNum(key);
}
Int_DigitalTube_Refresh();
}
}
第 11 章 蜂鸣器
11.1 蜂鸣器简介
蜂鸣器是一种能够发出声音的电子元器件,常用于报警、提示和音频信号输出等场景。
其内部结构如下图所示。
当电流通过线圈时会产生电磁场,电磁场与永磁体相互作用,从而使金属膜产生震动
而发声。为使金属膜持续震动,蜂鸣器需要使用震荡电路进行驱动。有些蜂鸣器元件内部
自带震荡驱动电路,这种蜂鸣器叫做有源蜂鸣器(Active Buzzer,自激式蜂鸣器);而有些
则不带震荡驱动电路,这种蜂鸣器叫做无源蜂鸣器(Passive Buzzer,它激式蜂鸣器)。
更多信息可参考如下内容。
(1)有源蜂鸣器

(2)无源蜂鸣器

11.2 需求描述
为按键矩阵增加按键提示音,要求按键按下后,蜂鸣器响 0.1s。
11.3 硬件设计
11.3.1 硬件原理图
本课程使用的是无源蜂鸣器,所以需要从外部输入一定频率的方波,方波的频率就是
蜂鸣器发声的频率,这里我们使用 500Hz 的方波即可。

11.4 软件设计
(1)Int_DigitalTube.h 和 Int_DigitalTube.c
将数码管项目中的 Int_DigitalTube.h 和 Int_DigitalTube.c 复制到当前项目的 Int 目录。
(2)Int_MatrixKey.h 和 Int_MatrixKey.c
将按键矩阵项目中的 Int_MatrixKey.h 和 Int_MatrixKey.c 复制到当前项目的 Int 目录。
(3)Int_Buzzer.h
在 Int 目录下创建 Int_Buzzer.h,写入以下内容。
cs
#ifndef __INT_BUZZER_H__
#define __INT_BUZZER_H__
#include <STC89C5xRC.H>
#define BUZZ P46
/**
* @brief 蜂鸣器响 0.1s
*
*/
void Int_Buzzer_Buzz();
#endif /* __INT_BUZZER_H__ */
(4)Int_Buzzer.c
在 Int 目录下创建 Int_Buzzer.c,写入以下内容。
cs
#include "Int_Buzzer.h"
#include "Util.h"
void Int_Buzzer_Buzz()
{
unsigned char counter = 100;
while (counter) {
BUZZ = ~BUZZ;
Delay1ms(1);
--counter;
}
}
(5)Main.c
cs
#include "Int_DigitalTube.h"
#include "Int_MatrixKey.h"
#include "Int_Buzzer.h"
int main()
{
u8 key_pressed = 0;
Int_DigitalTube_Init();
Int_DigitalTube_DisplayNum(key_pressed);
while (1) {
key_pressed = Int_MatrixKey_CheckKey();
if (key_pressed) {
Int_DigitalTube_DisplayNum(key_pressed);
Int_Buzzer_Buzz(); //按键提示音
}
Int_DigitalTube_Refresh();
}
}