你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

灵活的按键处理程序 FlexibleButton

[复制链接]
murphyzhao 发布时间:2018-12-27 13:49
本帖最后由 murphyzhao 于 2018-12-27 13:50 编辑 9 O4 C# Y) q- F; p% i0 }- \
& \  O5 u. x/ Q1 C' _' W! F& p* R
## 前言/ K# t* h% r9 m$ |' s. n# q7 |

' B& S9 t% c1 P  i5 \正好工作中用到按键处理,需要处理单击、长按等按键事件,然后就造了这么一个轮子,为了以后更方便地加入其它的项目中使用,遂将其开源到 GitHub 中。
; f+ G2 ?( ~5 [& Z6 K7 O: N# K# h- Q
后面发现 RT-Thread 软件包里也有一个开源的按键库 [MultiButton](http://github.com/liu2guang/MultiButton),看到这个按键库的时候,心想,完了,又重复造轮子了,好伤心 :joy:。想想,既然按键处理方式不同,而且时间已经花出去了,那就把轮子圆一圆,放到 GitHub 中,给有缘人用吧,然后就有了这个不太圆的轮子 [FlexibleButton](http://github.com/zhaojuntao/FlexibleButton)。; P- F5 E$ s7 M6 T% Q+ f. c  j

6 E7 Q' u) \( j& h, [9 ~+ T## 概述  K3 _# e: S- N/ j! S0 E
$ H) ?( y/ U/ C- G# B& G, N1 o
FlexibleButton 是一个基于 C 语言的小巧灵活的按键处理库。该按键库解耦了具体的按键硬件结构,理论上支持轻触按键与自锁按键,并可以无限扩展按键数量。另外,FlexibleButton 使用扫描的方式一次性读取所有所有的按键状态,然后通过事件回调机制上报按键事件。! B/ n4 Y! j) e, q" v3 U+ @& G
& N8 B1 z3 c3 q
该按键库使用 C 语言编写,驱动与应用程序解耦,便于灵活应用,比如用户可以方便地在应用层增加按键中断、处理按键功耗、定义按键事件处理方式,而无需修改 FlexibleButton 库中的代码。; Z/ A) R: }2 i. i8 l
& H2 D2 g( ^! N0 H7 u' z! U
另外,使用 C 语言标准库 API 编写,也使得该按键库可以无缝兼容任意的处理器平台,并且支持任意 OS 和 non-OS(裸机程序)。5 ?. F3 h" b& Z* b
+ |7 P$ |7 W! h5 T% T5 H6 J/ |5 A
另外,该按键库核心的按键扫描代码仅有三行,没错,就是经典的**三行按键扫描算法**,出自哪位大神之手就无从得知了,也欢迎知道此高人的读者文后留言哈。算法介绍可以去搜索引擎里查找,这里就不作介绍了。9 l2 S0 J/ `2 |# m5 X, E# J

8 A$ H) V4 Q0 b2 A## 获取
% y1 q/ v' l& z( {" c0 |) ?2 E( n- L
```SHELL# b; G/ G* m# |) z/ c
git clone http://github.com/zhaojuntao/FlexibleButton.git: T/ a  u8 ?8 W1 N& m; m# X
```. p$ K* J& H* {5 K5 v: t  F
7 w$ Y$ L: j- }4 h" \' k8 o$ s
## 测试1 R" e# C% [  a7 I7 y# d
/ Z7 @1 j1 `4 k) I6 q9 ^: Y
FlexibleButton 库中提供了一个 DEMO `flexible_button_demo.c`,这里基于 RT-Thread OS 进行测试,当然你可以选择使用其他的 OS,或者使用裸机测试,只需要移除 OS 相关的特性即可。
" r- Z, ~" Q" V$ j2 u5 C! [
' `/ P; I( F6 l: F3 F6 R- a将 FlexibleButton 库放到 RT-Thread BSP 下即可使用 scons 进行编译构建。; _6 k1 s) o! }( |7 v$ U- r

