小编今天原本想用proteus7.8做个计算器显示的仿真,结果被矩阵按键这块整好长时间。秃头秃头!!!
某站上普中科技的教学视频里的“矩阵按键”采用的是行列式的扫描方式实现确定按键位置的。但我用那个程序仿真没整出来。分析修改程序等等,弄了好久没整出来。于是我换了一种方式,用视频里介绍的**“逐行扫描”**的方式弄了一下,整出来了!!,先把我的实验过程分享给大家,在来谈谈我的理解。
仿真图如下:
计算器在proteus7.8中搜“KEYPAD”即可
代码如下:
#include <reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define GPIO_DIG P0
#define GPIO_KEY P1
uchar KeyValue;//设一个最后的传递值(给P0的)
void delay(uint x){//简单的延时函数
while(x--);
}
//采用逐行扫描的方式:
//低四位轮流输出低电平来对矩阵键盘进行逐行扫描,
//(1)当高四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
/*******************************************************************************
* 函 数 名 : KeyDown()
* 函数功能 : 矩阵按键的扫描,确定按下哪个键
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void KeyDown(void){
if((GPIO_KEY&0xf0)!=0xf0){//读取是否有按键按下,符合(1)的说明;补充:P1.0——P1.3是低四位,行表示
KeyValue=~(0xf0|GPIO_KEY); //这步很关键,得到KeyValue(P0)低四位数据,P0.0~P0.3亮的含义:分别表示是第一行到——第四行的某行亮
switch((GPIO_KEY&0xf0)){ //这个'|'要有
case(0X70): KeyValue|=0x10;break; //'4'行
case(0Xb0): KeyValue|=0x20;break; //'3'行
case(0Xd0): KeyValue|=0x40;break; //'2'行
case(0Xe0): KeyValue|=0x80;break; //'1'行
}
} else{GPIO_KEY=_crol_(GPIO_KEY,1);
} //如果高四位是为1,到下一行去判断
if(GPIO_KEY==0xef){GPIO_KEY=0xfe;} //这句很重要!!!,判断第四次移位是否完成:若低电平移到第五位P1.4,则返回P1.0重新轮询扫描!!!
}
void main(){
uchar i;
GPIO_KEY=0xfe; //设一个行的轮询的初始值,从第一行开始
while(1){ //死循环,不断的查询中
for(i=0;i<4;i++){ //完成一整次轮询
KeyDown(); //调用按键判断函数
delay(1000);
GPIO_DIG=KeyValue; //得到按键所在行列的信息
}
GPIO_KEY=0xfe; //再赋值准备下次轮询
i=0;//清零,为下次轮询准备
}
}
这个程序实现的结果是:随意按计算器上的一个键,通过发光二极管显示出这个键在哪行哪列。P0.0—P0.3分别表示第一行至第四行;P0.4—P0.7分别表示第一列至第四列
下面是按下*按键的显示结果图:
总结:这种“逐行扫描”相当于不间断的扫描每行,监听不同列的变化,若某列发生变化,则将行和列的变化信息返回给P1口,判断后得出结果。
问题分析:
为什么普中的“行列扫描”方式仿真的时候不行呢?
我想应该是按键按下时低电平的时间太短的问题。“行列扫描”方式要求按键按下时的低电平要有一段时间的保持,这样在P1口高低位互调时才能发挥想要的效果。
而仿真中计算器的按键按下去后低电平的时间很短,根本不够这种时间的转换,可能换成16个独立组合的行吧。我下面写了个“行列扫描”的程序验证了我这个想法
仿真图还是原来那个不变,但“行列扫描”的代码如下
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
#define GPIO_DIG P0
#define GPIO_KEY P1
uchar KeyValue;
void delay(uint x){
while(x--);
}
void KeyDown(void)
{
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
//测试行
switch(GPIO_KEY)
{
case(0X07): KeyValue=0x01;break;
case(0X0b): KeyValue=0x02;break;
case(0X0d): KeyValue=0x04;break;
case(0X0e): KeyValue=0x08;break;
}
//测试列
GPIO_KEY=0Xf0; //这时候应该还在按下的状态
while((GPIO_KEY==0xf0)==1); //检测按键松手检测
if(GPIO_KEY!=0xf0){
switch(GPIO_KEY)
{
case(0X70): KeyValue|=0x10;break;
case(0Xb0): KeyValue|=0x20;break;
case(0Xd0): KeyValue|=0x40;break;
case(0Xe0): KeyValue|=0x80;break;
}
}
while(GPIO_KEY!=0xf0);
}
}
void main()
{
while(1)
{
KeyDown(); //按键判断函数
delay(10000);
GPIO_DIG=KeyValue; //
}
}
这个行列扫描最后做出的结果是一个按键要按下两次才能在发光二极管处显示出他的位置信息。在仿真中列是P0.4—P0.7,行是P0.0—P0.3
这是我学习后的总结,如果哪位大佬觉得我说的不对,请在评论区批评指出最好有改正信息的连接。