
本帖最后由 murphyzhao 于 2018-12-27 13:50 编辑 + [0 `3 A( x3 S; d# k7 ?4 Q _0 B8 u/ h- [+ \ ## 前言 正好工作中用到按键处理,需要处理单击、长按等按键事件,然后就造了这么一个轮子,为了以后更方便地加入其它的项目中使用,遂将其开源到 GitHub 中。 9 W. v' m- O: H ?! F 后面发现 RT-Thread 软件包里也有一个开源的按键库 [MultiButton](http://github.com/liu2guang/MultiButton),看到这个按键库的时候,心想,完了,又重复造轮子了,好伤心 :joy:。想想,既然按键处理方式不同,而且时间已经花出去了,那就把轮子圆一圆,放到 GitHub 中,给有缘人用吧,然后就有了这个不太圆的轮子 [FlexibleButton](http://github.com/zhaojuntao/FlexibleButton)。 ## 概述 FlexibleButton 是一个基于 C 语言的小巧灵活的按键处理库。该按键库解耦了具体的按键硬件结构,理论上支持轻触按键与自锁按键,并可以无限扩展按键数量。另外,FlexibleButton 使用扫描的方式一次性读取所有所有的按键状态,然后通过事件回调机制上报按键事件。/ x- F( p" a% p6 \; R/ |# h1 L& F . d( x9 y: L" w 该按键库使用 C 语言编写,驱动与应用程序解耦,便于灵活应用,比如用户可以方便地在应用层增加按键中断、处理按键功耗、定义按键事件处理方式,而无需修改 FlexibleButton 库中的代码。 另外,使用 C 语言标准库 API 编写,也使得该按键库可以无缝兼容任意的处理器平台,并且支持任意 OS 和 non-OS(裸机程序)。2 }! A) B" _" n5 q4 d $ R5 ?, @: R; W% u2 _$ k 另外,该按键库核心的按键扫描代码仅有三行,没错,就是经典的**三行按键扫描算法**,出自哪位大神之手就无从得知了,也欢迎知道此高人的读者文后留言哈。算法介绍可以去搜索引擎里查找,这里就不作介绍了。 5 n% i0 p9 Y" B$ Z0 G/ \3 ~7 X ## 获取' o7 A% R- r# ^9 A& n( O1 p ```SHELL git clone http://github.com/zhaojuntao/FlexibleButton.git ``` - R9 K0 u& T) X3 s M ## 测试 FlexibleButton 库中提供了一个 DEMO `flexible_button_demo.c`,这里基于 RT-Thread OS 进行测试,当然你可以选择使用其他的 OS,或者使用裸机测试,只需要移除 OS 相关的特性即可。) ?' t6 Y# O& }* L& Z 将 FlexibleButton 库放到 RT-Thread BSP 下即可使用 scons 进行编译构建。+ C/ i. k9 I" W4 ~1 y 0 p; X0 H/ J# R! X( C" @* w ## DEMO 程序说明 ### 程序入口 ! s$ i/ w( w7 g7 m8 R, U. e2 _. C0 @ ```C- l# t5 s8 ~* t& a' P0 y int flex_button_main(void)' J7 c' N z! ?5 |8 O8 G6 o) X8 }) q {- H2 c2 c- L E H rt_thread_t tid = RT_NULL; user_button_init();# f$ @; k5 z J- |9 Z; |7 A2 K /* Create background ticks thread */ tid = rt_thread_create("flex_btn", button_scan, RT_NULL, 1024, 10, 10); if(tid != RT_NULL)$ P7 m7 c8 m W5 J. e3 Z { rt_thread_startup(tid); }& |% @& V/ G" x! ?4 g return 0;( I9 E# B3 V9 I; |. f }/ ]& j+ n+ P; r; K4 v& U5 W INIT_APP_EXPORT(flex_button_main); ```9 l( w, Y5 D! P7 _# e$ E 如上所示,首先使用 `user_button_init();` 初始化用户按键硬件,并挂载到 FlexibleButton库。然后,使用了 RT-Thread 的 `INIT_APP_EXPORT` 接口导出为上电自动初始化,创建了一个 “flex_btn” 名字的按键扫描线程,线程里扫描检查按键事件。 # `% ~; y1 E2 S B& _4 D ### 用户初始化代码 & ~, X3 Q @$ i" n2 w/ z `user_button_init();` 初始化代码如下所示: 2 x7 o6 P2 i) S: E' w& h; N ```C static void user_button_init(void): G1 X4 \1 f- T7 F { int i; rt_memset(&user_button[0], 0x0, sizeof(user_button));+ [7 A9 e! T$ N4 M user_button[USER_BUTTON_0].usr_button_read = button_key0_read;, X8 L. e: I( R8 _) _% k user_button[USER_BUTTON_0].cb = (flex_button_response_callback)btn_0_cb;) k; k& x. v" D user_button[USER_BUTTON_1].usr_button_read = button_key1_read; user_button[USER_BUTTON_1].cb = (flex_button_response_callback)btn_1_cb; user_button[USER_BUTTON_2].usr_button_read = button_key2_read;* I) l; K4 u! X {9 ?: @4 L user_button[USER_BUTTON_3].usr_button_read = button_keywkup_read; rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT); /* set KEY pin mode to input */ rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT); /* set KEY pin mode to input */ rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT); /* set KEY pin mode to input */( g o7 ^/ i+ P; K rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT); /* set KEY pin mode to input */ for (i = 0; i < USER_BUTTON_MAX; i ++) {! t5 i: g% c- k i2 q user_button.status = 0; user_button.pressed_logic_level = 0;" H: a* q$ h7 r+ Z" i user_button.click_start_tick = 20;( N; |% R6 b. ^; S! C* _+ m user_button.short_press_start_tick = 100;1 {- V) R; D$ U. ` user_button.long_press_start_tick = 200;: T# u1 u" ` s, H+ E user_button.long_hold_start_tick = 300;$ q6 d5 [! Y: r/ l4 [ if (i == USER_BUTTON_3) {" Q2 i8 m' ?- Y$ Y2 T- [0 c2 h user_button[USER_BUTTON_3].pressed_logic_level = 1;) X2 H3 d0 X$ B3 L( z9 z% \ } flex_button_register(&user_button);% j, o9 u$ O1 F6 x- M }% X5 V3 g) e8 R) f: C p- a } ```1 E% o: t" o5 ?" p$ N2 |: b% @& V `user_button_init();` 主要用于初始化按键硬件,设置按键长按和短按的 tick 数(RT-Thread上面默认一个 tick 为 1ms),然后注册按键到 FlexibleButton 库。+ X! B t$ e" x* c- h% b* O. v ### 事件处理代码 ```C+ Z/ K6 [0 `9 h4 Z' a, _( w9 S$ \7 g& z static void btn_0_cb(flex_button_t *btn) {5 g2 h ^' z2 }; `) a% O' ]; J rt_kprintf("btn_0_cb\n");9 _5 c. }" h7 A. C4 J$ U% @ switch (btn->event); U$ ]( C/ |7 B3 F( H; f5 ` { case FLEX_BTN_PRESS_DOWN: rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_DOWN]\n");' \( J8 c0 O; Q& \+ L6 j break;2 ^7 W0 C( t- c f* x9 U" D case FLEX_BTN_PRESS_CLICK: rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_CLICK]\n"); break;. M' b m8 c, o1 E; D case FLEX_BTN_PRESS_DOUBLE_CLICK: rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_DOUBLE_CLICK]\n");" C7 h7 G; k; P' b break;" \) Y( o b" g+ I0 L; ^1 l case FLEX_BTN_PRESS_SHORT_START: rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_SHORT_START]\n"); break;3 y: o7 `$ e. i* q' Z case FLEX_BTN_PRESS_SHORT_UP: rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_SHORT_UP]\n"); break; case FLEX_BTN_PRESS_LONG_START:. r) P% I+ ~% a8 R1 k1 I0 n$ m, e/ W5 m; _ rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_START]\n"); break;. H2 g- M4 i0 u2 {: L case FLEX_BTN_PRESS_LONG_UP:8 b3 U5 c$ r4 q' a" w rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_UP]\n"); break; case FLEX_BTN_PRESS_LONG_HOLD:5 Q2 M x0 }! F# R1 S6 x4 m* B rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_HOLD]\n");' m a! E$ ?) c8 t break; case FLEX_BTN_PRESS_LONG_HOLD_UP:9 p# r/ d6 P( H; d" B rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_HOLD_UP]\n"); h6 Y% ]! r. X9 Q) _ break; } } ``` ; K3 w+ t* c* @5 H" m ## FlexibleButton 代码说明5 P, ?* X6 B5 ?( q! r- O; j 5 Z- v2 H [ V6 m1 Y: v ### 按键事件定义' J# v' L1 }: v ~& P " `9 U1 \& U- N+ s0 y5 X) \5 \! k 按键事件的定义并没有使用 Windows 驱动上的定义,主要是方便嵌入式设备中的应用场景(也可能是我理解的偏差),按键事件定义如下:; Y6 k: M+ n% w* |' z4 d# V , u7 S+ I; R# \+ G$ _2 V+ K9 A ```C0 z6 Q0 H/ P3 i2 D- {$ J, q typedef enum { FLEX_BTN_PRESS_DOWN = 0, // 按下事件& W2 D* H3 g$ w- s) \ FLEX_BTN_PRESS_CLICK, // 单击事件. V, M9 `' A% g; D( c" }9 n FLEX_BTN_PRESS_DOUBLE_CLICK, // 双击事件 FLEX_BTN_PRESS_SHORT_START, // 短按开始事件 FLEX_BTN_PRESS_SHORT_UP, // 短按抬起事件 FLEX_BTN_PRESS_LONG_START, // 长按开始事件' }8 a2 T; e( v& x" I0 F FLEX_BTN_PRESS_LONG_UP, // 长按抬起事件 FLEX_BTN_PRESS_LONG_HOLD, // 长按保持事件 FLEX_BTN_PRESS_LONG_HOLD_UP, // 长按保持的抬起事件( C' t2 f: K+ f9 n" F5 _ FLEX_BTN_PRESS_MAX, FLEX_BTN_PRESS_NONE, } flex_button_event_t; ```6 {4 ]4 K ?6 g. ]5 P, m8 H ### 按键注册接口 + Q0 S5 b( m# H2 y" _: [ 使用该接口注册一个用户按键,入参为一个 flex_button_t 结构体实例的地址。 ; y. h, g" u: I. f$ p" ` ```C/ m$ o3 P; Z, u, X+ H8 `1 z int8_t flex_button_register(flex_button_t *button); ```8 {3 y+ A1 V2 n0 v) r ; x! V: g, a: T: B4 D ### 按键事件读取接口 $ A+ S P0 N' K l% g/ R! z 使用该接口获取指定按键的事件。 ```C flex_button_event_t flex_button_event_read(flex_button_t* button); ```` x; r. O' q# U- H1 k M( o/ `3 A& ?5 v ### 按键扫描接口% B" G6 X# a, P4 M+ m " l W! i# E0 ^ 按键扫描的核心函数,需要放到应用程序中定时扫描间隔 5-20ms 即可。# T8 I0 Z- P: `, H ```C void flex_button_scan(void);2 {( \, U# D% O2 j- w ``` ## 问题和建议; s- O+ O; V5 \5 N+ M; a8 y ~. h9 a1 M1 ^$ L6 Q! r+ W; z* s, H 如果您在应用的时候遇到了问题,或者有好的想法和建议,欢迎到这个 [issue](http://github.com/zhaojuntao/FlexibleButton/issues/1) 上讨论,谢谢。 |
编辑器不支持 markdown 格式![]() |