Giter Site home page Giter Site logo

jiejietop / buttondrive Goto Github PK

View Code? Open in Web Editor NEW
335.0 27.0 166.0 336 KB

纯C语言实现的一个按键驱动,可移植性强,支持单双击、连按、连按释放、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,1:创建按键,2:按键事件与回调处理函数链接映射。然后周期检查按键。

License: Apache License 2.0

C 84.76% Python 14.27% Objective-C 0.96%

buttondrive's Introduction

ButtonDrive

自己写的一个按键驱动,支持单双击、连按、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,创建按键,按键事件与回调处理函数链接映射,周期检查按键。 源码地址:https://github.com/jiejieTop/ButtonDrive

版权和许可

ButtonDrive 遵循 Apache License v2.0 开源许可协议,可以免费在商业产品中使用,并且不需要公开私有代码。

Apache License v2.0 鼓励代码共享和尊重原作者的著作权,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布(作为开源或闭源商业软件),但必须根据协议保留原作者版权声明。

前言

前几天写了个按键驱动,参考了MulitButton的数据结构的用法,逻辑实现并不一样。 在这里感谢所有的开源开发者,让我从中学到了很多,同时网络也是一个好平台,也希望所有的开发者能形成良性循环,从网络中学知识,回馈到网络中去。感谢MulitButton的作者0x1abin,感谢两位rtt的大佬:大法师流光

Button_drive简介

Button_drive是一个小巧的按键驱动,支持单击、双击、长按、连续触发等(后续可以在按键控制块中添加触发事件),理论上可无限量扩展Button,Button_drive采用按键触发事件回调方式处理业务逻辑,支持在RTOS中使用,我目前仅在RT-Thread上测试过。 写按键驱动的目的是想要将用户按键逻辑与按键处理事件分离,用户无需处理复杂麻烦的逻辑事件。

Button_drive使用效果

  1. 单击与长按

单击与长按

  1. 双击

双击

  1. 连按

连按

  1. 连按释放

连按释放

使用方法

  1. 创建按键句柄
Button_t Button1;
Button_t Button2; 
  1. 创建按键,初始化按键信息,包括按键名字、按键电平检测函数接口、按键触发电平。
  Button_Create("Button1",				//按键名字
                &Button1, 				//按键句柄
                Read_Button1_Level, 	//按键电平检测函数接口
                BTN_TRIGGER);		   	//触发电平
                
                ......
  1. 按键触发事件与事件回调函数链接映射,当按键事件被触发的时候,自动跳转回调函数中处理业务逻辑。
  Button_Attach(&Button1,BUTTON_DOWM,Btn2_Dowm_CallBack);		//按键单击
  Button_Attach(&Button1,BUTTON_DOUBLE,Btn2_Double_CallBack);	//双击
  Button_Attach(&Button1,BUTTON_LONG,Btn2_Long_CallBack);		//长按
				
				.......
  1. 周期调用回调按键处理函数即可,建议调用周期20-50ms。
Button_Process();     //需要周期调用按键处理函数

需要用户实现的 2 个函数:

  • 按键电平检测接口:
uint8_t Read_Button1_Level(void)
{
  return GPIO_ReadInputDataBit(BTN1_GPIO_PORT,BTN1_GPIO_PIN);
}

uint8_t Read_Button2_Level(void)
{
  return GPIO_ReadInputDataBit(BTN2_GPIO_PORT,BTN2_GPIO_PIN);
}

// 这是我在stm32上简单测试的伪代码,以实际源码为准
  • 按键逻辑处理
void Btn1_Dowm_CallBack(void *btn)
{
  PRINT_INFO("Button1 单击!");
}

void Btn1_Double_CallBack(void *btn)
{
  PRINT_INFO("Button1 双击!");
}

void Btn1_Long_CallBack(void *btn)
{
  PRINT_INFO("Button1 长按!");
  
  Button_Delete(&Button2);
  PRINT_INFO("删除Button1");
  Search_Button();
}

特点

