搜索
bottom↓
回复: 147

【MultiButton】并发状态事件驱动按键驱动模块,量产产品实践

  [复制链接]

出0入0汤圆

发表于 2016-9-2 11:53:08 | 显示全部楼层 |阅读模式
本帖最后由 半导体 于 2016-9-2 14:18 编辑

简介:
        MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调处理方式可以简化你的程序逻辑,去除冗余的按键处理硬编码,专注你的程序思路。
该模块已稳定用于公司多款量产智能硬件产品。

使用方法:
        1. 先申请一个按键结构
                struct Button button1;

        2. 初始化按键对象,绑定按键连接的GPIO引脚,read_button_pin() 为按键的GPIO读取函数,后一个参数为设置触发电平
                button_init(&button1, read_button_pin, 0);

        3. 注册按键事件,共有以下5种事件:
                        CLICK                        //每次按下都会触发
                        PRESSED                   //单击触发
                        DOUBLE_CLICK           //双击触发
                        LONG_RRESS_START   //长按开始触发一次
                        LONG_PRESS_HOLD    //长按过程一直触发
                        LONG_PRESS_STOP    //长按松手触发

                button_attach(&button1, SINGLE_CLICK, Callback_SINGLE_CLICK_Handler);
                button_attach(&button1, DOUBLE_CLICK, Callback_DOUBLE_Click_Handler);
                ...

        4. 启动按键
                button_start(&button1);

        5. 设置一个5ms间隔的定时器循环调用后台处理函数
                while(1) {
                        ....
                            if(timer_ticks == 5) {
                                timer_ticks = 0;
                                button_ticks();
                            }
                }


特性:
        MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理:
  1. struct Button {
  2.     uint16_t ticks;
  3.     uint8_t  state : 3;
  4.     uint8_t  debounce_cnt : 3;
  5.     uint8_t  active_level : 1;
  6.     uint8_t  button_level : 1;
  7.     uint8_t  (*hal_button_Level)(void);
  8.     CallBackFunc  cb[number_of_event];
  9.     struct Button* next;
  10. };
复制代码

这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。


Examples
  1. #include "button.h"

  2. struct Button button1;
  3. struct Button button2;

  4. int read_button1_pin()
  5. {
  6.     return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);  //HAL GPIO read.
  7. }
  8. ...

  9. int main()
  10. {
  11.     button_init(&button1, read_button1_pin, 0);  //初始化,绑定按键GPIO电平读取接口
  12.     button_init(&button2, read_button2_pin, 0);

  13.     button_attach(&button1, PRESSED, BTN1_SINGLE_CLICK_Handler);    //绑定 按键1 的单击事件回调
  14.     button_attach(&button1, DOUBLE_CLICK, BTN1_DOUBLE_Click_Handler);    //双击事件回调
  15.     button_attach(&button2, LONG_RRESS_START, BTN2_LONG_RRESS_START_Handler);  //绑定 按键2 长按开始事件回调
  16.     button_attach(&button2, LONG_PRESS_HOLD,  BTN2_LONG_PRESS_HOLD_Handler);
  17.     button_attach(&button2, LONG_PRESS_STOP,  BTN2_LONG_PRESS_STOP_Handler);

  18.     button_start(&button1);  //启动
  19.     button_start(&button2);
  20.    
  21.     //定时循环调用button_ticks() 后台处理函数,该调用方法由你的平台自行实现。
  22.     __timer_start(button_ticks, 0, 5);  
  23.    
  24.     while(ture)
  25.     {
  26.      ...
  27.     }
  28. }

  29. void BTN1_SINGLE_CLICK_Handler()
  30. {
  31.     //do something..
  32. }

  33. void BTN1_DOUBLE_Click_Handler()
  34. {
  35.     //do something..
  36. }
  37. ...
复制代码


模块下载地址:
https://github.com/0x1abin/MultiButton

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

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

出0入0汤圆

发表于 2016-9-2 12:00:44 | 显示全部楼层
学习了, 谢谢分享!

出0入0汤圆

发表于 2016-9-2 12:33:55 | 显示全部楼层
应该写个demo

出0入0汤圆

发表于 2016-9-2 12:34:20 | 显示全部楼层
申请一个结构体,分配设置这个结构体的成员,注册  

让我想起了Linux驱动程序的入口函数框架

