单片机汇编矩阵键盘实验(扫描法)

单片机汇编矩阵键盘实验(扫描法),第1张

关于扫描按键的原理,可以看下面这篇文章。

本文以循序渐进的思路,引导大家思考如何用最少的IO驱动更多的按键,并依次给出5种方案原理图提供参考。在实际项目中我们经常会遇到有按键输入的需求,但有的时候为了节省资源成本,我们都会选择在不增加硬件的情况下使用最少的控制器IO驱动更多的按键,那么具体是怎么做的呢,下面我们就以用5个IO引脚为例,讲下怎么设计可以实现更多的按键?共有5种设计思路,下面依次介绍。

思路一

首先通常想到的可能是下面这样的设计:

上图形式的按键就是我们通常说的行列式按键,它的驱动思路是这样的:

1 对IO1、2、3配置为推挽输出,依次只让其中一个输出为0其他输出为1。

2 对IO4、5进行读 *** 作,根据读出的结果判断哪个按键按下

例如:配置IO1、2、3为011,读IO4、5,若IO4为0则SW14按下,若IO5为0则SW15按下;

依次的配置IO1、2、3为101,读IO4、5,若IO4为0则SW24按下,若IO5为0则SW25按下;

依次的配置IO1、2、3为110,读IO4、5,若IO4为0则SW34按下,若IO5为0则SW35按下;

思路二

但是我们在不知道行列式按键之前我们肯定是依次将IO口接一个按键到GND或者到VCC,然后去读IO口去判断哪个按键按下,这也是最简单的方法,但是很浪费IO口,下面这种就结合了这种简单方法和行列式的思路,实现了又多增加3个按键,如下图:

这里我们的思路是先依次读IO1、2、3的电平来识别S1、2、3,哪个按键按下,其后的流程和思路一是一样的,这样就可以识别11个按键了。

思路三

按照扫描的思想,某一时刻设置一个IO口为0,其他IO口读,如果有IO口读到0,则有对应按键按下。比如IO1为0,然后读到IO5也为0,那么K15就是按下的。对照这样的思路,我们可以有下面的设计:

这个电路按键识别思路是这样的:

1 只配置IO1为0,其他IO读,若IO5读到0,则K15按下,若IO4读到0,则K14按下,依次识别K13,K12;

2 只配置IO2为0,其他IO读,若IO5读到0,则K25按下,若IO4读到0,则K24按下,依次识别K23;

3 只配置IO3为0,其他IO读,若IO5读到0,则K35按下,若IO4读到0,则K34按下;

4 只配置IO4为0,其他IO读,若IO5读到0,则K45按下;

思路四

对于思路3我们发现,如果只配置IO5为0,其他IO读,若IO1读到0,则K15按下,若IO2读到0,则K25按下,依次可识别K35和K45。这样就存在重复,那么有么有好的方法,解决这样的重复呢?我们发现,若配置IO1为0,K15按下,电流流向IO1的,若配置IO5为0,同样K15按下,电流是流向IO5的。这样我们就可以通过区分电流的流向来避免重复。于是就有了下图的设计:

这样就可以避免重复,IO5为0时,按K15,IO1是读不到0的。那么怎样设计,IO5为0时对应一个按键按下IO1为0呢?如是就有人想到下面的设计:

这个电路按键识别思路是这样的:

1 只配置IO1为0,其他IO读,若IO5读到0,则K51按下,若IO4读到0,则K41按下,依次识别K31,K21;

2 只配置IO2为0,其他IO读,若IO5读到0,则K52按下,若IO4读到0,则K42按下,依次识别K32,K21';

3 只配置IO3为0,其他IO读,若IO5读到0,则K53按下,若IO4读到0,则K43按下,依次识别K32’,K31';

4 只配置IO4为0,其他IO读,若IO5读到0,则K54按下,若IO4读到0,则K43’按下,依次识别K42’,K41';

