51单片机的知识以及部分实验

写在前面

一些重要的东西!!!

1.二进制与十六进制的转化

image-20220331232930486

2.烧录时各数据的含义

code:表示程序所占用 FLASH 的大小。

data:数据储存器内部 RAM 占用大小。

xdata:数据储存器外部 RAM 占用大小.

image-20220401082028401

51单片机

1.STC89C52芯片介绍

1.1 特性功能介绍

1:增强型 8051 单片机,6 时钟/机器周期和 12 时钟/机器周期可任意选择, 指令代码完全兼容传统 8051。

2:工作电压: 5.5V - 3.3V (5V 单片机) / 3.6V - 2.0V (3V 单片机)

3:工作频率范围: 0~40MHz,相当于普通 8051 的 0~80MHz,实际工作频 率可达 48MHz

4:用户应用程序空间: 4K / 8K / 13K / 16K / 32K / 64K 字节(STC89C516)

5:片上集成 1280 字节或 512 字节或 256 字节 RAM

6:通用 I/O 口(35/39 个), 复位后为:P1/P2/P3/P4 是准双向口/弱上拉(普 通 8051 传统 I/O 口); P0 口是开漏输出,作为总线扩展用时,不用加上拉电阻, 作为 I/O 口用时,需加上拉电阻。

7:ISP(在系统可编程) / IAP(在应用可编程), 无需专用编程器, 无 需专用仿真器,可通过串口( RxD/P3.0, TxD/P3.1)直接下载用户程序,数秒 即可完成一片。

8:有 EEPROM 功能

9:看门狗

10:内部集成 MAX810 专用复位电路(HD 版本和 90C 版本才有),外部晶体 20M 以下时,可省外部复位电路,复位脚可直接接地。

11:有 3 个 16 位定时器/ 计数器,其中定时器 0 还可以当成 2 个 8 位定时 器使用。

12:外部中断 4 路,下降沿中断或低电平触发中断,Power Down 模式可由外 部中断低电平触发中断方式唤醒

13:通用异步串行口(UART),还可用定时器软件实现多个 UART

14:工作温度范围: -40 ~ +85℃(工业级) / 0 ~ 75℃(商业级)

15:封装: LQFP-44, PDIP-40, PLCC-44, PQFP-44。

1.2 引脚介绍

  • 引脚示意图

image-20220331232157006

  • 引脚功能分类

​ ①电源引脚。如 VCC、GND

​ ②时钟引脚。如 XTAL1、XTAL2

​ ③编程控制引脚。如 RST、PSEN、ALE/PROG、EA/Vpp。(此处了解即可)

​ ④I/O 口引脚。如 P0、P1、P2、P3,4 组 8 位 I/O 口。

​ VCC(40 脚)、GND(20 脚):电源引脚,不同型号单片机接入对应电压, 常压为+5V,低压为+3.3V,大家在使用时要查看其芯片所要求的电压。

​ XTAL1(19 脚)、XTAL2(18 脚):外接时钟引脚。XTAL1 为片内振荡电路的 输入端,XTAL2 为片内振荡电路的输出端。8051 的时钟有两种方式,一种是片内 时钟振荡方式,即需在这两个引脚处外接石英晶振和振荡电容,振荡电容的值一 般取 10p~30p;另一种是外部时钟方式,即将 XTAL1 接地,外部时钟信号从 XTAL2 脚输入。通常使用第一种方式。

​ RST(9 脚):复位引脚。当输入连续两个机器周期以上高电平时为有效,用 来完成单片机的复位初始化操作,即单片机从头开始执行程序。

​ PSEN(29 脚):程序存储器允许输出控制端。在读外部程序存储器时 PSEN 低电平有效,以实现外部程序存储器单元的读操作,由于现在我们使用的单片机 内部已经有足够大的 ROM,所以几乎没有人再去扩展外部 ROM,因此这个引脚大 家只需了解即可。

