单片机原理及技术(五)—— 单片机与开关、键盘以及显示器件的接口设计(C51编程)

目录

一、单片机控制发光二极管显示

[1.1 单片机与发光二极管的连接](#1.1 单片机与发光二极管的连接)

[1.2 拉电流和灌电流](#1.2 拉电流和灌电流)

[1.3 I/O端口的编程控制](#1.3 I/O端口的编程控制)

二、开关状态检测

[2.1 开关控制单个LED灯亮灭](#2.1 开关控制单个LED灯亮灭)

三、单片机控制LED数码管的显示

[3.1 LED数码管的显示原理](#3.1 LED数码管的显示原理)

[3.2 LED数码管的静态显示与动态显示](#3.2 LED数码管的静态显示与动态显示)

[3.2.1 静态显示方式](#3.2.1 静态显示方式)

[3.2.2 动态显示方式](#3.2.2 动态显示方式)

四、按键式键盘

[4.1 按键式键盘接口设计](#4.1 按键式键盘接口设计)

[4.1.1 键盘输入的特点](#4.1.1 键盘输入的特点)

[4.2 独立式键盘的接口设计案例](#4.2 独立式键盘的接口设计案例)


一、单片机控制发光二极管显示

红色LED 的工作电压在1.8V至2.2V之间,绿色和黄色LED 的工作电压在2V至2.2V之间,蓝色和白色 LED的工作电压在3V至3.5V之间。

LED还有一个电流要求,也就是它的工作电流。一般来说,LED的工作电流在5mA至20mA之间。过高的电流会导致LED发热严重,缩短其寿命,而过低的电流则会导致LED亮度不足。

1.1 单片机与发光二极管的连接

如果P0口作为通用I/O使用,由于漏极开路,需要外接上拉电阻。而P1~P3口内部已有30千欧左右的上拉电阻。

在51单片机中,可以通过P1~P3口直接驱动发光二极管。

  1. 高电平LED灭: 当高电平(电压为Vcc)输入到P1~P3口时,将导致该口的输出为高电平(电压为Vcc)。此时,如果将LED的正极连接到P1~P3口,负极连接到地(GND),LED的两端之间将没有电压差,LED不会亮起,即LED灭。

  2. 低电平LED亮: 当低电平(电压为GND)输入到P1~P3口时,将导致该口的输出为低电平。此时,如果将LED的正极连接到P1~P3口,负极连接到Vcc,LED的两端之间会有电压差,LED会亮起,即LED亮。

注意:

  • 单片机P1~P3口的输出电平与Vcc和GND之间的电压差有关。在51单片机中,高电平一般为Vcc(一般为5V),低电平一般为GND(0V)。当P1~P3口输出高电平时,输出电压等于Vcc;当P1~P3口输出低电平时,输出电压等于GND。

1.2 拉电流和灌电流

拉电流和灌电流是在电路中实现高电平和低电平的两种方式。

  1. 拉电流(Sink Current):在拉电流方式下,单片机的I/O口通过接地电阻将电流引入地(GND),从而实现低电平。当I/O口处于低电平状态时,I/O口会将电流"拉入"地,使接在I/O口上的元件(如LED)处于亮的状态。拉电流方式适用于需要让电流从元件流向地的场合。

  2. 灌电流(Source Current):在灌电流方式下,单片机的I/O口通过输出的高电平信号提供电流给外部元件,使其处于高电平状态。当I/O口处于高电平状态时,I/O口会向外部元件提供电流,使其处于亮的状态。灌电流方式适用于需要将电流从单片机输出到外部元件的场合。

1.3 I/O端口的编程控制

在51单片机中,I/O端口的编程控制是通过对特定的寄存器进行操作来实现的。 选择需要操作的I/O端口,通过设置P0、P1、P2或P3寄存器来选择对应的端口。 确定该端口的工作模式,可以选择为输入模式或输出模式,确定该端口的电平状态,对于输出模式,可以选择为高电平或低电平。

举例:

制作一个流水灯,原理电路图如下所示,8个发光二极管D1~D8经限流电阻分别接至P0口的P0.0~P0.7引脚上,阳极共同接高电平。编写程序来控制发光二极管由上至下反复循环流水点亮,且每次点亮一个发光二极管。

单片机控制的流水灯电路示意图


程序代码:

cpp 复制代码
#include <STC15F2K60S2.H>

void Delayms(int ms);
void main(void)
{
	P2=0XA0;P0=0X00;P2=0X80;P0=0XFF;
	while(1)
	{
		P0=0XFE;
		Delayms(250);
		P0=0XFD;
		Delayms(250);
		P0=0XFB;
		Delayms(250);
		P0=0XF7;
		Delayms(250);
		P0=0XEF;
		Delayms(250);
		P0=0XDF;
		Delayms(250);
		P0=0XBF;
		Delayms(250);
		P0=0X7F;
		Delayms(250);
		P0=0XFF;
		Delayms(250);
	}
}
 
void Delayms(int ms)
{
	int i,j;
	for(i=0;i<ms;i++)
		for(j=845;j>0;j--);
}

cpp 复制代码
#include <STC15F2K60S2.H>

typedef unsigned char u8;
void Delayms(unsigned int xms);
#define PORT P0
 
u8 LED[] = {0x7e,0x3c,0x18,0x00,0x55,0xaa,0xdb,0x99};
 
void main()
{
	u8 i;
	u8 temp;
	PORT = 0xff;
	while(1)
	{	
		
	for(i = 0, temp = 0x01; i < 10; i++)
	{
		PORT = ~temp;
		temp <<= 1;
		Delayms(800);
	}
		for(i = 0, temp = 0x80; i < 10; i++)
		{
			PORT = ~temp;
			temp >>= 1;
			Delayms(800);
		}
		for(i = 0; i < 10; i++)
		{
			PORT <<= 1;
			Delayms(1000);
		}
		PORT = 0x80;
		for(i = 0,temp = 0x80; i < 10; i++)
		{
			PORT |=(temp >>= 1);
			Delayms(1000);
		}
		for(i= 0; i < 10; i++)
		{
			PORT = LED[i];
			Delayms(1000);
		}
	}

二、开关状态检测

2.1 开关控制单个LED灯亮灭

举例:

4个发光二极管D1~D4经限流电阻分别接至P2口的P2.0~P2.3引脚上,阳极共同接高电平。编写程序,每按下一次开关,D1的状态发生变化(亮灭)。

开关、LED与P2口的连接示意图


程序代码:

cpp 复制代码
#include <reg52.h>

sbit LED = P2^0; // 定义 LED 为 P2.0
sbit KEY = P1^5; // 定义 KEY 为 P1.5

void Delayms(int ms)
{
	 int i,j;
	 for(i=0;i<ms;i++)
		 for(j=845;j>0;j--);
}

void main()
{
    while(1)
    {
        if(KEY == 0) // 判断按键是否按下
        {
            LED = ~LED; // 翻转 LED 灯的状态
            while(!KEY); // 等待按键被松开
        }
    }
}

三、单片机控制LED数码管的显示

3.1 LED数码管的显示原理

LED数码管是一种常见的数字显示器件,用于显示数字、字母和符号等信息。LED数码管通常由多个LED组成,每个LED代表一个数字或字符的一部分。常见的LED数码管有共阳极和共阴极两种类型。

8段LED数码管的结构与外形示意图


以下是常见的LED数码管的段码表格,包括显示字符、共阴极字型码和共阳极字型码:

cpp 复制代码
字符      共阳极字型码         共阴极字型码
0            0xC0                0x3F
1            0xF9                0x06
2            0xA4                0x5B
3            0xB0                0x4F
4            0x99                0x66
5            0x92                0x6D
6            0x82                0x7D
7            0xF8                0x07
8            0x80                0x7F
9            0x90                0x6F
A            0x88                0x77
b            0x83                0x7C
C            0xC6                0x39
d            0xA1                0x5E
E            0x86                0x79
F            0x8E                0x71
P            0x8C                0x73
U            0xC1                0x3E
T            0xCE	             0x31
y            0x91                0x6E
H            0x89                0x76
L            0xC7                0x38
"灭"         0xFF                0x00

共阴极字型码表示当LED数码管为共阴极结构时,每个字母或数字所对应的段的亮灭状态。 共阳极字型码则表示当LED数码管为共阳极结构时,每个字母或数字所对应的段的亮灭状态。 通过控制相应的段亮灭状态,可以在LED数码管上显示出相应的字符。

举例:

用单片机控制一个8段LED数码管,先循环显示单个偶数:0、2、4、6、8,再显示单个奇数:1、3、5、7、9,如此反复循环显示。原理电路及仿真结果,如下图所示。

循环显示单个数字的原理电路及仿真结果示意图


程序代码:

cpp 复制代码
#include <reg52.h>

// 数码管显示的数字(0~9)
unsigned char code LED_CODE[] = {
    0xc0, // 0
    0xa4, // 2
    0x99, // 4
    0x82, // 6
    0x80, // 8
    0xf9, // 1	
    0xb0, // 3	
    0x92, // 5	
    0xf8, // 7  	
    0x90 	// 9	
};

void Delayms(int ms);
void main() {

    // 定义P2.0~P2.6为输出口
    P0 = 0x00;
    while(1) {

        unsigned char i;
        for(i=0; i<10; i++) {   // 显示0~9

            P0 = LED_CODE[i];   // P0输出数码管显示的编码
            Delayms(100);     // 延时1秒

        }

    }

}

void Delayms(int ms)
{
	int i,j;
	for(i=0;i<ms;i++)
		for(j=845;j>0;j--);
}

3.2 LED数码管的静态显示与动态显示

单片机控制LED数码管有两种显示方式:静态显示和动态显示。

3.2.1 静态显示方式

LED数码管的静态显示是指在某时刻显示一位固定的数字或字符,并保持不变。静态显示通常使用多位数码管来显示不同的位数,可以显示0到9的数字和一些字母、字符。

要实现LED数码管的静态显示,需要通过控制每个LED灯管的开关状态来显示所需的数字或字符。每个数字或字符对应的LED灯管需要点亮相应的LED灯,其他LED灯则保持熄灭。

优点:

  1. 显示清晰:LED数码管的亮度高,显示效果清晰,可以在不同光线环境下清晰可见。
  2. 容易识别:可以将数字或字母等信息直接显示出来,容易被人眼识别和理解。
  3. 简单实现:只需要将要显示的数字或字母等信息通过适当的信号控制,传递给相应的LED数码管即可,实现简单。

缺点:

  1. 限制显示内容:静态显示方式只能显示固定的数字或字母等信息,无法实现复杂的动态显示效果。
  2. 空间占用大:由于每个数字或字母都需要占用一个LED数码管,所以对于需要显示多个字符的情况,需要使用多个数码管,占用较大的空间。
  3. 占用I/O口线较多:每位数码管的段码线分别与一个单片机控制的8位I/O口锁存器输出相连,数码管数目增多,则需要增加I/O口的数目。
3.2.2 动态显示方式

LED数码管的动态显示的原理是通过快速切换各个数码管的亮灭状态,使人眼感知到数字或字符的连续变化。

优点:

  1. 连续性显示:通过动态刷新,LED数码管可以实现数字、字母或符号的连续显示,提供更加直观和流畅的信息展示效果。
  2. 实时更新:动态显示LED数码管可以实时更新显示内容,适用于需要实时显示的场景,如计时器、计数器等。
  3. 节省资源:相比静态显示LED数码管,动态显示只需要一个控制器和较少的引脚来控制多个数码管,节省了硬件资源的使用。
  4. 灵活性:动态显示可以通过改变刷新速度、亮灭时间和显示顺序来实现不同的显示效果,具有较大的灵活性。

缺点:

  1. 亮度低:动态显示的亮度相对较低,显示效果可能不如其他显示器件。
  2. 硬件要求较高:动态显示需要较高的刷新速度和精确的控制。
  3. 视觉疲劳:由于动态显示LED数码管的亮灭频率较高,持续观看可能会引起视觉疲劳或不适。

举例:

单片机控制2位数码管,静态显示2个数字'2'和'7'。

2位数码管静态显示的原理电路与仿真示意图


程序代码:

cpp 复制代码
#include <reg51.h>
//单片机控制2只共阳数码管同时显示27两位数字(采用动态显示)。

// 数码管显示的数字(1~8)
unsigned char code LED_CODE[] = {
    0xa4, // 2
    0xf8, // 7  
};
unsigned char code XIN[]={0x01,0x02};

void Delayms(int ms);
void main() {

    // 定义P2.0~P2.6为输出口
    P0 = 0x00;

    while(1) {
			  unsigned char i;	
        for(i=0; i<2; i++) {   // 显示0~9
            P0 = LED_CODE[i];   // P0输出数码管显示的编码
						P2 = XIN[i];
						
            Delayms(1);     // 延时1秒
					  P2=0x00;

        }
    }
}

void Delayms(int ms)
{
	int i,j;
	for(i=0;i<ms;i++)
		for(j=100;j>0;j--);
}

四、按键式键盘

键盘是由若干按键按照一定的规则组成。每一个按键实质上就是一个按钮开关,按结构可以分为有触点开关按键和无触点开关按键。

  1. 有触点开关按键(Mechanical Switch):这种按键开关内部包含金属触点,在按键按下时触点接通,释放时触点断开。有触点开关通常具有明显的按键感觉和声音反馈,适用于需要精确触发和反馈的场景。常见的有触点开关按键有:触摸式按键、薄膜按键、导电橡胶按键和按键式按键等。
  2. 无触点开关(Capacitive Switch):这种按键开关基于电容原理,无内部金属触点。当手指接近按键时,由于电容变化,开关被触发。无触点开关通常没有明显的按键感觉和声音反馈,适用于需要静音操作和长寿命的场景。常见的无触点开关按键有:电容式按键、光点式按键和磁感应按键等。

4.1 按键式键盘接口设计

在51单片机中,可以使用GPIO(通用输入输出)口和外部电阻网络来实现矩阵按键的输入。以下是一种常见的51单片机按键式键盘接口的实现步骤:

  1. 连接矩阵键盘:将矩阵键盘的行和列引脚连接到单片机的GPIO口。通常,行引脚连接到单片机的输出口,列引脚连接到单片机的输入口。
  2. 设置引脚模式:将相应的GPIO口设置为输入或输出模式。输出口用于扫描行,而输入口用于检测按键的状态。
  3. 扫描按键:通过逐行扫描和检测按键状态来确定被按下的按键。可以通过逐个将行引脚置为高电平和检测相应列引脚的状态来实现按键的扫描。
  4. 处理按键事件:根据按键的状态和位置,执行相应的操作或响应。例如,可以在单片机中编写相应的按键处理函数,根据按键的位置执行不同的功能或触发相应的中断。

按键式键盘的优点是操作简单,成本低廉。但它也有缺点,如容易受到外界干扰,按键的数量受限,不适用于需要同时按下多个按键的情况等。

4.1.1 键盘输入的特点

键盘中的一个按键开关的两端分别连接在行线和列线上,列线接地,行线通过电阻接到+5V上。

图中所示的t1和t3分别为键的闭合和断开过程的抖动期(呈现一串负脉冲),抖动时间的长短与开关的机械特性有关,一般为5ms~10ms,t2为稳定的闭合期,其时间由按键动作确定,一般为十分之几秒到几秒,t0、t4为断开期。

键盘开关及其行线波形图


4.2 独立式键盘的接口设计案例

举例:

单片机与4个独立按键k1~k4和8个LED指示灯构成一个独立式键盘系统。4个按键分别对应四种不同的点亮功能。按下k1按键时,8个LED正向(由上至下)流水点亮;按下k2按键时,8个LED反向(由下至上)流水点亮;按下k3按键时,低、高4个LED灯交替点亮;按下k4按键时,8个LED闪烁点亮。

独立式键盘的仿真图


程序代码:

cpp 复制代码
#include <reg51.h>
sbit k1=P1^0;
sbit k2=P1^1;
sbit k3=P1^2;
sbit k4=P1^3;
unsigned char code XIN[]={0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F};
void Delayms(int ms)
{
	int i,j;
	for(i=0;i<ms;i++)
		for(j=845;j>0;j--);
}

unsigned char keyal;
void Delayms(int ms);
void key_scan(void);
void forward(void);
void backward(void);
void alter(void);
void blink(void);

void main()
{
	keyal=0;
	while(1)
	{
		key_scan();
		switch(keyal){
			case 1:forward();
				break;
			case 2:backward();
				break;
			case 3:alter();
				break;
			case 4:blink();
				break;	
		}			
	}
}

void key_scan(void)
{
	P1=0xff;
		if((P1&0x0f)!=0x0f)
		{
			Delayms(10);
			if((P1&0x0f)!=0x0f)
			{
				
				if(k1==0)
					keyal=1;
				if(k2==0)
					keyal=2;
				if(k3==0)
					keyal=3;
				if(k4==0)
					keyal=4;
		}			while((P1&0x0f)!=0x0f);
	}
}

void forward(void)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		P3=XIN[i];
		Delayms(100);
	}
}

void backward(void)
{
	unsigned char i;
	for(i=8;i>-1;i--)
		P3=XIN[i];
		Delayms(100);
}

void alter(void)
{
	P3=0x0f;
	Delayms(100);
	P3=0xf0;
	Delayms(100);
}

void blink(void)
{
	P3=0xff;
	Delayms(100);
	P3=0x00;
	Delayms(100);
}
相关推荐
特种加菲猫20 分钟前
初阶数据结构之队列的实现
开发语言·数据结构·笔记
醉陌离25 分钟前
渗透测试学习笔记——shodan(3)
笔记·学习
家有狸花1 小时前
Node.js笔记(四)局域网聊天室2:服务端接受客户端消息
笔记·node.js
gfxr12122 小时前
笔记mfc11
笔记
微蓝课堂2 小时前
【微蓝课堂】机器人编程|树莓派系列|13-从零开始编写TM1637驱动程序
笔记·python·青少年编程·机器人
TensorFlowGAN2 小时前
华三预赛从零开始学习笔记(每日编辑,复习完为止)
笔记·学习·华三
垂杨有暮鸦⊙_⊙4 小时前
阅读《先进引信技术的发展与展望》识别和控制部分_笔记
笔记·学习
特种加菲猫4 小时前
初阶数据结构之栈的实现
开发语言·数据结构·笔记
明明真系叻4 小时前
第二十二周机器学习笔记:动手深度学习之——线性代数
笔记·深度学习·线性代数·机器学习·1024程序员节
大筒木老辈子4 小时前
Linux笔记---进程:初识进程
linux·服务器·笔记