278958159 发表于 2012-11-9 17:48:45

很不错,正需要这个,mark!

rt-ics 发表于 2012-11-12 17:30:38

向马老师致敬

晨昏 发表于 2012-11-18 12:35:34

学习了。正在琢磨,理解后,引进我做的小系统中。

Stone_up 发表于 2012-11-19 18:02:40

很好,谢谢。

xiongxie007 发表于 2013-1-4 00:07:11

谢谢楼主!

chenhua1991 发表于 2013-1-4 00:54:37

mark一下

sunplus 发表于 2013-1-15 07:46:45

mark回来实践下

ahauwangjie 发表于 2013-1-15 19:06:47

mark一下感谢马老师

代庆飞 发表于 2013-1-15 19:24:27

学习学习。。。。。。

hamipeter 发表于 2013-1-16 10:34:41

老贴标记下

rickly_hzy 发表于 2013-1-27 13:34:47

标记以消除浮躁的心!

一路向南 发表于 2013-2-4 15:48:10

顶起,正在学习4x4的键盘程序,打算用状态机实现连发长按功能。

hamipeter 发表于 2013-2-4 20:19:16

好帖留印

wbdzkj 发表于 2013-2-6 11:10:29

学习了,好资料

John_123 发表于 2013-2-20 08:30:06

nice thought about key process

oszp1688com 发表于 2013-2-22 14:20:52

本帖最后由 oszp1688com 于 2013-2-22 14:24 编辑

马老师:你这个按键 低层按键(I/0)扫描函数 中的那个 return key_return;   这一句不要程序中只是多出了两个警告对最终 结果无影响   
      在抄写你的程序漏写了这句 才发现的开始以为是变量 key_retun与下面那段中的变量相同造成的后面我把其中的一个变量换成大写发现还是和上面一样的现像
       本人不才,请马老师 帮忙解释下原因 谢谢


uchar key_drive(void) // 读键盘
{
        static uchar key_state=key_state_0,key_time=0; // 状态、按键计时
        uchar key_press,Key_return=N_Key;            // 按键值、返回事件
       
        key_press=key_input;   //读按键IO 电平 :松开1,按下为0。
       
        switch(key_state)
        {
                case key_state_0:   // 按键初始态
                if(!key_press) key_state=key_state_1; // 键被按下,状态转换到按键消抖和确认状态
                break;
               
                case key_state_1:// 按键消抖与确认态
                if(!key_press)
                {
                        key_time=0;
                        key_state=key_state_2;// 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态
                }
                else key_state=key_state_0;// 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
                break;
               
                case key_state_2:
                if(key_press)    // 按键释放
                {
                        Key_return=S_Key;// 返回单击事件
                        key_state=key_state_0;// 转换到按键初始态
                }
                else if(++key_time>=250) // 继续按下,计时加40ms(40ms为本函数循环执行间隔)
                {
                        Key_return=L_Key;// 按下时间>2000ms,此按键为长按操作,返回长键事件
                        key_state=key_state_3; // 转换到等待按键释放状态
                }
                break;
               
                case key_state_3:// 等待按键释放
                if(key_press) key_state=key_state_0;//按键已释放,转换到按键初始态
                break;
        }
        return Key_return; //?????????没有这一句,程序只是多了两个警告 对最终函数效果并没有产生影响 百思不知其解???????????????
}

uchar key_read(void)
{
        static uchar key_m=key_state_0,key_time_1=0;// 状态、双击时限
        uchar key_return=N_Key,key_temp;//反回事件,读键码
       
        key_temp=key_drive();//读键码
       
        switch(key_m)
        {
                case key_state_0:
                if(key_temp==S_Key) // 第1次单击,不返回,到下个状态判断后面是否出现双击
                {
                        key_time_1=0;// 初始化双击计时
                        key_m=key_state_1;// 转下一步
                }
                else key_return=key_temp;// 对于无键、长键,返回原事件
                break;
               
                case key_state_1:
                if(key_temp==S_Key) // 又一次单击(间隔肯定<500ms)
                {
                        key_return=D_Key;// 返回双击键事件,回初始状态
                        key_m=key_state_0; // 返回初始状态
                }
                else
                {   // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
                        if(++key_time_1>=150)
                        {
                                key_return=S_Key; // 500ms内没有再次出现单键事件,返回上一次的单键事件
                                key_m=key_state_0; // 返回初始状态
                        }
                }
                break;
        }
        return key_return;//返回按键事件
}