​ ALE/PROG(30 脚):在扩展外部 RAM 时,ALE 用于控制把 P0 口的输出低 8 位地址送锁存器锁存起来,以实现低位地址和数据的隔离。ALE 有可能是高电平 也有可能是低电平,当 ALE 为高电平时,允许地址锁存型号,当访问外部存储器 时,ALE 信号负跳变(即由正变负)将 P0 口上低 8 位地址信号送入锁存器;当 ALE 为高电平时,P0 口上的内容和锁存器输出一致。关于锁存器后面我们会有介 绍。在没有访问外部存储器期间,ALE 以 1/6 振荡周期频率输出(即 6 分频), 当访问外部存储器时,以 1/12 振荡周期输出(12 分频)。从这里可以看到,当 没有扩展外部 RAM 时,ALE 会以 1/6 振荡周期的固定频率输出,因此可以作为外 部时钟,或作为外部定时脉冲使用。PROG 为编程脉冲的输入端,单片机的内部 有程序存储器(ROM),它的作用是用来存放用户需要执行的程序,那么我们怎 样才能将写好的程序存入这个 ROM 中呢?实际上,我们是通过编程脉冲输入才写进去的,这个脉冲的输入端口就是 PROG。现在绝大多数单片机都已经不需要编 程脉冲引脚往内部写程序了,比如我们使用的 STC 单片机,它可以直接通过串口 往里面写程序,只需要三条线与计算机相连即可。而且现在的单片机内部都已经 带有丰富的 RAM,所以也不需要再扩展 RAM 了,因此 ALE/PROG 引脚用于不大, 大家了解即可。

​ EA/Vpp(31 脚):EA 接高电平时,单片机读取内部程序存储器。当扩展有 外部 ROM 时,当读取完内部 ROM 后自动读取外部 ROM。EA 接低电平时,单片机直 接读取外部 ROM。我们没有外扩 ROM,并且需要单片机直接读取内部程序存储器, 因此 EA/Vpp 脚直接接高电平。

​ P0 口(39 脚~32 脚):双向 8 位三态 I/O 口,每个口可独立控制。51 单片 机 P0 口内部没有上拉电阻,若输出高时为高阻态,不能正常输出高电平,因此 该组 I/O 口,每个口可独立控制。51 单片机 P0 口内部没有上拉电阻,若输出高 时为高阻态,不能正常输出高电平,因此该组 I/O 口在使用时务必要外接上拉电 阻,一般我们选择接入 10K 欧上拉电阻。

​ P1 口(1 脚~8 脚):准双向 8 位 I/O 口,每个口可独立控制,内部自带上拉 电阻,这种接口输出没有高阻态,输入也不能锁存,故不是真正的双向 I/O 口。 之所以称它为“准双向”是因为该口在作为输入使用前,要先向该口进行写 1 操作,然后单片机内部才可正确读出外部信号,也就是要使其先有个“准”备的 过程,所以才称为准双向口。对 52 单片机 P1.0 引脚的第二功能未 T2 定时器/ 计数器的外部输入,P1.1 引脚的第二功能为 T2EX 捕捉、重装触发,即 T2 的外 部控制端。

​ P2 口(21 脚~28 脚):准双向 8 位 I/O 口,每个口可独立控制,内部自带上 拉电阻,与 P1 口相似。

​ P3 口(10 脚~17 脚):准双向 8 位 I/O 口,每个口可独立控制,内部自带上 拉电阻。作为第一功能使用时就当做普通 I/O 口,与 P1 口相似。作为第二功能 使用时,各引脚的定义如下:image-20220331232630700

2.单片机的实验

2.1 点亮LED灯

  • 代码