. @% T$ b* {  r  L## DEMO 程序说明' x- F" s( _" {0 @0 F$ e  X

$ ^, M9 M( p$ ]: x### 程序入口, p, A1 A) B. g6 Y1 J- X" h

& W2 z1 n3 X. o( ~```C/ ~. {; V0 z8 D; F
int flex_button_main(void)" a+ c8 M. r- H' e4 U
{
$ j2 Y! s* r- k6 X- I4 G4 o$ I    rt_thread_t tid = RT_NULL;
7 n$ W# l: I+ V    user_button_init();( m' L, ~; t4 ~4 p! s6 z* Y% n: C
    /* Create background ticks thread */
+ A2 {8 [$ r1 B, {# H    tid = rt_thread_create("flex_btn", button_scan, RT_NULL, 1024, 10, 10);
& u2 H1 ~* Z* I% D7 s4 j2 p, `    if(tid != RT_NULL)
( b2 Q2 [/ x7 a" ]. w+ J4 d6 S    {4 H0 R' \( D  b8 p$ J
        rt_thread_startup(tid);
/ f' I/ R$ W. O. W. P2 F0 I& m    }
8 x- Z' I& H. w% w    return 0;1 [) z) ]( T6 S7 l: u
}
; S! o4 \1 F1 g9 h6 p. @INIT_APP_EXPORT(flex_button_main);
* i: m) T1 K* u7 H( c( K% @; \```
# l( j3 b: P5 l8 f3 c8 H2 Q. \- J$ f9 x0 F* ~
如上所示,首先使用 `user_button_init();` 初始化用户按键硬件,并挂载到 FlexibleButton库。然后,使用了 RT-Thread 的 `INIT_APP_EXPORT` 接口导出为上电自动初始化,创建了一个 “flex_btn” 名字的按键扫描线程,线程里扫描检查按键事件。, {7 X+ z( p% E. \- z
3 M6 Q0 ~* V4 w
### 用户初始化代码
8 R3 e4 W/ T5 r, ?* R/ Z+ W- z& A+ `4 w9 C9 ]; \
`user_button_init();` 初始化代码如下所示:4 m' _- U. X+ D' P2 g
9 p2 D) ~: l& ~) g6 t5 H5 A
```C9 m4 Y* B  h* j2 ^
static void user_button_init(void)* V0 l. V9 i& i& a) V5 c7 j" l
{2 u% R3 f3 u% j. M; z1 t& n
    int i;! H4 O$ ~" m) i3 }' b# c8 e0 z, d
    rt_memset(&user_button[0], 0x0, sizeof(user_button));9 d) k" _+ R+ r2 A# c
    user_button[USER_BUTTON_0].usr_button_read = button_key0_read;$ Q% y, k* k2 M) X9 p4 {9 U( O2 w# j" y
    user_button[USER_BUTTON_0].cb = (flex_button_response_callback)btn_0_cb;
0 D$ R! |0 e! U    user_button[USER_BUTTON_1].usr_button_read = button_key1_read;
/ H$ G9 A0 m9 e3 N- K8 t) H    user_button[USER_BUTTON_1].cb = (flex_button_response_callback)btn_1_cb;1 n: F. X0 u% A2 C0 J  x* {. @
    user_button[USER_BUTTON_2].usr_button_read = button_key2_read;
: n8 M( R9 w, k4 X    user_button[USER_BUTTON_3].usr_button_read = button_keywkup_read;
  m) d% a" i8 t5 f2 Q5 v    rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT); /* set KEY pin mode to input */1 c- w9 H. s8 R" ?) L  e
    rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT); /* set KEY pin mode to input */
- b4 P0 p/ U( `    rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT); /* set KEY pin mode to input */
3 f# P8 \- n! A5 y' w3 N    rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT); /* set KEY pin mode to input */0 [5 _' Z- g  K% ~9 m
    for (i = 0; i < USER_BUTTON_MAX; i ++)" K! h/ M0 M" ^) f- c7 @1 v
    {
: j) V" v7 U; e4 u        user_button.status = 0;
% S( @: F6 k6 B4 A0 K1 e8 J9 j        user_button.pressed_logic_level = 0;
/ J$ \! F) ~  g. `# F$ ?5 \) ^+ v        user_button.click_start_tick = 20;
% G9 b* |" W3 w/ W. V8 T3 Z+ [* h$ P        user_button.short_press_start_tick = 100;
: d" b3 v* z4 F( F) x! ?        user_button.long_press_start_tick = 200;
0 ]' a, j% S& Y0 Y" j+ ?        user_button.long_hold_start_tick = 300;
2 _6 l6 f- L0 p0 \0 E" |# q" J        if (i == USER_BUTTON_3)+ q$ `, A2 W9 K# }8 W
        {
5 E; H7 l3 ]! V; d            user_button[USER_BUTTON_3].pressed_logic_level = 1;
8 ?( ?3 \6 r, h' }3 x        }: u+ E% q- u1 ?5 N9 v8 G- P
        flex_button_register(&user_button);" M/ [$ K/ r5 o  ?. E% k; G3 M
    }5 a, ]# T' n# V1 G; J
}
0 K) x0 V8 E4 a: @6 H! |' X```9 a. \0 k% z9 J$ y& C: o