kuanglf 发表于 2013-3-5 16:12:07

学习记号!!!!!!

xiawenfeng2012 发表于 2013-3-7 15:15:57

不错,看了一个小时还没看完

clarkewayne 发表于 2013-3-30 11:57:38

学习了 受教了!!

yuhangjad 发表于 2013-3-31 21:02:19

收益非浅,学习了 马老师

didadida 发表于 2013-4-5 16:59:45

用状态机算是新手级别,写了3个小时。手边没有硬件验证,先贴出来犒劳下辛苦{:smile:}
uint ms1000,ms500;
uchar s_m=0,ms10,keyval=0,short=0,long=0;
void key()
{
        if(s_m==0)
        {
                if(~(PINC&0X01))s_m=1;
        }
        else if(s_m==1)//按下       
        {
                ms10=0;
                ms500=0;
                ms1000=0;
                s_m=2;       
        }
        else if(s_m==2)
        {
                if(ms10==10)
                {
                        ms10=0;
                        if(~(PINC&0X01))s_m=3;//确认按下
                        else s_m=0;//没有按下
                }       
        }
        else if(s_m==3)
        {
                if((PINC&0X01))
                {               
                        s_m=4;//松手
                        ms10=0;
                }
        }       
        else if(s_m==4)
        {
                if(ms10==10)
                {
                        ms10=0;
                        if((ms1000>=1000)&&(PINC&0X01))
                        {
                                if((short==0)&&(long==0))
                                {
                                        keyval=1;//长按
                                        long=1;
                                }
                                else if(long==1)
                                {
                                        long=0;
                                        keyval=0;//本次无效
                                }
                                else if(short==1)
                                {
                                        short=0;
                                        keyval=0;//本次无效
                                }
                                s_m=0;
                        }
                        if((ms1000<1000)&&(PINC&0X01))
                        {
                                if((short==0)&&(long==0))
                                {
                                        keyval=2;//短按
                                        short=1;
                                        ms500=0;
                                        s_m=5;
                                }
                                else if(long==1)
                                {
                                        long=0;
                                        keyval=0;//本次无效
                                        s_m=0;
                                }
                                else if(short==1)
                                {
                                        short=2;
                                        ms500=0;
                                        s_m=5;
                                }
                        }
                       
                }
        }
        else//(s_m==5)
        {
                if((~(PINC&0X01))&&(ms500<500))
                {
                        if(short==2)
                        {
                                keyval=4;//双击
                        }
                        s_m=0;
                }               
                if(ms500>500)
                {
                        if(short==1)
                        {
                                short=0;
                                keyval=3;//单击
                        }               
                        s_m=0;
                }       
        }
}
SIG(SIG_OVERFLOW0)//1ms定时中断
{
        ms10++;
        ms500++;
        ms1000++;
        key();       
}

yinhe 发表于 2013-4-14 09:13:42

记录一下

kingie2006 发表于 2013-5-17 10:10:57

标记,很好的帖子

zjk 发表于 2013-5-23 17:51:33

不错的设计,值得借鉴!

rantingting 发表于 2013-5-27 12:57:59

最实用的操作啊 好东西

sqmm 发表于 2013-6-17 10:34:05

本帖最后由 sqmm 于 2013-6-17 10:50 编辑

我老早实现过单个按键实现导航的程序
能够单击双击,3击....9击...N击
能够识别长按,单击后长按,双击后长按....N击后长按

直接用button_get()返回不同键码.
可以很方便实现导航.
用一个定时器,主循环保证150ms内有查询就OK
#include<avr/interrupt.h>
#include<cpu/cpu.h>
#include"avrbutton.h"

static dwordVal_t tsKeyDown;
static dwordVal_t tsKeyUp;
static dwordVal_t* ptsKeyLast = &tsKeyDown;        // 最近按键动作计时

static u8                        ButtonAction;        // 为主循环提供按钮事件信号
static u8                        KeyIsMulti;        // 连击标识,1:有可能连击
static u8                        KeyCount;        // 连击次数

#define MULTI_CLIK        150
#define LONG_PRESS        1000
#define KEY_REPEAT        300



u16 ms_diff(dwordVal_t* more, dwordVal_t* less)
{
        u16 ret = more->Val - less->Val;
        return ( ret & 0x8000 )?0xFFFF:ret;
}