出0入0汤圆

 楼主| 发表于 2016-9-2 13:56:59 | 显示全部楼层
ywlzh 发表于 2016-9-2 12:34
申请一个结构体,分配设置这个结构体的成员,注册  

让我想起了Linux驱动程序的入口函数框架 ...

是的,我确实是借鉴了Linux的C语言实现面向对象的编程思路,哈哈。

出0入0汤圆

 楼主| 发表于 2016-9-2 13:59:49 | 显示全部楼层

最下面的就是Dome啊,这个模块与平台无关的,可运行于任何嵌入式环境。

出0入0汤圆

发表于 2016-9-2 14:10:37 | 显示全部楼层
看这个接口,非常不错啊~~~

出0入0汤圆

发表于 2016-9-2 14:39:00 | 显示全部楼层
进来学习下!

出0入0汤圆

发表于 2016-9-2 15:14:54 | 显示全部楼层
感谢楼主分享经验

出0入0汤圆

发表于 2016-9-2 15:29:25 | 显示全部楼层
需要多大的空间。 小片可能用不上。

出0入0汤圆

 楼主| 发表于 2016-9-2 15:39:59 | 显示全部楼层
ycping 发表于 2016-9-2 15:29
需要多大的空间。 小片可能用不上。

一个按键一份占用一份数据结构的RAM,所以占用空间还是比较弹性的,其实也不需要多大,可根据你的项目需求做一些小裁剪。
再说了,现在程序空间几乎是我们不需要担心,更多是考虑健壮易维护的程序结构。

出0入0汤圆

发表于 2016-9-2 15:41:07 | 显示全部楼层
这个想法非常好啊。

出0入0汤圆

发表于 2016-9-2 16:15:23 | 显示全部楼层
谢谢分享,希望能用上

出0入0汤圆

发表于 2016-9-2 16:18:56 | 显示全部楼层
帮忙贴出来吧,核心部分代码:还是状态机
/**
  * @brief  Button driver core function, driver state machine.
  * @param  handle: the button handle strcut.
  * @retval None
  */
void button_handler(struct Button* handle)
{
        uint8_t read_gpio_level = handle->hal_button_Level();

        //ticks counter working..
        if((handle->state) > 0) handle->ticks++;

        /*------------button debounce handle---------------*/
        if(read_gpio_level != handle->button_level) { //not equal to prev one
                //continue read 3 times same new level change
                if(++(handle->debounce_cnt) >= kDebounceTicks) {
                        handle->button_level = read_gpio_level;
                        handle->debounce_cnt = 0;
                }

        } else { //leved not change ,counter reset.
                handle->debounce_cnt = 0;
        }

        /*-----------------State machine-------------------*/
        switch (handle->state) {
        case 0:
                if(handle->button_level == handle->active_level) {        //start press
                        if(handle->cb[CLICK]) handle->cb[CLICK]();
                       
                        handle->ticks = 0;
                        handle->state = 1;
                }
                break;

        case 1:
                if(handle->button_level != handle->active_level) { //released
                        handle->state = 2;

                } else if(handle->ticks > kLongTicks) {
                        if(handle->cb[LONG_RRESS_START]) handle->cb[LONG_RRESS_START]();

                        handle->state = 5;
                }
                break;

        case 2:
                if(handle->ticks > kClickTicks) {        //released
                        //press event
                        if(handle->cb[PRESSED]) handle->cb[PRESSED]();        //press event

                        handle->state = 0;        //reset

                } else if(handle->button_level == handle->active_level) { //press again
                        if(handle->cb[CLICK]) handle->cb[CLICK]();
                        handle->state = 3;
                }
                break;

        case 3:        //repeat press pressing
                if(handle->button_level != handle->active_level) {        //double releasd
                        //double click event
                        if(handle->cb[DOUBLE_CLICK]) handle->cb[DOUBLE_CLICK]();

                        handle->state = 0;
                }
                break;

        case 5:
                if(handle->button_level == handle->active_level) {
                        //continue hold trigger
                        if(handle->cb[LONG_PRESS_HOLD]) handle->cb[LONG_PRESS_HOLD]();

                } else { //releasd
                        if(handle->cb[LONG_PRESS_STOP]) handle->cb[LONG_PRESS_STOP]();
                        handle->state = 0; //reset
                }
                break;
        }
}

出0入0汤圆

