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

void main( ) { SystemIn itial(); Delay(10 000); Peripher alInitial() ; while(1) { KeyTask( ); //按键的任 务函数 } } /* 注释一: * 矩阵按键扫 描的详细过程 : * 先输出某 1 列低电平, 其它 2 列输 出高电平,延 时等待 2ms 后( 等此 3 列输 出同步稳定) , * 再分别判断 3 行的 输入 IO 口, 如果 发现哪…

100%1 / 836
上拉电阻拉高的 H(高电平)才行,比如刚好本教程所用的 51 片机内 IO 输出的 H(高电平)是依
内部的上拉电阻产生,如果是其它“非上拉电阻产生的高电平”与“低电平”短接就有“短路烧坏芯片”的
风险,这时就需要额外增三极管开漏式输出”电路或者外挂“开漏式输出集成芯片”电路。继续回到正
题,为什么列输出每切换一次就能识别 3 个按键的状态?举个例子,比如当列输出状态处于(P2.5 L,
P2.4 H,P2.3 H)下,我们读取行输入 P2.2 口,行输入的 P2.2 与列输出 P2.5,P2.4,P2.3 “交
叉处” 3 个按键 S1,S2S3,此时,如果 P2.2 口是 L(低电平)那么必然是 S1“被按下”因为想让 P2.2
口是 L S1 个能,而 S1 “被下”,另两个 S2,S3 使“被下”,P2.2 口也 H
而绝对不会 L,因为 S2,S3 的列输出 P2.4 H,P2.3 H,H H 相互短接输出的结果必然 H。
本节例程实现的功能:9 个矩阵按键,每按下 1 个按键都触发一次蜂鸣器鸣叫。
#include "REG52.H"
#define KEY_VOICE_TIME 50
#define KEY_SHORT_TIME 20 //按键去抖动的“滤波”时间
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);
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;
volatile unsigned char vGu8KeySec=0; //按键的触发序号
void main()
{
SystemInitial();
Delay(10000);
PeripheralInitial();
while(1)
{
KeyTask(); //按键的任务函数
}
}
/* 注释一:
* 矩阵按键扫描的详细过程
* 先输出某 1 列低电平,其它 2 列输出高电平,延时等待 2ms 后(等此 3 列输出同步稳定)
* 再分别判断 3 行的输入 IO 口, 如果发现哪一行是低电平,就说明对应的某个按键被触发。
* 依次循环切换输出的 3 种状态,并且分别判断输入的 3 行,就可以检测完 9 个按键。矩阵按键
* 去抖动处理方法跟我前面讲的独立按键去抖动方法是一样的,不再重复多讲。
*/
void KeyScan(void) //此函数放在定时中断里每 1ms 扫描一次
{
static unsigned char Su8KeyLock=0;
static unsigned int Su16KeyCnt=0;
static unsigned char Su8KeyStep=1;
switch(Su8KeyStep)
{
case 1: //按键扫描输出第一列低电平
COLUMN_OUTPUT1=0;
COLUMN_OUTPUT2=1;
COLUMN_OUTPUT3=1;
Su16KeyCnt=0; //延时计数器清零
Su8KeyStep++; //切换到下一个运行步骤
break;
case 2: //延时等待 2ms 后(等此 3 列输出同步稳定。不是按键的去抖动延时
Su16KeyCnt++;
if(Su16KeyCnt>=2)
{
Su16KeyCnt=0;
Su8KeyStep++; //切换到下一个运行步骤
}
break;
case 3:
if(1==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
Su8KeyStep++; //如果没有按键按下,切换到下一个运行步骤
Su8KeyLock=0; //按键自锁标志清零
Su16KeyCnt=0; //按键去抖动延时计数器清零,此行非常巧妙
}
else if(0==Su8KeyLock) //有按键按下,且是第一次触发
{
if(0==ROW_INPUT1&&1==ROW_INPUT2&&1==ROW_INPUT3)
{
Su16KeyCnt++; //去抖动延时计数器
if(Su16KeyCnt>=KEY_SHORT_TIME)
{
Su16KeyCnt=0;
Su8KeyLock=1;//自锁 1,避免一直触发,只有松开按键,此标志位才会被清零
vGu8KeySec=1; //触发 1 号键 对应 S1
}
}
else if(1==ROW_INPUT1&&0==ROW_INPUT2&&1==ROW_INPUT3)
{
Su16KeyCnt++; //去抖动延时计数器
if(Su16KeyCnt>=KEY_SHORT_TIME)
{
Su16KeyCnt=0;
Su8KeyLock=1;//自锁 1,避免一直触发,只有松开按键,此标志位才会被清零
vGu8KeySec=2; //触发 2 号键 对应 S2
}
}
else if(1==ROW_INPUT1&&1==ROW_INPUT2&&0==ROW_INPUT3)
{
Su16KeyCnt++; //去抖动延时计数器
if(Su16KeyCnt>=KEY_SHORT_TIME)
{
Su16KeyCnt=0;
Su8KeyLock=1;//自锁 1,避免一直触发,只有松开按键,此标志位才会被清零
vGu8KeySec=3; //触发 3 号键 对应 S3
}
}
}
break;