SIGNAL ( MutiFunction_ISR )
{
        if( !ms_passed(ptsKeyLast,21) )// 消抖
                return;
        u8 state = PIN_H(MutiFunction_KEY);
        if(!state)
        {//按键弹起
                if(ButtonAction>=3)//重复后驱动复位
                {
                        KeyCount = 0;
                        ButtonAction = 0;
                        KeyIsMulti = 0;
                        return;
                }
                ButtonAction = 2;
                tsKeyUp.Val = SysClock.Val;
                ptsKeyLast = &tsKeyUp;
                if(KeyIsMulti)
                {
                        KeyCount ++;
                }
        }
        else
        {//按键按下
                ButtonAction = 1;
                tsKeyDown.Val = SysClock.Val;
                ptsKeyLast = &tsKeyDown;
                if( ms_diff(&tsKeyDown, &tsKeyUp)<MULTI_CLIK )// 有可能是连击
                {
                        KeyIsMulti = 1;
                        if(KeyCount==0)
                                KeyCount = 1;
                }
        }
}


u8 button_get(void)
{
        u8 ret = 0;
        if(ButtonAction==0)
        {
                return ret;
        }
        else if(ButtonAction==2)
        {
                if( ms_passed(ptsKeyLast, MULTI_CLIK) )
                {
                        if(KeyIsMulti)
                        {
                                ret = KeyCount;
                        }
                        else// 要么单击要么长按
                        {
                                ret = 1;// 单击       
                        }
                        KeyCount = 0;
                        ButtonAction = 0;
                        KeyIsMulti = 0;                       
                }
        }
        else if(ButtonAction==1)
        {// 很可能产生连击事件.
                if( ms_passed(ptsKeyLast, LONG_PRESS) )
                {
                        ButtonAction = 3;
                        ret = KeyCount?KeyCount:0xFF;
                }
               
        }
        else if(ButtonAction==3)
        {
                if( ms_passed(ptsKeyLast, KEY_REPEAT) )
                {
                        if( PIN_L(MutiFunction_KEY) )//按键弹起确认
                        {
                                KeyCount = 0;
                                ButtonAction = 0;
                                KeyIsMulti = 0;
                                return ret;
                        }
                        //ptsKeyLast->Val = SysClock.Val;
                        //ret = KeyCount?KeyCount:0xFF;
                }
        }
        return ret;
}



// 初始化按钮端口,打开按钮电平跳变中断
void button_init(u8 id)
{
        DRIVER(MutiFunction_KEY, IN);
        CLR( MutiFunction_KEY );
        MASK_1(EIMSK, MutiFunction_INT);
        MASK(EICRB, 0x1/*0b01*/<<((MutiFunction_INT-4)<<1));
        ButtonAction = 0;
}

fiddly 发表于 2013-6-17 11:13:50

今天才看到这么火帖子,学习!

大哈欠E 发表于 2013-6-22 01:12:14

mark,实在想睡觉了,不能熬夜。

shiy 发表于 2013-6-22 07:22:44

先标记一下,回头细看

hustloong 发表于 2013-7-17 13:34:14

标记一下

sunchao151 发表于 2013-7-19 14:46:10

MARK
学习应用中……

416356084@qq.co 发表于 2013-7-19 16:39:50

{:handshake:},很好的帖子,

404710520 发表于 2013-7-19 17:05:05

下班了回去在看

lsz0628 发表于 2013-7-19 17:31:24

mark...........

jacky82512 发表于 2013-7-24 16:14:15

m a r k 看看留个标记

1501697860 发表于 2013-7-30 15:13:45

多谢大家,学习了

liubuwai 发表于 2013-8-8 23:32:37

诶好贴啊多看多谢 多领悟马老师应该多开点这种贴啊思想上的真正提高啊。。

Rainfieldwood 发表于 2013-8-12 11:03:03

mark!!{:lol:}

liaogang1314 发表于 2013-8-17 16:01:45

马老师 我这个长短按键程序对吗?
#define KEY_OFF_ON   RB5

unsigned int uiTimer1_Count;

unsigned char ucKey_Count_Start;
unsigned char ucSet_Down_Short;
unsigned char ucSet_Down_Long;
unsigned char ucSet_Short_Flag;
unsigned char ucSet_Long_Flag;

