OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 11859|回复: 15

关于51单片机的矩阵键盘扫描的两种方法的困惑,高手解答~

[复制链接]

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
发表于 2013-5-5 19:36:22 | 显示全部楼层 |阅读模式
        一般的按键查询法有2种:
(1)把键盘程序放在主程序的while(1)循环里不停的查询。
 
(2)
为防漏键,将按键程序放在定时器0的中断服务程序里,约每10ms中断一次;其返回键值赋给一个全局变量key_value,然后在主函数里将根据key_value的值来做出相应的动作!
        分析上述两种方法:对于第一种方法,如果主程序特别长且很消耗时间,那么很可能出现按键漏扫的情况,不可靠。第二种情况是每隔段时间就去扫描下按键,理论上和主程序的while(1)循环里代码是否长和消耗时间应该无关,应该是很可靠的一种按键扫描方法!但事实就不是这样,下面请看代码,很简单的哦~
//用定时器中断扫描矩阵键盘程序
#include <reg52.h>
#define s8 signed char
#define u8 unsigned char
#define u16  unsigned int
#define u32  unsigned long int
//#define PORT P1
//共阳极不带小数点
u8 code segment[]  = {0XC0, 0XF9, 0XA4, 0XB0, 0X99, 0X92, 0X82, 0XF8,
                               0X80, 0X90, 0X88, 0X83, 0XC6, 0XA1, 0X86, 0X8E};
u8 code position[] = {0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F,0X00,0XFF};
u8 key_value = 20u;//u表示无符号整型
u16 tmr0_value = 10000u;
void rough_delay_1ms(u16);
void digitron_static_display(u8, u8);
void init_tmr0(void);
u8 scan_MatrixKey(void); 

int main(void)
{
     init_tmr0();//初始化定时器0
     while (1)
    {
        //用7段数码管将矩阵键盘的键值显示出来
        digitron_static_display(key_value, 8);//8表示位选全开
        //此处延时后按键就不灵敏了,注销延时则按键很灵敏
        rough_delay_1ms(20);//
    }
 
 return 0;
}
//翻转扫描法
u8 scan_MatrixKey(void)
{
    //为了不分散大家注意力,此处的代码就省略了!
}
//初始化定时器0
void init_tmr0(void)
{
 TMOD = 0x01;//设置tmr0为工作方式1
 EA = 1;//开总中断
 ET0 = 1;//开定时器0中断
 TH0 = (65535u - tmr0_value) >> 8;//得到高8位
 TL0 = (65536u - tmr0_value) & 0x00ff;//得到低8位
 TR0 = 1;//启动定时器0
 
    return;
}
//注:定时器溢出后若没重装初值就重新从0开始计数(0—65535)
//计数到65536就溢出!
void tmr0(void) interrupt 1//定时器0中断
{   
    TR0 = 0;//关闭定时器0
    //重新给定时器0赋初值
    TH0 = (65536u - tmr0_value) >> 8;
    TL0 = (65536u - tmr0_value) & 0x00ff;
    key_value = scan_MatrixKey();//扫描矩阵键盘
    TR0 = 1;//启动定时器0
 
    return;
}
//数码管静态显示函数(i对应段选,j对应位选)              
void digitron_static_display(u8 i, u8 j)
{
    if (i>=0 && i<=15u)//过滤除键值以外的数值
    {
        P0 = segment;
        P2 = position[j];
    }
    
 return; 
}
void rough_delay_1ms(u16 t)//粗略延时t毫秒
{
    u16 i;
    for (; t>0; --t)
    {
        for (i=115u; i>0; --i);
    }
   
    return;
}
        现象:注销主程序里的while(1)循环里的延时后,按键非常灵敏,很难出错!但延时20ms后,按 按键 就经常没反应,有时要按好几次才有反应;延时100ms的话,按键几乎就很难有反应了,按半天都没动静!
       困惑:理论上,用定时器定期扫描的方法应该和主程 while(1)循环里的代码无关呀,这种异常现象是怎么回事呀?急求高人解答啊!    
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

9

主题

1385

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
11471
金钱
11471
注册时间
2013-3-8
在线时间
1226 小时
发表于 2013-5-5 20:05:17 | 显示全部楼层
回复【楼主位】可乐虎:
---------------------------------
给的程序不全,没办法分析
我现假设scan_MatrixKey()这个函数返回值在没有按键按下去时返回0,或者其他一个固定值,当你在WHILE(1)中加延时超过或接近定时器刷新按键时间时,按键就会不灵敏,这是正常现像,
假设你在WHILE(1)中延时100ms,则在延时这100ms过程中,定时器中断了10次左右。假设在没有按键按下去时scan_MatrixKey()返回值为20,则除非在最后一次中断你按下按键,在其他时间按下按键都回返回20,

