ButtonDrive

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

源码地址:https://github.com/jiejieTop/ButtonDrive

前言

前几天写了个按键驱动,参考了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工程

关于rtkpgs

简介 (English)

buildpkg 是用于生成 RT-Thread package 的快速构建工具。

一个优秀的 package 应该是这样的:

  1. 代码优雅, 规范化。
  2. examples 例程,提供通俗易懂的使用例程。
  3. SConscript 文件,用于和 RT-Thread 环境一起进行编译。
  4. README.md 文档,向用户提供必要的功能说明。
  5. docs 文件夹, 放置除了 README 之外的其他细节文档。
  6. license 许可文件,版权说明。

为了方便快速的生成 RT-Thread package 规范化模板 以及 减轻开源仓库迁移 RT-Thread 的前期准备工作的负担,基于此目的的 buildpkg 应运而生,为开发 Rt-Thread 的 package 的开发者提供辅助开发工具。

序号 支持功能 描述
1 构建 package 模板 创建指定名称 package , 自动添加 readme /版本号/ github ci脚本/demo/开源协议文件
2 迁移开源仓库 从指定 git 仓库构建 package , 自动添加readme/版本号/ github ci脚本/demo/开源协议文件, 但是迁移的仓库需要用户自己按照实际情况修改
3 更新 package 生成package后可以再次更新之前设定的版本号,开源协议或者scons脚本等

使用说明

1. 构建package

buildpkg.exe make pkgdemo

2. 迁移开源仓库

buildpkg.exe make cstring https://github.com/liu2guang/cstring.git

3. 更新package

buildpkg.exe update pkgname

4. 可选配置

长参数 短参数 描述
--version=v1.0.0 -v v1.0.0 设置 package 的版本
--license=MIT -l MIT 设置 package 所遵循的版权协议
--submodule -s 删除 git 子模块

Windows10 及 Linux 平台的演示动图

测试平台

序号 测试平台 测试结果
1 win10 exe测试通过, py测试通过
2 win7 exe待测试, py待测试
3 mac py脚本不知道是否兼容, 没有测试条件, 后面维护下
4 linux py脚本不知道是否兼容, 没有测试条件, 后面维护下

联系人

纯C语言写的按键驱动,将按键逻辑与按键处理事件分离~的更多相关文章

  1. 不好意思啊,我上周到今天不到10天时间,用纯C语言写了一个小站!想拍砖的就赶紧拿出来拍啊

    花10天时间用C语言做了个小站 http://tieba.yunxunmi.com/index.html 简称: 云贴吧 不好意思啊,我上周到今天不到10天时间,用纯C语言写了一个小站!想拍砖的就赶紧 ...

  2. Linux按键驱动程序设计--从简单到不简单【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51399353 混杂设备驱动模型: 1. 混杂设备描述 在Linux系统中,存在一 ...

  3. Linux按键驱动程序设计详解---从简单到不简单【转】

    转自:http://blog.csdn.net/coding__madman/article/details/51399353 版权声明:本文为博主原创文章,未经博主允许不得转载. 混杂设备驱动模型: ...

  4. 异想家纯C语言矩阵运算库

    Sandeepin最近做的项目中需要在嵌入式芯片里跑一些算法,而这些单片机性能不上不下,它能跑些简单的程序,但又还没到上Linux系统的地步.所以只好用C语言写一些在高级语言里一个函数就解决的算法了, ...

  5. 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

    文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...

  6. 自己用C语言写NXP S32K116 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 每次我有了新的EVA ...

  7. 自己用C语言写dsPIC / PIC24 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...

  8. 自己用C语言写单片机PIC18 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...

  9. 自己用C语言写单片机PIC16 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 为什么自己写bootl ...

随机推荐

  1. jQuery - 03. each、prevaAll、nextAll、获取属性、修改属性attr/val/text()、jq.height/width、offset()./position()./scrol Left/Top 、事件绑定bind、delegate、on、事件解绑、事件对象、多库共存

    each 方法 $ ( selector).each(function( index,element) {  } );   参数一表示当前元素在所有匹配元素中的索引号 参数二表示当前元素(DOM对象) ...

  2. .Net Core WebApi简单创建及使用

    按照目前的软件开发发展趋势中,不管是前后端分离还是提供数据服务,WebApi使用的越来越广泛,而且.NET Core也是我们.NET开发人员未来发展的趋势,所以说学会使用.NET Core Api是非 ...

  3. Python基础:Python运行的两种基本方式

    完成Python的安装之后,我们可以开始编写Python代码以及运行Python程序了.我们来看一下运行Python具体有哪几种方式 1.REPL 所谓REPL即read.eva.print.loop ...

  4. Nginx实现高可用(了解)

    使用nginx实现反向代理和负载均衡时,nginx就是整个网站的入口了,所以需要保证nginx的高可用 主要资料包:链接:https://pan.baidu.com/s/1z_-xEM3uUICtZi ...

  5. 小白学习VUE第一篇文章---如何看懂网上搜索到的VUE代码或文章---使用VUE的三种模式:

    小白学习VUE第一篇文章---如何看懂网上搜索到的VUE代码或文章---使用VUE的三种模式: 直接引用VUE; 将vue.js下载到本地后本目录下使用; 安装Node环境下使用; ant-desig ...

  6. java架构之路-(源码)mybatis的一二级缓存问题

    上次博客我们说了mybatis的基本使用,我们还捎带提到一下Mapper.xml中的select标签的useCache属性,这个就是设置是否存入二级缓存的. 回到我们正题,经常使用mybatis的小伙 ...

  7. Python中使用pip安装库时指定镜像源为豆瓣镜像源

    场景 在使用pip进行安装库时,使用默认的库会很慢,甚至有时会出现远程主机中断了一个现有连接. 怎样在使用pip install 时指定镜像源为豆瓣镜像源. 实现 pip install moviep ...

  8. hadoop高可用安装和原理详解

    本篇主要从hdfs的namenode和resourcemanager的高可用进行安装和原理的阐述. 一.HA安装 1.基本环境准备 1.1.1.centos7虚拟机安装,详情见VMware安装Cent ...

  9. 001:photoshop教程

    1:添加辅助线:有个位移的坐标系图标:点击标志中,鼠标按着不动.之后拖动到对应的位置. 2:量距离:第一行.第二个图标.直接测量像素. 3:切割图片: 3.1:第三行.第一列:选择切片工具 3.2:之 ...

  10. 05:videoToolbox:硬解码

    videoToolbox:硬解码 前言:VTDecompressionSession 工作流程: 1:创建解压的会话. 2:配置会话属性. 3:解压视频帧数据. 4:释放会话.释放资源. 介绍  VT ...