Button_drive开放源码,按键控制块采用数据结构方式,按键事件采用枚举类型,确保不会重复,也便于添加用户需要逻辑,采用宏定义方式定义消抖时间、连按触发时间、双击时间间隔、长按时间等,便于修改。 同时所有被创建的按键采用单链表方式连击,用户只管创建,无需理会按键处理,只需调用Button_Process()即可,在函数中会自动遍历所有被创建的按键。 支持按键删除操作,用户无需在代码中删除对应的按键创建于映射链接代码,也无需删除关于按键的任何回调事件处理函数,只需调用Button_Delete()函数即可,这样子,就不会处理关于被删除按键的任何状态。当然目前按键内存不会释放,如果使用os的话,建议释放按键内存。

按键控制块
/*
	每个按键对应1个全局的结构体变量。
	其成员变量是实现消抖和多种按键状态所必须的
*/
typedef struct button
{
	/* 下面是一个函数指针,指向判断按键手否按下的函数 */
	uint8_t (*Read_Button_Level)(void); /* 读取按键电平函数,需要用户实现 */
  
  char Name[BTN_NAME_MAX];
  	
  uint8_t Button_State              :   4;	  /* 按键当前状态(按下还是弹起) */
  uint8_t Button_Last_State         :   4;	  /* 上一次的按键状态,用于判断双击 */
  uint8_t Button_Trigger_Level      :   2;    /* 按键触发电平 */
  uint8_t Button_Last_Level         :   2;    /* 按键当前电平 */
  
  uint8_t Button_Trigger_Event;     /* 按键触发事件,单击,双击,长按等 */
  
  Button_CallBack CallBack_Function[number_of_event];
  uint8_t Button_Cycle;	           /* 连续按键周期 */
  
  uint8_t Timer_Count;			/* 计时 */
  uint8_t Debounce_Time;		/* 消抖时间 */
  
  uint8_t Long_Time;		  /* 按键按下持续时间 */
  
  struct button *Next;
  
}Button_t;
触发事件
typedef enum {
  BUTTON_DOWM = 0,
  BUTTON_UP,
  BUTTON_DOUBLE,
  BUTTON_LONG,
  BUTTON_CONTINUOS,
  BUTTON_CONTINUOS_FREE,
  BUTTON_ALL_RIGGER,
  number_of_event, /* 触发回调的事件 */
  NONE_TRIGGER
}Button_Event;
宏定义选择
#define BTN_NAME_MAX  32     //名字最大为32字节

/* 按键消抖时间40ms, 建议调用周期为20ms
 只有连续检测到40ms状态不变才认为有效,包括弹起和按下两种事件
*/

#define CONTINUOS_TRIGGER             0  //是否支持连续触发,连发的话就不要检测单双击与长按了	

/* 是否支持单击&双击同时存在触发,如果选择开启宏定义的话,单双击都回调,只不过单击会延迟响应,
   因为必须判断单击之后是否触发了双击否则,延迟时间是双击间隔时间 BUTTON_DOUBLE_TIME。
   而如果不开启这个宏定义,建议工程中只存在单击/双击中的一个,否则,在双击响应的时候会触发一次单击,
   因为双击必须是有一次按下并且释放之后才产生的 */
#define SINGLE_AND_DOUBLE_TRIGGER     1 

/* 是否支持长按释放才触发,如果打开这个宏定义,那么长按释放之后才触发单次长按,
   否则在长按指定时间就一直触发长按,触发周期由 BUTTON_LONG_CYCLE 决定 */
#define LONG_FREE_TRIGGER             0 

#define BUTTON_DEBOUNCE_TIME 	  2   //消抖时间      (n-1)*调用周期
#define BUTTON_CONTINUOS_CYCLE  1	  //连按触发周期时间  (n-1)*调用周期  
#define BUTTON_LONG_CYCLE       1	  //长按触发周期时间  (n-1)*调用周期 
#define BUTTON_DOUBLE_TIME      15 	//双击间隔时间  (n-1)*调用周期  建议在200-600ms
#define BUTTON_LONG_TIME 	      50		/* 持续n秒((n-1)*调用周期 ms),认为长按事件 */

#define TRIGGER_CB(event)   \
        if(btn->CallBack_Function[event]) \
          btn->CallBack_Function[event]((Button_t*)btn)