/**************************************************************************************
实验名称:点亮第一个LED
接线说明:
实验现象:下载程序后“LED模块”的D1指示灯点亮
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

sbit LED1=P2^0;	//将P2.0管脚定义为LED1

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{
	LED1=0;	//LED1端口设置为低电平
	while(1)
	{
		
	}		
}

2.2 流水灯实验

  • 代码
/**************************************************************************************
实验名称:LED流水灯实验
接线说明:	
实验现象:下载程序后“LED模块”的D1-D8指示左右点亮
注意事项:																				  
***************************************************************************************/
#include "reg52.h"
#include "intrins.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

#define LED_PORT	P2	//使用宏定义P2端口

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
   	u8 i=0;

	LED_PORT=~0x01;
	delay_10us(50000);
	while(1)
	{
		//方法1:使用移位+循环实现流水灯
		for(i=0;i<8;i++)
		{
			LED_PORT=~(0x01<<i);	//将1右移i位,然后取反将结果赋值到LED_PORT
			delay_10us(50000);
		}
		
		//方法2:使用循环+_crol_或_cror_函数实现流水灯
//		for(i=0;i<7;i++)	 //将led左移一位
//		{									  
//			LED_PORT=_crol_(LED_PORT,1);
//			delay_10us(50000); 	
//		}
//		for(i=0;i<7;i++)	//将led右移一位
//		{
//			LED_PORT=_cror_(LED_PORT,1);
//			delay_10us(50000);	
//		}	
	}		
}

2.3 蜂鸣器实验

  • 代码
