button drive

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

前言

前几天写了个按键驱动,参考了MulitButton的数据结构的用法,逻辑实现并不一样。

在这里感谢所有的开源开发者,让我从中学到了很多,同时网络也是一个好平台,也希望所有的开发者能形成良性循环,从网络中学知识,回馈到网络中去。感谢MulitButton的作者0x1abin,感谢两位rtt的大佬:大法师、流光。

Button_drive简介

Button_drive是一个小巧的按键驱动,支持单击、双击、长按、连续触发等(后续可以在按键控制块中添加触发事件),理论上可无限量扩展Button,Button_drive采用按键触发事件回调方式处理业务逻辑,支持在RTOS中使用,我目前仅在RT-Thread上测试过。

写按键驱动的目的是想要将用户按键逻辑与按键处理事件分离,用户无需处理复杂麻烦的逻辑事件。

Button_drive使用效果

单击与长按

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2nhPnwqF-1571148176402)(https://github.com/jiejieTop/...]

双击

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtGvjTjq-1571148176403)(https://github.com/jiejieTop/...]

连按

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QUypOh2s-1571148176403)(https://github.com/jiejieTop/...]

连按释放

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LfsbuM1W-1571148176404)(https://github.com/jiejieTop/...]

使用方法

创建按键句柄

Button_t Button1;

Button_t Button2;

创建按键,初始化按键信息,包括按键名字、按键电平检测函数接口、按键触发电平。

Button_Create("Button1", //按键名字

&Button1, //按键句柄

Read_Button1_Level, //按键电平检测函数接口

BTN_TRIGGER); //触发电平

......

按键触发事件与事件回调函数链接映射,当按键事件被触发的时候,自动跳转回调函数中处理业务逻辑。

Button_Attach(&Button1,BUTTON_DOWM,Btn2_Dowm_CallBack); //按键单击

Button_Attach(&Button1,BUTTON_DOUBLE,Btn2_Double_CallBack); //双击

Button_Attach(&Button1,BUTTON_LONG,Btn2_Long_CallBack); //长按

.......

周期调用回调按键处理函数即可,建议调用周期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中直接配置使用!

步骤如下:

选择在线软件包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gG59d3Yn-1571148176404)(https://github.com/jiejieTop/...]

选择软件包属性为外设相关

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amGYFH8P-1571148176404)(https://github.com/jiejieTop/...]

选择button_drive

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O0a0wqdi-1571148176404)(https://github.com/jiejieTop/...]

进入驱动的选项配置(自带默认属性)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O5sGP0CE-1571148176405)(https://github.com/jiejieTop/...]

如果不懂按键的配置是什么意思,按下“shift+?”,即可有解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EYxByBVm-1571148176407)(https://github.com/jiejieTop/...]

编译生成mdk/iar工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e0pdTFhz-1571148176407)(https://github.com/jiejieTop/...]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXNX8VeJ-1571148176407)(https://github.com/jiejieTop/...]

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

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

代码优雅, 规范化。

examples 例程,提供通俗易懂的使用例程。

SConscript 文件,用于和 RT-Thread 环境一起进行编译。

README.md 文档,向用户提供必要的功能说明。

docs 文件夹, 放置除了 README 之外的其他细节文档。

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. 迁移开源仓库

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 平台的演示动图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CT6Hxh28-1571148176408)(/figures/buildpkg.gif)]

测试平台

序号

测试平台

测试结果

1

win10

exe测试通过, py测试通过

2

win7

exe待测试, py待测试

3

mac

py脚本不知道是否兼容, 没有测试条件, 后面维护下

4

linux

py脚本不知道是否兼容, 没有测试条件, 后面维护下

联系人

喜欢就关注我吧!

相关代码可以在公众号后台获取。

欢迎关注“物联网IoT开发”公众号

