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

sbit ROW_I NPUT2=P2^ 1; //第 2 行输入口。 sbit ROW_I NPUT3=P2^ 0; //第 3 行输入口。 sbit COLUM N_OUTPUT1 =P2^5; //第 1 列输 出口。 sbit COLUM N_OUTPUT2 =P2^4; //第 2 列输 出口。 sbit COLUM N_OUTPUT3 =P2^3; //第 3 列输 出口。 volatile u nsigned c har …

100%1 / 836
上图 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 电路是灌入式驱动方式。
}
}
/* 注释二:
* 本节破题的关键:
* 矩阵按键涉及的按键数量很多,但是实际项目上一般只需要少数个别按键具备这种
* “单击”与“连续均匀触发”的特殊技能,因此,在代码上,必须把这类“特殊技能按键”与
* “大众按键”区分开来,才能相互清晰互不干扰。本节的“特殊技能按键” S1 S9。
* 如果觉得本节的讲解不够详细具体,请先阅读一下前面章节“独立按键按住不松手的连续均匀触发”
*/
void KeyScan(void) //此函数放在定时中断里每 1ms 扫描一次
{
static unsigned char Su8KeyLock=0;
static unsigned int Su16KeyCnt=0;
static unsigned char Su8KeyStep=1;
static unsigned char Su8ColumnRecord=0;
switch(Su8KeyStep)
{
case 1:
if(0==Su8ColumnRecord)
{
COLUMN_OUTPUT1=0;
COLUMN_OUTPUT2=1;
COLUMN_OUTPUT3=1;
}
else if(1==Su8ColumnRecord)
{
COLUMN_OUTPUT1=1;
COLUMN_OUTPUT2=0;
COLUMN_OUTPUT3=1;
}
else
{
COLUMN_OUTPUT1=1;
COLUMN_OUTPUT2=1;
COLUMN_OUTPUT3=0;
}
Su16KeyCnt=0;
Su8KeyStep++;
break;
case 2: //等待列输出稳定,但不是去抖动延时
Su16KeyCnt++;
if(Su16KeyCnt>=2)