搜索
bottom↓
回复: 51

"新型的按键扫描程序"的改进版,欢迎大家继续改进

[复制链接]

出0入0汤圆

发表于 2011-3-6 09:40:06 | 显示全部楼层 |阅读模式
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999

以下代码为在上面的文章和my_avr代码的基础上进行改进:


static volatile unsigned long g_PreKey;          //前次按键值
static volatile unsigned long g_NowKey;          //当前按键值
   volatile unsigned long g_ShortKeyCode;//短按键,单次触发
   volatile unsigned long g_LongKeyCode; //长按键,持续触发
void KeyRead(void)  
{
//g_NowKey ^ g_PreKey           :边缘触发  
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
//触发方式 ^  g_KeyCode           :仅更新变化部分按键值
static unsigned char CntPlus=0;
     g_PreKey  = g_NowKey;  
     g_NowKey  = KeyScan();
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;   
    if (g_NowKey^g_PreKey^g_NowKey^g_LongKeyCode)
    {        
if(CntPlus++>LONG_KEY_TIME)//是否符合长按键要求
{
               g_LongKeyCode|= g_NowKey;//添加长按键值
          }  
    }
else  
{
CntPlus=0;//无新增按键出现,计数归零
g_LongKeyCode &= g_NowKey; //剔除已释放的按键
}           
}

核心算法:
//g_NowKey ^ g_PreKey           :边缘触发  
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
//触发方式 ^  g_KeyCode           :仅更新变化部分按键值


特点: 1.支持长短按键同时并行触发。
2.将短按键触发判据改为下降沿触发,即增加了检测短按键释放功能

欢迎各位大侠指点和改进,期望着更高效和强大的代码出现

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

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2011-3-6 11:53:51 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-3-6 11:54:41 | 显示全部楼层
HAO
TKS

出0入0汤圆

发表于 2011-3-6 12:10:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-3-6 12:59:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-3-6 13:35:37 | 显示全部楼层
强大

出0入0汤圆

发表于 2011-3-6 13:41:06 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2011-3-6 22:32:26 | 显示全部楼层
回复【楼主位】mojinpan  
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
以下代码为在上面的文章和my_avr代码的基础上进行改进:
static volatile unsigned long g_prekey;          //前次按键值
static volatile unsigned long g_nowkey;          //当前按键值
   volatile unsigned long g_shortkeycode;//短按键,单次触发
   volatile unsigned long g_longkeycode; //长按键,持续触发
void keyread(void)  
{
//g_nowkey ^ g_prekey           :边缘触发  
//g_nowkey & (g_nowkey ^......
-----------------------------------------------------------------------
发现一个bug:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;
这个语句末尾的"^g_LongKeyCode"本来是解决长按释放时会同时出现一个短按键判断成功的问题,但是加上去以后又引入了新问题,那就是当,长按键未松手时: g_PreKey & (g_NowKey ^ g_PreKey)=0,g_LongKeyCode=1,相与为1,结果某个短按键被误判.
目前只能将:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;
改为:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey);
那么遗留问题就是:长按键松手是会同时出现一个短按键,需另外构建一个判据:同时出现长按键和短按键时,短按键无效.(目前还没有想好比较好的方法)

出0入0汤圆

发表于 2011-3-7 08:43:23 | 显示全部楼层
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
【3楼】 kevintang 同学添加的释放功能比较有用

unsigned char Trg;  
unsigned char Cont;  
unsigned char Release;  

void KeyRead( void )  
{  
    unsigned char ReadData = PINB^0xff;      // 1  读键值

    Trg = ReadData & (ReadData ^ Cont);      // 2  得到按下触发值
    Release=  (ReadData ^ Trg ^ Cont);       // 3  得到释放触发值
    Cont = ReadData;                         // 4  得到所有未释放的键值
}

这东西的实用性是不可置疑的,我在多个项目上使用了,效果非常好
我比较期待的是有没有同学能证明这个怎么推导出来的.....(布尔代数是否可以?)

