Giter VIP home page Giter VIP logo

multibutton's Introduction

MultiButton

简介

MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。

使用方法

1.先申请一个按键结构

struct Button button1;

2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平

button_init(&button1, read_button_pin, 0, 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  button_id;
	uint8_t  (*hal_button_Level)(uint8_t  button_id_);
	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_PRESS_START 达到长按时间阈值时触发一次
LONG_PRESS_HOLD 长按期间一直触发

Examples

#include "button.h"

unit8_t btn1_id = 0;

struct Button btn1;

uint8_t read_button_GPIO(uint8_t button_id)
{
	// you can share the GPIO read function with multiple Buttons
	switch(button_id)
	{
		case btn1_id:
			return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
			break;

		default:
			return 0;
			break;
	}
}
void BTN1_PRESS_DOWN_Handler(void* btn)
{
	//do something...
}

void BTN1_PRESS_UP_Handler(void* btn)
{
	//do something...
}

...

int main()
{
	button_init(&btn1, read_button_GPIO, 0, btn1_id);
	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_PRESS_START, BTN1_LONG_PRESS_START_Handler);
	button_attach(&btn1, 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)
	{}
}

multibutton's People

Contributors

0x1abin avatar advanced-lj avatar armku avatar cloudsir avatar glacierty avatar happyfacade avatar junbo-zheng avatar mr-mcf avatar sj13757790563 avatar xidameng avatar z-tam-code 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  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

multibutton's Issues

在Button结构体加入个uint8_t参数方便统一按键读取入口,并且解决多按键读取问题

修后的键读取入口初始化方法

void bsp_InitKeyVar(void)
{
    /* 初始化按键FIFO */
    ringbuffer_init(&s_key_kfifo, &s_buf[0], KEY_FIFO_SIZE);
    /* 给每个按键结构体成员变量赋一组缺省值 */
    for (uint8_t i = 0; i < KEY_COUNT; i++)
    {
        button_init(&s_tButton[i], IsKeyDownFunc, 1, i);
        button_attach(&s_tButton[i], PRESS_DOWN, button_callback);
        button_attach(&s_tButton[i], PRESS_UP, button_callback);
        button_attach(&s_tButton[i], PRESS_REPEAT, button_callback);
        button_attach(&s_tButton[i], SINGLE_CLICK, button_callback);
        button_attach(&s_tButton[i], DOUBLE_CLICK, button_callback);
        button_attach(&s_tButton[i], LONG_PRESS_START, button_callback);
        // button_attach(&s_tButton[i], LONG_PRESS_HOLD, button_callback); 
        button_start(&s_tButton[i]);
    }
}

回调键值处理

void button_callback(void *button)
{
    uint8_t btn_val;
    uint8_t ret = 255;
    struct Button *btn = (struct Button *)button;
    btn_val = get_button_event(btn);
    switch (btn_val)
    {
    case PRESS_DOWN:
        ret = (btn->key_id) * number_of_event + PRESS_DOWN;
        break;
    case PRESS_UP:
        ret = (btn->key_id) * number_of_event + PRESS_UP;
        break;
    case PRESS_REPEAT:
        ret = (btn->key_id) * number_of_event + PRESS_REPEAT;
        break;
    case SINGLE_CLICK:
        ret = (btn->key_id) * number_of_event + SINGLE_CLICK;
        break;
    case DOUBLE_CLICK:
        ret = (btn->key_id) * number_of_event + DOUBLE_CLICK;
        break;
    case LONG_PRESS_START:
        ret = (btn->key_id) * number_of_event + LONG_PRESS_START;
        break;
    case LONG_PRESS_HOLD:
        ret = (btn->key_id) * number_of_event + LONG_PRESS_HOLD;
        break;
    }
    if (ret != KEY_NONE)
    {
        ringbuffer_put_force(&s_key_kfifo, &ret, 1); 
    }
}

键值判断宏

/* 按键值宏 */
#define KEY_NONE                        255                                             /* 无按键 */
#define KEY_PRESS_DOWN(key_id)          (key_id * number_of_event + PRESS_DOWN)         /* 按键按下 */
#define KEY_PRESS_UP(key_id)            (key_id * number_of_event + PRESS_UP)           /* 按键弹起 */
#define KEY_PRESS_REPEAT(key_id)        (key_id * number_of_event + PRESS_REPEAT)       /* 重复按下触发 */
#define KEY_SINGLE_CLICK(key_id)        (key_id * number_of_event + SINGLE_CLICK)       /* 单击按键事件 */
#define KEY_DOUBLE_CLICK(key_id)        (key_id * number_of_event + DOUBLE_CLICK)       /* 双击按键事件 */
#define KEY_LONG_PRESS_START(key_id)    (key_id * number_of_event + LONG_PRESS_START)   /* 达到长按时间阈值时触发一次 */
#define KEY_LONG_PRESS_HOLD(key_id)     (key_id * number_of_event + LONG_PRESS_HOLD)    /* 长按期间一直触发 */