发表于 2016-9-2 16:35:54 | 显示全部楼层
正在学习,谢谢分享

出0入0汤圆

发表于 2016-9-2 17:04:04 | 显示全部楼层
难得好资料,谢谢分享

出0入198汤圆

发表于 2016-9-2 18:07:36 | 显示全部楼层
赞,支持下!!

文档完善,代码规整,不错的开源的软件,感谢分享。

按键事件绑定是项目中非常常用的功能,以前就想好好设计下,现在可以直接试用楼主的。

出0入0汤圆

发表于 2016-9-2 20:11:22 | 显示全部楼层
非常不错。。感谢开源。

出10入10汤圆

发表于 2016-9-2 20:22:47 | 显示全部楼层
感谢开源      

出0入0汤圆

发表于 2016-9-2 22:16:49 | 显示全部楼层
谢谢,好的按键处理模块

出0入0汤圆

发表于 2016-9-2 22:44:18 来自手机 | 显示全部楼层
太牛逼了

出0入70汤圆

发表于 2016-9-2 22:54:27 | 显示全部楼层
很好, 这接口不错

出0入0汤圆

发表于 2016-9-2 23:01:11 来自手机 | 显示全部楼层
感谢分享 . . .

出0入4汤圆

发表于 2016-9-2 23:24:32 | 显示全部楼层
学习了        

出0入0汤圆

 楼主| 发表于 2016-9-3 00:48:36 | 显示全部楼层
sunnydragon 发表于 2016-9-2 18:07
赞,支持下!!

文档完善,代码规整,不错的开源的软件,感谢分享。


谢谢armink大神前辈,你的库写得非常棒,你是我在论坛看到程序写得最有品味的人了

出0入0汤圆

发表于 2016-9-3 10:13:20 | 显示全部楼层
感谢楼主分享经验

出0入0汤圆

发表于 2016-9-3 10:35:04 | 显示全部楼层
GITHUB 上不去,怎么办,能不能再论坛上传一份,便于大家学习,下载,谢谢

出0入0汤圆

发表于 2016-9-3 10:50:45 | 显示全部楼层
谢谢开源分享

出0入0汤圆

发表于 2016-9-3 12:46:28 来自手机 | 显示全部楼层
哥们,能否打包到论坛啊,那个网站下载不了

出20入25汤圆

发表于 2016-9-3 13:59:05 来自手机 | 显示全部楼层
假设实现菜单,直接绑定是不是不便

出0入0汤圆

发表于 2016-9-3 15:48:52 | 显示全部楼层
其实可以试试把那个总共只有3行代码的按键判断放在这个模块里边

出0入0汤圆

 楼主| 发表于 2016-9-3 16:41:53 | 显示全部楼层
chenchaoting 发表于 2016-9-3 13:59
假设实现菜单,直接绑定是不是不便

你可以把菜单的状态机放在按键回调里处理,比如:
void BTN1_SINGLE_CLICK_Handler()
{
    if(menu_state == 1) {
        menu1_update();
        menu_state = 2;
    }
    else if(menu_state = 2)  {
        menu2_update();
        menu_state = 3;
   }
.....
}
类似这样。。你的程序逻辑也许会更清晰。

出0入0汤圆

 楼主| 发表于 2016-9-3 16:48:54 | 显示全部楼层
mandzy 发表于 2016-9-3 15:48
其实可以试试把那个总共只有3行代码的按键判断放在这个模块里边

那个3行代码的帖子我看过,应用场景还是比较局限性的,并且可读性很差,你要花时间去仔细理解它,除非是资源很极端的情况才去用这种取巧的代码,平时还是以可读性、易维护为重点,毕竟需求经常在变。。

出0入0汤圆

发表于 2016-9-3 16:49:06 | 显示全部楼层
不错,学习一下

出0入0汤圆

发表于 2016-9-3 20:08:25 来自手机 | 显示全部楼层
可能不搞软件了,还是记号

出20入0汤圆

发表于 2016-9-3 21:33:08 | 显示全部楼层
谢谢分享。写的很好。

出0入0汤圆

发表于 2016-9-4 01:47:14 来自手机 | 显示全部楼层
写的清晰易懂

出0入0汤圆

发表于 2016-9-4 07:02:33 | 显示全部楼层
楼主:button.c中第29行有错误,第二个参数和第三个参数反了