例子
  Button_Create("Button1",
              &Button1, 
              Read_KEY1_Level, 
              KEY_ON);
  Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack);                       //单击
  Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack);                   //双击
  Button_Attach(&Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack);             //连按  
  Button_Attach(&Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack);    //连按释放  
  Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack);                       //长按


  Button_Create("Button2",
              &Button2, 
              Read_KEY2_Level, 
              KEY_ON);
  Button_Attach(&Button2,BUTTON_DOWM,Btn2_Dowm_CallBack);                     //单击
  Button_Attach(&Button2,BUTTON_DOUBLE,Btn2_Double_CallBack);                 //双击
  Button_Attach(&Button2,BUTTON_CONTINUOS,Btn2_Continuos_CallBack);           //连按
  Button_Attach(&Button2,BUTTON_CONTINUOS_FREE,Btn2_ContinuosFree_CallBack);  //连按释放
  Button_Attach(&Button2,BUTTON_LONG,Btn2_Long_CallBack);                     //长按

  Get_Button_Event(&Button1);
  Get_Button_Event(&Button2);

后续

流光大佬的要求,让我玩一玩RTT的rtkpgs,打算用Button_drive练一练手吧。

ButtonDrive在env使用

目前我已将按键驱动做成软件包(packages),如果使用RT-Thread操作系统的话,可以在env中直接配置使用!

步骤如下:

  1. 选择在线软件包

  1. 选择软件包属性为外设相关

  1. 选择button_drive

  1. 进入驱动的选项配置(自带默认属性)

  1. 如果不懂按键的配置是什么意思,按下“shift+?”,即可有解释

  1. 编译生成mdk/iar工程

buttondrive's People

Contributors

happyfacade avatar jiejietop avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

buttondrive's Issues

Add_Button函数的疑问?

请问Add_Button(Button_t* btn)这个函数,其中的while循环处理有什么作用?感觉好像并没有什么作用...

抖动累积造成误触

void Button_Cycle_Process(Button_t *btn)
{
  uint8_t current_level = (uint8_t)btn->Read_Button_Level();//获取当前按键电平
  
  if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME)) //按键电平发生变化,消抖
  {
      btn->Button_Last_Level = current_level; //更新当前按键电平
      btn->Debounce_Time = 0;                 //确定了是按下
      
      ......
  }

  ......
}

btn->Debounce_Time仅在if内清零,如果发生多次抖动,btn->Debounce_Time依然会累加,并最终被认为是一次有效按键。应该修改为:

void Button_Cycle_Process(Button_t *btn)
{
  uint8_t current_level = (uint8_t)btn->Read_Button_Level();//获取当前按键电平
  
  if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME)) //按键电平发生变化,消抖
  {
      btn->Button_Last_Level = current_level; //更新当前按键电平
      btn->Debounce_Time = 0;                 //确定了是按下
      
      ......
  }
 else if(current_level == btn->Button_Last_Level)
 {
      btn->Debounce_Time = 0;
 }

 ......
}

按住按键上电初始化,会导致后续按键无法正常响应

您好,使用中发现了一个问题,不知是否合理:

1、在按住按键上电初始化时,Button_Create函数中如下语句会将按键按下的状态赋值给Last_Level
btn->Button_Last_Level = btn->Read_Button_Level(); //按键当前电平

2、后续初始化后松开按键,然后再次触发按键,会发现Button_State的状态保持在UP状态,无法正常响应了。

不知道有没有地方疏漏,请帮忙看看是否存在问题,多谢了

是否可以跳过np和xtrap的检测

错误报告

uint8_t Button_State :4 ; /* 按键当前状态(按下还是弹起) /
uint8_t Button_Last_State :4 ; /
上一次的按键状态,用于判断双击 /
uint8_t Button_Trigger_Level :2 ; /
按键触发电平 /
uint8_t Button_Last_Level :2 ; /
按键当前电平 */
环境:IAR,stm8,实测,这几个的位域操作与stm8的io电平读取返回函数冲突,
return ((BitStatus)(GPIOx->IDR & (uint8_t)GPIO_Pin));
这个返回值为bool类型的值实际上是对应的uint8_t值,在高位引脚的时候导致Button_Last_Level ,Button_Trigger_Level ,无论如何不能等于io返回值,导致一直报错,望楼主把4个状态位改为bool类型,或去掉后边的位域操作符。

读取电平函数 指针

建议读取电平函数指针 带1-2个参数,
读取该 button 连接的引脚 需要知道 引脚的数值不是吗?

学习一下

学习一下,如果能支持组合按键就完美了

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.