从单片机基础到程序框架(全集 2019pdf版) - 第523页

上图 10 5.1.3 3*3 矩阵按键的 电路 矩阵按键与 前面章节独 立按键的 “按住不松手的 连续均匀 触发” 的处理思路是一样的 。 在电 脑上删除某 个 文 件 某 行 文 字 的 时 候 , 单 击 一 次 “ 退 格 按 键 [Backsp ace] ” , 就 删 除 一 个 文 字 , 如 果 按 住 “ 退 格 按 键 [Backspace ]”不松 手, 就会“ 连续 均匀 ”的 触发 “删 除” 的功 能, 自动…

100%1 / 836
第一百零五节: 矩阵按键按住不松手的连续均匀触发。
【105.1 按住不松手的连续均匀触发。
上图 105.1.1 有源蜂鸣器电路
上图 105.1.2 LED 电路
上图 105.1.3 3*3 矩阵按键的电路
矩阵按键与前面章节独立按键的“按住不松手的连续均匀触发”的处理思路是一样的在电脑上删除某
退[Backspace]退
[Backspace]”不松手,就会“连续均匀”的触发“删除”的功能,自动逐个把整行文字删除清空,这就
“按住不松手的连续均匀触发”应用案例之一。除此之外,在很多需要人机交互的项目中都有这样的功能,
为了快速加减某个数值,按住某个按键不松手某个数值有节奏地快速往上加或者快速往下减。这种按住
不松手连续均匀触发”的按键识别,在程序上有“3 个时间”需要留意 1 个是按键单击的“滤波时间
2 个是按键“从单击进入连击的间隔时(此时间是“单击”“连击”分界线) 3 个是按键“连
击”的间隔时间
本节能如1)8 按键马灯 1 LED S1
按键,“亮的 LED”就“往左边跑一步”相反,每触发一次 S9 按键“亮的 LED”就“往右边跑一步”。如果
按住 S1 或者 S9 不松手就连续触发,“亮的 LED”就“连续跑”,一直跑到左边或者右边的尽头。2)按键每
“单击”一次 S1 或者 S9 蜂鸣器就鸣叫一次,但是,当按键“从单击进入连击”后,蜂鸣器就不鸣叫代码
如下:
#include "REG52.H"
#define KEY_VOICE_TIME 50
#define KEY_SHORT_TIME 20 //按键单击的“滤波”时间
#define KEY_ENTER_CONTINUITY_TIME 240 //按键“从单击进入连击”的间隔时间
#define KEY_CONTINUITY_TIME 64 //按键“连击”的间隔时间
#define BUS_P0 P0 //8 LED 灯一一对应单片机 P0 口总线
void T0_time();
void SystemInitial(void) ;
void Delay(unsigned long u32DelayTime) ;
void PeripheralInitial(void) ;
void BeepOpen(void);
void BeepClose(void);
void VoiceScan(void);
void KeyScan(void);
void KeyTask(void);
void DisplayTask(void); //显示的任务函数(LED 显示状态)
sbit P3_4=P3^4;
sbit ROW_INPUT1=P2^2; //第 1 行输入口。
sbit ROW_INPUT2=P2^1; //第 2 行输入口。
sbit ROW_INPUT3=P2^0; //第 3 行输入口。
sbit COLUMN_OUTPUT1=P2^5; //第 1 列输出口。
sbit COLUMN_OUTPUT2=P2^4; //第 2 列输出口。
sbit COLUMN_OUTPUT3=P2^3; //第 3 列输出口。
volatile unsigned char vGu8BeepTimerFlag=0;
volatile unsigned int vGu16BeepTimerCnt=0;
unsigned char Gu8LedStatus=0; //LED 灯的状态
unsigned char Gu8DisplayUpdate=1; //显示的刷新标志
volatile unsigned char vGu8KeySec=0; //按键的触发序号
volatile unsigned char vGu8ShieldVoiceFlag=0; //屏蔽声音的标志
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
KeyTask();
DisplayTask(); //显示的任务函数(LED 显示状态)
}
}
/* 注释一:
* Gu8DisplayUpdate 这类“显示刷新变量”在“显示框架”里是很常见的,而且屡用屡爽
* 目的是,既能及时刷新显示,又能避免主函数“不断去执行显示代码”而影响程序效率。
*/
void DisplayTask(void) //显示的任务函数(LED 显示状态)
{
if(1==Gu8DisplayUpdate) //需要刷新一次显示
{
Gu8DisplayUpdate=0; //及时清零,避免主函数“不断去执行显示代码”而影响程序效率
//Gu8LedStatus 是左移的位数,范围(0 7),决定了跑马灯的显示状态。
BUS_P0=~(1<<Gu8LedStatus); //“左移<<”之后的“取反~”,因 LED 电路是灌入式驱动方式。
}
}