出0入0汤圆

 楼主| 发表于 2016-9-4 10:41:44 | 显示全部楼层
minier 发表于 2016-9-4 07:02
楼主:button.c中第29行有错误,第二个参数和第三个参数反了

那不是错误,你再仔细看下,handle->button_level = handle->hal_button_Level();这个初始化读取一次引脚电平。

出0入0汤圆

发表于 2016-9-4 22:04:35 | 显示全部楼层
很有创意

出0入0汤圆

发表于 2016-9-4 22:04:54 来自手机 | 显示全部楼层
好东西啊我看看

出0入0汤圆

发表于 2016-9-5 13:24:57 | 显示全部楼层
mark  看着很好用,学习学习

出0入0汤圆

发表于 2016-9-7 10:25:11 | 显示全部楼层
封装的不错,加上组合键功能就更完美了

出0入8汤圆

发表于 2016-9-7 11:17:00 | 显示全部楼层
半导体 发表于 2016-9-3 16:41
你可以把菜单的状态机放在按键回调里处理,比如:
void BTN1_SINGLE_CLICK_Handler()
{

在 GUI 架构里面,我想还是要以 GUI 为第一视角,
即要在 GUI 的 handler 里面处理按键事件(或者说按键消息),毕竟 GUI 的 handler 程序,并不是只单单关注 key 的,还有其他事情要集中处理,
而在 key 的 handler 里面处理 GUI,我想可能不是一个很好的思路。

出0入0汤圆

发表于 2016-9-7 13:55:48 | 显示全部楼层
感谢分享,很厉害~!

出0入0汤圆

发表于 2016-9-7 15:58:17 | 显示全部楼层
感谢分享,学习收藏之~

出0入0汤圆

发表于 2016-9-7 16:37:34 | 显示全部楼层
程序清晰,简单易读,不错

出0入0汤圆

发表于 2016-9-7 17:43:14 来自手机 | 显示全部楼层
建议加上松开触发的动作

出0入0汤圆

发表于 2016-9-8 08:31:57 | 显示全部楼层
security 发表于 2016-9-7 11:17
在 GUI 架构里面,我想还是要以 GUI 为第一视角,
即要在 GUI 的 handler 里面处理按键事件(或者说按键 ...

把按键事件的回调函数作为按键发射器,写一个FIFO,app程序通过获取FIFO值来得到按键值(相当于平时写的读键函数),这样就和平时写的按键操作一样了。

出0入0汤圆

发表于 2016-9-8 08:38:33 | 显示全部楼层
强烈建议增加按键事件,希望能和键盘一样强大,应该就缺松开触发和热键(组合功能)。
ps:想用这个,可惜却少两个必须的按键事件,持续关注中。。。

出0入0汤圆

发表于 2016-9-8 13:06:13 | 显示全部楼层
mark, 按键模块封装,感谢楼主无私分享,谢谢。

出0入0汤圆

发表于 2016-9-8 16:39:17 | 显示全部楼层
给楼主 +1 ★

出40入18汤圆

发表于 2016-9-8 19:43:03 | 显示全部楼层
给楼主续1S

出0入0汤圆

发表于 2016-9-10 08:09:24 | 显示全部楼层
谢谢楼主无私分享

出0入0汤圆

 楼主| 发表于 2016-9-10 13:50:16 | 显示全部楼层
单飞 发表于 2016-9-8 08:38
强烈建议增加按键事件,希望能和键盘一样强大,应该就缺松开触发和热键(组合功能)。
ps:想用这个,可惜 ...

你好,你说得很有道理,刚更新的版本已经可以满足你的需求。

出0入0汤圆

发表于 2016-9-10 14:41:45 | 显示全部楼层
请问一下支持组合按键吗?

出0入0汤圆

 楼主| 发表于 2016-9-11 13:43:19 来自手机 | 显示全部楼层
chenguanghua 发表于 2016-9-10 14:41
请问一下支持组合按键吗?

支持了,在回调里查询其它按键事件就可以。

出0入0汤圆

发表于 2016-9-12 07:58:48 | 显示全部楼层
半导体 发表于 2016-9-11 13:43
支持了,在回调里查询其它按键事件就可以。

组合键?没找到哦(或者没看明白)

出0入0汤圆

 楼主| 发表于 2016-9-12 08:51:44 | 显示全部楼层
单飞 发表于 2016-9-12 07:58
组合键?没找到哦(或者没看明白)

比如你在按键1的回调里查询你要组合的按键情况,类似这样:
void BTN1_PRESS_DOWN_Handler(void* btn)
{
    if(get_button_event(&btn2) != NONE_PRESS) {
        //button1 & button2
    }
}

出0入0汤圆

发表于 2016-9-12 08:57:56 来自手机 | 显示全部楼层
历害,完全满足我的需要,感谢lz无私奉献,谢谢

出0入0汤圆

发表于 2016-9-12 10:54:50 | 显示全部楼层
1、双击时,PRESS_UP(弹起事件)会比PRESS_DOWN(按下事件)多触发一次。
2、PRESS_REPEAT,测试的时候没触发过。
3、number_of_event这个也不知道干嘛的。
4、如果可以,长按的连发功能,可调连发速度。
5、如果可以,长按事件,可调的触发时长。
6、关于组合键,你提供的方案只支持组合键同时按下的情况,不支持先下基键再按另一个组合键。(必要性:①由于一些机械原因,能同时满足两个按键同时按下是一件不容易的事;②不能支持按下基键,再单击另一个组合键的连续操作)

出0入0汤圆

 楼主| 发表于 2016-9-12 11:44:47 | 显示全部楼层
单飞 发表于 2016-9-12 10:54
1、双击时,PRESS_UP(弹起事件)会比PRESS_DOWN(按下事件)多触发一次。
2、PRESS_REPEAT,测试的时候没 ...

1. 你说的这个情况我这边是正常的,每一次拿下按键的动作都会有一个PRESS_DOWN,松手抬起时有一个PRESS_UP,事件,所以一次双击动作一共有2个PRESS_DOWN和2次PRESS_UP;
2. PRESS_REPEAT是一个连击事件,通过回调传入的结构指针(通过强转void*指针得到按键结构体)中的repeat变量扩展3连击,4连击...等事件;
3. number_of_event这个并不是事件(通过名字可知道),是用于计数事件个数以让创建结构体时分配回调指针内存长度(BtnCallback  cb[number_of_event];);
4.  关于触发速度,可由2中所说方法根据ticks变量作为时标,从长按事件的按下开始计数;
5. 同4方法一样(记得多用传入的结构体指针做事);
6. 你说的那些情况都支持的。

附上我的测试工程(Nucleo-F446RE)

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2016-9-12 13:25:02 | 显示全部楼层
本帖最后由 单飞 于 2016-9-12 13:26 编辑
半导体 发表于 2016-9-12 11:44
1. 你说的这个情况我这边是正常的,每一次拿下按键的动作都会有一个PRESS_DOWN,松手抬起时有一个PRESS_U ...


1、2你是对的,我的打印信息有一个打错了。
3、...
4、5希望提供接口函数。
6、关于组合键的问题:
首先已知按键的时间都是单一的按键事件,如果通过回调函数来实现的组合按键的话就需要费点功夫。
假设组合按键是两个(可能是多个),BTN1是基键,BTN2是另一个常规按键
①两键同时按下触发组合事件
void BTN1_PRESS_DOWN_Handler(void* btn)
{
    if(get_button_event(&btn2) == PRESS_DOWN) {
        //button1 & button2
    }
}

注:这种方法从原理上讲并不应该发生,因为BTN1作为基键原则上需要该键先触发,但是事实并不总是这样的。

②先按下基键,在按下另一个组合键
void BTN2_PRESS_DOWN_Handler(void* btn)
{
    if(get_button_event(&btn1) == LONG_PRESS_HOLD) {
        //button1 & button2
    }
   elsse{
        //常规操作
   }
}

注:②方法有局限性,首先基键必须要达到LONG_PRESS_HOLD的触发时间,一般这个条件都是满足的,但是事实不总是这样的;另一个更需要关注的问题是,当我们同时按下的时候(事实上先按下了BTN1,但时我们觉得是同时按下的,其实就是①存在原因),
不幸的是BTN1_PRESS事件和组合事件同时发生了,处理的办法是想办法不要让它俩共存,或者再加点等待效果会更好。

综上,当前该系统用于实现组合按键时,最好的写法是只选上面的①、②当中的一个,两个放在一起会干扰,或还有其他好的办法。。。

出0入0汤圆

发表于 2016-9-12 13:39:21 | 显示全部楼层
单飞 发表于 2016-9-12 13:25
1、2你是对的,我的打印信息有一个打错了。
3、...
4、5希望提供接口函数。

事实上这有②这种情况效果比较好,即必须先按下基键,然后再按另一个组合键。
单独来写①这种情况,效果不好,干扰很严重。不知道有没更好的处理方法

出0入0汤圆

 楼主| 发表于 2016-9-12 14:40:43 | 显示全部楼层
单飞 发表于 2016-9-12 13:25
1、2你是对的,我的打印信息有一个打错了。
3、...
4、5希望提供接口函数。

首先感谢您这么建设性地给我意见,帮助我做出不少改进!
我们重点再说下那个组合键问题。

我先说下我们的键盘组合键(热键)操作逻辑,通常是 Ctrl+其他按键,Alt+其他按键,我们就拿Ctrl+A这个例子来说:
1. 这个Ctrl就是你所说的基建,它在通常的场景下单独按下是没有功能;
2. 单独按下A按键有它的另外功能;
3. 经常的热键组合基本是Ctrl+A、Ctrl+B、Ctrl+Alt+Z;
4. 绝对不会出现像A+B这样类型的组合热键。
所以像你所说的①情况是不会出现,基建一定是要明确先被按下的,就像键盘A+Ctrl这样按会出现不同的结果.
如果你产品正常操作有像你所说的要两个按键“同时”按下这样的操作定义,你反而需要考虑一下你产品这样的操作设计是不是合理,如果A单独按下有它的功能,B也有,那A+B这样操作势必有一个按键会先被按下,因为没有“同时”这种概念,这样容易导致误操作。

当然,有一些特殊情况下确实是要定义两个按键“同时”按下的,比如手机的“开机同时按下音量键+电源键进入.....”这样操作,这种情况还是建议直接对判断引脚电平进行逻辑操作。
最后,如果你真的就是要用这个模块功能去定义一些“同时”的操作,方法还是有的,只是不是很优雅。

在BTN1和BTN2的回调里同时加上这样的逻辑判断:
void BTN1_PRESS_DOWN_Handler(void* btn)
{
    if(get_button_event(&btn2) == NONE_PRESS) {
        //单独按下正常操作
    } else {
        //button1 & button2
    }
}

void BTN2_PRESS_DOWN_Handler(void* btn)
{
    if(get_button_event(&btn1) == NONE_PRESS) {
        //单独按下正常操作
    } else {
        //button1 & button2
    }
}
注意!一定是NONE_PRESS,而不是你用的LONG_PRESS_HOLD,因为按键只要被按下就会产生事件,没被按下就是NONE_PRESS。

后面有什么更好更优雅的解决办法我们再探讨下。

出0入0汤圆

发表于 2016-9-12 14:52:59 | 显示全部楼层
半导体 发表于 2016-9-12 14:40
首先感谢您这么建设性地给我意见,帮助我做出不少改进!
我们重点再说下那个组合键问题。

忘说一个条件了,就是热键只能使用一个弹起事件。
我现在的产品就是这么用的,总共6个键,每个都有单独的功能,有一个特殊的按键作为热键(我这里是【返回键】)。
其实所谓的组合功能,本身是一个隐藏功能,除了产品的限制还有这个原因,使它不能作为一个单独的热键。

出0入0汤圆

发表于 2016-9-12 14:59:50 | 显示全部楼层
半导体 发表于 2016-9-12 14:40
首先感谢您这么建设性地给我意见,帮助我做出不少改进!
我们重点再说下那个组合键问题。

其实我说的这种情况在键盘上是有的,例如:windows键,单独按下是没响应,弹起时有相应(打开开始菜单);组合功能,windows+d是显示桌面。

出0入0汤圆

发表于 2016-9-12 15:04:18 | 显示全部楼层
看起来很强大,顶一下。

出0入0汤圆

 楼主| 发表于 2016-9-12 15:04:45 | 显示全部楼层
单飞 发表于 2016-9-12 14:59
其实我说的这种情况在键盘上是有的,例如:windows键,单独按下是没响应,弹起时有相应(打开开始菜单) ...

对,那你的按键是定义抬起时才先响应操作的话,那一切都好办了,哈哈。

出0入0汤圆

发表于 2016-9-12 15:07:27 | 显示全部楼层
本帖最后由 单飞 于 2016-9-12 15:09 编辑
半导体 发表于 2016-9-12 15:04
对,那你的按键是定义抬起时才先响应操作的话,那一切都好办了,哈哈。 ...


63楼就是站在这个基础上讲的,不过还是没想到好的解决方案。
第二个组合键容易误触,误触之后还会和组合事件紧挨在发生

出0入0汤圆

 楼主| 发表于 2016-9-12 15:20:15 | 显示全部楼层
单飞 发表于 2016-9-12 15:07
63楼就是站在这个基础上讲的,不过还是没想到好的解决方案。
第二个组合键容易误触,误触之后还会和组合 ...

感觉看似简单的一个按键也是一门大学问啊,它直接关系到用户跟产品的交互体验。

出0入0汤圆

发表于 2016-9-12 15:27:12 | 显示全部楼层
半导体 发表于 2016-9-12 15:20
感觉看似简单的一个按键也是一门大学问啊,它直接关系到用户跟产品的交互体验。 ...

我觉得这个问题需要按键驱动的支持

出0入0汤圆

发表于 2016-9-12 15:38:53 | 显示全部楼层
再支持一下楼主。
一般我们(可能只有我),习惯的写法不是把按键操作都汇总到一个回调函数里的,感觉多界面操作的时候太麻烦了,也不方便,而且和驱动层不好分离。
习惯这种写法:
void main(void)
{
    u8 key_val = 0;
   
    while(1)
    {
        key_val = bsp_GetKey();//读按键
        
        if(key_val == KEY_NONE) continue;//按键未按下
        
        switch(key_val)
        {
            case KEY_LEFT_PRESS: //【左】
            {
                printf("左键\r\n");
            }break;
            
            case KEY_RIGHT_PRESS: //【右】
            {
                printf("右键\r\n");
            }break;
            
            case KEY_UP_PRESS: //【上】
            {
                printf("上键\r\n");
            }break;
            
            case KEY_DOWN_UNDO: //【下】
            {
                printf("下键\r\n");
            }break;
            
            default: break;
        }
    }
}

出0入0汤圆

发表于 2016-9-12 15:42:31 | 显示全部楼层
那么这里就给按键开一个环形缓存区用于储存键值,只需把楼主的代码里再加一层,在回调函数里写缓存区,上层提供一个读缓存区的函数就可以了。
在战舰上写了一个demo,使用了Freertos

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2016-9-12 17:59:48 | 显示全部楼层
学习了, 谢谢分享!

出0入0汤圆

发表于 2016-9-18 22:48:57 | 显示全部楼层
security 发表于 2016-9-7 11:17
在 GUI 架构里面,我想还是要以 GUI 为第一视角,
即要在 GUI 的 handler 里面处理按键事件(或者说按键 ...

是啊,再有菜单的程序里面,菜单的主状态机是运行的主线程,再在菜单状态机里面响应按键事件,我也是习惯这么写!!

出0入0汤圆

发表于 2016-9-19 11:10:49 | 显示全部楼层
学习一下!

出0入0汤圆

 楼主| 发表于 2016-9-23 16:18:38 | 显示全部楼层
http://www.amobbs.com/thread-5660321-1-1.html

出0入0汤圆

发表于 2016-9-24 08:19:17 | 显示全部楼层
感谢楼主分享经验

出0入0汤圆

发表于 2016-9-24 09:20:41 | 显示全部楼层
好东西,学习一下

出0入0汤圆

发表于 2016-9-24 09:21:56 | 显示全部楼层
不错的东西

出0入0汤圆

发表于 2016-9-24 09:25:39 | 显示全部楼层
本帖最后由 qq335702318 于 2016-9-24 09:27 编辑
  1. #ifdef __cplusplus  
  2. extern "C" {  
  3. #endif  

  4. void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level);
  5. void button_attach(struct Button* handle, PressEvent event, BtnCallback cb);
  6. PressEvent get_button_event(struct Button* handle);
  7. int  button_start(struct Button* handle);
  8. void button_stop(struct Button* handle);
  9. void button_ticks(void);

  10. #ifdef __cplusplus
复制代码



c++写的吗?

出0入0汤圆

发表于 2016-9-24 09:29:54 | 显示全部楼层
wangyy@dianzi 发表于 2016-9-18 22:48
是啊,再有菜单的程序里面,菜单的主状态机是运行的主线程,再在菜单状态机里面响应按键事件,我也是习惯 ...

73、74楼就是解决这个问题

出0入0汤圆

发表于 2016-9-24 13:10:21 | 显示全部楼层
这是一个好帖子,楼主很赞

出0入0汤圆

发表于 2016-9-29 23:59:15 | 显示全部楼层
是不是有问题

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2016-9-30 10:38:19 | 显示全部楼层

谢谢提醒,修复了,查询事件方式是后来才加入,所以。。。。

出0入0汤圆

发表于 2016-9-30 11:19:55 | 显示全部楼层
并发状态事件驱动按键驱动模块,量产产品实践

出0入0汤圆

发表于 2016-10-14 16:23:14 | 显示全部楼层
请教楼主,有点没看明白你这个双击事件放在按键按下里面判断的用意,感觉可以放到后面else里面啊?这样多连击都不会有问题,不然多连击会包含前面的连击事件哦

        case 2:
                if(handle->button_level == handle->active_level) { //press down again
                        handle->event = (uint8_t)PRESS_DOWN;
                        EVENT_CB(PRESS_DOWN);
                        handle->repeat++;
                        if(handle->repeat == 2) {
                                EVENT_CB(DOUBLE_CLICK); // repeat hit
                        }

                        EVENT_CB(PRESS_REPEAT); // repeat hit
                        handle->ticks = 0;
                        handle->state = 3;
                } else if(handle->ticks > SHORT_TICKS) { //released timeout
                        if(handle->repeat == 1) {
                                handle->event = (uint8_t)SINGLE_CLICK;
                                EVENT_CB(SINGLE_CLICK);
                        } else if(handle->repeat == 2) {
                                handle->event = (uint8_t)DOUBLE_CLICK;
                               ;==========================
                        }
                        handle->state = 0;
                }
                break;

出0入0汤圆

发表于 2016-10-14 16:40:47 来自手机 | 显示全部楼层
感觉和armfly的按键检测有点像

出0入0汤圆

发表于 2016-10-14 17:54:40 | 显示全部楼层
单个按键不能定义两个或更多的功能么?
看了下代码。

其实很多情况是单个按键单击是一个功能,双击又是另外一个功能。

出0入0汤圆

 楼主| 发表于 2016-10-17 10:06:54 | 显示全部楼层
alex_feng 发表于 2016-10-14 17:54
单个按键不能定义两个或更多的功能么?
看了下代码。

肯定可以啊,你把6个事件都捆绑在一个按键都可以。

出0入4汤圆

发表于 2016-10-17 10:29:53 | 显示全部楼层
支持多建同按吗?

也就是 自梭按键和不自锁的按键混合的情况。

出0入0汤圆

发表于 2016-10-19 15:42:14 | 显示全部楼层
已下载,不错,学习了~

出0入0汤圆

发表于 2016-11-6 22:42:07 | 显示全部楼层
按键驱动模块c代码

出10入0汤圆

发表于 2016-11-7 13:32:21 | 显示全部楼层
PRESS_DOWN        按键按下,每次按下都触发
PRESS_UP        按键弹起,每次松开都触发
PRESS_REPEAT        重复按下触发,变量repeat计数连击次数
SINGLE_CLICK        单击按键事件
DOUBLE_CLICK        双击按键事件
LONG_RRESS_START        达到长按时间阈值时触发一次
LONG_PRESS_HOLD        长按期间一直触发
这几个功能如果能够写成BITMASK形式会更舒服一点,省了一个按键写6行

出10入0汤圆

发表于 2016-11-7 13:54:30 | 显示全部楼层
目前为止按键、GUI、产品相关代码,都还很难独立分开。
每次都想井水不犯河水,最后还是黄河般泛滥。

出0入0汤圆

发表于 2016-11-17 23:44:32 | 显示全部楼层
Mark,按键模块,谢谢楼主!

出0入0汤圆

发表于 2016-11-18 00:48:40 | 显示全部楼层
好东西,先马克

出0入0汤圆

发表于 2016-11-18 01:51:32 | 显示全部楼层
谢谢,学习一下。按键模块

出0入0汤圆

发表于 2016-11-18 07:23:10 来自手机 | 显示全部楼层
谢谢分享。很好的学习资料。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-26 05:14

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

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