矩阵键盘与定时器
一、矩阵键盘
1、矩阵键盘概述
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式
采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态
2、扫描的概念
数码管扫描(输出扫描):显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描):读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
扫描的特点:节省I/O口
3、代码实现
#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能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。
- 高优先级中断可以打断低优先级中断
- 中断对外界紧急事件的实时处理能力
- 中断源、中断优先级
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
#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流水灯
#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
#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
#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;
}
}
*/