ソースを参照

[提交] 提交按键库

liu2guang 8 年 前
コミット
3f108ce0ba
6 ファイル変更494 行追加0 行削除
  1. 120 0
      README.md
  2. 19 0
      SConscript
  3. 58 0
      examples/event_async.c
  4. 37 0
      examples/event_inquire.c
  5. 199 0
      multi_button.c
  6. 61 0
      multi_button.h

+ 120 - 0
README.md

@@ -0,0 +1,120 @@
+# MultiButton
+
+## 简介
+MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。
+
+## 使用方法
+1.先申请一个按键结构
+
+```
+struct Button button1;
+```
+2.初始化按键对象,绑定按键的GPIO电平读取接口**read_button_pin()** ,后一个参数设置有效触发电平
+
+```
+button_init(&button1, read_button_pin, 0);
+```
+3.注册按键事件
+
+```
+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语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理:
+
+```
+struct Button {
+	uint16_t ticks;
+	uint8_t  repeat: 4;
+	uint8_t  event : 4;
+	uint8_t  state : 3;
+	uint8_t  debounce_cnt : 3; 
+	uint8_t  active_level : 1;
+	uint8_t  button_level : 1;
+	uint8_t  (*hal_button_Level)(void);
+	BtnCallback  cb[number_of_event];
+	struct Button* next;
+};
+```
+这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。
+
+
+## 按键事件
+
+事件 | 说明
+---|---
+PRESS_DOWN | 按键按下,每次按下都触发
+PRESS_UP | 按键弹起,每次松开都触发
+PRESS_REPEAT | 重复按下触发,变量repeat计数连击次数
+SINGLE_CLICK | 单击按键事件
+DOUBLE_CLICK | 双击按键事件
+LONG_RRESS_START | 达到长按时间阈值时触发一次
+LONG_PRESS_HOLD | 长按期间一直触发
+
+
+## Examples
+
+```
+#include "button.h"
+
+struct Button btn1;
+
+int read_button1_GPIO() 
+{
+	return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
+}
+
+int main()
+{
+	button_init(&btn1, read_button1_GPIO, 0);
+	button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler);
+	button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler);
+	button_attach(&btn1, PRESS_REPEAT,     BTN1_PRESS_REPEAT_Handler);
+	button_attach(&btn1, SINGLE_CLICK,     BTN1_SINGLE_Click_Handler);
+	button_attach(&btn1, DOUBLE_CLICK,     BTN1_DOUBLE_Click_Handler);
+	button_attach(&btn1, LONG_RRESS_START, BTN1_LONG_RRESS_START_Handler);
+	button_attach(&btn2, LONG_PRESS_HOLD,  BTN1_LONG_PRESS_HOLD_Handler);
+	button_start(&btn1);
+	
+	//make the timer invoking the button_ticks() interval 5ms.
+	//This function is implemented by yourself.
+	__timer_start(button_ticks, 0, 5); 
+	
+	while(1) 
+	{}
+}
+
+void BTN1_PRESS_DOWN_Handler(void* btn)
+{
+	//do something...
+}
+
+void BTN1_PRESS_UP_Handler(void* btn)
+{
+	//do something...
+}
+
+...
+```
+

+ 19 - 0
SConscript

@@ -0,0 +1,19 @@
+from building import * 
+
+# get current dir path
+cwd = GetCurrentDir()
+
+# init src and inc vars
+src = []
+inc = []
+
+# add MultiButton common include
+inc = inc + [cwd]
+
+# add MultiButton basic code
+src = src + ['./multi_button.c']
+
+# add group to IDE project
+group = DefineGroup('MultiButton', src, depend = ['PKG_USING_MULTIBUTTON'], CPPPATH = inc)
+
+Return('group')

+ 58 - 0
examples/event_async.c

@@ -0,0 +1,58 @@
+#include "multi_button.h"
+
+struct Button btn1;
+struct Button btn2;
+
+int read_button1_GPIO() 
+{
+	return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
+}
+
+int read_button2_GPIO() 
+{
+	return HAL_GPIO_ReadPin(B2_GPIO_Port, B2_Pin);
+}
+
+int main()
+{
+	button_init(&btn1, read_button1_GPIO, 0);
+	button_init(&btn2, read_button2_GPIO, 0);
+	
+	button_attach(&btn1, PRESS_DOWN,       BTN1_PRESS_DOWN_Handler);
+	button_attach(&btn1, PRESS_UP,         BTN1_PRESS_UP_Handler);
+	button_attach(&btn1, PRESS_REPEAT,     BTN1_PRESS_REPEAT_Handler);
+	button_attach(&btn1, SINGLE_CLICK,     BTN1_SINGLE_Click_Handler);
+	button_attach(&btn1, DOUBLE_CLICK,     BTN1_DOUBLE_Click_Handler);
+	button_attach(&btn1, LONG_RRESS_START, BTN1_LONG_RRESS_START_Handler);
+	button_attach(&btn2, LONG_PRESS_HOLD,  BTN1_LONG_PRESS_HOLD_Handler);
+	
+	button_attach(&btn2, PRESS_DOWN,       BTN2_PRESS_DOWN_Handler);
+	button_attach(&btn2, PRESS_UP,         BTN2_PRESS_UP_Handler);
+	button_attach(&btn2, PRESS_REPEAT,     BTN2_PRESS_REPEAT_Handler);
+	button_attach(&btn2, SINGLE_CLICK,     BTN2_SINGLE_Click_Handler);
+	button_attach(&btn2, DOUBLE_CLICK,     BTN2_DOUBLE_Click_Handler);
+	button_attach(&btn2, LONG_RRESS_START, BTN2_LONG_RRESS_START_Handler);
+	button_attach(&btn2, LONG_PRESS_HOLD,  BTN2_LONG_PRESS_HOLD_Handler);
+	
+	button_start(&btn1);
+	button_start(&btn2);
+	
+	//make the timer invoking the button_ticks() interval 5ms.
+	//This function is implemented by yourself.
+	__timer_start(button_ticks, 0, 5); 
+	
+	while(1) 
+	{}
+}
+
+void BTN1_PRESS_DOWN_Handler(void* btn)
+{
+	//do something...
+}
+
+void BTN1_PRESS_UP_Handler(void* btn)
+{
+	//do something...
+}
+
+...

+ 37 - 0
examples/event_inquire.c

@@ -0,0 +1,37 @@
+#include "multi_button.h"
+
+struct Button btn1;
+
+int read_button1_GPIO() 
+{
+	return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
+}
+
+
+int main()
+{
+	static uint8_t btn1_event_val;
+	
+	button_init(&btn1, read_button1_GPIO, 0);
+	button_start(&btn1);
+	
+	//make the timer invoking the button_ticks() interval 5ms.
+	//This function is implemented by yourself.
+	__timer_start(button_ticks, 0, 5); 
+	
+	while(1) 
+	{
+		if(btn1_event_val != get_button_event(&btn1)) {
+			btn1_event_val = get_button_event(&btn1);
+			
+			if(btn1_event_val == PRESS_DOWN) {
+				//do something
+			} else if(btn1_event_val == PRESS_UP) {
+				//do something
+			} else if(btn1_event_val == LONG_PRESS_HOLD) {
+				//do something
+			}
+		}
+	}
+}
+

+ 199 - 0
multi_button.c

@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
+ * All rights reserved
+ */
+
+#include "multi_button.h"
+
+#define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((Button*)handle)
+	
+//button handle list head.
+static struct Button* head_handle = NULL;
+
+/**
+  * @brief  Initializes the button struct handle.
+  * @param  handle: the button handle strcut.
+  * @param  pin_level: read the HAL GPIO of the connet button level.
+  * @param  active_level: pressed GPIO level.
+  * @retval None
+  */
+void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level)
+{
+	memset(handle, sizeof(struct Button), 0);
+	handle->event = (uint8_t)NONE_PRESS;
+	handle->hal_button_Level = pin_level;
+	handle->button_level = handle->hal_button_Level();
+	handle->active_level = active_level;
+}
+
+/**
+  * @brief  Attach the button event callback function.
+  * @param  handle: the button handle strcut.
+  * @param  event: trigger event type.
+  * @param  cb: callback function.
+  * @retval None
+  */
+void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
+{
+	handle->cb[event] = cb;
+}
+
+/**
+  * @brief  Inquire the button event happen.
+  * @param  handle: the button handle strcut.
+  * @retval button event.
+  */
+PressEvent get_button_event(struct Button* handle)
+{
+	return (PressEvent)(handle->event);
+}
+
+/**
+  * @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) >= DEBOUNCE_TICKS) {
+			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 down
+			handle->event = (uint8_t)PRESS_DOWN;
+			EVENT_CB(PRESS_DOWN);
+			handle->ticks = 0;
+			handle->repeat = 1;
+			handle->state = 1;
+		} else {
+			handle->event = (uint8_t)NONE_PRESS;
+		}
+		break;
+
+	case 1:
+		if(handle->button_level != handle->active_level) { //released press up
+			handle->event = (uint8_t)PRESS_UP;
+			EVENT_CB(PRESS_UP);
+			handle->ticks = 0;
+			handle->state = 2;
+
+		} else if(handle->ticks > LONG_TICKS) {
+			handle->event = (uint8_t)LONG_RRESS_START;
+			EVENT_CB(LONG_RRESS_START);
+			handle->state = 5;
+		}
+		break;
+
+	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;
+
+	case 3:
+		if(handle->button_level != handle->active_level) { //released press up
+			handle->event = (uint8_t)PRESS_UP;
+			EVENT_CB(PRESS_UP);
+			if(handle->ticks < SHORT_TICKS) {
+				handle->ticks = 0;
+				handle->state = 2; //repeat press
+			} else {
+				handle->state = 0;
+			}
+		}
+		break;
+
+	case 5:
+		if(handle->button_level == handle->active_level) {
+			//continue hold trigger
+			handle->event = (uint8_t)LONG_PRESS_HOLD;
+			EVENT_CB(LONG_PRESS_HOLD);
+
+		} else { //releasd
+			handle->event = (uint8_t)PRESS_UP;
+			EVENT_CB(PRESS_UP);
+			handle->state = 0; //reset
+		}
+		break;
+	}
+}
+
+/**
+  * @brief  Start the button work, add the handle into work list.
+  * @param  handle: target handle strcut.
+  * @retval 0: succeed. -1: already exist.
+  */
+int button_start(struct Button* handle)
+{
+	struct Button* target = head_handle;
+	while(target) {
+		if(target == handle) return -1;	//already exist.
+		target = target->next;
+	}
+	handle->next = head_handle;
+	head_handle = handle;
+	return 0;
+}
+
+/**
+  * @brief  Stop the button work, remove the handle off work list.
+  * @param  handle: target handle strcut.
+  * @retval None
+  */
+void button_stop(struct Button* handle)
+{
+	struct Button** curr;
+	for(curr = &head_handle; *curr; ) {
+		struct Button* entry = *curr;
+		if (entry == handle) {
+			*curr = entry->next;
+//			free(entry);
+		} else
+			curr = &entry->next;
+	}
+}
+
+/**
+  * @brief  background ticks, timer repeat invoking interval 5ms.
+  * @param  None.
+  * @retval None
+  */
+void button_ticks()
+{
+	struct Button* target;
+	for(target=head_handle; target; target=target->next) {
+		button_handler(target);
+	}
+}
+

+ 61 - 0
multi_button.h

@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
+ * All rights reserved
+ */
+ 
+#ifndef _MULTI_BUTTON_H_
+#define _MULTI_BUTTON_H_
+
+#include "stdint.h"
+#include "string.h"
+
+//According to your need to modify the constants.
+#define TICKS_INTERVAL    5	//ms
+#define DEBOUNCE_TICKS    3	//MAX 8
+#define SHORT_TICKS       (300 /TICKS_INTERVAL)
+#define LONG_TICKS        (1000 /TICKS_INTERVAL)
+
+
+typedef void (*BtnCallback)(void*);
+
+typedef enum {
+	PRESS_DOWN = 0,
+	PRESS_UP,
+	PRESS_REPEAT,
+	SINGLE_CLICK,
+	DOUBLE_CLICK,
+	LONG_RRESS_START,
+	LONG_PRESS_HOLD,
+	number_of_event,
+	NONE_PRESS
+}PressEvent;
+
+typedef struct Button {
+	uint16_t ticks;
+	uint8_t  repeat : 4;
+	uint8_t  event : 4;
+	uint8_t  state : 3;
+	uint8_t  debounce_cnt : 3; 
+	uint8_t  active_level : 1;
+	uint8_t  button_level : 1;
+	uint8_t  (*hal_button_Level)(void);
+	BtnCallback  cb[number_of_event];
+	struct Button* next;
+}Button;
+
+#ifdef __cplusplus  
+extern "C" {  
+#endif  
+
+void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level);
+void button_attach(struct Button* handle, PressEvent event, BtnCallback cb);
+PressEvent get_button_event(struct Button* handle);
+int  button_start(struct Button* handle);
+void button_stop(struct Button* handle);
+void button_ticks(void);
+
+#ifdef __cplusplus
+} 
+#endif
+
+#endif