搜索
bottom↓
回复: 12

矩阵键盘+组合按键+连_发功能(请马老师指点下思路)

[复制链接]

出0入0汤圆

发表于 2008-5-14 22:59:42 | 显示全部楼层 |阅读模式
这个按键程序是看了马老师的书,改写的一个按键程序,已在仿真板通过,单键,某几个单键连_发都没有问题,但组合按键检测却存在些问题,10最合按键总有几次检测不到,不是很灵敏,程序的思路肯定是有点问题,请马老师指点下

//按键初始化
void key_board_int1(void)
{
KEY_DDR_ROW   |=  KEY_ROW_ALL;                                                           //行输出,
KEY_PORT_ROW  &=~ KEY_ROW_ALL;
KEY_DDR_COL   &=~ KEY_COL_ALL;                                                           //列输入,并使能内部上拉
KEY_PORT_COL  |=  KEY_COL_ALL;
}

void key_board_int2(void)                      //翻转
{
KEY_DDR_ROW   &=~ KEY_ROW_ALL;                                                           //行输入,并使能内部上拉
KEY_PORT_ROW  |=  KEY_ROW_ALL;
KEY_DDR_COL   |=  KEY_COL_ALL;                                                           //列输出;
KEY_PORT_COL  &=~ KEY_COL_ALL;
}