c语言自动按键脚本,纯C语言写的按键驱动,将按键逻辑与按键处理事件分离~相关推荐

  1. c语言平曲线,算法——纯C语言最小二乘法曲线拟合

    算法--纯C语言最小二乘法曲线拟合 写完,还没来得及写注释,已通过Matlab的polyfit验证(阶数高或者数据量太大会有double数据溢出的危险,低阶的都吻合),时间有点紧,程序注释,数学推导等 ...

  2. linux下c语言聊天室程序,纯C语言Socket实现聊天室

    最近在学习嵌入式开发,练习C语言小项目,基本是参考别人的代码,做了些修改实现了聊天室,纯C语言编写. 想直接看源码的跳到最后. 一.练习内容 socket通信流程 管道的使用 epoll的使用 首先要 ...

  3. c语言自动创建的main,c语言main.c文件简单解析

    屏幕快照 2016-05-29 15.40.53.png 下面我们针对上述代码做一个简单的解析 ========================= 一.#include 1.#include 是c语言 ...

  4. python传奇自动打怪脚本_易语言传奇sf自动打怪脚本源码

    從目前的傳奇推出的新地圖來看,闖赤月殺蜘蛛還是很刺激的.特別是像我這樣的老玩家,在傳奇中混了很長一段時間了,殺一些簡單的怪,已經很乏味了.好不容易出了新地圖,總不可能放過吧,但大家也別小看了這些蜘蛛哦 ...

  5. c语言 自动包含头文件,C语言不要重复包含.h头文件和.c文件

    http://blog.csdn.net/unix21/article/details/8450235 2012 1.不要重复包含头文件 --以上出自<C语言程序设计:现代方法(第2版)> ...

  6. 易语言执行linux脚本,初识易语言到编写SHELL生成器

    下面开始实际的代码编写步骤.首先要先插入几个常量用来存放SHELL代码,点击插入按钮-选择常量(图15),右键选择插入新长文本常量(图16),常量名称改为一个方便易记的名字,最好是SHELL类型或者是 ...

  7. c语言自动取数函数,C语言中取随机数的函数

    匿名用户 1级 2009-08-11 回答 1. 基本函数 在C语言中取随机数所需要的函数是: int rand(void); void srand (unsigned int n); rand()函 ...

  8. c语言自动生成球队名称,C语言实现队(循环队列)

    之前用链表实现队,这里用数组实循环队列,今天又看了一下队列,发现少了计算队列长度的函数 队列的实现代码,牺牲一个元素存储空间 #include#define maxsize 10 typedef in ...

  9. c语言 自动计时的秒表,c语言实现的简单秒表计时器

    #include #include #include #include struct tm //定义时间结构体,包括时分秒和10毫秒 { int hours,minutes,seconds; int ...

  10. c语言 自动dump 函数,(转)写个dump_stack

    简单实现dump_stack 0.首先确保你能写个内核模块:打印"hello kernel" 如果熟悉dump_stack的话,完全可以绕开此文,或者自己去看dump_stack代 ...

最新文章

  1. Single Shot Multibox Detection (SSD)实战(下)
  2. MySQL的安装过程
  3. java寄存器_汇编学习 1 寄存器的作用 寻址方式 - DraculaW - JavaEye技术网站
  4. 编译cscope-15.8a遇到的问题与解决方案
  5. mp4 拍摄时间如何看_时间不多了,如何备考期末最有效?这些复习技巧,看了你就会了...
  6. 嵌入式Linux安装Python环境,linux环境下安装python 3
  7. Maven本地库添加Oracle JDBC驱动
  8. 大厂面试:一个四年多经验程序员的BAT面经
  9. 【转】Windows系统中ckplayer视频边下边放,视频转码mp4及last atom in file was not a moov atom问题...
  10. Python 资源库大全
  11. 技术圈儿002---高并发整体可用性:一文详解降级、限流和熔断
  12. Python 最抢手、Java 最流行、Go 最有前途,7000 位程序员揭秘 2019 软件开发现状...
  13. java垃圾回收机制简介
  14. 建筑结构抗震设计新技术
  15. 如何助力鸿蒙发展,实话实说:华为以一已之力,能不能推动鸿蒙系统的发展 - 区块网...
  16. jdon的设计模式详细解读
  17. Android Studio 更换个性化主题
  18. excel去除小数点后面的数据,将数字取整
  19. 利好消息再释放 重卡自动驾驶的智慧物流“掘金”计划
  20. 【Spring】IDEA中创建Spring项目

热门文章

  1. FlashFXP,flashfxp安装
  2. 如何选择适合你的兴趣爱好(四十六),豫剧
  3. linux源码编译ipk,Ubuntu亲自手动编译Openwrt (DreamBox): for njit....ipk
  4. qq视频转码失败怎么办_迅捷视频转换器转换失败的解决方法
  5. 偶然接触oracle中的变量
  6. matlab gui制作,MATLAB GUI制作教程
  7. Android App图标尺寸
  8. android自定义sidebar,Android仿微信通讯录列表侧边栏效果
  9. 学术蓝答辩PPT模板
  10. 保姆级透明背景动画导出 —— json格式(Mac系统)