一个支持多种组合按键的驱动程序
FlexibleButton 是一个基于标准 C 语言的小巧灵活的按键处理库,支持按键单击、连击、短按、长按、自动消抖,可以自由设置组合按键,可用于中断和低功耗场景。
该按键库解耦了具体的按键硬件结构,理论上支持轻触按键与自锁按键,并可以无限扩展按键数量。另外,FlexibleButton 使用扫描的方式一次性读取所有所有的按键状态,然后通过事件回调机制上报按键事件。核心的按键扫描代码仅有三行,没错,就是经典的 三行按键扫描算法。使用 C 语言标准库 API 编写,也使得该按键库可以无缝兼容任意的处理器平台,并且支持任意 OS 和 non-OS(裸机编程)。 git下载地址 博客下载地址
具体源码如下
flexible_button.h
#ifndef __FLEXIBLE_BUTTON_H__
#define __FLEXIBLE_BUTTON_H__
#include "stdint.h"
#define FLEX_BTN_SCAN_FREQ_HZ 50
#define FLEX_MS_TO_SCAN_CNT(ms) (ms / (1000 / FLEX_BTN_SCAN_FREQ_HZ))
#define MAX_MULTIPLE_CLICKS_INTERVAL (FLEX_MS_TO_SCAN_CNT(300))
typedef void (*flex_button_response_callback)(void*);
typedef enum
{
FLEX_BTN_PRESS_DOWN = 0,
FLEX_BTN_PRESS_CLICK,
FLEX_BTN_PRESS_DOUBLE_CLICK,
FLEX_BTN_PRESS_REPEAT_CLICK,
FLEX_BTN_PRESS_SHORT_START,
FLEX_BTN_PRESS_SHORT_UP,
FLEX_BTN_PRESS_LONG_START,
FLEX_BTN_PRESS_LONG_UP,
FLEX_BTN_PRESS_LONG_HOLD,
FLEX_BTN_PRESS_LONG_HOLD_UP,
FLEX_BTN_PRESS_MAX,
FLEX_BTN_PRESS_NONE,
} flex_button_event_t;
typedef struct flex_button
{
struct flex_button* next;
uint8_t (*usr_button_read)(void *);
flex_button_response_callback cb;
uint16_t scan_cnt;
uint16_t click_cnt;
uint16_t max_multiple_clicks_interval;
uint16_t debounce_tick;
uint16_t short_press_start_tick;
uint16_t long_press_start_tick;
uint16_t long_hold_start_tick;
uint8_t id;
uint8_t pressed_logic_level : 1;
uint8_t event : 4;
uint8_t status : 3;
} flex_button_t;
#ifdef __cplusplus
extern "C" {
#endif
flexible_button.c
#include "flexible_button.h"
#ifndef NULL
#define NULL 0
#endif
#define EVENT_SET_AND_EXEC_CB(btn, evt) \
do \
{ \
btn->event = evt; \
if(btn->cb) \
btn->cb((flex_button_t*)btn); \
} while(0)
#define BTN_IS_PRESSED(i) (g_btn_status_reg & (1 << i))
enum FLEX_BTN_STAGE
{
FLEX_BTN_STAGE_DEFAULT = 0,
FLEX_BTN_STAGE_DOWN = 1,
FLEX_BTN_STAGE_MULTIPLE_CLICK = 2
};
typedef uint32_t btn_type_t;
static flex_button_t *btn_head = NULL;
btn_type_t g_logic_level = (btn_type_t)0;
btn_type_t g_btn_status_reg = (btn_type_t)0;
static uint8_t button_cnt = 0;
int32_t flex_button_register(flex_button_t *button)
{
flex_button_t *curr = btn_head;
if (!button || (button_cnt > sizeof(btn_type_t) * 8))
{
return -1;
}
while (curr)
{
if(curr == button)
{
return -1;
}
curr = curr->next;
}
button->next = btn_head;
button->status = FLEX_BTN_STAGE_DEFAULT;
button->event = FLEX_BTN_PRESS_NONE;
button->scan_cnt = 0;
button->click_cnt = 0;
button->max_multiple_clicks_interval = MAX_MULTIPLE_CLICKS_INTERVAL;
btn_head = button;
g_logic_level |= (button->pressed_logic_level << button_cnt);
button_cnt ++;
return button_cnt;
}
static void flex_button_read(void)
{
uint8_t i;
flex_button_t* target;
btn_type_t raw_data = 0;
for(target = btn_head, i = button_cnt - 1;
(target != NULL) && (target->usr_button_read != NULL);
target = target->next, i--)
{
raw_data = raw_data | ((target->usr_button_read)(target) << i);
}
g_btn_status_reg = (~raw_data) ^ g_logic_level;
}
static uint8_t flex_button_process(void)
{
uint8_t i;
uint8_t active_btn_cnt = 0;
flex_button_t* target;
for (target = btn_head, i = button_cnt - 1; target != NULL; target = target->next, i--)
{
if (target->status > FLEX_BTN_STAGE_DEFAULT)
{
target->scan_cnt ++;
if (target->scan_cnt >= ((1 << (sizeof(target->scan_cnt) * 8)) - 1))
{
target->scan_cnt = target->long_hold_start_tick;
}
}
switch (target->status)
{
case FLEX_BTN_STAGE_DEFAULT:
if (BTN_IS_PRESSED(i))
{
target->scan_cnt = 0;
target->click_cnt = 0;
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_DOWN);
target->status = FLEX_BTN_STAGE_DOWN;
}
else
{
target->event = FLEX_BTN_PRESS_NONE;
}
break;
case FLEX_BTN_STAGE_DOWN:
if (BTN_IS_PRESSED(i))
{
if (target->click_cnt > 0)
{
if (target->scan_cnt > target->max_multiple_clicks_interval)
{
EVENT_SET_AND_EXEC_CB(target,
target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ?
target->click_cnt :
FLEX_BTN_PRESS_REPEAT_CLICK);
target->status = FLEX_BTN_STAGE_DOWN;
target->scan_cnt = 0;
target->click_cnt = 0;
}
}
else if (target->scan_cnt >= target->long_hold_start_tick)
{
if (target->event != FLEX_BTN_PRESS_LONG_HOLD)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD);
}
}
else if (target->scan_cnt >= target->long_press_start_tick)
{
if (target->event != FLEX_BTN_PRESS_LONG_START)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_START);
}
}
else if (target->scan_cnt >= target->short_press_start_tick)
{
if (target->event != FLEX_BTN_PRESS_SHORT_START)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_START);
}
}
}
else
{
if (target->scan_cnt >= target->long_hold_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_HOLD_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else if (target->scan_cnt >= target->long_press_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_LONG_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else if (target->scan_cnt >= target->short_press_start_tick)
{
EVENT_SET_AND_EXEC_CB(target, FLEX_BTN_PRESS_SHORT_UP);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
else
{
target->status = FLEX_BTN_STAGE_MULTIPLE_CLICK;
target->click_cnt ++;
}
}
break;
case FLEX_BTN_STAGE_MULTIPLE_CLICK:
if (BTN_IS_PRESSED(i))
{
target->status = FLEX_BTN_STAGE_DOWN;
target->scan_cnt = 0;
}
else
{
if (target->scan_cnt > target->max_multiple_clicks_interval)
{
EVENT_SET_AND_EXEC_CB(target,
target->click_cnt < FLEX_BTN_PRESS_REPEAT_CLICK ?
target->click_cnt :
FLEX_BTN_PRESS_REPEAT_CLICK);
target->status = FLEX_BTN_STAGE_DEFAULT;
}
}
break;
}
if (target->status > FLEX_BTN_STAGE_DEFAULT)
{
active_btn_cnt ++;
}
}
return active_btn_cnt;
}
flex_button_event_t flex_button_event_read(flex_button_t* button)
{
return (flex_button_event_t)(button->event);
}
uint8_t flex_button_scan(void)
{
flex_button_read();
return flex_button_process();
}
ToolKit
ToolKit是一套应用于嵌入式系统的通用工具包,可灵活应用到有无RTOS的程序中,采用C语言面向对象的思路实现各个功能,尽可能最大化的复用代码,目前为止工具包包含:循环队列、软件定时器、事件集。
- Queue 循环队列
- 支持动态、静态方式进行队列的创建与删除。
- 可独立配置缓冲区大小。
- 支持数据最新保持功能,当配置此模式并且缓冲区已满,若有新的数据存入,将会移除最早数据,并保持缓冲区已满。
- Timer 软件定时器
- 支持动态、静态方式进行定时器的创建与删除。
- 支持循环、单次模式。
- 可配置有无超时回调函数。
- 可配置定时器工作在周期或间隔模式。
- 使用双向链表,超时统一管理,不会因为增加定时器而增加超时判断代码。
- Event 事件集
- 支持动态、静态方式进行事件集的创建与删除。
- 每个事件最大支持32个标志位。
- 事件的触发可配置为**“标志与”和“标志或”**。
2 、文件目录
toolkit
├── include // 包含文件目录
| ├── toolkit.h // toolkit头文件
| └── toolkit_cfg.h // toolkit配置文件
├── src // toolkit源码目录
| ├── tk_queue.c // 循环队列源码
| ├── tk_timer.c // 软件定时器源码
| └── tk_event.c // 事件集源码
├── samples // 例子
| ├── tk_queue_samples.c // 循环队列使用例程源码
| ├── tk_timer_samples.c // 软件定时器使用例程源码
| └── tk_event_samples.c // 事件集使用例程源码
└── README.md // 说明文档
git下载地址 博客下载地址
本篇文章编写的目的是为了记录个人在github上看到的优秀的代码模块,同时自己也是有在日常中使用,在这里分享给读者们,感谢github上大佬优秀的代码分享,在这里表示敬仰!!! 同时也希望阅读者有更好的资源代码可以交流区一起分享下啦!
|