【eBox生态圈】基于事件驱动的全功能按键,长按单次或连续

2019-12-10 18:30发布

本帖最后由 shentqlf 于 2019-5-17 08:25 编辑

好久没发帖子了,露个脸,对之前发的那个事件驱动做了一些优化

按键程序是单片机中最常用的一个功能了,但是想写好一个通用的按键驱动程序并不太简单,我在eBox的架构下写了一个全功能的按键程序,分享出来。请各路大神斧正优化。

先介绍下按键的功能
1、单击--按键按下后触发一个事件
2、释放--按键释放后触发一个事件
3、长按--按键按下后不松开事件,在这个状态下会有两种需求,
        A、单次触发--当系统检测到长按发生后,触发一次事件
        B、连续触发--当系统检测到长按发生后,需要每隔一段时间触发一次事件。
4、双击--在特定的间隔时间内连续按下两次按键,触发一个事件(本驱动为支持,个人认为这个功能在单片机上可由别的逻辑方法代替。所以我就没上心做这个这支持)
5、软件滤波--为了防止IO的抖动(一次按下可能会触发多次),在没有硬件滤波的情况下需要做软件滤波,最好是非阻塞的滤波,如果是阻塞模式的,则会让单片机大大浪费时间在延时上。

实现方法

1、初始化按键对象
        A、是哪个GPIO
        B、初始态是高还是低
  1.     EventGpio(Gpio *io,uint8_t first_state)
  2.     {
  3.         this->io = io;
  4.         this->first_state = first_state;
  5.         state = first_state;
  6.         click_pushed = 0;
  7.         click_released = 0;
  8.         long_pressed = 0;
  9.         
  10.         event_high = NULL;
  11.         event_low = NULL;
  12.         event_pos_edge = NULL;
  13.         event_neg_edge = NULL;
  14.         event_click = NULL;
  15.         event_release = NULL;
  16.         event_long_press = NULL;
  17.         long_press_type = Continue;
  18.     }
复制代码

2、所有事件的入口
  1.     void (*event_high)();
  2.     void (*event_low)();
  3.     void (*event_pos_edge)();
  4.     void (*event_neg_edge)();
  5.     void (*event_click)();
  6.     void (*event_release)();
  7.     void (*event_long_press)();
复制代码

3、需要一个初始化传递过来的GPIO和一个循环函数
  1.     void EventGpio::begin()
  2. {
  3.     if(this->first_state == 0)
  4.         io->mode(INPUT_PD);
  5.     else
  6.         io->mode(INPUT_PU);
  7. }
  8.     virtual void loop(void);
复制代码

4、一些宏定义配置,类型和变量
  1. //软件滤波时间ms
  2. #define IO_EDGE_FILTER_COUNTS   0//默认不使用软件滤波
  3. //长按事件触发事件
  4. #define LONG_PRESS_COUNTS       2000//2000ms
  5. //长按事件连续触发间隔
  6. #define LONG_PRESS_INTERVAL       100//100ms
  7. public:
  8.     typedef enum type{
  9.         Single = 0,
  10.         Continue = 1
  11.     }LongPressType_t;

  12.        LongPressType_t long_press_type;
  13. private:
  14.     Gpio *io;
  15.     uint8_t state;
  16.     uint32_t last_time;
  17.     uint16_t long_pressed;
  18.     uint8_t click_pushed;
  19.     uint8_t click_released;
  20.     uint8_t first_state;
  21.     uint8_t changed;
  22.     uint16_t long_press_triger_time;
复制代码
附加功能

除了按键的功能外还支持了上升沿事件下降沿事件,当然还有更简单的高低电平事件。这些触发是电平绝对值触发的事件。按键的单击,释放是相对的。

变量检测
事件驱动还支持了变量变化触发功能,由EvnentVar实现
1、变量变化事件--当变量发生变化后触发事件
2、变量变大事件--当变量变大触发事件
3、变量减小事件--当变量变小触发事件

