mojinpan 发表于 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.将短按键触发判据改为下降沿触发,即增加了检测短按键释放功能

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

tangwei039 发表于 2011-3-6 11:53:51

mark

zzjjhh250 发表于 2011-3-6 11:54:41

HAO
TKS

yywin 发表于 2011-3-6 12:10:57

mark

GZLJZ 发表于 2011-3-6 12:59:46

mark

ffbiao 发表于 2011-3-6 13:35:37

强大

luhuaneda 发表于 2011-3-6 13:41:06

mark

mojinpan 发表于 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);
那么遗留问题就是:长按键松手是会同时出现一个短按键,需另外构建一个判据:同时出现长按键和短按键时,短按键无效.(目前还没有想好比较好的方法)

Etual 发表于 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得到所有未释放的键值
}

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

wangxb330226 发表于 2011-3-7 09:00:36

mark

yuzr 发表于 2011-3-7 09:45:43

mark

mojinpan 发表于 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合并:按下按键至到释放,注意我的代码是带消抖的,你的代码是不带消抖,因为你没有判边缘触发

gallle 发表于 2011-3-7 11:31:16

学无止境

wanzhouyang29 发表于 2011-6-1 15:35:24

mark

xiaoao360 发表于 2011-6-12 16:25:19

对这个比较感兴趣!

xiaoao360 发表于 2011-6-12 21:03:25

http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648093WDZIPV.jpg
(原文件名:真值表.jpg)

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

xslff 发表于 2011-6-12 21:10:56

先顶后下!

coyool 发表于 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 wdtasm("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 = 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++;       
                                if(InputCnt >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>ParaMax || Para.intPara< ParaMin)
                                        {
                                                Para.intPara=ParaMin;/* 并判断是否大最大值与最小值之间 */
                                        }
                                if(RunFlag.Solo.ToFive)
                                {
                                        if(Para.intPara%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>ParaMax || Para.intPara< ParaMin)
                                {
                                        Para.intPara=ParaMax;        /* 并判断是否大最大值与最小值之间 */
                                }
                        if(RunFlag.Solo.ToFive)
                        {
                                if(Para.intPara%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;
        }
}

zhuhanliang 发表于 2011-6-13 12:44:08

mark,很强大

licongdwqx 发表于 2011-6-13 12:48:36

都是高手啊,学习···

jobwork 发表于 2011-6-14 12:15:41

//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
感觉这句没带消抖
      来一毛刺干扰    1^(1^0)=0;正常
      毛刺消失后      0^(0^1)=0; ???? 出了键值了
是不是我理解错了?????????????

ljmdzyx 发表于 2011-10-31 12:21:48

mark

duzhang 发表于 2011-10-31 19:49:26

mark

allen6kid 发表于 2011-12-9 12:59:58

mark了慢慢看

taocongrong 发表于 2011-12-12 17:19:57

jun427 发表于 2012-3-8 12:34:29

学习!

chengtina 发表于 2012-3-28 09:48:16

先记号,我按键还一直是很老的方法做的,有时候用起来还真不怎么好用

416369123 发表于 2012-3-28 09:49:40

路过收藏

vjcmain 发表于 2012-3-28 09:55:56

学习了,我一直按键处理做得不好

laign 发表于 2012-3-28 14:37:25

MARK 按键处理

GNMXD 发表于 2012-3-28 15:04:11

先 mark ,再细看下。。。

yanjs1016 发表于 2012-3-28 15:05:47

精益求精,值得学习呀。

bluestone2012 发表于 2012-3-28 15:20:39

生命不息,学习不止

eleczj 发表于 2012-4-15 15:24:55

{:lol:}。。。改天我用9212试试看。。。。

ige007 发表于 2012-4-15 15:36:26

先记号,再慢慢看

weiwei4 发表于 2012-4-17 22:37:27

值得学习借鉴下!

ppdd 发表于 2012-5-1 23:46:24

学习。。。。

D.lovers 发表于 2012-5-2 00:01:48

下周抽时间玩一下

taomo 发表于 2012-6-3 10:49:59

实践证明,很好用

hongqi1029 发表于 2012-7-8 16:24:32

很精彩!

hongqi1029 发表于 2012-7-9 09:50:50

高潮,期待高潮

hongqi1029 发表于 2012-7-9 09:54:20

coyool 发表于 2011-6-13 11:51 static/image/common/back.gif
我一般不喜欢只有一个按键有效的按键扫描程序
我来抛砖引玉



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

oldmen 发表于 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 ...

手机上的,做个记号!

longfeixue 发表于 2014-10-31 17:10:51

学习一下,顺便问下,这中方法检测的应该是独立式按键吧,它能用于检测矩阵键盘码

zsg211550 发表于 2014-10-31 17:54:20

好老的帖子了,当年很受启发呢

hex6703 发表于 2014-11-13 17:26:48

mark                                 

linbo411 发表于 2015-8-6 16:16:24

组合按键好弄吗

lovecxm 发表于 2015-8-6 23:32:56

数学推理加真值表用在按键上,思路真心好

kiss2024 发表于 2015-8-12 15:24:06

强大 MARK

jxcrg_t35 发表于 2015-8-12 22:04:53

谢谢分享..

dulala 发表于 2015-8-30 09:58:18

有想法,有思路,不错

py-industry 发表于 2015-8-31 10:09:21

按键的处理觉得还是有算法可循的,但是主要是按键的判断时间(消抖时间5-10MS),怎么样处理不占用MCU的时间对于我的使用来说才是关键!一个项目不可能只用来键盘扫描,中断处理也是有限的。
页: [1]
查看完整版本: "新型的按键扫描程序"的改进版,欢迎大家继续改进