5 只配置IO5为0,其他IO读,若IO4读到0,则K54’按下,若IO3读到0,则K53’按下,依次识别K52’,K51'。

思路五

很多人可能认为思路四已经识别20个按键了,但是真的就没有其他方法了吗?不要忘了,我们还没有将思路二你介绍的那种最简单的方法结合进去,于是又可以多5个按键,如下图:

这样我们可以先识别K01、K02、K03、K04、K05,若没有按键按下然后再和思路四的设计一样去识别其他按键。但这样存在一个问题,如果IO1配置为0,IO5读到0,那么怎么知道是K51按下还是K05按下呢,这里只需要在程序里做下判断,先判断下是不是K05按下,若不是就是K51,因为按键K01、K02、K03、K04、K05在5个IO口都为读取的情况下,就可以识别,不需要扫描识别处理,相当于这5个按键优先级高与其他按键。

总结

综合上述,5个IO口最多可以识别25个按键,思路五程序上处理比较麻烦,若实际中只按思路四设计,也可识别20个按键,那么如果有N个IO口可识别多少按键呢?这里给出如下公式:

假设有N个IO口按照思路三可以识别N(N-1)/2个;

按照思路四可识别N(N-1)个;

按照思路5可以识别N(N-1)+N个。

最后再说下,如果实际设计时,还是按思路四设计好,软件也没那么麻烦。如果是你的话你会选择哪种方法呢?你还有没有其他的设计方法呢?

#define uint unsigned int

#define uchar unsigned char

#define Key_Scanf P1 //4X4按键显示;

#define Print_Out P0 //显示数字P0;

#define PORT P2 //数码选择;

#define SYSLED P0^7; //系统指示灯;

uchar code table[]={0xff,0x4f,0x12,0x06,0x4c,0x24,0x20,0x0f,0x00,0x0c,0x01}; //0~9数字

void display(uchar); //显示

void delay_50us(uint t); //延时50微秒,防抖动

uchar keyscan(void); //键盘

void SYS_LED(void); //指示灯

uchar a=0;

uchar keynum;

void delay_50us(uint t)

{

uchar j;

for(;t>0;t--)

for(j=19;j>0;j--);

}

void SYS_LED()

{

SYSLED = 0;

delay_50us(100);

SYSLED = 1;

}

void display( uchar keynum) //根据keynum返回值显示数字

{

PORT=0xfe;

Print_Out=table[keynum];

}

uchar keyscan()

{

Key_Scanf=0xbf; // 扫描第一横行,后四位为1,前四位线与,然后跟原来数值比较,如果不等,则把对应的数字赋给keynum

if((Key_Scanf&0xbf)!=0xbf)

{

delay_50us(100);

if((Key_Scanf&0xbf)!=0xbf)//消抖

{

SYS_LED();

a=Key_Scanf&0xbf;

switch(a)

{

case 0x77:keynum=1;break;

case 0x7b:keynum=2;break;

case 0x7d:keynum=3;break;

default:break;

}

}

}

Key_Scanf=0xdf; //扫描第二横行,后四位为1,前四位线与;

if((Key_Scanf&0xdf)!=0xdf)

{

delay_50us(100);

if((Key_Scanf&0xdf)!=0xdf)

{

a=Key_Scanf&0xdf;

switch(a)

{

case 0x7e:keynum=4;break;

case 0xb7:keynum=5;break;

case 0xbb:keynum=6;break;

default: break;

}

}

}

Key_Scanf=0xef; //扫描第三横行,后四位为1,前四位线与;

if((Key_Scanf&0xef)!=0xef)

{

delay_50us(100);

if((Key_Scanf&0xef)!=0xef)

{

a=Key_Scanf&0xef;

switch(a)

{

case 0xbd:keynum=7;break;

case 0xbe:keynum=8;break;

case 0xe7:keynum=9;break;

default: break;

}

}

}

Key_Scanf=0xf7; //扫描第四横行,后四位为1,前四位线与;

if((Key_Scanf&0xf7)!=0xf7)

{

delay_50us(100);

if((Key_Scanf&0xf7)!=0xf7)

{

a=Key_Scanf&0xf7;

switch(a)

{

case 0xeb:keynum=10;break;

default: break;

}

}

}

return keynum;

}