char read_keyboard()
{       
static char key_state = 0,  key_value1,key_value2;
static char key_value3,key_value4;
static int  key_time = 0;
char key_return = No_key,key_value_buf;

        switch (key_state)
        {
                case 0:
                        key_board_int1();
                        KEY_PORT_ROW &=~ KEY_ROW_ALL;                                              // 必须送2次!!!
                        key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                // 读列电平
                                                     
                        if (key_value_buf != No_key)
                        {
                                key_state = 1;                                                         // 有按键
                                break;                                                                     // 转消抖确认状态
                        }
                        break;
                case 1:
                        if (++key_time >= 8)
                                {          
                            key_board_int1();
                            KEY_PORT_ROW &=~ KEY_ROW_ALL;                                              // 必须送2次!!!
                            key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                // 读列电平
                           
                            key_board_int2();                           // 翻转
                            KEY_PORT_COL  &=~ KEY_COL_ALL;
                            key_value_buf |= (~KEY_COL_ALL) & KEY_PIN_ROW;        // 读行电平,并同上次上一步的结果或
                           
                            key_value1 = key_value_buf;
                            if (key_value1 != No_key)
                                    {
                                            key_state = 2;
                                                key_time = 0;
                                            break;
                                    }
                            key_state = 0;
                            break;
                       }
                    break;
          case 2:
                  key_board_int1();
                        KEY_PORT_ROW &=~ KEY_ROW_ALL;                                              // 必须送2次!!!
                        key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                // 读列电平
                       
                        key_board_int2();                           // 翻转
                        KEY_PORT_COL  &=~ KEY_COL_ALL;
                        key_value_buf |= (~KEY_COL_ALL) & KEY_PIN_ROW;        // 读行电平,并同上次上一步的结果或
                 
                      key_value2 = key_value_buf;                                      
                        if (key_value2 == key_value1)                // 再次扫描,
                        {
                                switch (key_value2)                            // 与状态1的相同,确认按键
                                {                                                                                            // 键盘编码,返回编码值
                                        case 0b11101110:
                                                key_return = K1_1;           // 0
                                                break;
                                        case 0b11101101:   
                                                key_return = K1_2;           // 1
                                                break;
                                        case 0b11101011:
                                                key_return = K1_3;           // 2
                                                break;
                                        case 0b11100111:
                                                key_return = K1_4;
                                                break;
                                        case 0b11011110:
                                                key_return = K2_1;
                                                break;
                                        case 0b11011101:
                                                key_return = K2_2;
                                                break;
                                        case 0b11011011:
                                                key_return = K2_3;
                                                break;
                                        case 0b11010111:
                                                key_return = K2_4;
                                                break;
                                        case 0b10111110:
                                                key_return = K3_1;
                                                break;
                                        case 0b10111101:
                                                key_return = K3_2;
                                                break;
                                        case 0b10111011:
                                                key_return = K3_3;
                                                break;
                                        case 0b10110111:
                                                key_return = K3_4;
                                                break;
                    case 0b01111110:
                                                key_return = K4_1;
                                                break;
                                        case 0b01111101:
                                                key_return = K4_2;
                                                break;
                                        case 0b01111011:
                                                key_return = K4_3;
                                                break;
                                        case 0b01110111:
                                                key_return = K4_4;
                                                break;
                                        case 0b01100110:
                                                key_return = KF_0;            // 组合按键F+0
                                                break;
                                        case 0b01100101:
                                                key_return = KF_1;            // 组合按键F+1
                                                break;               
                    default:
                        break;
                                }
                                key_state = 3;                                                     // 转入等待按键释放状态
                        }
                        else
                                key_state = 0;                                                     // 两次列电平不同返回状态0,(消抖处理)
            break;       
                                                       
        case 3:                                                                                 
                key_board_int1();
                KEY_PORT_ROW &=~ KEY_ROW_ALL;                                              // 必须送2次!!!
                key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                // 读列电平               
               
                key_board_int2();                           // 翻转
                KEY_PORT_COL  &=~ KEY_COL_ALL;
                key_value_buf |= (~KEY_COL_ALL) & KEY_PIN_ROW;        // 读行电平,并同上次上一步的结果或
                key_value3 = key_value_buf;       
               
                if ( key_value3 == No_key)
                        key_state=0;                                                     // 按键已释放,转换到按键初始态
                        else if ((key_value3 == 0b11100111)||(key_value3 == 0b11011110))
                          {
                            if (++key_time >= 300)                // 按键时间计数
                              {
                                    key_state = 4;        // 按下时间>1s,状态转换到计时2
                                    key_time = 0;                                       // 清按键计数器
                                    key_return = KJ;                           // 输出“18”
                              }
                        break;
                          }
                        break;
        case 4:
           key_board_int1();
           KEY_PORT_ROW &=~ KEY_ROW_ALL;                                              // 必须送2次!!!
           key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                // 读列电平               
               
           key_board_int2();                           // 翻转
           KEY_PORT_COL  &=~ KEY_COL_ALL;
           key_value_buf |= (~KEY_COL_ALL) & KEY_PIN_ROW;        // 读行电平,并同上次上一步的结果或
           key_value4 = key_value_buf;       
          
           if ( key_value4 == No_key)
                        key_state=0;                                                     // 按键已释放,转换到按键初始态
                        else if (++key_time >= 20)                // 按键时间计数
                        {
                                key_time = 0;                                       // 清按键计数器
                                key_return = KJ;                           // 输出“18”
                                break;
                        }
          break;
    default:
      break;
}
return key_return;
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2008-5-14 23:16:00 | 显示全部楼层
楼上的兄弟,你的程序太长了.要睡了,没来得及看,但据我所知,一个键盘扫描程序通常不超过50行的.(包括消抖等功能)

出0入0汤圆

 楼主| 发表于 2008-5-15 10:46:20 | 显示全部楼层
因程序较长,简单说下思路:
每10MS扫描一次键盘
状态0:检测是否有键按下;
状态1:延时80MS,读键值(关键是要能读双键按下)
状态2:确认按键,同状态1比较,相同则返回键值;
状态3:计数1,(按3S,连_发,某两个键具备连_发功能);
状态4:计数2,连_发功能

现在的问题是双键按下,检测不灵敏,我想是状态的转换思路不大好,程序是没有问题的!

出0入0汤圆

发表于 2008-5-15 13:02:50 | 显示全部楼层
组合键的设计不是这么简单.

首先观察PC键盘的组合键,比如ALT键,单独按下是不起"作用"的,而是同其它键构成组合.而且组合按键的操作过程应该是先按下ALT,再按相配合的键.因为在实际操作中,肯定不能保证两个键是"同时"按下的.

因此你首先必须在你的4*4键盘上确定一个键为"先"按下,构成组合先导键,该键不起到其它作用.

下面给个状态转换参考设计思路:

假定KF为组合先导键(取消其K4_4的返回)
每10MS扫描一次键盘

状态0:检测是否有键按下;
状态1:消抖处理
状态2:确认按键,同状态1比较,除KF键外相同则返回键值,转状态3;KF键转状态A;
状态3:计数1,(按3S,连_发判断);
状态4:计数2,连_发功能
.......

状态A:检测是否有KF键+其他键按下;有转状态B.
状态B:消抖处理
状态C:同状态B比较,确认组合按键,返回组合键值,转状态3.

以上仅是简单的状态主框架,各个状态中可能出现的转换没有考虑,供参考.
另,先把功能正确实现,然后考虑简化和优化.

出0入0汤圆

 楼主| 发表于 2008-5-15 13:28:57 | 显示全部楼层
谢谢马老师的指点,已经有了些头绪了,原来如此!
但如果我要用到“先导键”单按时有其它功能,这个思路是否可以行呢?(已经有头绪,这是看了马老师的解答马上产生的疑问,还未深思)

出0入0汤圆

发表于 2008-5-15 13:41:19 | 显示全部楼层
这就更复杂了.实际上,人工按键很难做到2个键"同时"按下的.当你把“先导键”先按下了,那么到底你是按单键,还是准备是组合键?所以PC上的“先导键”,如SHIFT,ALT等,没有单独的功能.

如果你必须要如此设计的话,那么必须规定时间,如“先导键”按下300ms后,没有其它键按下则返回单键功能,在300ms内有其它键按下为组合键.

不管怎样,在状态2中.KF键都需要进入单独的处理状态.

出0入0汤圆

 楼主| 发表于 2008-5-15 13:49:46 | 显示全部楼层
一语点醒梦中人,我空想了一星期还不如马老师的一句话点醒!

出0入0汤圆

发表于 2008-5-15 14:16:11 | 显示全部楼层
你在LZ位说看了我的书,那么尽管我在书中没有给出更复杂的按键设计,可已经明确的说明设计一个好的按键系统也是需要一定的能力的.

要学会正确的分析实际的系统,并从实际抽象到理论,用状态正确的描述.

希望LZ能将此按键系统设计出来,作为例子公开.大家可以在此基础上深入学习,讨论和进行优化.

这是真正的基本功的训练和学习,实际上它对任何的MCU都是一样的,其思想和方法不仅仅局限AVR.当你深入学习后,到了实践工作中,不管你是否使用AVR,你会感到,我书中所介绍的许多思想方法比那些传统51书要先进,并且更加面向应用.

我提倡这样的交流.

出0入0汤圆

 楼主| 发表于 2008-5-15 14:32:46 | 显示全部楼层
是的,马老师,我以前是自学PIC,苦与无老师指点,一直都是闭门造车,PIC方面的书05年前出版的我几乎都买了,但是水平换那样,感觉并没有真正的入门,今年春我在武汉武胜路新华书店看到老师的书,就买了下来,初看后,对AVR越发感兴趣,就又买了套开发设备,以老师的书为教科书,边学边练,自我感觉每天都在进步!我已经感到了老师书中的威力了!
    这个例子我一定会做出来,以感谢老师的指点,老师的鼓励是我前进的动力。

出0入0汤圆

发表于 2010-6-23 08:12:55 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-3 16:39:05 | 显示全部楼层
mark  too

出0入0汤圆

发表于 2010-9-5 21:27:33 | 显示全部楼层
LZ做出来没有啊,怎么没声音了

出0入0汤圆

发表于 2010-9-22 17:50:26 | 显示全部楼层
我编写教程的P287,例9-1,就是连_发,先看懂原理,应用到矩阵键盘道理是相同的。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 02:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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