不知我写的你看明白了没。
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:09:32 | 显示全部楼层
回复【2楼】augustedward:
---------------------------------
//行(列)扫描法
u8 scan_MatrixKey(void)
{
    #define ORT 1//可以增强程序的可移植性
u8 row; 
u8 column = 20;//初始值不能为0到15(因为0到15为键值)
u8 temp;
u8 MatrixKey_value = 20;

    //使矩阵键盘的四行键依次置0时的P1口值
u8 code set_key_row[] = {0XF7,0XFB,0XFD,0XFE};
//0到15号键依次被按下时P1口分别对应的值
u8 code scan_key_column[] = 
{0XE7,0XD7,0XB7,0X77,0XEB,0XDB,0XBB,0X7B, 
 0XED,0XDD,0XBD,0X7D,0XEE,0XDE,0XBE,0X7E};

for (row=0; row<4; ++row)//使矩阵键盘的四行键依次置0
{
PORT = set_key_row[row];
temp = ORT;//row为0时若键0所在行有键按下,则PORT口的值会发生变化
temp = temp & 0XF0;//可以判断PORT口的值是否发生变化
if (temp != 0XF0)
        {
rough_delay_1ms(10);//按下去抖延时(可调)
//temp在上次被赋PORT的值后就始终存的上次PORT的值
//故要判断PORT值是否变化就得对temp再次赋PORT的值
temp = ORT;
temp = temp & 0XF0;
            if (temp != 0XF0)
{
                //此时就得检测具体是哪个键被按下了
                //每行按键置0一次都得对4列按键各扫描一次,共4行故得扫描16次
for (column=0; column<16; ++column)
{
//任意键被按下都会使PORT对应scan_key_column[]中的某个值
//例如按下某键时PORT=0XE7,则column必为0,则按下的是0号键
if (scan_key_column[column] == ORT)
{
while (scan_key_column[column] == ORT);//按键的松手检测
rough_delay_1ms(5);//松手去抖延时(可调)
while (scan_key_column[column] == ORT);//再次确认是否松手
MatrixKey_value = column;//此时的column为按键键值                                                                                            
    break;
                    }                     
}                             
}
}
  
    }
    
    return MatrixKey_value;  
}
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:11:17 | 显示全部楼层
回复【2楼】augustedward:
---------------------------------
你看 我的键盘程序在没有按键按下时 返回的是20
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

9

主题

1385

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
11471
金钱
11471
注册时间
2013-3-8
在线时间
1226 小时
发表于 2013-5-5 20:21:18 | 显示全部楼层
回复【4楼】可乐虎:
---------------------------------
那就对了呀,所以你会发现按键不灵敏!你可以把返回值20改为返回0-15之间的任意值,烧进去再看看,是不是当发现不灵敏时显示的是一个固定值,或两个值闪一下。
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:23:18 | 显示全部楼层
回复【2楼】augustedward:
---------------------------------
嗯 你回答的太好了,就是你说的意思,如果延时过长的话,定时器的多次中断重新刷新了key_value的值,导致那次按键无效!
看来你的基本功很扎实啊 呵呵 !顺便问下 你对用lcd12864做多级菜单了解不?或者了解ucgui不?
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:25:19 | 显示全部楼层
回复【5楼】augustedward:
---------------------------------
“你可以把返回值20改为返回0-15之间的任意值,烧进去再看看”  不是这样的,只需要把u8 MatrixKey_value = 20;前加个static就好了哦 呵呵!这样就避免了被多次的中断更新了key_value的值   哈哈
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

9

主题

1385

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
11471
金钱
11471
注册时间
2013-3-8
在线时间
1226 小时
发表于 2013-5-5 20:25:22 | 显示全部楼层
回复【6楼】可乐虎:
---------------------------------
多级菜单了解,UCGUI也会用,不过也有很长时间没有用了,主要是工作中不常用,有点生疏了。
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:30:10 | 显示全部楼层
回复【8楼】augustedward:
---------------------------------
能加你QQ吗?诚心向你学习!可以短息告诉我 呵呵
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-5 20:33:10 | 显示全部楼层
回复【8楼】augustedward:
---------------------------------
我的QQ1982211588
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

27

主题

774

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
1473
金钱
1473
注册时间
2013-4-12
在线时间
77 小时
发表于 2013-5-5 22:40:00 | 显示全部楼层
为什么要用延时呢?干嘛不用变量?
t++;
简单是多么的快乐,而快乐就因你而简单!微芯动力LTKKS
回复 支持 反对

使用道具 举报

6

主题

153

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2013-4-4
在线时间
0 小时
发表于 2013-5-6 23:42:22 | 显示全部楼层
延时过程中检测到按键->设定值->返回继续延时
所以会出现不灵敏?
~~~~ 欢迎讨论,拒绝谩骂 ~~~~
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-5-7 15:08:55 | 显示全部楼层
回复【12楼】兰斯洛:
---------------------------------
不灵  是因为主函数的延时时间大于键盘的扫描时间,当此次按键的键值还未发生作用,又迎来了下次扫描,就将键值改成了初始值,这样此次按键 就白按了,故只需将存放键值的变量改为静态或全局的 就可以保存此次的键值直到下次按键被按下才能更新键值
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

4

主题

40

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
221
金钱
221
注册时间
2013-1-29
在线时间
29 小时
发表于 2013-6-6 23:23:57 | 显示全部楼层
我一般是把按键扫描放到while里,显示放到中断
什么是母体?什么是真实?——《黑客帝国》 我在电子世界里?——《创战纪》 ...这里是世界尽头...——《异次元骇客》
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-6-5
在线时间
1 小时
发表于 2013-6-7 10:27:43 | 显示全部楼层
按键消抖不是所有的按键都能用软件来消抖的.
定时器设成1ms中断一次.
你的数码管没设消影啊.
fhh学习中......
回复 支持 反对

使用道具 举报

86

主题

417

帖子

0

精华

高级会员

Rank: 4

积分
781
金钱
781
注册时间
2013-2-20
在线时间
0 小时
 楼主| 发表于 2013-6-8 21:26:40 | 显示全部楼层
各位 写个队列 就可以很好的解决问题了啊
乐于思考,敢于请教;问人不累,诲人不倦!本人CSDN博客:http://blog.csdn.net/dcx1205 学习嵌入式的同学不要错过啊!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 11:24

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表