9 d+ r' \/ b) s4 t- }7 j! Q9 J4 z `user_button_init();` 主要用于初始化按键硬件,设置按键长按和短按的 tick 数(RT-Thread上面默认一个 tick 为 1ms),然后注册按键到 FlexibleButton 库。
4 K6 u  S9 S/ j8 x4 ?; `* X5 r& M; e. u# [  e0 [
### 事件处理代码1 v! q$ W+ i# ~9 B/ k
/ _% Z0 R' s/ O" `
```C/ E3 ]* Y4 a5 v" M' ~
static void btn_0_cb(flex_button_t *btn)
& p& x: U7 u+ E( i1 l9 R9 e{- H$ N0 d" z2 \' O* h  h
    rt_kprintf("btn_0_cb\n");
0 O% B$ C9 R% g" x& Q; J. t    switch (btn->event)
% r; a: B  T5 f+ g    {! O7 o7 u5 i5 b  B6 W
        case FLEX_BTN_PRESS_DOWN:
( e/ E  f6 s/ F; m$ i6 n            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_DOWN]\n");6 i4 ~: d$ k* w0 o) Y2 w
            break;
+ L- k) z' j  ~  C7 @        case FLEX_BTN_PRESS_CLICK:
5 {: S( D) W5 j0 {; x: c            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_CLICK]\n");% b2 X: n0 L! c* X4 P' h
            break;' h+ f2 d& u/ h( t8 I6 Q
        case FLEX_BTN_PRESS_DOUBLE_CLICK:  l5 c0 {7 i" D2 v
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_DOUBLE_CLICK]\n");- }* {9 x! p3 U! A4 D6 k
            break;5 J) x2 F' |+ b
        case FLEX_BTN_PRESS_SHORT_START:' L( X, {- E5 B0 C0 v
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_SHORT_START]\n");
) y6 t' l* Q" H2 n8 x' h% T7 V            break;$ o" }9 @/ z! a1 c6 o9 x2 @
        case FLEX_BTN_PRESS_SHORT_UP:+ P+ g# {" N4 @8 ^7 H2 p
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_SHORT_UP]\n");  v9 u, F3 w3 f- F( o
            break;
& A5 N7 I7 s2 q& _, k& l        case FLEX_BTN_PRESS_LONG_START:4 F9 G. c8 b0 I( [6 I
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_START]\n");
+ x3 q# |0 c! N) W- q! E  Y            break;6 V+ K, j' i! j8 p% m
        case FLEX_BTN_PRESS_LONG_UP:0 T/ N+ ^/ w" \+ n0 g
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_UP]\n");: ~4 J. _0 K6 ^; r7 X4 o1 |7 O  n7 [( X
            break;' R( ]4 X8 B7 r4 G0 o
        case FLEX_BTN_PRESS_LONG_HOLD:# \4 v# w3 t  }% t  k0 i7 L0 D6 E  v
            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_HOLD]\n");
5 J5 ^$ B2 F. f% V. E: L' z+ f            break;0 J0 T8 B* S, o2 r
        case FLEX_BTN_PRESS_LONG_HOLD_UP:
, f" K4 k" E* Y; |( l' o            rt_kprintf("btn_0_cb [FLEX_BTN_PRESS_LONG_HOLD_UP]\n");% Y' n  [+ B. ]5 ?$ i: d
            break;
( M; [/ B( A9 W0 B0 J    }
5 g" p" D. P  \* w$ P: b}% J, ~$ u8 d" @: ^6 k1 Y$ u
```
& R$ ]4 e! T3 \% d, L7 W, X, t, O& i2 l& o
## FlexibleButton 代码说明; B" H1 g3 Y% Y" p% @! B( B2 F
/ F3 H4 b- G2 F9 }' d$ C) n& U& s
### 按键事件定义
) W6 J* H5 m# }7 d$ ]5 a. t% {  ~# R' B
按键事件的定义并没有使用 Windows 驱动上的定义,主要是方便嵌入式设备中的应用场景(也可能是我理解的偏差),按键事件定义如下:
! ^8 b0 U- q7 P" G% {$ e/ z& R. V) u7 v9 D& X1 x# u
```C* {. T% Q7 B% t
typedef enum
* A6 O/ o# _9 H8 N" R{4 }3 S' i/ _5 T/ k2 E1 q
    FLEX_BTN_PRESS_DOWN = 0,        // 按下事件3 w1 p1 k4 v+ z% \( ^
    FLEX_BTN_PRESS_CLICK,           // 单击事件
- Q/ H0 h. _7 i; x  z% a! |    FLEX_BTN_PRESS_DOUBLE_CLICK,    // 双击事件' G6 ?9 j6 S& p2 m8 ^
    FLEX_BTN_PRESS_SHORT_START,     // 短按开始事件
0 G$ C6 B3 i5 W/ H+ b7 _( F  u    FLEX_BTN_PRESS_SHORT_UP,        // 短按抬起事件
: S) I& D* ^+ ~* w5 X0 z( m    FLEX_BTN_PRESS_LONG_START,      // 长按开始事件; P' x' t7 K$ i3 C1 ?, b6 O% p
    FLEX_BTN_PRESS_LONG_UP,         // 长按抬起事件
/ u4 x( F4 ^! n1 R, ]# N5 ~- v& ~8 Y    FLEX_BTN_PRESS_LONG_HOLD,       // 长按保持事件$ ^' `. u3 j, S+ b# @
    FLEX_BTN_PRESS_LONG_HOLD_UP,    // 长按保持的抬起事件- S+ `: @, a2 l6 Y" V# w) A! ]
    FLEX_BTN_PRESS_MAX,  x, v) p, a: b. z
    FLEX_BTN_PRESS_NONE,) j  p" d" T+ m- d4 Y" W
} flex_button_event_t;! j( i) G, C( i0 b- E
```
8 e  v7 p, V5 H. e4 L0 {) s* O* N
1 E' @+ \5 s2 x6 ]$ d0 l### 按键注册接口
. X$ v& M4 E% `. q( p; ~* @% B( G* X1 Z; T2 ]
使用该接口注册一个用户按键,入参为一个 flex_button_t 结构体实例的地址。
! i- t, ]- r4 A) _8 I* l; O' N$ q. w
```C+ D. Z6 q% ^8 C$ Y% h8 c
int8_t flex_button_register(flex_button_t *button);
7 l4 U. p) ?# M- Z```
. M& C4 ~- M- c- B( F. D
# L  A+ i% I9 [### 按键事件读取接口0 j; A# k5 n9 Y# O6 W4 M
0 ]2 N1 Q9 c' v2 I  t8 j  h; h
使用该接口获取指定按键的事件。/ r5 x, A, d. i, q% C" v0 ?

