
一、嵌入式键盘的种类 4 n5 Q* O5 h1 L3 {# b 嵌入式系统中常用的键盘有三种类型:& u7 x7 n* T7 l7 W $ D8 |" w8 p8 g* @: L- h 1 `) v, N$ \ r" \ Y$ g5 ` 1、线式键盘:每个按键对应一个IO口。 6 |6 b0 O E' O 2、矩阵式键盘:当按键数目较多时,可以采用。 3、专用键盘:这种键盘本身有芯片对按键进行扫描,并形成一定格式的扫描码,用户程序通过中断或查询的方式读取该扫描码。- v/ N T' C2 C9 w. y |
我手边现在的两个ARM开发板都只有简单按键,因此主要是关注线式键盘的工作方式和特点,结合我手机键盘上按键的功能,现在我关注的键盘实现有以下几种方式:* \: |4 }) h* h; b
2 P( K/ s3 a: {# ^- o* [& R# L9 |
1、单纯的一次按键操作:当按下该按键时,执行一个对应的功能函数。5 ^+ w2 s3 x- {) ]3 Q: u; K& p
这种功能的实现也有好几种方式:(1)中断,将IO口配置为中断,在中断服务子程序中执行功能函数。(2)中断,在中断中置标志位,在前台主循环中,根据标志执行相应的子程序。(3)查询,在主循环中不断查询IO口的状态,若发现被按下,则调用相应的功能子程序。(4)前面几种方式,都比较简单,而实现的按键工作方式也比较简单,如果应用程序采用操作系统,则可以借助操作系统的强大功能较好的处理按键,对于单次按键,可以很好的实现软件消除抖动,按键处理可以在按下时、也可以在释放时,也可以同时对按下和释放产生按键消息。以下较复杂的按键处理都在操作系统中实现。
6 M5 d8 v& X# ^! q2 T
2、按键的短击和连击
$ X+ }$ {( D _% N5 w, o' j6 W
这种情况按键消息在按下时产生,消抖确认后产生一次按键消息;按下时间超过一定值,则判断为连击:以后每隔一段较短时间,产生一次按键消息。这种连击主要用于嵌入式系统的参数调节,在没有可供直接输入参数的条件下,参数的改变只能通过按+-键调节,如果每按一次加1,那就太慢了,因此提供这种连击功能。( X8 J7 S; ?" o
4 W% ?% ?7 c! a: Y9 T7 a
这种情况按键消息在释放时产生,在按下等待释放时进行时间累计,释放后根据按下时间的长短判读为短击或长击,发送对应的按键消息。我们比较常见的例子就是手机的关机功能,该案件短按时起挂断、返回作用,如果按键时间较长,则执行关机功能。这可以用软件扫描按键来实现。& }1 |" l" ~; o) W3 j0 s
2 y' Y9 H; O \5 b t
4、按键的单击和和双击0 c* z- K3 i, U
4 E- N3 X$ @/ }# K, W
在windows操作系统中,对双击用的很好。在嵌入式系统中,如果要利用较少的按键实现更多的控制方式,也可以引入按键的双击判断功能。不过这种实现起来可能比较麻烦,在我写这篇博文时,前面三种都已经实现了,双击的实现还在构思之中,暂时的思路是这样:利用状态机,该状态机有等待按下、延时确认1、延时确认2、等待释放、释放确认等待双击、双击确认、双击等待释放和双击释放八种状态,判读的关键:(1)等待释放阶段进行计时,在释放确认时如果时间较长,则直接输出单击消息,回到等待按键状态。(2)如果时间短,则在释放确认等待阶段继续计时,如果超时,则输出单击消息,回到等待按键状态。(3)如果在双击等待阶段检测到 按键的按下状态,则经延时确认输出双击消息。
6 W" G/ c7 a* R/ E6 B1 B
5、多键按下时候的处理问题) d" o0 X9 V S& Z
4 U W" {% A; |' Z' Q9 e
! R! A' w3 z8 J4 R/ m0 r7 s
(1)如果每个按键对应一个任务处理,这些按键可以相互独立,不受影响。但这样太浪费系统资源了。
% a$ P8 u& \0 J% @3 K4 t
(2)如果几个按键接到同一个IO口,用一个任务进行处理多键按下的情况就比较麻烦。一个键先按下,在未释放时又按下了另一个键,涉及到后面按键如何判断的问题。由于现在开发板上没有这种键盘,这个问题暂时搁下。
6、组合按键7 o7 g8 v& \3 a
S; G1 l, C# u+ K4 s3 k
8 A6 n8 @+ \$ r. E5 c0 l ~8 ^
这个问题,我反而觉得比较简单一点,线键盘中专门设置一个或两个按键作为组合功能键,其它作为普通功能键,平时并不去检测组合功能键,而只在按键确认时检测组合功能键是否按下,如果为按下状态,则发出组合按键消息;否则发单独按键消息。
ç¶ææº.pdf
2015-1-7 11:03 上传
点击文件名下载附件
241.29 KB, 下载次数: 26
& ]# o% u1 Y) y3 A; q9 x$ @
一、短击与连击的实现
1、特点:该按键可同时实现短击、超过一定时间不释放,则按键自动重复。
2、实现背景:在UCOS操作系统下,单独的一个键,利用状态机。
3、程序的简单分析:: a& U; I4 f6 S3 ]1 `
void Task_FiveOK(void *pdata)( g! ^" N8 \) |* Y* a# P
{
u8 FiveOKState; //FiveOK是我给这个键起的名字& w9 L' C1 f8 }% }$ A* `
' C9 k8 X3 t% _) Y3 L
KeyStateMachine KeySM="WaitForPress"; //键盘状态机是一个枚举变量,
pdata=pdata; // 初始状态为等待按下: `/ t# I/ N$ ^$ J) V
for(;;)( _1 Q: j6 Q# @+ l" ]; I0 d1 h
{' ]4 L% O6 V4 `# j9 V
OSTimeDly(1); //时钟频率为100Hz,每隔10ms扫描一次键盘。
! d3 [" n7 w3 q! r. S# w3 a! O: X
FiveOKState=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15); //最简单的单线按键,直接读取电平, |. D( H/ b# u4 u# ~; x$ \
switch(KeySM) //根据状态机的当前状态' y" c& ~# d7 N; W5 F3 U% c0 ?1 u
{
case WaitForPress: //等待按键3 h f7 p) W( b0 U4 Z
if(FiveOKState==0) //低电平为有键按下8 |. ` c M7 ^+ k8 I$ v0 _, u
( z2 q* `+ R9 B7 r5 b1 l3 u
{KeySM=KeyVerify1; //设置状态为按键确认1,10ms后进行按键确认。9 z7 S' m5 z: v
}! ^/ t8 d1 q2 @( k2 Q7 G6 L. J
. S$ A! b/ Y o) `: I4 ~* N
break;+ ]# V+ i4 _9 O5 b+ Q- P
9 O$ d. \7 j( G2 r3 j% u; }
case KeyVerify1: //第一次确认,如果仍然是低电平,则进入确认2状态
. n. g% e( ]/ `8 M: R0 _; M6 W" C
if(FiveOKState==0)* ]) D1 U) o/ S
{KeySM=KeyVerify2;
2 e3 ^9 i4 x. a
}: i0 F* e) _. s+ B4 D# P% l
6 @# F% m8 I) q0 W+ o3 y
else' a# W7 S$ b( w. V
{KeySM=WaitForPress; //否则回到等待按键状态
2 @2 d! H. ~1 [- U# f: a
}6 h$ V3 F1 u! _3 _
break;- _; K3 Q$ u/ m$ M4 D; V3 _1 L
case KeyVerify2: //确认2状态下,如果仍为低电平,则真的按下了,' J8 ] W; T1 @
& B$ i, L1 ^# |; p* R* k" Y
if(FiveOKState==0)
{KeySM=WaitForRelease; //转入等待释放阶段- z6 L) Y6 |3 u @1 d
4 b) e) m; l4 r% x
Key[KeyNum]=KeyOK;
OSQPost(KeyOSQ,&Key[KeyNum]); //向消息队列发送按键值
KeyNum++;
! ?7 f$ V$ w! I, x5 ~3 ]
}
: z& J( ]5 L' ^; [) x4 @2 t* f
else
) V- \% f& w( Y: X
{KeySM=WaitForPress;
9 D! [2 f7 D& L' v t) D
}
break;
case WaitForRelease: //按键会保持一段时间,并进行按下时间计数
( j/ k6 A4 k3 A8 { D' W
if(FiveOKState==0)- y* d% A- v4 t
{KeySM=WaitForRelease;
PressTimeCount++; //按下时间计数/ m0 S B& o) B {3 t& e
3 G/ v S: I, E5 W! B9 a
if(!LongPress) //连击标志未设置时
{1 M% q( `* ~( u& {; @
if(PressTimeCount==100) 计时超过1s,设置连击标志
{
LongPress=1;, ~/ g5 H4 y7 K r, C9 D8 I
PressTimeCount=0;
}+ |& q7 O" x" u, y* i: J
}" o8 I# A8 F7 ~/ g) V* u9 S0 v
Else //连击标志已设置
{
if(PressTimeCount==10) 每隔0.1s发送一次按键消息" ?: |. p% y$ E% N
{
Key[KeyNum]=KeyOK;0 I. L1 _# x6 k: L* Q
OSQPost(KeyOSQ,&Key[KeyNum]);
KeyNum++;
PressTimeCount=0;6 t# `6 B& B" x, v/ {9 |
}
} 8 ?' Q: f% z: s3 ^& O& k. p
}2 t4 _& [' c: U$ P- w2 E0 f
else
{6 Y" Y) O) \, m: U7 O9 p
KeySM=KeyReleasing;; h/ B! [2 G! g/ D6 [- K
}
break;
case KeyReleasing: //在等待释放阶段,检测到高电平转入此阶段
if(FiveOKState==0) //如果检测到低电平,说明刚刚的高电平为干扰,回到等待释放阶段。: w1 G, x2 P$ a; }* v
{KeySM=WaitForRelease;
PressTimeCount++;" c+ m' P2 k/ }/ S+ r9 T% T
}7 l" A& Z" p+ J0 M
Else //如果仍然是高电平,则的确是释放了,回到等到按键状态。
{KeySM=WaitForPress;
LongPress=0;
PressTimeCount=0;
}& Z o; ~2 F% h, D
break;
default:& u' w( g: y" w( J2 ]
break;
}7 K) `/ [! C. N! s1 g) V/ N
} * ~' v! T E6 U
}
二、短击与长击的实现
1、特点:该按键的功能可区分短击和长击。长击是指超过一定的时间按键有效。7 J+ _: ^) j A/ b- L. z! A" k8 t
2、实现背景:在UCOS操作系统下,单独的一个键,利用状态机。在按键按下阶段进行计数,最终在按键释放后根据时间的长短判断是发出短击消息还是长击消息。$ K. L* F& [- R; l! W
3、程序的简单分析:
void Task_FiveOK(void *pdata)& U3 g/ `9 @* W, ?
{
u8 FiveOKState; //FiveOK是我给这个键起的名字
KeyStateMachine KeySM="WaitForPress"; //键盘状态机是一个枚举变量,
pdata=pdata; // 初始状态为等待按下
for(;;)! T* Q" C0 i3 ?9 c6 S% l( Q
{
OSTimeDly(1); //时钟频率为100Hz,每隔10ms扫描一次键盘。 W% l. L" j/ J+ t$ n; j+ {
FiveOKState=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15); //最简单的单线按键,直接读取电平
switch(KeySM) //根据状态机的当前状态8 O4 o0 b- M3 ?, ~; d$ p* Q. e
{/ u. b6 M+ T9 b7 b6 q
case WaitForPress: //等待按键
if(FiveOKState==0) //低电平为有键按下
{KeySM=KeyVerify1; //设置状态为按键确认1,10ms后进行按键确认。
}
break;
case KeyVerify1: //第一次确认,如果仍然是低电平,则进入确认2状态) x5 T$ o; a+ U( y. X8 t- M) X# b
if(FiveOKState==0)! b$ R8 T) X$ o& m$ w1 N( ^
{KeySM=KeyVerify2;1 [9 n0 V% Y$ L* O8 k% Z" @
}
else
{KeySM=WaitForPress; //否则回到等待按键状态
}$ a9 e5 K$ _6 ]
break;
case KeyVerify2: //前面的实现与单击和连击的实现类似,下面就有了区别。
if(FiveOKState==0)
{KeySM=WaitForRelease;
}0 O; q3 A, k" @" j! j* O, J
else. }. i+ Y- e1 W8 R5 R, a1 o: @
{ KeySM="WaitForPress";* ~' r8 f' Q4 Z6 f: D
}
break;' K0 T" _ p' r# G2 I: j/ I" y7 `
case WaitForRelease: //在等待释放阶段,只是纯粹的进行时间计数。' \; H! v; [2 k; k4 z' l) l( G
if(FiveOKState==0)
{KeySM=WaitForRelease;2 n% Y) w! l4 l: y, Z9 C n4 U: }
PressTimeCount++;
}0 ~. o) F. D" D* W% T
else& T7 o$ `: H6 q
{
KeySM=KeyReleasing;
}
break;
case KeyReleasing:
if(FiveOKState==0)
{KeySM=WaitForRelease;6 g5 ?& H2 v& I
PressTimeCount++;: v* c- ]( ^; [. o8 X+ m) @
}+ m5 P0 {8 N d* C' R
Else //两次检测到高电平,则按键真的释放了, ^: ~7 ~( m1 [' f" L- {
{KeySM=WaitForPress; //下面的状态为按键等待状态。
if(PressTimeCount>70) //如果超过0.7s,则判断为长击。
{ Key[KeyNum]=KeyLong;+ h8 j0 i* j/ ~1 w" {
OSQPost(KeyOSQ,&Key[KeyNum]);
KeyNum++;
if ( KeyNum>=30)5 v1 T- _/ r0 w3 P! E! u+ }
{ KeyNum="0"; }! ]3 b/ ]1 V0 D* m7 g0 P* P
}
Else //低于0.7s判断为短击3 y, q8 _- U ?# R- e: n
{ Key[KeyNum]=KeyShort;
OSQPost(KeyOSQ,&Key[KeyNum]);+ x" P: n Z( G! k: R6 B
KeyNum++;
if ( KeyNum>=30)
{ KeyNum="0"; }
}# w' f7 c! T0 j2 c- w+ y: U/ k
PressTimeCount=0; //按下时间计数清零。
}
break;
default:2 p6 @* G. L: Z4 h, x
break;! m2 Q. ?" {( _* e2 v, q7 f
}- f" s' t8 Y+ E+ |" ^
} $ k/ o- K# h C
}
恩恩,希望大家喜欢。还有料哦。
1、特点:可识别双击。
2、利用状态机实现,不过比较麻烦,好在一般的应用中都用不到它,一个任务扫描一个按键,比较奢侈啊!
3、实现方法:在第一次按键释放后,根据按下阶段的计时,如果已经超时,则直接输出单击消息,回到等待按键状态;如果未超过双击计时时间,则等待双击,如果在规定时间内按键按下,则输出双击消息。2 g0 @9 j% S3 ~+ V9 M0 ~6 i
4、程序如下。
typedef enum{WaitForPress,KeyVerify1,KeyVerify2,WaitForRelease,KeyReleasing,DBClickVerify,DBClickWaitForRelease}KeyStateMachine; //按键状态机枚举变量,比较复杂
void Task_FiveOK(void *pdata) //按键处理任务+ C; O3 o& H, v8 h) g2 R
{& r9 i# {! |+ k9 _& V. F3 i* D" a: e
u8 FiveOKState;
KeyStateMachine KeySM="WaitForPress";5 ?% m; F4 ]- a" k2 f, G) Q
pdata=pdata;0 Y/ a/ ~3 h: W- Q2 D
for(;;)
{
OSTimeDly(1);
FiveOKState=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15);4 A- S K- N% I4 f9 o0 w6 Y
switch(KeySM)
{
case WaitForPress:
if(FiveOKState==05 R+ X, a: K0 I0 X0 j8 d/ I) F% J$ W
{KeySM=KeyVerify1;9 |3 @6 Q) Z/ S7 o; G2 i
} u$ W d! Z8 X( L$ w' l. c6 ^( t! ?
break;
case KeyVerify1:- J7 m" N: T/ U. f3 g. o8 C
if(FiveOKState==0)
{KeySM=KeyVerify2;
}
else6 B; r" V }. y# V! r' ~$ u
{KeySM=WaitForPress;* w, [5 n: }( L: a
}" d/ B2 h# u/ U- ~$ ?4 Q$ X
break;
case KeyVerify2: n c5 g" I( A) {9 o9 T) S
if(FiveOKState==0)$ n& Q* Q4 _7 G# h
{KeySM=WaitForRelease;: W' X6 t& y* M7 r/ N
}, S) x. L, e4 ?9 \
else( d0 i+ B5 |2 l
{ KeySM="WaitForPress";
}
break; f# x, b E0 o2 ]5 R. R/ V
case WaitForRelease:$ N) F, B& s( s# n0 K
if(FiveOKState==0); E9 X! j4 T: U( a
{KeySM=WaitForRelease;* s) d8 _" |- f& F
PressTimeCount++; //单击按下计时
}
else
{
KeySM=KeyReleasing;
} z. C* h! |. N+ z1 [* g& o5 C
break;
case KeyReleasing: //重点在按键释放后,为了结构清晰,释放没有进行消抖确认
{ PressTimeCount++; //在释放阶段延续按下时间计数) F1 o& Q) C8 ^) k1 Q
if(PressTimeCount>70) //如果时间超过0.7s,则直接输出单击消息
{ KeySM="WaitForPress";5 r/ q5 k) u3 {# A+ j7 u
PressTimeCount=0;
Key[KeyNum]=KeyLong;
OSQPost(KeyOSQ,&Key[KeyNum]);
KeyNum++;
if ( KeyNum>=30)
{ KeyNum="0"; }
}3 X, ^2 T- N& l6 ^( {8 D
Else //如果及时时间未到,而又检测到低电平,则可能发生了双击8 i: w5 ]) q( V) F* @8 W! c8 w
{
if(FiveOKState==0)
{ 5 @3 l$ F/ [; G; n! v
KeySM=DBClickVerify; & O3 y7 J; k" l3 g! M
}3 t% P* G" s% x0 H" G2 Y
}" o. a( z, X& x* }, M+ L
}, z' \$ D+ p. ^# x O! L" a: f. l% V
break;
case DBClickVerify: //没有进行确认,进入到该阶段,直接输出双击信息/ W, Y! Y6 Y9 `+ W& `' S$ c
{
KeySM=DBClickWaitForRelease;
PressTimeCount=0;
Key[KeyNum]=KeyShort;' r* ?2 [5 M3 n+ U4 P5 K
OSQPost(KeyOSQ,&Key[KeyNum]);* }$ n& l6 q# t. P
KeyNum++;9 d; `3 c+ Y5 T
if ( KeyNum>=30)5 T1 b+ u2 d0 L; K
{ KeyNum="0"; }
}
break;
case DBClickWaitForRelease: //双击释放后,进入到按键等待状态+ S) l, w9 }1 c" ?& d& E1 x, O" y/ ]" R
{8 P' |* {1 r4 C: j
if(FiveOKState==1)/ e3 m5 D1 \2 E8 C- d
{ KeySM="WaitForPress";5 p4 w$ a3 ~% S( p
}' u% K0 Q( d/ R
}
break;
default:* m2 r8 G, A7 t1 I) X
break;
}. Y$ ?3 M" z% i
}
}& N ^3 n0 s6 v2 x* V- n4 l, J" ?