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

【经验分享】STM32使用HAL库实现按键的单击、双击、长按

[复制链接]
STMCU小助手 发布时间:2022-4-10 21:50
前言
( `/ a; \  k! _: R编程开发环境:STM32CubeIDE

) w9 Z% n2 r# g9 Q 按键的单击、双击、长按等在MCU编程中是比较常见且常用的事件,本文章基于STM32来实现,具体思路用在其他MCU也是如此。
: l; J& m/ w: P+ g# v, a- h# D& @- W: b" p/ ~. l1 J1 G
具体思路
" ^$ e: R4 |6 I- K初始化一个全局标记
( @, d. ^$ \5 G! M$ A1 H* H+ \按键中断事件发生后置位标记
9 U9 A' J, j/ N& X( hwhile死循环中一直检测这个标记,如果被置位那么进行消抖,然后再次检测连接KEY的IO是否处于按下状态,如是则认为本次按键有效
8 j! |% @- K1 z2 r6 d' Z第一次按键事件有效后,启动定时器定时300ms,在此定时期间内如果有二次按下那么就是双击,如果没有按下,等到300ms定时时间到后读取IO电平,如果处于松开状态那么本次就是单击事件,如果还是按下状态那么就再次启动700定时器,700ms过后再次读取IO电平是否处于按下状态,如是那么就是长按。
  V4 h( Z% ?1 g' O, b工程配置/ ~7 r" Z8 S0 |; I/ @! i7 @
我是使用STM32CUBEMX配置生成的工程,配置按键:
( N! Y. y- ~, B9 x- Z$ |; Y2 e/ s; o1 c6 M' W
20200919164941415.png 6 s. F( j8 P+ }" _/ C3 o2 m

& G: L2 Y. @9 m% g! RSTM32的定时器有向上、向下计数模式,向上计数就是从0计数到N然后触发中断,向下计数模式就是倒计时到0,由于我们需要二次启动定时器来检测长按,所以再次配置为向下计数模式,分频为7199,计数3000就是定时300ms:( H+ r( o( T" B$ X1 L& c

0 t4 Q0 Y5 ]  O2 w  z/ w 20200919165238384.png ' I' W3 c# t: _/ }

% t0 @! v' A5 n# s' h8 q6 m中断配置:1 `( Y; u% S) _4 L- M/ F/ W0 _
) z1 G. D. }4 B$ s
20200919165306462.png
' g5 Q5 @) ]* l3 Y1 E; G: ^# {; C8 ?- E: w% |% q* K1 _6 W) t+ o2 c
代码实现+ i7 P3 H& ~; b8 ^

  1. $ Q8 Z# i: i* G9 {
  2. /*
    ( u+ f# U; V5 Y2 Y: H+ v" g" e8 R
  3. * key_state位说明:, P9 C7 _8 V& ?; B* _
  4. *" P; e% J- N: Q1 t
  5. * bit[15]:按键事件完成标志位: p0 T) G& o5 v: S3 L" N( i
  6. * bit[14]:按键中断触发标志位
    0 p. L) \& X  E+ {  D1 q, Q' m
  7. * bit[13]:长按事件触发需要启动第二次定时器标志位! S8 d9 D$ j  J% L- U
  8. * bit[12~0]: 按键计数,=1:单击  =2:双击  =3:长按# y. g% i* T- R/ G' p; p# `3 `
  9. */% C8 g# U: }9 a* c
  10. __IO uint16_t key_state = 0;
    $ D9 ~7 q* {6 e+ ?
  11. # ~. K$ h: z( x
  12. int __io_putchar(int ch)
    - f6 s/ p6 [7 d3 U0 o
  13. {4 [3 t8 g2 C  @( \( u* [
  14.         HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 100);
    ! F- ^8 }# N6 s% i9 q0 d
  15.         return ch;; l+ a% i$ ]' C1 N% G" _. p0 ^2 I$ Z
  16. }7 [: H' s! D) |: r

  17. 7 `. X) J4 U5 F7 t# y
  18. void TIM4_Stop(void)) t+ n7 E  ^& C2 j( D, T
  19. {; l8 [7 f, M$ d
  20.         HAL_TIM_Base_Stop_IT(&htim4);1 T4 o& S9 ?% R3 f* z/ u! l
  21. }
    5 t1 Z- T4 T6 O# S: _5 F) [1 O
  22. $ R1 p2 o. K0 ^1 L6 m" ~+ o+ J9 x
  23. void TIM4_Start(uint16_t timeout)
    ! }, z7 H3 F; R1 v% y
  24. {) [6 B9 I, h  A  f+ Q
  25.         __HAL_TIM_SET_COUNTER(&htim4, timeout);7 ]& v: z2 `, L9 j; H1 C. i& t
  26.         HAL_TIM_Base_Start_IT(&htim4);, t$ H% x) a9 b! C- z
  27. }
    + F% z, X- S6 K% v7 |
  28. 3 V) S7 S- T2 w4 R' W! r
  29. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)9 x2 D4 |5 v+ G, D5 ?% K* G
  30. {- {; h# ^, k5 N
  31.         if(htim->Instance == htim4.Instance)
    . f/ u# _2 W! W9 M) Q( j  W$ @
  32.         {- }( q; G! \; H/ W. J
  33.                 TIM4_Stop();& E( c' r+ m* l, A  ]  b' G, j
  34.                 if(key_state & 0X2000)    // 如果是700ms的超时
    & n3 _* z# \. e, B8 W7 F7 @
  35.                 {
    ' e: t# g9 F) s! j* l: z* p
  36.                         // 再次检测按键如果是按下状态,那么就是长按事件,否则事件无效
    # H1 n0 M; F5 H2 n! h
  37.                         if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
    3 R# p- c7 V, L7 s9 {; D
  38.                         {( I! N4 k" T, a# y+ y7 U
  39.                                 key_state = 0X8003;
    : u2 M& q" J# k  K
  40.                         }else* p5 g+ O4 X! ~3 N# W, s
  41.                         {" r  W$ R! O+ u1 W# Z, S
  42.                                 key_state = 0X8000;4 k) H! v; c0 K0 Z! `. w" z
  43.                         }
    5 o% ]) L  w& E( `3 C, w' H0 ?
  44.                 }9 P, ?8 ?' B9 j: Y* O& l3 j
  45.                 else  // 如果是300ms的超时
    - A1 l4 W2 f9 f* r% \
  46.                 {
    $ w. @4 t6 I2 E% _  q( y3 B) h. n
  47.                          // 300ms时间到再次检测按键状态如果仍处于按下状态,那么就启动700ms的超时,1 I/ q- G$ C7 c
  48.                          // 700ms时间到后再次检测按键如果是按下状态,那么就是长按事件了。
    ) t7 `/ A1 H- }) B6 U& W
  49.                         if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)  // 按键处于按下状态
    4 y0 w* ]7 R; q; m" w2 S& {& @
  50.                         {
    ( z- b8 O$ _+ b1 Q2 E7 D/ c- c) O5 c6 \
  51.                                 key_state |= 0X2000;  // 标记启动了700ms的超时定时
    7 J- @1 c/ }. c
  52.                                 TIM4_Start(7000);     // 启动700ms超时
    + b  Y$ [1 z7 T4 n- [% e- F
  53.                         }, ^0 U* P$ n2 z2 L9 |
  54.                         else  // 按键处于松开状态
    5 a6 f  [# l) s; B0 q# y
  55.                         {/ l- ^1 Q9 Y& G3 F) N& z
  56.                                 if((key_state & 0X1FFF) == 1)        // 按键按下计数=1,是单击事件
    ) w0 _  Z3 t2 l! V0 d: G' h
  57.                                 {
    # p8 m0 k9 l4 g% h! v- ~
  58.                                         key_state = 0X8001;- N, t7 s+ F4 Z
  59.                                 }else if((key_state & 0X1FFF) >= 2)  // 按键按下计数>=2,是双击事件$ H: A/ j& G1 S: F9 q* @5 m
  60.                                 {
    ( k  u7 {" c) _' `9 r9 F
  61.                                         key_state = 0X8002;+ u6 N7 b. ~  O% m5 E
  62.                                 }else                                // 按键按下计数为其他值,本次按键事件无效% U4 W4 \8 k+ H: g% n
  63.                                 {
    % v# C  i5 V- H  K) J# J! _/ W4 y
  64.                                         key_state = 0;
    * y  C" W6 G) P+ K
  65.                                 }) W5 _0 r2 @+ w2 G/ H/ {
  66.                         }& [  d9 F! w; t9 Z# J3 c( W% v# g
  67.                 }* w, x3 f! a6 X$ q( e. Q, S5 B
  68.         }
    / m& z# i! i, I  e
  69. }8 R4 a$ ?! W0 q

  70. / F" C6 C: R2 n2 H+ ?
  71. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)9 a7 u) M+ P, K2 k8 H( j  T3 Y
  72. {
    4 n) F9 @0 ?: t) ^) m9 J+ U
  73.         switch(GPIO_Pin)' F& p) i/ q4 O$ x6 X  T
  74.         {
    2 z# p5 i* v0 U( G% I
  75.         case KEY_Pin:% k0 `% j6 y* `1 n& E
  76.                 if((key_state & 0X8000) == 0)
    ) `% l9 O+ x  q' P
  77.                 {1 T$ m, U, ]  f+ e0 v0 E% W* \: K. C' K
  78.                         key_state |= 0X4000;    // 标记按键中断触发,在while死循环中进行消抖和的启动定时器
    " _9 c3 z2 A/ m4 |7 |. h; Z1 B
  79.                 }
      d2 d4 m6 f- C, Z" z& ?
  80.                 break;8 N- w* f6 b1 O7 \- _4 u
  81.         default:break;3 u7 k$ Y# D( E
  82.         }' S! W+ \" n  C* s) r) s
  83. }* a. t5 P: e8 K7 t

  84. " _: ]0 J: q2 K5 e8 `  d% o
  85. int main(void)
    9 i5 D, H6 ^( W
  86. {2 W8 _" b1 L, c9 }$ [+ }
  87.         HAL_Init();. @' v; E; f; K' S4 ~: _7 \7 a
  88.         SystemClock_Config();
    6 ]! ?9 L; b  K1 T" A
  89.         MX_GPIO_Init();* n" l' o( [, C; w3 I
  90.         MX_USART1_UART_Init();
    / M5 a7 D/ b/ L& y. |5 X8 C3 a7 e
  91.         MX_TIM4_Init();
    5 O7 [, r7 ]( r! h/ P
  92.         __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);    // STM32的定时器初始化完毕之后如果不清理中断标记为会有直接进入中断的问题7 T1 p' }2 o. n9 x+ H
  93. * \2 W+ z6 R# K2 L2 X
  94.         while (1)
    6 d. W% F: ?3 t/ f
  95.         {0 w6 ?% Y1 E4 }3 _; }
  96.                 if(key_state & 0X4000)     // 按键中断触发* h7 `" F; G6 h# R; Q
  97.                 {
    # t) N$ n* A$ \+ U1 A- L( g
  98.                         key_state &= ~0x4000;  // 清除事件% m. E, d6 R8 a" `! s0 a) O" [+ ~* P
  99.                         HAL_Delay(60);         // 消抖延时
      N' ]$ E  m# ]! f* @) W* `

  100. , m1 a# w. ^$ r7 \8 j3 ?
  101.                         // 消抖完毕之后再次检测按键是否为按下状态,如是则本次按键有效,否则视为无效并清除标识。' p$ s% g7 ~3 a9 H4 F2 L
  102.                         if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET), V3 z" F4 W3 C  z0 f7 C
  103.                         {
    4 @; }0 U/ u! N- c9 ^& [
  104.                                 if((key_state & 0X1FFF) == 0)  // 首次按下时启动300ms超时定时器- M. ^3 r3 `5 h: g* A
  105.                                 {
    . ^- s$ {1 ~5 x8 o
  106.                                         TIM4_Start(3000);
    ; _+ D) B: p0 K3 S: t) b) E2 D
  107.                                 }( {' t0 y8 \/ j' K4 b
  108.                                 key_state++;                    // 按键按下计数增加
    # {1 v3 X- J8 N- o* O3 e& _7 a2 W2 x
  109.                         }% g3 U0 u; ~6 R1 \# ^. e& u) n
  110.                 }+ r9 O6 {5 T# T' L2 o2 z# W  v. {
  111. 2 O; S1 L' |7 K! P/ _3 j) Y( o
  112.                 if(key_state & 0X8000)  // 按键事件发成了
    7 y8 A3 C; ~6 n$ H
  113.                 {
    % B7 a  E0 b6 q9 S. `: d1 m& f
  114.                         switch(key_state)
    $ |; i4 {% B( Y! d; R% m
  115.                         {
    6 V7 k6 w# I! R, w. G
  116.                         case 0X8001:3 w& R% k/ s. X) k4 X; U' Y
  117.                                 printf("single\r\n");
    2 w$ V- i7 N' ^* A9 u: i$ R; V
  118.                                 break;$ `+ X/ J/ K! d+ g5 W( c
  119.                         case 0X8002:
    2 {1 s+ s% Z0 _3 c5 e  W
  120.                                 printf("double\r\n");- M/ Q% z* T% x5 `0 n1 B
  121.                                 break;0 L# k. t; t# x# B7 Y$ e  e' f
  122.                         case 0X8003:
    , L1 @. S6 |3 d; G
  123.                                 printf("hold\r\n");
    $ c! v/ ^( C7 F; Y( @( P& E
  124.                                 break;
    7 u5 A, b1 l8 m9 n1 y- z9 e
  125.                         default:break;6 @& u; t" l/ {) v0 X# |  s
  126.                         }* A! `% q, e1 k1 d
  127.                         key_state = 0;  // 清除事件/ Z6 C+ R! B* }8 v
  128.                 }! |/ J, P5 q  \: ?0 l
  129.         }
    2 L  B+ |0 |/ @7 m5 G% A9 i
  130. }7 r( ~8 M: v: a. |5 C4 s! k; `" ^
复制代码

- p4 c/ k; I$ y6 h& Q- X; K实验效果7 \# y! @# o5 L) ?2 D2 e+ |
按键单击、双击、长按实验效果如下:3 Q2 n& w! \0 C* {2 w* g0 ^; O  P
: N. X; R, P; c$ K7 `
20200919165532374.gif 3 _$ m4 q6 t+ w" D
; ^# l* W( Z+ F+ v# u
9 r. X& `! {& b' i. [9 Q
赞 收藏 1 评论0 发布时间:2022-4-10 21:50

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版