/**************************************************************************************
实验名称:蜂鸣器实验
接线说明:	
实验现象:下载程序后蜂鸣器发出声音,一段时间后关闭
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

sbit BEEP=P2^5;	//将P2.5管脚定义为BEEP

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u16 i=2000;

	while(1)
	{
	   	while(i--)//循环2000次
		{
			BEEP=!BEEP;  //产生一定频率的脉冲信号
			delay_10us(100);
		}
		i=0;    //清零
		BEEP=0; //关闭蜂鸣器
	}
}
  • 起风了蜂鸣器演奏
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit voice=P2^5;
uchar code sound[]={0xff,
                    0x2b,0x30,0x30,0x10,0x2b,0x30,0x30,0x10,0x2b,0x20,0x26,0x20,0x20,0x20,
                    0x26,0x20,0x2b,0x30,0x30,0x10,0x2b,0x30,0x30,0x10,0x2b,0x10,0x26,0x10,
                    0x2b,0x10,0x30,0x10,0x40,0x20,0x40,0x20,0x2b,0x30,0x30,0x10,0x2b,0x30,
                    0x30,0x10,0x2b,0x20,0x26,0x20,0x20,0x20,0x26,0x20,0x2b,0x30,0x26,0x10,
                    0x2b,0x20,0x30,0x20,0x2b,0x20,0x2b,0x20,0xff,0x20,0xff,0x20,0x2b,0x30,
					0x30,0x10,0x2b,0x30,0x30,0x10,0x2b,0x20,0x26,0x20,0x20,0x20,0x26,0x20,
					0x2b,0x30,0x26,0x10,0x2b,0x20,0x30,0x20,0x39,0x20,0x39,0x20,0x26,0x10,
					0x2b,0x10,0x30,0x10,0x2b,0x10,0x30,0x20,0x30,0x20,0x26,0x10,0x2b,0x10,
					0x30,0x10,0x2b,0x10,0x30,0x30,0x20,0x10,0x26,0x10,0x2b,0x10,0x30,0x10,
					0x2b,0x10,0x30,0x20,0x30,0x20,0x30,0x20,0x2b,0x20,0x26,0x20,0x30,0x20,0x1c,0x20,0x20,0x10,0x1c,0x10,
                    0x1c,0x30,0x30,0x10,0x19,0x20,0x1c,0x10,0x19,0x10,0x19,0x20,0x19,0x20,
                    0x1c,0x10,0x19,0x10,0x19,0x20,0x26,0x20,0x18,0x10,0x15,0x10,0x18,0x10,
                    0x19,0x10,0x1c,0x20,0x20,0x20,0x1c,0x20,0x20,0x10,0x1c,0x10,0x1c,0x10,
                    0x20,0x10,0x1c,0x10,0x20,0x10,0x1c,0x20,0x20,0x10,0x2b,0x10,0x2b,0x10,
                    0x20,0x20,0x20,0x10,0x26,0x20,0x26,0x20,0x30,0x20,0x2b,0x20,0x26,0x20,
                    0x30,0x20,0x1c,0x20,0x20,0x10,0x1c,0x10,0x1c,0x30,0x30,0x10,0x19,0x20,
                    0x1c,0x10,0x19,0x10,0x19,0x20,0x19,0x20,0x1c,0x10,0x19,0x10,0x19,0x20,
                    0x26,0x20,0x18,0x10,0x15,0x10,0x18,0x10,0x19,0x10,0x1c,0x20,0x20,0x20,
                    0x1c,0x20,0x13,0x10,0x13,0x10,0x13,0x20,0x20,0x20,0x1c,0x20,0x13,0x10,
                    0x13,0x10,0x13,0x10,0x20,0x20,0x1c,0x10,0x1c,0x20,0x1c,0x20,0x1c,0x20,
                    0x1c,0x20,0xff,0x20,0xff,0x20,  0x18,0x20,0x15,0x20,0x13,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,0x0e,0x10,
                    0x10,0x10,0x10,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,0x19,0x10,0x18,0x10,
                    0x18,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,
                    0x0e,0x10,0x10,0x10,0x10,0x10,0x13,0x30,0x15,0x20,0x18,0x10,0x1c,0x10,
                    0x1c,0x10,0x18,0x20,0x1c,0x10,0x15,0x10,0x18,0x10,0x1c,0x20,0x1c,0x10,
                    0x18,0x30,0x13,0x20,0x13,0x20,0x13,0x10,0x12,0x10,0x13,0x20,0x13,0x10,
                    0x15,0x20,0xff,0x20,0xff,0x20,0x18,0x20,0x15,0x20,0x13,0x20,0x0e,0x10,0x10,0x10,
                    0x10,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,
                    0x15,0x20,0x13,0x20,0x0e,0x10,0x10,0x10,0x10,0x20,0x0e,0x10,0x10,0x10,
                    0x10,0x20,0x0e,0x10,0x10,0x10,0x10,0x10,0x13,0x30,0x15,0x20,0x18,0x10,
                    0x1c,0x10,0x1c,0x10,0x13,0x30,0x15,0x20,0x18,0x10,0x1c,0x10,0x1c,0x10,
                    0x18,0x30,0x18,0x20,0x18,0x20,0xff,0x20,0xff,0x20,0x1c,0x10,0x13,0x10,
                    
                    0x15,0x20,0x18,0x10,0x1c,0x10,0x1c,0x10,0x13,0x30,0x15,0x20,0x18,0x10,
                    0x1c,0x10,0x1c,0x10,0x18,0x30,0x18,0x20,0x18,0x20,0x18,0x20,0x18,0x20,0x00,
                   };
uchar zdjs=0, jp;
del(yj);
void main(void)
  {
      uint dpjs=0;
      uchar yj;
      TMOD=0x01,IE=0x82;
      TH0=0xd8, TL0=0xef;
   TR0=1;
while(1)
   {  
      zdjs=0;
      dpjs++; yj=sound[dpjs]; 
     dpjs++; jp=sound[dpjs];
   while(zdjs!=jp)
       { 
      if(yj!=0xff)
   {
             if(yj!=0)
               {
             voice=!voice;
                 del(yj);
                }
                else
          {
          dpjs=0; 
                      break;
                     }
    }
    else
      {
        voice=0;
                 del(jp);
    }
      }
       }
   }
time0() interrupt 1  using 1
      {
         TH0=0xd8, TL0=0xef;
         zdjs++;
           }
del(yj)
    {
      uchar yj2=2;
       while(yj!=0)
         {      
               while(yj2!=0)
                  {
                     yj2--;
                     }
                  yj2=2;
             yj--;  
       }
   
     }

2.4 数码管点亮

  • 数码管介绍

​ 共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极 COM 接到+5V,当某一字段发光二极管的 阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就 不亮。

​ 共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极 COM 接到地线 GND 上,当某一字段发光 二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相 应字段就不亮。

这里面写的代码都是针对共阴数码管的。

  • 数码管码表

①共阴数码管码表

显示的数字 十六进制对应值 二进制对应值
0 0x3f 00111111
1 0x06 00000110
2 0x5b 01011011
3 0x4f 01001111
4 0x66 01100110
5 0x6d 01101101
6 0x7d 01111101
7 0x07 00000111
8 0x7f 01111111
9 0x6f 01101111
A 0x77 01110111
B 0x7c 01111100
C 0x39 00111001
D 0x5e 01011110
E 0x79 01111001
F 0x71 01110001
无显示 0x00 00000000

②共阳数码管码表

显示的数字 十六进制对应值 二进制对应值
0 0xC0 11000000
1 0xF9 11111001
2 0xA4 10100100
3 0xB0 10110000
4 0x99 10011001
5 0x92 10010010
6 0x82 10000010
7 0xF8 11111000
8 0x80 10000000
9 0x90 10010000
A 0x88 10001000
B 0x83 10000011
C 0xC6 11000110
D 0xA1 10100001
E 0x86 10000110
F 0x8E 10001110
无显示 0xFF 11111111
  • 静态数码管的点亮代码
/**************************************************************************************
实验名称:静态数码管实验
接线说明:	
实验现象:下载程序后“数码管模块”最左边数码管显示数字0
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

#define SMG_A_DP_PORT	P0	//使用宏定义数码管段码口

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	SMG_A_DP_PORT=gsmg_code[0];//将数组第1个数据赋值给数码管段选口
	while(1)
	{
	   	
	}		
}
  • 动态数码管

使用了IO拓展芯片74HC138来操控

  • 74HC138芯片的原理

三通道输入、八通道输出译码器

真值表解码对应↓

image-20220401095508961

​ 从上面的管脚功能定义说明及真值表可以知道该芯片使用方法很简单,给 E1、E2 使能管脚低电平,E3 管脚为高电平,至于哪个管脚输出有效电平(低电 平),要看 A0,A1,A2 输入管脚的电平状态。如果 A0,A1,A2 都为低电平,则 Y0 输出有效电平(低电平),其他管脚均输出高电平。如果 A0 为高电平,A1, A2 都为低电平,则 Y1 输出有效电平(低电平),其他管脚均输出高电平。其他几种输出大家可以对照真值表查看。如果 E1、E2 使能管脚任意一个为高电平或 者 E3 为低电平,不论输入是什么,输出都为高电平。

​ 快速查看的方法:A0、A1、A2 输入就相当于 3 位 2 进制数,A0 是 低位,A1 是次高位,A2 是高位。而 Y0-Y7 具体哪一个输出有效电平,就看输入 二进制对应的十进制数值。比如输入是 101(A2,A1,A0),其对应的十进制数 是 5,所以 Y5 输出有效电平(低电平)。

  • 74HC138芯片对应的管脚号

image-20220401095722384

  • 代码
/**************************************************************************************
实验名称:动态数码管实验
接线说明:	
实验现象:下载程序后“数码管模块”显示01234567
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

#define SMG_A_DP_PORT	P0	//使用宏定义数码管段码口

//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

/*******************************************************************************
* 函 数 名       : smg_display
* 函数功能		 : 动态数码管显示
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void smg_display(void)
{
	u8 i=0;

	for(i=0;i<8;i++)
	{
	   	switch(i)//位选
		{
			case 0: LSC=1;LSB=1;LSA=1;break;   //这边LSC或LSB或LSA为1的时候对应的是上面的L
			case 1: LSC=1;LSB=1;LSA=0;break;
			case 2: LSC=1;LSB=0;LSA=1;break;
			case 3: LSC=1;LSB=0;LSA=0;break;
			case 4: LSC=0;LSB=1;LSA=1;break;
			case 5: LSC=0;LSB=1;LSA=0;break;
			case 6: LSC=0;LSB=0;LSA=1;break;
			case 7: LSC=0;LSB=0;LSA=0;break;
		}
		SMG_A_DP_PORT=gsmg_code[i];  //传送段选数据
		delay_10us(100);     //延时一段时间,等待显示稳定
		SMG_A_DP_PORT=0x00;  // 消影
	}
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	while(1)
	{
	   	smg_display();
	}		
}

2.5 独立按键

  • 按键去抖的方法

1,先设置 IO 口为高电平(由于开发板 IO 都有上拉电阻,所以默认 IO 为高 电平)。

2,读取 IO 口电平确认是否有按键按下。

3,如有 IO 电平为低电平后,延时几个毫秒。

4,再读取该 IO 电平,如果仍然为低电平,说明按键按下。

5,执行按键控制程序。

  • 独立按键对应的管脚号

image-20220401102219903

  • 代码
/**************************************************************************************
实验名称:独立按键实验
接线说明:	
实验现象:下载程序后,按下“独立按键”模块中K1键,控制D1指示灯亮灭
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

//定义独立按键控制脚
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;

//定义LED1控制脚
sbit LED1=P2^0;

//使用宏定义独立按键按下的键值
#define KEY1_PRESS	1
#define KEY2_PRESS	2
#define KEY3_PRESS	3
#define KEY4_PRESS	4
#define KEY_UNPRESS	0	

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);
}

/*******************************************************************************
* 函 数 名       : key_scan
* 函数功能		 : 检测独立按键是否按下,按下则返回对应键值
* 输    入       : mode=0:单次扫描按键
				   mode=1:连续扫描按键
* 输    出    	 : KEY1_PRESS:K1按下
				   KEY2_PRESS:K2按下
				   KEY3_PRESS:K3按下
				   KEY4_PRESS:K4按下
				   KEY_UNPRESS:未有按键按下
*******************************************************************************/
u8 key_scan(u8 mode)
{
	static u8 key=1;

	if(mode)key=1;  //连续扫描按键
	if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))  //任意按键按下
	{
		delay_10us(1000);  //消抖
		key=0;
		if(KEY1==0)
			return KEY1_PRESS;
		else if(KEY2==0)
			return KEY2_PRESS;
		else if(KEY3==0)
			return KEY3_PRESS;
		else if(KEY4==0)
			return KEY4_PRESS;	
	}
	else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)	//无按键按下
	{
		key=1;			
	}
	return KEY_UNPRESS;		
}
/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u8 key=0;

	while(1)
	{
	   	key=key_scan(0);
		if(key==KEY1_PRESS)//检测按键K1是否按下
			LED1=!LED1; //LED1状态翻转	
	}		
}