void Key_Long_Short(void)
{
    if(KEY_OFF_ON == 0)                                              //检测按键按下
   {
         ucKey_Count_Start = 1;
         if((KEY_OFF_ON == 0) && (uiTimer1_Count >= 8))            //按键500MS为短按
         {
            ucSet_Down_Short = 1;
            ucSet_Down_Long = 0;
         }
         if((KEY_OFF_ON == 0) && (uiTimer1_Count >= 77))            //按键5S为长按
         {
            ucSet_Down_Short = 0;
            ucSet_Down_Long = 1;
         }
   }
   else
   {
          if((KEY_OFF_ON == 1) && (ucSet_Down_Short == 1))
          {
            ucSet_Down_Short = 0;
            ucSet_Short_Flag = 1;                                    //一个完整的短按键操作完成
          }
          if((KEY_OFF_ON == 1) && (ucSet_Down_Long == 1))
          {
            ucSet_Down_Long = 0;
            ucSet_Long_Flag = 1;                                    //一个完整的长按键操作完成
          }
          ucKey_Count_Start = 0;
   }

   if(ucKey_Count_Start == 1)                                       //按键开始定时器计数
   {
          TMR1ON = 1;
   }
   else if(ucKey_Count_Start == 0)
   {
          TMR1ON = 0;
   }
}

void Power_On_Off(void)
{
    if(ucSet_Long_Flag == 1)                                                             //是长按键
    {   
      ucSet_Long_Flag = 0;
      PCA9635LedoutSet(0x2a, 0xaa, 0xaa, 0x0a, 0x00);                                    //开机
      return;
    }
    else if(ucSet_Short_Flag == 1)                                                      //是短按键
    {   
      ucSet_Short_Flag = 0;
      PCA9635LedoutSet(0x2a, 0x00, 0x00, 0x00, 0x00);                                  //关机
      return;
    }   
}

ZJSXHWL000000 发表于 2013-9-20 06:32:29

讨论太好了,有意思。

zjt761215 发表于 2013-9-20 06:46:08

谢谢 老师 希望再出点好书

金牛AKI 发表于 2013-10-8 11:51:43

好帖子 顶起 哈哈

JESTER9 发表于 2013-10-8 13:09:12


学习了!

在途中 发表于 2014-2-13 17:54:52

看起来容易,真正自己独立整理思路去写确为不易

g527727372 发表于 2014-2-13 20:53:38

呵呵,学习了!!

g527727372 发表于 2014-2-13 20:54:36

呵呵!谢谢分析!

longfeixue 发表于 2014-2-21 13:19:47

耐心看了一遍,讨论的太好了。

longfeixue 发表于 2014-2-21 13:29:05

以后还得再看

ltmooner 发表于 2014-2-21 14:59:50


技术贴,mark

atg525 发表于 2014-2-21 17:24:06

mark                                 

tdchenke 发表于 2014-2-24 16:07:50

好贴学习了

cld795 发表于 2014-3-18 22:09:57

mark         

zkf0100007 发表于 2014-3-19 00:11:10

手机mark下,改天细看

1米49 发表于 2014-3-19 08:56:27

先收藏,回头看

机器人天空 发表于 2014-3-19 09:08:25

mark。。。。。。。

鼻儿眼睛花 发表于 2014-3-19 10:42:58

缺少面向对象的思想,小众编程。

crazydtone 发表于 2014-4-11 09:59:19

谢谢马老师,受教了!
Mark...

haizaolan 发表于 2014-4-11 10:43:30

看完这个帖子,自己增加了自信,马老师帖子中一直强调的“编程思想和方法是编程的核心”这个观点,我严重同意。

机器人天空 发表于 2014-4-11 11:20:58

八错{:lol:}

wushifeng 发表于 2014-4-11 16:26:44

好帖,收藏

tigeroser 发表于 2014-6-2 19:33:58

这个一个我看过的最简单&最复杂的帖子,琢磨很长时间啦

jinxphf 发表于 2014-7-28 18:09:29

学习了!

ZYBing 发表于 2014-7-28 18:35:17

谢谢分享!

tjiang 发表于 2014-9-22 15:56:26

很好的状态机按键设计,目前很多按键设计都得益于这篇帖。

mk_avatar 发表于 2014-11-11 11:13:02

mark!!!!

停靠点 发表于 2014-11-11 11:34:34

好思路!