我自己玩的demo在这里https://github.com/cctv180/STM32H743XI_Demo
改后的源码https://github.com/cctv180/MultiButton

为什么回调连击的时候不修改事件

case 2:
    if (handle->button_level == handle->active_level) {
        handle->event = (uint8_t)PRESS_DOWN;
        EVENT_CB(PRESS_DOWN);
        handle->repeat++;
        handle->event = (uint8_t)PRESS_REPEAT;//为什么不加这句,不加回调出去不就错了事件类型。
        EVENT_CB(PRESS_REPEAT);
        handle->ticks = 0;
        handle->state = 3;
    } else if (handle->ticks > SHORT_TICKS) {
        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;
            EVENT_CB(DOUBLE_CLICK);
        }

关于按键去抖动

源码写得精彩!拜读并仔细分析了一下,有个问题想请教。按键去抖需要3次按键值(间隔5ms)相同才能确认,但是在状态机处理中,感觉没有完全用去抖处理后的按键值进行判断处理,而是用上次保存或去抖处理后的按键值进行处理,这样对一些需要延时判断的按键动作判断会不会有影响?能否加一个按键去抖有效标志做辅助判断,一旦去抖完毕,此标志有效,后续状态机只有在标志有效情况下才取按键值进行处理,处理完后标志置为无效。不知我的理解是否正确?请指点,谢谢!

多个按键共用同一套长短按阈值参数

多个按键对象共用同一套长短阈值按判断参数,在按键实际功能不同时有很大的问题。

例如我现在的工程,长按power输出,这个长按至少需要1.2秒,但是关闭power输出是短按,同时判断区间来到80ms。(为此在作者的基础上添加了一个短按时间阈值触发事件)
但是像旋钮按键或者其他功能按键则没有这样特化的阈值判断,就可以人性化一些。

一个简单的解决方案

在结构体中添加长短按阈值的参数存储,在初始化时绑定即可

作者的架构真不错,我要在此基础上把我旋转编码器的驱动也改了

:)

按键删除

请问void button_stop(struct Button* handle)这个函数, 在哪将传入的按键删除了, 压根都没看到这个逻辑..

MultiButton状态图

multi_button
这是我整理的 MultiButton 状态图,分享给大家,一定程度上能帮助学习源码。
黄色框框里的注释是用户关注的事件,即会调用事件对应的回调函数。
黄色框框上方的注释是状态机实际切换状态的事件。

button_ticks这个函数在MultiTimer的定时器回调函数中调用导致程序跑飞

image

我是在APM32F107中移植了MultiTimer和MultiButton,如上图,MultiTimer的是用Systick作为系统滴答的,然后在MultiButton的5ms软件定时器回调函数中调用button_ticks,但是程序跑飞了

`void exampleTimer1Callback(MultiTimer* timer, void *userData)
{
button_ticks();
MultiTimerStart(timer, 5, exampleTimer1Callback, userData);
}

void exampleTimer2Callback(MultiTimer* timer, void *userData)
{

}

void exampleTimer3Callback(MultiTimer* timer, void *userData)
{

MultiTimerStart(timer, 4567, exampleTimer3Callback, userData);

}

void MyBSP_Init(void)
{
MyADC_DMA_Init();
MyButton_Init();

SysTick_Config(SystemCoreClock / 1000);
MultiTimerInstall(PlatformTicksGetFunc);
MultiTimerStart(&timer1, 5, exampleTimer1Callback, NULL);
MultiTimerStart(&timer2, 5000, exampleTimer2Callback, NULL);
MultiTimerStart(&timer3, 3456, exampleTimer3Callback, NULL);	

}`

Example里有两个小问题

1.这里注册了个btn2的事件,(上文只有btn1一个按键)
button_attach(&btn2, LONG_PRESS_HOLD, BTN1_LONG_PRESS_HOLD_Handler);

2.int read_button1_GPIO()
函数返回值与传递值类型不一致导致
button_init(&btn1, read_button1_GPIO, 0);
在MDK下编译会报错,统一为 uint8_t 好一点
uint8_t read_button1_GPIO();

unit8_t 和 uint8_t?

unit8_t btn1_id = 0;
struct Button btn1;

uint8_t read_button_GPIO(uint8_t button_id)
{
	// you can share the GPIO read function with multiple Buttons
	switch(button_id)
	{
		case btn1_id:
			return HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin);
		default:
			return 0;
	}
}

哈?

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.