2.6 矩阵按键

  • 扫描按键

1: 行列扫描法:行列扫描法检测时,先送一列为低电平,其余几列全为高电平(此时我们确 定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电 平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列 的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平, 这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。 当然我们也可以将行线置低电平,扫描列是否有低电平。从而达到整个键盘的检测。

2:线翻转法:线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果 有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值, 由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键。

  • 管脚对应

image-20220401104212893

  • 代码

注:二进制的数字排序都是从高位到低位的

注:接通了就是0,没接通就是1

/**************************************************************************************
实验名称:矩阵按键实验
接线说明:	
实验现象:下载程序后,按下“矩阵按键”模块中S1-S16键,对应数码管最左边显示0-F
注意事项:																				  
***************************************************************************************/
#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

#define KEY_MATRIX_PORT	P1	//使用宏定义矩阵按键控制口		

#define SMG_A_DP_PORT	P0	//使用宏定义数码管段码口

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
				0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};	

/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}
/*******************************************************************************
* 函 数 名       : key_matrix_ranks_scan
* 函数功能		 : 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输    入       : 无
* 输    出    	 : key_value:1-16,对应S1-S16键,
				   0:按键未按下
*******************************************************************************/
u8 key_matrix_ranks_scan(void)
{
	u8 key_value=0;

	KEY_MATRIX_PORT=0xf7; //给第一列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xf7)//判断第一列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值	
		{
			case 0x77: key_value=1;break;
			case 0xb7: key_value=5;break;
			case 0xd7: key_value=9;break;
			case 0xe7: key_value=13;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xf7);//等待按键松开	
	
	KEY_MATRIX_PORT=0xfb;//给第二列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfb)//判断第二列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值	
		{
			case 0x7b: key_value=2;break;
			case 0xbb: key_value=6;break;
			case 0xdb: key_value=10;break;
			case 0xeb: key_value=14;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xfb);//等待按键松开	
	
	KEY_MATRIX_PORT=0xfd;//给第三列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfd)//判断第三列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值	
		{
			case 0x7d: key_value=3;break;
			case 0xbd: key_value=7;break;
			case 0xdd: key_value=11;break;
			case 0xed: key_value=15;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xfd);//等待按键松开	

	KEY_MATRIX_PORT=0xfe;//给第四列赋值0,其余全为1
	if(KEY_MATRIX_PORT!=0xfe)//判断第四列按键是否按下
	{
		delay_10us(1000);//消抖
		switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值	
		{
			case 0x7e: key_value=4;break;
			case 0xbe: key_value=8;break;
			case 0xde: key_value=12;break;
			case 0xee: key_value=16;break;
		}
	}
	while(KEY_MATRIX_PORT!=0xfe);//等待按键松开
	
	return key_value;		
}

