一、矩阵键盘
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最低位置1TF0 = 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;}
}
*/