出0入0汤圆

发表于 2011-3-7 09:00:36 | 显示全部楼层
mark

出50入0汤圆

发表于 2011-3-7 09:45:43 | 显示全部楼层
mark

出0入0汤圆

 楼主| 发表于 2011-3-7 09:57:27 | 显示全部楼层
回复【8楼】Etual  
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
【3楼】 kevintang 同学添加的释放功能比较有用
unsigned char trg;  
unsigned char cont;  
unsigned char release;  
void keyread( void )  
{  
    unsigned char readdata = pinb^0xff;      // 1  读键值
    trg = readdata & (readdata ^ cont);      // 2  得到按下触发值
    release=  (readdata ^ trg ^ cont);       // 3  得到释放触发值
    cont = readdata;    ......
-----------------------------------------------------------------------
请看我开头部分的描述:
核心算法:  
//g_NowKey ^ g_PreKey           :边缘触发   
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发  
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发  
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
你给出的函数中变量和我的函数中变量的关系如下:
ReadData=g_NowKey;
trg=g_shortkeycode;
Releas=g_longkeycode;

对比看,我上面的写法和你提供的代码是完全等效的,那么下面我来"推导"一下:

这些运算是根据状态机的思想来设计出来的,注意几个关键词:现态,次态(过去的状态),即我程序里的g_NowKey 和g_PreKey

    那么再来分析按键的过程,g_NowKey ^ g_PreKey即表示,那么按键的边缘触发就是g_NowKey !=g_PreKey,也就是g_NowKey ^ g_PreKey,但是g_NowKey ^ g_PreKey比g_NowKey !=g_PreKey的表示方法要好,及g_NowKey ^ g_PreKey能按位来表示按键状态,即能实现并发的表示g_NowKey !=g_PreKey

再看//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
实际上翻译过来就是:前一次状态是高电平的边缘触发,即上升沿触发

//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
实际上翻译过来就是:前一次状态是低电平的边缘触发,即上升沿触发

剩下的就是当前按键的判据了:
你的代码是:Cont = ReadData;                         // 4  得到所有未释放的键值
我的代码是://g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
按照上面的将我的代码来翻译的话就是 当前:电平状态 ^ 边缘触发,结果分成4种:
      电平状态 ^ 边缘触发  结果
1.      0           0       0      //一直没有按键
2.      1           0       1      //一直没有松开按键
3.      0           1       1      //突然松开按键
4.      1           1       0      //突然按下按键
以上状态中的2和3合并:按下按键至到释放,注意我的代码是带消抖的,你的代码是不带消抖,因为你没有判边缘触发

出0入96汤圆

发表于 2011-3-7 11:31:16 | 显示全部楼层
学无止境

出0入0汤圆

发表于 2011-6-1 15:35:24 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-6-12 16:25:19 | 显示全部楼层
对这个比较感兴趣!

出0入0汤圆

发表于 2011-6-12 21:03:25 | 显示全部楼层

(原文件名:真值表.jpg)

点击此处下载 ourdev_648094FKFGTM.doc(文件大小:30K) (原文件名:真值表.doc)

出0入0汤圆

发表于 2011-6-12 21:10:56 | 显示全部楼层
先顶后下!

出0入0汤圆

发表于 2011-6-13 11:51:51 | 显示全部楼层
我一般不喜欢只有一个按键有效的按键扫描程序
我来抛砖引玉

#define LittleSound 18

#define testbit(var,bit) ((var)&(1<<(bit)))//位测试
#define testbit(var,bit) ((var)&(1<<(bit)))//位测试
#define setbit(var,bit)  ((var)|=(1<<(bit)))//位置1
#define clrbit(var,bit)  ((var)&=~(1<<(bit)))//位清零  clrbit(p3,1);就是了
#define nop (__no_operation())          //将后者指令行缩写为nop;
#define di  (__disable_interrupt())     //将后者指令行缩写为di;
#define ei  (__enable_interrupt())      //将后者指令行缩写为ei;
#define wdt  asm("LD BTCON,#02H");

void CheckKeys()
        {
                uchar tmp;
                uchar tmp2 = 0;
                for(tmp=0; tmp<3; tmp++)
                        {
                                tmp2 =0;
                                if(tmp==0)
                                {
                                        if(testbit(P0,0))
                                        {
                                                tmp2 = 0x55;
                                        }
                                }
                                if(tmp==1)
                                {
                                        if(testbit(P0,2))
                                        {
                                                tmp2 = 0x55;
                                        }
                                }
                                if(tmp==2)
                                {
                                        if(testbit(P0,1))
                                        {
                                                tmp2 = 0x55;
                                        }
                                }
                               
                                /* 判断 */
                                if(tmp2 == 0x55)
                                {
                                        InputCnt[tmp] = 0;
                                        if(tmp == 0)
                                        {
                                                Input.Solo.KeyAdd =0;
                                                KeyTreated.Solo.KeyAdd =0;
                                        }
                                        if(tmp == 1)
                                        {
                                                Input.Solo.KeySub =0;
                                                KeyTreated.Solo.KeySub =0;
                                        }
                                        if(tmp == 2)
                                        {
                                                Input.Solo.KeyEnter =0;
                                                KeyTreated.Solo.KeyEnter =0;
                                        }
                                }
                               
                                InputCnt[tmp]++;       
                                if(InputCnt[tmp] >15)
                                {
                                        if(tmp == 0)
                                        {
                                                Input.Solo.KeyAdd =1;
                                        }
                                        if(tmp == 1)
                                        {
                                                Input.Solo.KeySub =1;
                                        }
                                        if(tmp == 2)
                                        {
                                                Input.Solo.KeyEnter =1;
                                        }
                                }
                        }
}

       
void KeyDecide()
{
//<<<<<<<<<<<<<<<连加连减判断>>>>>>>>>>>>>>>>>
         if((Input.Solo.KeyAdd^Input.Solo.KeySub) )/* 异或 */
        {
                if (RepeatTimer>=RepeatTime)
                {
                        RepeatTimer=0;
                        if(Input.Solo.KeyAdd==1)
                        {
                                KeyTreated.Solo.KeyAdd=0;        //按键有效
                                RunFlag.Solo.ToFive = 1;
                        }
                        if(Input.Solo.KeySub==1)
                        {
                                KeyTreated.Solo.KeySub=0;        //按键有效
                                RunFlag.Solo.ToFive = 1;
                        }                                       
                        if(RepeatTime>80)
                        {
                                RepeatTime = 80;
                        }
                if(RepeatTime >60)        //加速
                        {
                                RepeatTime -= 5;
                        }
                }
        }
        else
        {
                RepeatTime = 150;                 //复位连击计时
                RepeatTimer = 0;
        }
}


void KeyAction_KeyEnter()
{
        if((Input.Solo.KeyEnter) && (!KeyTreated.Solo.KeyEnter ))
        {
                KeyTreated.Solo.KeyEnter =1;
                AutoOffTimer = 0;
                test = 20;
                if(EeFlag != RD24)
                {
                        EeFlag = WR24;
                }
               
       
                BellTimer = LittleSound;
                if(SetStatus ==1)
                {
                        RunFlag.Solo.SetType = 0;
                        SetStatus = 0;
                }
                else if(SetStatus ==0)
                {
                        SetStatus = 1;
                        AutoOffTimer = 8;
                }
                else if(SetStatus > 1)
                {
                        ShowSpecialTimer = 10;
                        AutoOffTimer = AutoOffTimerMAX;
                        RunFlag.Solo.SetType = 0;
                        if(++SetStatus >3)
                        {
                                SetStatus = 0;
                                RunFlag.Solo.SetType = 0;
                                ShowSpecialTimer = 0;
                                AutoOffTimer = 0;
                        }
                }
        }
}

void KeyAction_KeySub()
{               
        uchar tmp;
        if ((Input.Solo.KeySub) && (!KeyTreated.Solo.KeySub ))
        {
                KeyTreated.Solo.KeySub =1;
                AutoOffTimer = AutoOffTimerMAX;
                if(SetStatus == 1)
                {
                        AutoOffTimer = 8;
                }
                if(SetStatus>0)
                        {
                                BellTimer = LittleSound;
                                ShowSpecialTimer = 0;
                                if(SetStatus !=1)
                                if(!RunFlag.Solo.SetType)
                                {
                                        RunFlag.Solo.SetType = 1;
                                        return;
                                }
                                tmp=SetStatus-1; /* SetStatus大于1时,为参数设置状态 */
                                /* 设置中参数减1 */
ToFive:
                                if(--Para.intPara[tmp]>ParaMax[tmp] || Para.intPara[tmp]< ParaMin[tmp])
                                        {
                                                Para.intPara[tmp]=ParaMin[tmp];/* 并判断是否大最大值与最小值之间 */
                                        }
                                if(RunFlag.Solo.ToFive)
                                {
                                        if(Para.intPara[tmp]%5 !=0)
                                        {
                                                goto ToFive;
                                        }
                                }
                                RunFlag.Solo.ToFive = 0;
                        }
        }
}

void KeyAction_KeyAdd()
{
        uchar tmp;
        if ((Input.Solo.KeyAdd) && (!KeyTreated.Solo.KeyAdd ))
        {
                KeyTreated.Solo.KeyAdd =1;
                AutoOffTimer = AutoOffTimerMAX;
                if(SetStatus == 1)
                {
                        AutoOffTimer = 8;
                }
                if(SetStatus>0)
                {
                        BellTimer = LittleSound;
                        ShowSpecialTimer = 0;
                        if(SetStatus !=1)
                        if(!RunFlag.Solo.SetType)
                        {
                                RunFlag.Solo.SetType = 1;
                                return;
                        }
                        tmp=SetStatus-1;       
                        /* 设置中参数加1 */
ToFive:
                        if(++Para.intPara[tmp]>ParaMax[tmp] || Para.intPara[tmp]< ParaMin[tmp])
                                {
                                        Para.intPara[tmp]=ParaMax[tmp];        /* 并判断是否大最大值与最小值之间 */
                                }
                        if(RunFlag.Solo.ToFive)
                        {
                                if(Para.intPara[tmp]%5 !=0)
                                {
                                        goto ToFive;
                                }
                        }
                        RunFlag.Solo.ToFive = 0;
                }
        }
}

void KeyActions()
{
        if(KeyTreated.Solo.KeyEnter)
        {
                if((KeyTimer==0))
                {
                        ShowSpecialTimer = 10;
                        SetStatus =2;
                        BellTimer = LittleSound + 10;
                        AutoOffTimer =AutoOffTimerMAX;
                }
                return;
        }
        else
        {
                KeyTimer =10;
        }
       
        KeyAction_KeyEnter();
        KeyAction_KeyAdd();
        KeyAction_KeySub();
        if(Error > 0)
        {
                SetStatus = 0;
        }
}

出0入0汤圆

发表于 2011-6-13 12:44:08 | 显示全部楼层
mark,很强大

出0入0汤圆

发表于 2011-6-13 12:48:36 | 显示全部楼层
都是高手啊,学习···

出0入0汤圆

发表于 2011-6-14 12:15:41 | 显示全部楼层
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
感觉这句没带消抖
      来一毛刺干扰    1^(1^0)=0;正常
      毛刺消失后      0^(0^1)=0; ???? 出了键值了
是不是我理解错了?????????????

出0入0汤圆

发表于 2011-10-31 12:21:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-10-31 19:49:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-12-9 12:59:58 | 显示全部楼层
mark了慢慢看
头像被屏蔽

出0入0汤圆

发表于 2011-12-12 17:19:57 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入8汤圆

发表于 2012-3-8 12:34:29 | 显示全部楼层
学习!

出0入10汤圆

发表于 2012-3-28 09:48:16 | 显示全部楼层
先记号,我按键还一直是很老的方法做的,有时候用起来还真不怎么好用

出0入0汤圆

发表于 2012-3-28 09:49:40 | 显示全部楼层
路过收藏

出0入0汤圆

发表于 2012-3-28 09:55:56 | 显示全部楼层
学习了,我一直按键处理做得不好

出0入0汤圆

发表于 2012-3-28 14:37:25 | 显示全部楼层
MARK 按键处理

出0入0汤圆

发表于 2012-3-28 15:04:11 | 显示全部楼层
先 mark ,再细看下。。。

出0入0汤圆

发表于 2012-3-28 15:05:47 | 显示全部楼层
精益求精,值得学习呀。

出0入0汤圆

发表于 2012-3-28 15:20:39 | 显示全部楼层
生命不息,学习不止

出0入0汤圆

发表于 2012-4-15 15:24:55 | 显示全部楼层
。。。改天我用9212试试看。。。。

出0入0汤圆

发表于 2012-4-15 15:36:26 | 显示全部楼层
先记号,再慢慢看

出0入13汤圆

发表于 2012-4-17 22:37:27 来自手机 | 显示全部楼层
值得学习借鉴下!

出0入0汤圆

发表于 2012-5-1 23:46:24 | 显示全部楼层
学习。。。。

出0入4汤圆

发表于 2012-5-2 00:01:48 | 显示全部楼层
下周抽时间玩一下

出0入0汤圆

发表于 2012-6-3 10:49:59 | 显示全部楼层
实践证明,很好用

出0入0汤圆

发表于 2012-7-8 16:24:32 | 显示全部楼层
很精彩!

出0入0汤圆

发表于 2012-7-9 09:50:50 | 显示全部楼层
高潮,期待高潮

出0入0汤圆

发表于 2012-7-9 09:54:20 | 显示全部楼层
coyool 发表于 2011-6-13 11:51
我一般不喜欢只有一个按键有效的按键扫描程序
我来抛砖引玉

能把你的按键程序完整的给我发一份吗?阿莫论坛上你发的应该是不完整的。hongqi1029@126.com,qq:35039828

出0入0汤圆

发表于 2012-7-9 12:10:03 来自手机 | 显示全部楼层
mojinpan 发表于 2011-3-6 22:32
回复【楼主位】mojinpan  
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&amp;bbs_id=999 ...

手机上的,做个记号!

出0入0汤圆

发表于 2014-10-31 17:10:51 | 显示全部楼层
学习一下,顺便问下,这中方法检测的应该是独立式按键吧,它能用于检测矩阵键盘码

出0入0汤圆

发表于 2014-10-31 17:54:20 | 显示全部楼层
好老的帖子了,当年很受启发呢

出0入0汤圆

发表于 2014-11-13 17:26:48 | 显示全部楼层
mark                                   

出0入0汤圆

发表于 2015-8-6 16:16:24 | 显示全部楼层
组合按键好弄吗

出0入0汤圆

发表于 2015-8-6 23:32:56 来自手机 | 显示全部楼层
数学推理加真值表用在按键上,思路真心好

出0入0汤圆

发表于 2015-8-12 15:24:06 | 显示全部楼层
强大 MARK

出0入0汤圆

发表于 2015-8-12 22:04:53 | 显示全部楼层
谢谢分享..

出0入0汤圆

发表于 2015-8-30 09:58:18 | 显示全部楼层
有想法,有思路,不错

出0入0汤圆

发表于 2015-8-31 10:09:21 | 显示全部楼层
按键的处理觉得还是有算法可循的,但是主要是按键的判断时间(消抖时间5-10MS),怎么样处理不占用MCU的时间对于我的使用来说才是关键!一个项目不可能只用来键盘扫描,中断处理也是有限的。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-7-23 12:28

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

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