EventManager

前面所说的EventGpio和EvnetVar用户可能需要创建出很多对象,为了统一管理这些对象,使用了一个链表,用户可以将EventVar和EvnetGpio对象添加到管理器中。通过管理的一个一个loop实现刷新。
  1. EventGpio btn(&PA8, 1);

  2. uint8_t volume = 0;
  3. EventVar<uint8_t> var(&volume);

  4. EventManager event_manager;
  5. void setup()
  6. {
  7.     ebox_init();
  8.     UART.begin(115200);
  9.     print_log(EXAMPLE_NAME, EXAMPLE_DATE);
  10.     //event_io_1.begin(1);
  11.    
  12.    
  13.     btn.event_click = click;
  14.     btn.event_release = release;
  15.     btn.event_long_press = long_press;
  16.     btn.long_press_type = EventGpio::Continue;
  17. //    btn.event_high = high;
  18. //    btn.event_low = low;
  19. //    btn.event_neg_edge = neg;
  20. //    btn.event_pos_edge = pos;
  21.    
  22.     var.event_changed = change;
  23.     var.event_nag_edge = down;
  24.     var.event_pos_edge = up;
  25.     btn.begin();
  26.    
  27.     event_manager.add(&btn);
  28.     event_manager.add(&var);
  29. }
  30. uint32_t last;
  31. uint8_t flag = 0;
  32. int main(void)
  33. {
  34.     setup();

  35.     while(1)
  36.     {
  37.         event_manager.loop();
  38.         delay_ms(1);
  39.         if(millis() - last > 1000)
  40.         {
  41.             last = millis();
  42.             if(flag==0)
  43.                 volume++;
  44.             else
  45.                 volume--;
  46.             if(volume >= 3)
  47.                 flag = 1;
  48.             else if (volume == 0)
  49.                 flag = 0;
  50.         }
  51.         
  52.     }
  53. }
复制代码
详细实现内容,我就不贴了,太长太啰嗦,大家可以直接下载工程浏览,如有优化空间还请各路大神指导
工程文件目录:eBox_FrameworkProjectstm32f10x-mdmdk

ggithub持续更新地址:
https://github.com/eboxmaker/eBox_Framework
工程文件:
eBox_Framework.rar (14.91 MB, 下载次数: 19) 2019-5-17 08:25 上传 点击文件名下载附件
eBox_Framework

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
16条回答
hellokv1688
1楼-- · 2019-12-11 10:00
感谢楼主分享
lb0857
2楼-- · 2019-12-11 14:55
学习楼主带来的  全功能按键,长按单次或连续  收藏中 稍后拜读
takashiki
3楼-- · 2019-12-11 18:29
shentqlf 发表于 2019-5-17 10:37
是c++的。


2、所有事件的入口,这个搞成虚函数是不是好点,函数带个参数是不是好点,这样多个对象可以绑定到同一个事件上。
shentqlf
4楼-- · 2019-12-11 21:02
本帖最后由 shentqlf 于 2019-5-21 18:49 编辑
takashiki 发表于 2019-5-21 10:09
2、所有事件的入口,这个搞成虚函数是不是好点,函数带个参数是不是好点,这样多个对象可以绑定到同一个 ...


你的意思是不是像c#那种 回调函数入口带一个sender的参数?
这样一个入口可以判断是哪个事件触发的信息
takashiki
5楼-- · 2019-12-11 23:56
 精彩回答 2  元偷偷看……
shentqlf
6楼-- · 2019-12-12 02:13
本帖最后由 shentqlf 于 2019-5-21 21:23 编辑
takashiki 发表于 2019-5-21 20:01
是的


从技术上可以实现。但是这个应用场所能给出个例子吗?
也就是目前的事件所不能完成的应用场景。
目前我感觉对于按键这种类型。不带那个参数也能满足各种需求。
如果是更复杂的事件,可能需要用你说的那种架构。

一周热门 更多>