佳哥517 发表于 2014-11-11 12:19:47

Mark,谢谢分享

蓝蓝的恋 发表于 2014-11-11 12:55:33

其实不是不想做的好,而是根本没有能力做好!这句话在理,顶楼主~

oaixuw 发表于 2014-11-11 13:39:19

很好的东西,谢了

jiespring 发表于 2014-11-11 14:19:23

谢谢,马克一下

孙为 发表于 2014-11-13 16:47:08

好贴子,学习了

孙为 发表于 2014-11-13 16:47:51

再简单的功能要是想写好了,写通用了,写稳定了,还真不是一件容易的事

zwhzwh_11 发表于 2014-11-13 18:08:16

不mark了,果断打印珍藏

signal10 发表于 2014-11-13 18:16:13

多谢分享。

frank_88888 发表于 2014-11-13 18:18:01

好资料,收藏!

Cumu 发表于 2015-4-30 15:26:46

非常有用记一下

tjcf 发表于 2015-5-3 10:42:01

好东西一定要顶

sos9616 发表于 2015-5-14 18:51:11

mark,很不错

wang55 发表于 2015-5-14 19:28:10

好帖,收藏一下。

gsq19920418 发表于 2015-5-14 19:36:23

MARK一下好了

zwxoec 发表于 2015-5-14 22:37:45

耐心看完,感慨!向马潮老师学习,将细节做到极致!

hao999a 发表于 2015-5-15 14:32:41

mark,谢谢楼主的分享            

lnso 发表于 2015-8-29 08:50:35

马老师,这一个按键要是接在中断上要如何处理?中断发生后如何处理长按,短按??

鹰凖明 发表于 2015-9-3 16:49:59

主程序执行过程中想从主程序初始化重头执行,gai

马老师,我向您请教一个关于C语言的问题:主程序执行过程中想从主程序初始化重头执行,该怎么编写指令或用什么其他办法实现这个功能呢?敬请您的回复,感谢!

machao 发表于 2015-9-8 21:45:46

鹰凖明 发表于 2015-9-3 16:49
马老师,我向您请教一个关于C语言的问题:主程序执行过程中想从主程序初始化重头执行,该怎么编写指令或用 ...

简单的方法是嵌入一句汇编代码:jmp 0000h

machao 发表于 2015-9-8 21:49:05

lnso 发表于 2015-8-29 08:50
马老师,这一个按键要是接在中断上要如何处理?中断发生后如何处理长按,短按?? ...

不建议用外部中断去处理

lnso 发表于 2015-9-8 22:10:43

machao 发表于 2015-9-8 21:49
不建议用外部中断去处理

项目只有一个按键,而且要做低功耗,逼不得已啊

鹰凖明 发表于 2015-9-10 10:44:40

machao 发表于 2015-9-8 21:45
简单的方法是嵌入一句汇编代码:jmp 0000h

感谢指导!马老师,你说的这个跳转指令后面的数值是main函数的起始地址值吗?

carryonli 发表于 2015-9-10 11:00:35

好复杂,如果用AD输入那种,一根线,多简单。

zjk 发表于 2015-9-10 14:09:47

学习下状态机。。。

koenlee93 发表于 2016-4-17 08:02:51

这个不错,曾经用过有些产品,按键程序就写的不好让人感觉按键坏了一样

bigwei 发表于 2016-5-6 12:31:25

受益匪浅,mark

yangbo18416 发表于 2016-8-26 14:59:19

Mark,以后可以多一个解决的思路了

whhb8612 发表于 2016-8-27 09:31:39

受教了,感谢楼主

kukudi 发表于 2016-8-28 16:36:12

按键基于状态机算法,确实是一种非常好的思路,也不容易乱。

夕阳林中栖 发表于 2016-10-3 06:53:23

学习学习,谢谢分享

liubiao6832170 发表于 2016-10-21 10:51:25

学习谢谢老师

w_dehong 发表于 2016-10-21 10:55:19

收藏了,感觉特别好

klbs 发表于 2016-10-21 10:56:01

这个可以节省一些IO点啊,呵呵,学习了,谢谢

efree 发表于 2016-12-18 18:11:02

代码好,说的也好,支持。
页: 1 2 3 [4] 5
查看完整版本: 题目:多功能按键设计。利用一个I/O口,接一个按键,实现3功能操作:单击 + 双击 + 长按。