Răsfoiți Sursa

update button pkg

jiejieTop 7 ani în urmă
părinte
comite
8af0264da0
5 a modificat fișierele cu 1063 adăugiri și 0 ștergeri
  1. 248 0
      README.md
  2. 12 0
      SConscript
  3. 525 0
      button.c
  4. 132 0
      button.h
  5. 146 0
      example.c

+ 248 - 0
README.md

@@ -1,2 +1,250 @@
 # rtpkg_button
 rtt packages button drive
+自己写的一个按键驱动,支持单双击、连按、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,创建按键,按键事件与回调处理函数链接映射,周期检查按键。
+源码地址:[https://github.com/jiejieTop/ButtonDrive](https://github.com/jiejieTop/ButtonDrive)。作者:[杰杰](https://github.com/jiejieTop)
+
+
+## 前言
+前几天写了个按键驱动,参考了[MulitButton](https://github.com/0x1abin/MultiButton)的数据结构的用法,逻辑实现并不一样。
+在这里感谢所有的开源开发者,让我从中学到了很多,同时网络也是一个好平台,也希望所有的开发者能形成良性循环,从网络中学知识,回馈到网络中去。感谢[MulitButton](https://github.com/0x1abin/MultiButton)的作者[0x1abin](https://github.com/0x1abin),感谢两位rtt的大佬:[大法师](https://github.com/uestczyh222)、[流光](https://github.com/liu2guang)。
+## Button_drive简介
+Button_drive是一个小巧的按键驱动,支持单击、双击、长按、连续触发等(后续可以在按键控制块中添加触发事件),理论上可无限量扩展Button,Button_drive采用按键触发事件回调方式处理业务逻辑,支持在RTOS中使用,我目前仅在[RT-Thread](https://github.com/RT-Thread/rt-thread)上测试过。
+写按键驱动的目的是想要将用户按键逻辑与按键处理事件分离,用户无需处理复杂麻烦的逻辑事件。
+
+## Button_drive使用效果
+1. **单击与长按**
+
+![单击与长按](https://github.com/jiejieTop/ButtonDrive/blob/master/png/1.png?raw=true)
+
+2. **双击**
+
+![双击](https://github.com/jiejieTop/ButtonDrive/blob/master/png/2.png?raw=true)
+
+3. **连按**
+
+![连按](https://github.com/jiejieTop/ButtonDrive/blob/master/png/3.png?raw=true)
+
+4. **连按释放**
+
+![连按释放](https://github.com/jiejieTop/ButtonDrive/blob/master/png/4.png?raw=true)
+
+## 使用方法
+1. 创建按键句柄
+```
+Button_t Button1;
+Button_t Button2; 
+```
+2. 创建按键,初始化按键信息,包括按键名字、按键电平检测函数接口、按键触发电平。
+```
+  Button_Create("Button1",				//按键名字
+                &Button1, 				//按键句柄
+                Read_Button1_Level, 	//按键电平检测函数接口
+                BTN_TRIGGER);		   	//触发电平
+                
+                ......
+```
+3. 按键触发事件与事件回调函数链接映射,当按键事件被触发的时候,自动跳转回调函数中处理业务逻辑。
+```
+  Button_Attach(&Button1,BUTTON_DOWM,Btn2_Dowm_CallBack);		//按键单击
+  Button_Attach(&Button1,BUTTON_DOUBLE,Btn2_Double_CallBack);	//双击
+  Button_Attach(&Button1,BUTTON_LONG,Btn2_Long_CallBack);		//长按
+				
+				.......
+```
+4. 周期调用回调按键处理函数即可,建议调用周期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);
+```
+
+
+## 后续
+[流光](https://github.com/liu2guang)大佬的要求,让我玩一玩RTT的[rtkpgs](https://github.com/liu2guang/buildpkg),打算用Button_drive练一练手吧。
+
+## ButtonDrive在env使用
+目前我已将按键驱动做成软件包(packages),如果使用RT-Thread操作系统的话,可以在env中直接配置使用!
+
+步骤如下:
+
+1. **选择在线软件包**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/5.png?raw=true)
+
+2. **选择软件包属性为外设相关**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/6.png?raw=true)
+
+3. **选择button_drive**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/7.png?raw=true)
+
+4. **进入驱动的选项配置(自带默认属性)**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/8.png?raw=true)
+
+5. **如果不懂按键的配置是什么意思,按下“shift+?”,即可有解释**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/9.png?raw=true)
+
+6. **编译生成mdk/iar工程**
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/10.png?raw=true)
+
+![](https://github.com/jiejieTop/ButtonDrive/blob/master/png/11.png?raw=true)
+
+
+## 联系人
+
+* 邮箱:[1161959934@qq.com](mailto:1161959934@qq.com)
+* 主页:[jiejie](https://github.com/jiejieTop)
+* 仓库:[Github](https://github.com/jiejieTop)

+ 12 - 0
SConscript

@@ -0,0 +1,12 @@
+Import('RTT_ROOT')
+Import('rtconfig')
+from building import *
+
+cwd     = GetCurrentDir()
+src     = Glob('*.c') 
+
+CPPPATH = [cwd]
+
+group = DefineGroup('button_drive', src, depend = ['PKG_USING_BUTTON'], CPPPATH = CPPPATH)
+
+Return('group')

+ 525 - 0
button.c

@@ -0,0 +1,525 @@
+/************************************************************
+  * @brief   button_drive
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    button.c
+  ***********************************************************/
+
+#include "button.h"
+
+#ifdef PKG_USING_BUTTON
+
+/*******************************************************************
+ *                          Variable Declaration                              
+ *******************************************************************/
+
+static struct button* Head_Button = RT_NULL;
+
+
+/*******************************************************************
+ *                         Function Declaration   
+ *******************************************************************/
+static char *StrnCopy(char *dst, const char *src, rt_uint32_t n);
+static void Print_Btn_Info(Button_t* btn);
+static void Add_Button(Button_t* btn);
+
+
+/************************************************************
+  * @brief   Create a Button 
+  * @param   name:button name 
+  * @param   btn:button structure
+  * @param   read_btn_level:Button trigger level reading function,
+  *                 Return the level of the rt_uint8_t type by yourself
+  * @param   btn_trigger_level:Button trigger level
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+void Button_Create(const char *name,
+                  Button_t *btn, 
+                  rt_uint8_t(*read_btn_level)(void),
+                  rt_uint8_t btn_trigger_level)
+{
+  if( btn == RT_NULL)
+  {
+    RT_DEBUG_LOG(RT_DEBUG_THREAD,("struct button is RT_NULL!"));
+  }
+  
+  memset(btn, 0, sizeof(struct button));      //Clear structure information
+ 
+  StrnCopy(btn->Name, name, BTN_NAME_MAX);    //button name 
+  
+  
+  btn->Button_State = NONE_TRIGGER;                     //Button status
+  btn->Button_Last_State = NONE_TRIGGER;                //Button last status
+  btn->Button_Trigger_Event = NONE_TRIGGER;             //Button trigger event
+  btn->Read_Button_Level = read_btn_level;              //Button trigger level reading function
+  btn->Button_Trigger_Level = btn_trigger_level;        //Button trigger level
+  btn->Button_Last_Level = btn->Read_Button_Level();    //Button current level
+  btn->Debounce_Time = 0;
+  
+  RT_DEBUG_LOG(RT_DEBUG_THREAD,("button create success!"));
+  
+  Add_Button(btn);          //Added to the singly linked list when button created
+  
+  Print_Btn_Info(btn);      //printf info
+ 
+}
+
+/************************************************************
+  * @brief   burron trigger events are attach to callback function
+  * @param   btn:button structure
+  * @param   btn_event:button events
+  * @param   btn_callback : Callback handler after the button is triggered.Need user implementation
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  ***********************************************************/
+void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback)
+{
+  if( btn == RT_NULL)
+  {
+    RT_DEBUG_LOG(RT_DEBUG_THREAD,("struct button is RT_NULL!"));
+  }
+  
+  if(BUTTON_ALL_RIGGER == btn_event)
+  {
+    for(rt_uint8_t i = 0 ; i < number_of_event-1 ; i++)
+      btn->CallBack_Function[i] = btn_callback;   //A callback function triggered by a button event ,Used to handle button events 
+  }
+  else
+  {
+    btn->CallBack_Function[btn_event] = btn_callback; 
+  }
+}
+
+/************************************************************
+  * @brief   Delete an already created button
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+void Button_Delete(Button_t *btn)
+{
+  struct button** curr;
+  for(curr = &Head_Button; *curr;) 
+  {
+    struct button* entry = *curr;
+    if (entry == btn) 
+    {
+      *curr = entry->Next;
+    } 
+    else
+    {
+      curr = &entry->Next;
+    }
+  }
+}
+
+/************************************************************
+  * @brief   Get Button Event Info
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  ***********************************************************/
+void Get_Button_EventInfo(Button_t *btn)
+{
+  for(rt_uint8_t i = 0 ; i < number_of_event-1 ; i++)
+  {
+    if(btn->CallBack_Function[i] != 0)
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Button_Event:%d",i));//print
+    }      
+  } 
+}
+
+/************************************************************
+  * @brief   Get Button Event
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  ***********************************************************/
+rt_uint8_t Get_Button_Event(Button_t *btn)
+{
+  return (rt_uint8_t)(btn->Button_Trigger_Event);
+}
+
+/************************************************************
+  * @brief   Get Button State
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  ***********************************************************/
+rt_uint8_t Get_Button_State(Button_t *btn)
+{
+  return (rt_uint8_t)(btn->Button_State);
+}
+
+/************************************************************
+  * @brief   button cycle processing function
+  * @param   btn:´¦ÀíµÄ°´¼ü
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    This function must be called in a certain period. The recommended period is 20~50ms.
+  ***********************************************************/
+void Button_Cycle_Process(Button_t *btn)
+{
+  rt_uint8_t current_level = (rt_uint8_t)btn->Read_Button_Level();  //Get the current button level
+  
+  if((current_level != btn->Button_Last_Level)&&(++(btn->Debounce_Time) >= BUTTON_DEBOUNCE_TIME))   //Button level changes, debounce
+  {
+      btn->Button_Last_Level = current_level; //Update current button level
+      btn->Debounce_Time = 0;                 //button is pressed
+      
+      //If the button is not pressed, change the button state to press (first press / double trigger)
+      if((btn->Button_State == NONE_TRIGGER)||(btn->Button_State == BUTTON_DOUBLE))
+      {
+        btn->Button_State = BUTTON_DOWM;
+      }
+      //free button
+      else if(btn->Button_State == BUTTON_DOWM)
+      {
+        btn->Button_State = BUTTON_UP;
+        RT_DEBUG_LOG(RT_DEBUG_THREAD,("button release"));
+      }
+  }
+  
+  switch(btn->Button_State)
+  {
+    case BUTTON_DOWM :                                        // button dowm 
+    {
+      if(btn->Button_Last_Level == btn->Button_Trigger_Level) 
+      {
+        #ifdef CONTINUOS_TRIGGER                              //Support continuous triggering
+
+        if(++(btn->Button_Cycle) >= BUTTON_CONTINUOS_CYCLE)
+        {
+          btn->Button_Cycle = 0;
+          btn->Button_Trigger_Event = BUTTON_CONTINUOS; 
+          TRIGGER_CB(BUTTON_CONTINUOS);                      //continuous triggering
+          RT_DEBUG_LOG(RT_DEBUG_THREAD,("continuous triggering"));
+        }
+        
+        #else
+        
+        btn->Button_Trigger_Event = BUTTON_DOWM;
+      
+        if(++(btn->Long_Time) >= BUTTON_LONG_TIME)            //Update the trigger event before releasing the button as long press
+        {
+          #ifdef LONG_FREE_TRIGGER
+          
+          btn->Button_Trigger_Event = BUTTON_LONG; 
+          
+          #else
+          
+          if(++(btn->Button_Cycle) >= BUTTON_LONG_CYCLE)      //Continuous triggering of long press cycles
+          {
+            btn->Button_Cycle = 0;
+            btn->Button_Trigger_Event = BUTTON_LONG; 
+            TRIGGER_CB(BUTTON_LONG);    //long triggering
+          }
+          #endif
+          
+          if(btn->Long_Time == 0xFF)    //Update time overflow
+          {
+            btn->Long_Time = BUTTON_LONG_TIME;
+          }
+          RT_DEBUG_LOG(RT_DEBUG_THREAD,("Long press"));
+        }
+          
+        #endif
+      }
+
+      break;
+    } 
+    
+    case BUTTON_UP :                                        //button up
+    {
+      if(btn->Button_Trigger_Event == BUTTON_DOWM)          //Trigger click
+      {
+        if((btn->Timer_Count <= BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State == BUTTON_DOUBLE)) // double click
+        {
+          btn->Button_Trigger_Event = BUTTON_DOUBLE;
+          TRIGGER_CB(BUTTON_DOUBLE);    
+          RT_DEBUG_LOG(RT_DEBUG_THREAD,("double click"));
+          btn->Button_State = NONE_TRIGGER;
+          btn->Button_Last_State = NONE_TRIGGER;
+        }
+        else
+        {
+            btn->Timer_Count=0;
+            btn->Long_Time = 0;                   //Detection long press failed, clear 0
+          
+          #ifndef SINGLE_AND_DOUBLE_TRIGGER
+            TRIGGER_CB(BUTTON_DOWM);              //click
+          #endif
+            btn->Button_State = BUTTON_DOUBLE;
+            btn->Button_Last_State = BUTTON_DOUBLE;
+          
+        }
+      }
+      
+      else if(btn->Button_Trigger_Event == BUTTON_LONG)
+      {
+        #ifdef LONG_FREE_TRIGGER
+          TRIGGER_CB(BUTTON_LONG);                    //Long press
+        #else
+          TRIGGER_CB(BUTTON_LONG_FREE);               //Long press free
+        #endif
+        btn->Long_Time = 0;
+        btn->Button_State = NONE_TRIGGER;
+        btn->Button_Last_State = BUTTON_LONG;
+      } 
+      
+      #ifdef CONTINUOS_TRIGGER
+        else if(btn->Button_Trigger_Event == BUTTON_CONTINUOS)  //Press continuously
+        {
+          btn->Long_Time = 0;
+          TRIGGER_CB(BUTTON_CONTINUOS_FREE);                    //Press continuously free
+          btn->Button_State = NONE_TRIGGER;
+          btn->Button_Last_State = BUTTON_CONTINUOS;
+        } 
+      #endif
+      
+      break;
+    }
+    
+    case BUTTON_DOUBLE :
+    {
+      btn->Timer_Count++;                                       //Update time 
+      if(btn->Timer_Count>=BUTTON_DOUBLE_TIME)
+      {
+        btn->Button_State = NONE_TRIGGER;
+        btn->Button_Last_State = NONE_TRIGGER;
+      }
+      #ifdef SINGLE_AND_DOUBLE_TRIGGER
+      
+        if((btn->Timer_Count>=BUTTON_DOUBLE_TIME)&&(btn->Button_Last_State != BUTTON_DOWM))
+        {
+          btn->Timer_Count=0;
+          TRIGGER_CB(BUTTON_DOWM);    
+          btn->Button_State = NONE_TRIGGER;
+          btn->Button_Last_State = BUTTON_DOWM;
+        }
+        
+      #endif
+
+      break;
+    }
+
+    default :
+      break;
+  }
+  
+}
+
+/************************************************************
+  * @brief   Traversing the way to scan the button without losing each button
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    This function is called periodically, it is recommended to call 20-50ms once.
+  ***********************************************************/
+void Button_Process(void)
+{
+  struct button* pass_btn;
+  for(pass_btn = Head_Button; pass_btn != RT_NULL; pass_btn = pass_btn->Next)
+  {
+      Button_Cycle_Process(pass_btn);
+  }
+}
+
+/************************************************************
+  * @brief   Search Button
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+void Search_Button(void)
+{
+  struct button* pass_btn;
+  for(pass_btn = Head_Button; pass_btn != RT_NULL; pass_btn = pass_btn->Next)
+  {
+    RT_DEBUG_LOG(RT_DEBUG_THREAD,("button node have %s",pass_btn->Name));
+  }
+}
+
+/************************************************************
+  * @brief   Handle all button callback functions
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    Not implemented yet
+  ***********************************************************/
+void Button_Process_CallBack(void *btn)
+{
+  rt_uint8_t btn_event = Get_Button_Event(btn);
+
+  switch(btn_event)
+  {
+    case BUTTON_DOWM:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your press trigger"));
+      break;
+    }
+    
+    case BUTTON_UP:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your trigger release"));
+      break;
+    }
+    
+    case BUTTON_DOUBLE:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your double-click trigger"));
+      break;
+    }
+    
+    case BUTTON_LONG:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your long press trigger"));
+      break;
+    }
+    
+    case BUTTON_LONG_FREE:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your long press trigger free"));
+      break;
+    }
+    
+    case BUTTON_CONTINUOS:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add your continuous trigger processing logic"));
+      break;
+    }
+    
+    case BUTTON_CONTINUOS_FREE:
+    {
+      RT_DEBUG_LOG(RT_DEBUG_THREAD,("Add processing logic for your continuous trigger release"));
+      break;
+    }
+      
+  } 
+}
+
+
+/**************************** The following is the internal call function ********************/
+
+/************************************************************
+  * @brief   Copy the specified length string
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+static char *StrnCopy(char *dst, const char *src, rt_uint32_t n)
+{
+  if (n != 0)
+  {
+    char *d = dst;
+    const char *s = src;
+    do
+    {
+        if ((*d++ = *s++) == 0)
+        {
+            while (--n != 0)
+                *d++ = 0;
+            break;
+        }
+    } while (--n != 0);
+  }
+  return (dst);
+}
+
+/************************************************************
+  * @brief   Print button related information
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+static void Print_Btn_Info(Button_t* btn)
+{
+  
+  RT_DEBUG_LOG(RT_DEBUG_THREAD,("button struct information:\n\
+              btn->Name:%s \n\
+              btn->Button_State:%d \n\
+              btn->Button_Trigger_Event:%d \n\
+              btn->Button_Trigger_Level:%d \n\
+              btn->Button_Last_Level:%d \n\
+              ",
+              btn->Name,
+              btn->Button_State,
+              btn->Button_Trigger_Event,
+              btn->Button_Trigger_Level,
+              btn->Button_Last_Level));
+  Search_Button();
+}
+/************************************************************
+  * @brief   Connect buttons with a single linked list
+  * @param   RT_NULL
+  * @return  RT_NULL
+  * @author  jiejie
+  * @github  https://github.com/jiejieTop
+  * @date    2018-xx-xx
+  * @version v1.0
+  * @note    RT_NULL
+  ***********************************************************/
+static void Add_Button(Button_t* btn)
+{
+  struct button *pass_btn = Head_Button;
+  
+  while(pass_btn)
+  {
+    pass_btn = pass_btn->Next;
+  }
+  
+  btn->Next = Head_Button;
+  Head_Button = btn;
+}
+
+#endif
+
+
+

+ 132 - 0
button.h

@@ -0,0 +1,132 @@
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <string.h>
+
+#define BTN_NAME_MAX  32     //Ãû×Ö×î´óΪ32×Ö½Ú
+
+/* 
+ The button debounce time is 40ms, the recommended calling period is 20ms.
+ It is considered valid only if the 40ms state is continuously detected, including bounce and press.
+*/
+
+//#define CONTINUOS_TRIGGER               //Whether to support continuous triggering, do not detect single-click and long-press if you send even
+
+/* Whether to support click and double-click while there is a trigger,
+ if you choose to enable the macro definition, single-click double-clickback,
+ but click will delay the response.Because it must be judged whether a 
+ double click is triggered after clicking, the delay time is the double-click 
+ interval BUTTON_DOUBLE_TIME.If you do not enable this macro definition,
+ it is recommended that there is only one click/double click in the project. 
+ Otherwise, a click will be triggered when the response is double-clicked.
+ Because the double click must be generated after one press and release.
+*/
+
+//#define SINGLE_AND_DOUBLE_TRIGGER      
+
+/* Whether long-press release is supported or not. If this macro definition is turned on, a long press is triggered after a long press release.
+    Otherwise, long press is triggered for a long time, the trigger period is determined by BUTTON_LONG_CYCLE*/
+//#define LONG_FREE_TRIGGER              
+
+#ifndef  BUTTON_DEBOUNCE_TIME
+#define BUTTON_DEBOUNCE_TIME   2   //Debounce time  (n-1)*call cycle
+#endif
+
+#ifndef  BUTTON_CONTINUOS_CYCLE
+#define BUTTON_CONTINUOS_CYCLE  1  //Double-click the trigger cycle time  (n-1)*call cycle
+#endif
+
+#ifndef  BUTTON_LONG_CYCLE
+#define BUTTON_LONG_CYCLE       1  //Long press trigger cycle time   (n-1)*call cycle
+#endif
+
+#ifndef  BUTTON_DOUBLE_TIME
+#define BUTTON_DOUBLE_TIME      15 //Double click interval  (n-1)*call cycle  Recommended at 200-600ms
+#endif
+
+#ifndef BUTTON_LONG_TIME
+#define BUTTON_LONG_TIME       50  //For n seconds ((n-1)*call cycle)ms, think long press event
+#endif
+
+
+#define TRIGGER_CB(event)   \
+        if(btn->CallBack_Function[event]) \
+          btn->CallBack_Function[event]((Button_t*)btn)
+
+typedef void (*Button_CallBack)(void*);   //The button triggers the callback function and needs to be implemented by the user.
+
+
+
+typedef enum {
+  BUTTON_DOWM = 0,
+  BUTTON_UP,
+  BUTTON_DOUBLE,
+  BUTTON_LONG,
+  BUTTON_LONG_FREE,
+  BUTTON_CONTINUOS,
+  BUTTON_CONTINUOS_FREE,
+  BUTTON_ALL_RIGGER,
+  number_of_event,                         /* The event that triggered the callback */
+  NONE_TRIGGER
+}Button_Event;
+
+/*
+  Each button corresponds to a global structure variable.
+  Its member variables are necessary to implement filtering and multiple button states.
+*/
+typedef struct button
+{
+  /* The following is a function pointer pointing to the function that determines whether the button is pressed or not. */
+  rt_uint8_t (*Read_Button_Level)(void); /* Read the button level function, you need to implement */
+  
+  char Name[BTN_NAME_MAX];
+  
+  rt_uint8_t Button_State              :   4;    /* The current state of the button (pressed or bounced) */
+  rt_uint8_t Button_Last_State         :   4;    /* The last button state used to determine the double click */
+  rt_uint8_t Button_Trigger_Level      :   2;    /* Button trigger level */
+  rt_uint8_t Button_Last_Level         :   2;    /* Button current level */
+  
+  rt_uint8_t Button_Trigger_Event;               /* Button trigger event, click, double click, long press, etc. */
+  
+  Button_CallBack CallBack_Function[number_of_event];
+  
+  rt_uint8_t Button_Cycle;                       /* Continuous button cycle */
+  
+  rt_uint8_t Timer_Count;                        /* Timing */
+  rt_uint8_t Debounce_Time;                      /* Debounce time */
+  
+  rt_uint8_t Long_Time;                          /* Button press duration */
+  
+  struct button *Next;
+  
+}Button_t;
+
+
+
+
+/* Function declaration for external calls */
+
+void Button_Create(const char *name,
+                  Button_t *btn, 
+                  rt_uint8_t(*read_btn_level)(void),
+                  rt_uint8_t btn_trigger_level);
+                  
+void Button_Attach(Button_t *btn,Button_Event btn_event,Button_CallBack btn_callback);   
+                  
+void Button_Cycle_Process(Button_t *btn);   
+                  
+void Button_Process(void);
+                  
+void Button_Delete(Button_t *btn);   
+  
+void Search_Button(void);     
+                  
+void Get_Button_EventInfo(Button_t *btn);
+rt_uint8_t Get_Button_Event(Button_t *btn);
+rt_uint8_t Get_Button_State(Button_t *btn);
+void Button_Process_CallBack(void *btn);
+                  
+#endif

+ 146 - 0
example.c

@@ -0,0 +1,146 @@
+
+#include "button.h"
+
+/**
+  ******************************************************************
+                              Variable declaration
+  ******************************************************************
+  */ 
+
+Button_t Button1;
+Button_t Button2; 
+
+
+/**
+  ******************************************************************
+                             Function declaration
+  ******************************************************************
+  */ 
+  
+static void BSP_Init(void);
+
+void Btn1_Dowm_CallBack(void *btn)
+{
+  PRINT_INFO("Button1 Click!");
+}
+
+void Btn1_Double_CallBack(void *btn)
+{
+  PRINT_INFO("Button1 Double click!");
+}
+
+void Btn1_Long_CallBack(void *btn)
+{
+  PRINT_INFO("Button1 Long press!");
+}
+
+void Btn1_Continuos_CallBack(void *btn)
+{
+  PRINT_INFO("Button1 Press continuously!");
+}
+void Btn1_ContinuosFree_CallBack(void *btn)
+{
+  PRINT_INFO("Button1 Press continuously free!");
+}
+
+
+void Btn2_Dowm_CallBack(void *btn)
+{
+  PRINT_INFO("Button2 Click!");
+}
+
+void Btn2_Double_CallBack(void *btn)
+{
+  PRINT_INFO("Button2 Double click!");
+}
+
+void Btn2_Long_CallBack(void *btn)
+{
+  PRINT_INFO("Button2 Long press!");
+}
+
+void Btn2_Continuos_CallBack(void *btn)
+{
+  PRINT_INFO("Button2 Press continuously!");
+}
+void Btn2_ContinuosFree_CallBack(void *btn)
+{
+  PRINT_INFO("Button2 Press continuously free!");
+}
+
+/**
+  ******************************************************************
+  * @brief   main
+  * @author  jiejie
+  * @version V1.0
+  * @date    2018-xx-xx
+  ******************************************************************
+  */ 
+int main(void)
+{
+  uint32_t i = 100000;
+  
+  BSP_Init();
+  
+  Button_Create("Button1",
+              &Button1, 
+              Read_KEY1_Level, 
+              KEY_ON);
+  Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack);                       //Click
+  Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack);                   //Double click
+  Button_Attach(&Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack);             //Press continuously
+  Button_Attach(&Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack);    //Press continuously free
+  Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack);                       //Long press
+
+
+  Button_Create("Button2",
+              &Button2, 
+              Read_KEY2_Level, 
+              KEY_ON);
+  Button_Attach(&Button2,BUTTON_DOWM,Btn2_Dowm_CallBack);                     //Click
+  Button_Attach(&Button2,BUTTON_DOUBLE,Btn2_Double_CallBack);                 //Double click
+  Button_Attach(&Button2,BUTTON_CONTINUOS,Btn2_Continuos_CallBack);           //Press continuously
+  Button_Attach(&Button2,BUTTON_CONTINUOS_FREE,Btn2_ContinuosFree_CallBack);  //Press continuously free
+  Button_Attach(&Button2,BUTTON_LONG,Btn2_Long_CallBack);                     //Long press
+
+  Get_Button_Event(&Button1);
+  Get_Button_Event(&Button2);
+
+  while(1)                            
+  {
+
+    Button_Process();     //Need to call the button handler function periodically
+
+    Delay_ms(20);
+    
+    
+  }
+}
+
+/**
+  ******************************************************************
+  * @brief   BSP_Init£¬ÓÃÓÚËùÓа弶³õʼ»¯
+  * @author  jiejie
+  * @version V1.0
+  * @date    2018-xx-xx
+  ******************************************************************
+  */ 
+static void BSP_Init(void)
+{
+  /* LED Config */
+  LED_GPIO_Config();
+  
+  /* USART Config */
+  USART_Config();
+  
+  /* button config */
+  Key_GPIO_Config();
+  
+  
+ /* print info */
+  PRINT_INFO("welcome to learn jiejie stm32 library!\n");
+  
+}
+
+
+/********************************END OF FILE***************************************/