/*******************************************************************************
* 函 数 名       : key_matrix_flip_scan
* 函数功能		 : 使用线翻转扫描方法,检测矩阵按键是否按下,按下则返回对应键值
* 输    入       : 无
* 输    出    	 : key_value:1-16,对应S1-S16键,
				   0:按键未按下
*******************************************************************************/
u8 key_matrix_flip_scan(void)
{
	static u8 key_value=0;

	KEY_MATRIX_PORT=0x0f;//给所有行赋值0,列全为1
	if(KEY_MATRIX_PORT!=0x0f)//判断按键是否按下
	{
		delay_10us(1000);//消抖
		if(KEY_MATRIX_PORT!=0x0f)
		{
			//测试列
			KEY_MATRIX_PORT=0x0f;
			switch(KEY_MATRIX_PORT)//保存行为0,按键按下后的列值	
			{
				case 0x07: key_value=1;break;
				case 0x0b: key_value=2;break;
				case 0x0d: key_value=3;break;
				case 0x0e: key_value=4;break;
			}
			//测试行
			KEY_MATRIX_PORT=0xf0;
			switch(KEY_MATRIX_PORT)//保存列为0,按键按下后的键值	
			{
				case 0x70: key_value=key_value;break;
				case 0xb0: key_value=key_value+4;break;
				case 0xd0: key_value=key_value+8;break;
				case 0xe0: key_value=key_value+12;break;
			}
			while(KEY_MATRIX_PORT!=0xf0);//等待按键松开	
		}
	}
	else
		key_value=0;

	return key_value;
}

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u8 key=0;

	while(1)
	{
	   	key=key_matrix_ranks_scan();
		if(key!=0)
			SMG_A_DP_PORT=gsmg_code[key-1];//得到的按键值减1换算成数组下标对应0-F段码		
	}		
}
posted @ 2022-07-14 20:14  ihuahua1415  阅读(162)  评论(0)    收藏  举报
*/