Skip to content

矩阵键盘与定时器

一、矩阵键盘

1、矩阵键盘概述

在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式

采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态

2、扫描的概念

数码管扫描(输出扫描):显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果

矩阵键盘扫描(输入扫描):读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果

扫描的特点:节省I/O口

3、代码实现

c
#include <REGX52.H>
#include "Delay.h"

/**
  * @brief  矩阵键盘读取按键键码
  * @param
  * @retval KeyNumber 按下按键的键码值
			如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回0
  */
unsigned char MatrixKey()
{
	unsigned char KeyNumber=0;
	
	P1=0xFF;
	P1_3=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
	
	P1=0xFF;
	P1_2=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
	if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
	if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
	if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
	
	return KeyNumber;
}

二、定时器原理

1、定时器概述

定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成

定时器作用: 用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。替代长时间的Delay,提高CPU的运行效率和处理速度

2、STC89C52定时器资源

定时器个数:3个(T0、T1、T2)

T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源

3、定时器框图

每隔"一秒",计数单元的数值就增加一,当计数单元数值增加到"设定的闹钟提醒时间"时,计数单元就会向中断系统发出中断申请

4、定时器的工作模式

模式1:16位定时器/计数器

SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHz

12T mod:每1µs计数

5、中断系统概念

中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。

当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。

当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。

  1. 高优先级中断可以打断低优先级中断
  2. 中断对外界紧急事件的实时处理能力
  3. 中断源、中断优先级

6、中断系统流程

7、STC89C52中断资源

中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)

中断优先级个数:4个

中断号:

8、定时器和中断系统

为了方便讲解,这里使用的中断系统图是传统51单片机的图

9、定时器相关寄存器

寄存器是连接软硬件的媒介

在单片机中寄存器就是一段特殊的RAM存储器,一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式

寄存器相当于一个复杂机器的“操作按钮”

三、计时器的使用

1、LED闪烁

计数器:0~65535

每隔1µs计数加,总共定时时间65535µs

64535离计数器溢出差值1000,所以计时时间为1ms

c
#include <REGX52.H>

void Timer0_Init() {
    //TMOD = 0x01;    	//0000 0001 => 影响前四位
    TMOD &= 0xF0;		//把TMOD低四位清零、高四位保留
    TMOD |= 0x01; 		//把TMOD最低位置1
    TF0 = 0;
    TR0 = 1;
    TH0 = 64535 / 256;
    TL0 = 65535 % 256 + 1;
    ET0 = 1;			//允许中断
    EA = 1;
    PT0 = 0;
}

void main() {
    Timer0_Init();
    while (1) {

    }
}

void Timer0_Routine() interrupt 1 {
	static unsigned int T0Cnt = 0;
    TH0 = 64535 / 256;
    TL0 = 65535 % 256 + 1;
	T0Cnt++;
	if(T0Cnt >= 1000) {
		T0Cnt = 0;
		P2_0 = ~P2_0;
	}
}

2、LED流水灯

c
#include <REGX52.H>
#include <INTRINS.H>

void Delay(unsigned int xms) {
    unsigned char i, j;
    while (xms--) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
    }
}

/**
  * @brief  获取独立按键键码
  * @param
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key() {
    unsigned char KeyNumber = 0;

    if (P3_1 == 0) {
        Delay(20);
        while (P3_1 == 0);
        Delay(20);
        KeyNumber = 1;
    }
    if (P3_0 == 0) {
        Delay(20);
        while (P3_0 == 0);
        Delay(20);
        KeyNumber = 2;
    }
    if (P3_2 == 0) {
        Delay(20);
        while (P3_2 == 0);
        Delay(20);
        KeyNumber = 3;
    }
    if (P3_3 == 0) {
        Delay(20);
        while (P3_3 == 0);
        Delay(20);
        KeyNumber = 4;
    }

    return KeyNumber;
}

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param
  * @retval
  */
void Timer0Init() {
    TMOD &= 0xF0;	//设置定时器模式
    TMOD |= 0x01;	//设置定时器模式
    TL0 = 0x18;		//设置定时初值
    TH0 = 0xFC;		//设置定时初值
    TF0 = 0;		//清除TF0标志
    TR0 = 1;		//定时器0开始计时
    ET0 = 1;
    EA = 1;
    PT0 = 0;
}


unsigned char KeyNum, LEDMode;

void main() {
    P2 = 0xFE;
    Timer0Init();
    while (1) {
        KeyNum = Key();		//获取独立按键键码
        if (KeyNum)			//如果按键按下
        {
            if (KeyNum == 1)//如果K1按键按下
            {
                LEDMode++;	//模式切换
                if (LEDMode >= 2)LEDMode = 0;
            }
        }
    }
}

void Timer0_Routine() interrupt 1 {
    static unsigned int T0Count;
    TL0 = 0x18;        	//设置定时初值
    TH0 = 0xFC;      	//设置定时初值
    T0Count++;   		//T0Count计次,对中断频率进行分频
    if (T0Count >= 500)	//分频500次,500ms
    {
        T0Count = 0;
        if (LEDMode == 0)		//模式判断
            P2 = _crol_(P2, 1);	//LED输出
        if (LEDMode == 1)
            P2 = _cror_(P2, 1);
    }
}

3、定时器时钟

main.c

c
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"

unsigned char Sec = 55, Min = 59, Hour = 23;

void main() {
    LCD_Init();
    Timer0Init();

    LCD_ShowString(1, 1, "Clock:");    //上电显示静态字符串
    LCD_ShowString(2, 1, "  :  :");

    while (1) {
        LCD_ShowNum(2, 1, Hour, 2);    //显示时分秒
        LCD_ShowNum(2, 4, Min, 2);
        LCD_ShowNum(2, 7, Sec, 2);
    }
}

void Timer0_Routine() interrupt 1 {
    static unsigned int T0Count;
    TL0 = 0x18;//设置定时初值
    TH0 = 0xFC;//设置定时初值
    T0Count++;
    if (T0Count >= 1000)//定时器分频,1s
    {
        T0Count = 0;
        Sec++;//1秒到,Sec自增
        if (Sec >= 60) {
            Sec = 0;//60秒到,Sec清0,Min自增
            Min++;
            if (Min >= 60) {
                Min = 0;//60分钟到,Min清0,Hour自增
                Hour++;
                if (Hour >= 24) {
                    Hour = 0;//24小时到,Hour清0
                }
            }
        }
    }
}

Timer0.c

c
#include <REGX52.H>

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param
  * @retval
  */
void Timer0Init(void) {
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	T0Count++;
	if(T0Count>=1000)
	{
		T0Count=0;
		
	}
}
*/

Released under the MIT License.