void main(void)

{

while(1)

{

keyscan();

display(keynum);

}

}

bit keyscan()//扫描按键函数

{

bit yes=0;

unsigned char temp;

P2=0xfe;//扫描第1行

temp=P2;//读取端口值,检测是否有按键按下

if(temp!=0xfe)

{

delay(15);//按下去抖

temp=P2;

if(temp!=0xfe)//再一次判断,确实有键按下再执行

{

yes=1;

while(P2!=0xfe);

delay(15);

while(P2!=0xfe);

switch(temp)//获取键值

{

case 0x7e:NUM=14;break;

case 0xbe:NUM=0;break;

case 0xde:NUM=12;break;

case 0xee:NUM=11;break;

}

}

}

P2=0xfd;//扫描第2行

temp=P2;

if(temp!=0xfd)

{

delay(15);//按下去抖

temp=P2;

if(temp!=0xfd)

{

yes=1;

while(P2!=0xfd);

delay(15);

while(P2!=0xfd);

switch(temp)

{

case 0x7d:NUM=7;break;

case 0xbd:NUM=8;break;

case 0xdd:NUM=9;break;

case 0xed:NUM=15;break;

}

}

}

P2=0xfb;//扫描第3行

temp=P2;

if(temp!=0xfb)

{

delay(15);//按下去抖

temp=P2;

if(temp!=0xfb)

{

yes=1;

while(P2!=0xfb);

delay(15);

while(P2!=0xfb);

switch(temp)

{

case 0x7b:NUM=4;break;

case 0xbb:NUM=5;break;

case 0xdb:NUM=6;break;

case 0xeb:NUM=10;break;

}

}

}

P2=0xf7;//扫描第4行

temp=P2;

if(temp!=0xf7)

{

delay(15);//按下去抖

temp=P2;

if(temp!=0xf7)

{

yes=1;

while(P2!=0xf7);

delay(15);

while(P2!=0xf7);

switch(temp)

{

case 0x77:NUM=1;break;

case 0xb7:NUM=2;break;

case 0xd7:NUM=3;break;

case 0xe7:NUM=13;break;

}

}

}

return yes;

}

其中NUM为全局变量,每次检测到有按键按下时会返回‘1’,通过读取NUM来获得键值,四个switch语句中的NUM值完全取决于你要把该按键设定为何值而定的。

本程序的键盘扫描的原理就是进行行扫描,每一行先对端口写行扫描字,然后再读取端口值,若该行有按键按下,则按键对应列线被拉为低电平。扫描完四行就可以检测一遍16个键了。

#include <intrinsh>  //_nop_();所需头文件

/键盘扫描错误/

void scan()

{

unsigned int media;

P2=0x0f;

_nop_();     //在这里延时一个或几个指令周期试试

P2=P2|0xf0;

key=P2;

}

7个脚作扫描输出(比如0000001做左移或1111110做左移;当然也可以做右移),8个脚做采集输入。每扫描输出一个脚,就检查下采集输入脚有无变化,有变化,再查出是哪个按钮按下去了就OK了。

电路网上面有,简单得很。当然你也可以用4094做输出,只用一个4094,占三个I/O口。用4021做输入检测,也只占三个I/O口。

希望对你有帮助。

以上就是关于单片机汇编矩阵键盘实验(扫描法)全部的内容,包括:单片机汇编矩阵键盘实验(扫描法)、设计一个3 行×4列的矩阵键盘,采用行扫描法编写程序,当有按键动作时,能够获得按键、51单片机 矩阵键盘扫描等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9337543.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存