& f5 S5 t5 Z5 {  }```C
& n/ Z" Y3 S7 J/ S- t* Eflex_button_event_t flex_button_event_read(flex_button_t* button);# p! L* ~, V$ [- ]" L
````8 q; |8 \- D2 h) L7 E
# k: a7 j8 V% U9 D, {
### 按键扫描接口
) A5 |' z, R% \7 [: _- g! i9 a. @; Z2 l
按键扫描的核心函数,需要放到应用程序中定时扫描间隔 5-20ms 即可。$ Y2 c3 {5 K, u* q# L

( }! j6 G- B2 g: l. N: g6 G```C& z7 q6 e: `# H2 Z8 Y+ H! R
void flex_button_scan(void);& p( y% U. f4 p% |7 e9 E# X6 m
```& L# Z7 t) H; x
9 a; d' ^( n2 W* _" G
## 问题和建议
$ R3 {& c3 ^' q  G5 H, x3 t8 ^* K, }. c2 e& A  t* d  F
如果您在应用的时候遇到了问题,或者有好的想法和建议,欢迎到这个 [issue](http://github.com/zhaojuntao/FlexibleButton/issues/1) 上讨论,谢谢。6 K6 s( |5 Q; e( @& X4 N

' D. y$ L( g  Z- _# b
收藏 评论1 发布时间:2018-12-27 13:49

举报

1个回答
murphyzhao 回答时间:2018-12-27 13:52:41
编辑器不支持 markdown 格式

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版