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

STM32单片机和FPGA按键消大全来了!

[复制链接]
gaosmile 发布时间:2020-3-20 14:10

- T" N9 C9 S( ^. e2 E9 V写在前面:
( c/ ~: K& ^1 ^# l6 i3 _

5 m. D. J3 }4 X) ]
微信图片_20200320140847.jpg # G$ B; o* a& N  |
按键去抖:由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我们手动按键然后释放,这个动作中稳定闭合的时间超过了20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专门的去抖动芯片,但通常我们采用软件延时的方法就可以解决抖动问题。9 [$ _2 q( [) U" L1 ~$ w4 B7 L4 @
1、单片机中按键消抖程序
1 v3 W' m8 Y; s, s. S/ H# @1.1  单片机中,比如STM32中,一般的方法(最简单的方法)
$ B* {- L) T9 D  S% s
软件消抖程序:: q+ A8 F" ^* K; q( {

  1. - E( i% H/ s+ n7 z8 y
  2. if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==1)
    6 K  D0 D( ~: s$ z5 U
  3.            {  ' c5 T9 A" f6 Z9 i4 u  a
  4.                   delay_ms(20);//延时20ms再去检测按键值. d+ ^. n+ A, D9 u/ E
  5.           if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==0) // 相当于下降沿
    + R* y3 m4 t' f2 L1 B- F1 S
  6. {
    # ~7 h7 S/ `, I/ C. Q
  7. KEY1 = 1;  //表示KEY1被按下# K; t: u! `0 C! \5 h' t; Z. q3 S
  8. }
    , ~/ |. _( Q9 [; K; o
  9. }
复制代码
; x7 }7 P$ U( ^7 L
1.2 比较全面的按键消抖程序及按键状态检测程序
0 }( y7 x- s8 l7 Z; e  F第一步:初始化全局时间戳的定时器,一般采用SysTick定时器来产生,每ms一次tick即可。
( B5 g* W! y9 ], k; s+ r: c第二步:初始化按键对应的IO,复用为边沿触发的外部中断。7 a8 X1 X" T' a0 @0 Y
第三步:在外部中断函数中添加按键事件处理函数。' G$ V# I" d5 N% @4 f) f+ |
代码部分:
  1. $ w0 ]+ Y; l, x: P0 H
  2. typedef struct _Key_t  ' F3 ~, B9 d5 U/ ^0 S
  3. {  2 p6 o4 R! b, @) f! X0 N9 g
  4.   u32 last_time;  
    9 ]$ W3 `! C, N, }6 ?+ h% P
  5.   enum  
    ; k* ?, B- ^; x* Y& ]: ]2 }
  6.   {  
      J6 O2 L, b, G# {- y7 ]
  7.     May_Press,  . g+ e9 z# T( k2 {
  8.     Release,  / z% A7 ?9 n( W, I9 J; _. p: @: a" E
  9.   }private_state;  7 a1 g9 Q& P5 F+ X4 ~
  10.   enum  
    * b/ u: n( v$ h- x
  11.   {  
    . C; h2 q& M+ ]% c
  12.     No_Press,  
    ' j" F# ~6 P9 K. e
  13.     Short_Press,  
    8 O& {- d- J# b$ J- L
  14.     Long_Press,  
    " t) J% p  l/ z% ^/ V- l8 q* m; ~
  15.   }state;  
    $ O4 [! B. o# m" Q7 V" b+ b+ A4 F
  16. }Key_t;( M+ I) Z8 G5 G7 V. Y7 b, ]' d- H
  17. 3 q  q, x% g8 e: q  |2 D
  18. #define Is_ShortPress_Threshold  1500
复制代码
; v0 }, W9 }8 y
简单定义一个按键状态的结构体,用于管理每个按键的状态。顺便再定义一个长短按的识别阈值,用于区分按键的长短按。* s# K& }1 @+ x, i/ a4 H, P
  1. 7 b* R% Y2 \3 c' s( n3 `
  2. if(key_state.private_state==Release)          0 W1 N0 P. t( h3 L/ `: W
  3. {  
    ; ~7 o  V. q3 ]7 a; @' R, `
  4.   if(KEY==0)  ( A  v; h: S) `9 X: c. d
  5.   {  * K! N: Y/ f  A5 b8 C
  6.     key_state.private_state=May_Press;  5 n  @8 y* o2 o+ a1 B. g# [* e6 t2 [4 }
  7.     key_state.last_time=course_ms();  
    - N( @$ r3 t$ |
  8.   }  
    & R2 U4 }2 K; T6 f0 A
  9. }  
    4 V( b. [* k# o6 L( B* _0 k
  10. else if(key_state.private_state==May_Press)  
    3 `$ t! y6 V5 o* q$ D2 ?
  11. {  + }' c7 m7 `% U% Q  U9 c) @$ ?
  12.   if(KEY==1)  5 R* g- J  u# T. G% w6 K" [- A2 @. w
  13.   {  : U2 O+ m4 ]1 P6 x7 \, V0 h( ?4 I
  14.     if((course_ms()-key_state.last_time>10)&&(course_ms()-key_state.last_time
    # G. H; q; u4 w9 j
  15.     {  
    + U( Z6 }, w3 V8 {+ p
  16.       key_state.state=Short_Press;  0 Y; k  O# [1 D- P; ]
  17.       key_state.private_state=Release;  0 h) C4 y0 S# O
  18.     }  % z0 L5 F. I+ v: ]( M" h
  19.     else if(course_ms()-key_state.last_time>Is_ShortPress_Threshold)  * x% p0 V3 f  s4 f% |
  20.     {  
    1 ?, ?- N1 y  W( N9 p6 n* X. |  ?/ `
  21.       key_state.state=Long_Press;  
    , C7 \' f0 o- Q( A4 Q' @" O+ G2 F
  22.       key_state.private_state=Release;  
    9 E- M- E& d5 M- U* m8 F& t
  23.     }  
    ! J/ y0 x+ F$ D, A4 [" S
  24.     else  & f- ?8 J: h0 {& x0 O
  25.       key_state.private_state=Release;  . ?# r  o, e( K1 B- w3 |) H  O& E
  26.   }  
    5 }: R" b: B( O3 I3 @" K
  27. }
复制代码
5 d( Y7 W! f, M8 s4 F5 t+ n6 K
( Q/ h# ^3 }: s1 z. }1 K
6 B9 `) p5 I; q  ^. n; P& r; s
, i- M4 Q7 y  H: ?' |
以上为需要添加到中断处理函数的按键事件处理函数,算法的核心是一个状态机。在本例中,按键被默认上拉,按下接地。course_ms()为获取全局时间戳的函数。
' T" h9 v7 _( G( T0 @' R7 T1 D8 E

0 e# J" d- o. r( a* n
思路解释如下:按键状态结构体有一个用于识别的状态位,默认处于Release,也就是释放的状态。一旦按键被按下,中断触发,此时检查是否是Relase状态,如果是就检查按键是否被拉低,如果是,此时进入May_Press状态,也就是可能是按下的,并且记录此时的时间戳,这一步是消抖的关键。当按键被释放,由于是边沿触发,会再次进行处理,此时检查和上一次触发之间的时间戳之差,如果小于10ms我们就认为是抖动,此时不会对按键输出状态进行修改,而是直接将按键状态置回Relase状态,反之检查差值和长短按阈值之间的关系,将state置位为对应的状态。消抖的核心在于记录时间戳,而这只是一个简单的赋值操作,并不耗费时间。
9 q' u8 h1 b5 w1 [! m效率上来说,延时消抖花费时间在无意义延时上,而相对较好的定时轮询还是不可避免的在轮询,而现在这种方式完全是中断性质的。唯一多出的开销(全局时间戳)并不是只可以用于按键消抖,另外在HAL库中存在直接获取tick的函数,这样实现就更方便了。经实际测试,消抖效果可以达到其他两种消抖算法的水平。
# P, {# J2 p0 {/ Y; ]2、FPGA按键消抖程序/ |, J' \" Q( S  K% \. n* _- Y* x. {
首先,做两个假定,以方便后面的描述:6 _6 i1 d) i# ~& |: C: ~
& M( N" S' H1 ^0 Y- l+ B9 H% L
  • 假定按键的默认状态为0,被按下后为1
  • 假定按键抖动时长小于20ms,也即使用20ms的消抖时间& k3 Z( F2 ]5 \5 C6 F% L$ g' |$ b

9 ~+ U: t: T$ N- x# U核心:方案8 ?" M  I- H! a( W0 X
# \3 i) }+ M1 R( g! g& d4 l
  • 最容易想到的方案
    ! t- V/ w/ E' m# @- X6 e

' Q& ?6 [; U( w$ a在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平。% I9 O  z# Q; T2 Q) @; z& e

9 Z- q, n9 L  k6 Q- N
  • 或许这才是最容易想的方案5 |/ Q) f9 |  c
$ P8 G: L+ k6 m  [" x$ W: A
在20ms计时的过程中,有任何的电平变化都立即复位计时
& J. c3 L  E, f: f9 U0 W& Q8 x+ H: M
9 `( t: B; u6 O1 p6 m# J
  • 消除按键反应延时抖方案
    ( p( }& c- |0 |* p+ k' T7 j' X
6 t  R* f3 L% Q% C6 _. G
在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动
测试平台设计(修改代码以仿真的1us代替实际1ms)
( e( Q$ W$ Y  l
8 F1 _  Z9 [" n: \$ \, D7 z
  • 无抖动 上升沿抖动5毫秒
  • 下降沿抖动15毫秒
  • 上升和下降沿均抖动19毫秒2 F* H, E/ u' @$ v+ W# V5 u
  附加测试(可以不通过)
  • 抖动25毫秒7 w/ S9 P# r, h! F
+ v! K8 S, X$ S8 P- F+ T
代码
8 A8 f  q& H2 C0 y2 m% H方案1
, r0 B- y. c& U2 ?) ^
  1. 4 f1 ]- g8 Z, S4 I
  2. module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out
    # B) g' {; _& ^1 h9 f
  3. );    // 20ms parameter//    localparam TIME_20MS = 1_000_000;
    ! Y: H9 |: ^: d# M. i
  4.     localparam TIME_20MS = 1_000;       // just for test    // variable
    6 L3 X- _6 k& p6 H
  5.     reg [20:0] cnt;    reg key_cnt;      s' U6 R3 I& e) d" `3 u" c
  6.     // debounce time passed, refresh key state- U/ K: Y3 A# Z2 W6 ^1 E
  7.     always @(posedge clk or negedge nrst) begin
    7 d% }0 b. y# c5 z+ h$ N$ U
  8.         if(nrst == 0)
    - B' ^% L* Y6 D/ j) c) H- f! @
  9.             key_out <= 0;        else if(cnt == TIME_20MS - 1)
    7 z+ g7 a2 e: t- n& {7 K' O) b+ v
  10.             key_out <= key_in;    end
    : Q/ t- l9 j- I) Q4 Z

  11. 8 W- w% H. z' \% I- q
  12.     // while in debounce state, count, otherwise 0
    % Y, B$ p, W7 G  k9 S( H! F
  13.     always @(posedge clk or negedge nrst) begin1 r4 Q; z: {" `# n8 L8 S
  14.         if(nrst == 0)
    $ b3 f5 T+ W0 d& P) J7 b
  15.             cnt <= 0;        else if(key_cnt)2 n/ f: D6 T. ^1 c
  16.             cnt <= cnt + 1'b1;
    4 [. o7 k) m- I  M
  17.         else
    - T4 x* G) @7 Q& P
  18.             cnt <= 0;
    1 J2 g" K* o8 L) U0 V
  19.     end& x0 i, w: ^6 ^

  20.   A# ~, j# E( e+ @
  21.      //
    2 v* C4 {& c" W. x
  22.      always @(posedge clk or negedge nrst) begin
    1 G0 O. g/ z* ?* n- f" ^* P( [3 j
  23.             if(nrst == 0): V2 g' t3 b- C
  24.                 key_cnt <= 0;            else if(key_cnt == 0 && key_in != key_out): i! h% Z/ \- _5 |. n- W. z
  25.                 key_cnt <= 1;            else if(cnt == TIME_20MS - 1)
    6 r' ]% M8 i1 ~, r: o& Z: p
  26.                 key_cnt <= 0;     endendmodule
复制代码
4 |" A$ g6 v* Y4 j1 w6 z/ u
方案2
! H3 B9 r# Q$ t( W1 Q
  1. 1 M( T, S% H0 h
  2. module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out! h! Q& a  O( K, H" Q0 E( m
  3.     );//    localparam TIME_20MS = 1_000_000;
    + a: v( K% z5 T* F4 {% t4 x
  4.     localparam TIME_20MS = 1_000;    reg key_cnt;    reg [20:0] cnt;    always @(posedge clk or negedge nrst) begin" |' F( t' f1 J: k1 h6 p
  5.         if(nrst == 0)" S% W) P. _, x
  6.             key_cnt <= 0;        else if(cnt == TIME_20MS - 1)
    8 b& N3 ]6 l" [
  7.             key_cnt <= 0;        else if(key_cnt == 0 && key_out != key_in)' F& E$ M1 }% c, s
  8.             key_cnt <= 1;    end
    % o8 G5 [3 ?- F8 E: R2 ^* i: [0 [
  9. 0 g6 D  z! m; Y8 t& p1 ]% O
  10.     always @(posedge clk or negedge nrst) begin% \/ d5 \8 Z3 L' S/ U) \; z5 b" k
  11.         if(nrst == 0)
    % i: l$ R% J" k$ l* r- `+ I- }/ ?3 v
  12.             cnt <= 0;        else if(key_cnt) begin' i# t9 n/ \. g; T; \3 {
  13.             if(key_out == key_in)
    ! m9 [% D, i" V1 O0 U9 Q: A
  14.                 cnt <= 0;            else
    7 K/ Z' B1 B8 E1 M4 l
  15.                 cnt <= cnt + 1'b1;
    ! G7 j1 E9 i3 ^! i0 G/ O
  16.         end
    % ~" K; H4 Y, _: f5 ?
  17.         else2 p" c6 ]4 }8 {& v* s. H$ t( X
  18.             cnt <= 0;    end
    ( ?6 I! X' Q6 @) k( O

  19. . k  @/ B9 h* x3 S0 a" o% V# P5 u
  20.      always @(posedge clk or negedge nrst) begin7 `# Y8 c: T0 V
  21.             if(nrst == 0)
    $ R. ]6 L3 x! q0 k7 [8 ?# E
  22.                 key_out <= 0;            else if(cnt == TIME_20MS - 1)( W0 T, `3 @1 T3 W8 v1 o
  23.                 key_out <= key_in;     endendmodule
复制代码

. c0 C' k1 f) L( I方案3; I2 Z0 C9 w- e) n  K: V
  1. 4 ]% \) z, }- [
  2. module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out
    6 F6 o, V/ T' c4 N9 j) V  g
  3.     );//    localparam TIME_20MS = 1_000_000;
    0 f0 m; E. U6 _) {0 _7 r/ m
  4.     localparam TIME_20MS = 1_000;       // just for test
    + h6 B; y* S+ K  h; M9 \& Z

  5. 7 V' r: W* C* N9 Q/ e9 N
  6.     reg key_cnt;    reg [20:0] cnt;    always @(posedge clk or negedge nrst) begin
    ( P+ V- E/ [7 h3 Y% }; |5 S% Q
  7.         if(nrst == 0)7 b4 b8 m- l' Z- s; Q
  8.             key_cnt <= 0;        else if(key_cnt == 0 && key_out != key_in)) T$ j1 t: c- a) `9 }
  9.             key_cnt <= 1;        else if(cnt == TIME_20MS - 1)
    : X, Q# Y! I, `& i
  10.             key_cnt <= 0;    end
    7 s! y5 q1 k1 U

  11. $ X! [: G1 }3 K. e) G! ?9 L
  12.     always @(posedge clk or negedge nrst) begin6 W3 }- U( m- @
  13.         if(nrst == 0)
    $ @6 z1 R. S% @7 {
  14.             cnt <= 0;        else if(key_cnt)
    8 w5 @7 h' D9 Q9 `9 ?# U# N2 w
  15.             cnt <= cnt + 1'b1;2 C9 S' r1 U9 q, a  ~
  16.         else
    3 x- y6 R( @2 g  L
  17.             cnt <= 0;    end: _% e) e9 P$ K) E# @/ y9 U. J

  18. & s) X* D: C5 |2 R3 T: k
  19.     always @(posedge clk or negedge nrst) begin
    * W1 F3 J0 @0 D; V0 u* d. m
  20.         if(nrst == 0)
    : b% g4 \  w# N+ ^
  21.             key_out <= 0;        else if(key_cnt == 0 && key_out != key_in)0 q( [8 v  N& a8 j# J, K" ?- g
  22.             key_out <= key_in;    endendmodule
复制代码
测试代码

  1. ! _  i! F2 w/ X  h% d4 n" h
  2. // 按键消抖测试电路// 时间单位`timescale 1ns/10ps// modulemodule  debounce_tb;    // time period parameter
    + I8 _/ n7 m6 I5 I! m
  3.     localparam T = 20;    // variable
    1 l: q' i) u( m. v
  4.     reg clk, nrst;    reg key_in;    wire key_out;    // instantiate    debounce uut(
    + A4 U) h% U1 b) }' M
  5.         .clk    (clk    ),/ \  X/ g3 K+ s
  6.         .nrst   (nrst   ),1 D- X9 E0 \8 ~+ x* ]% q# z$ m2 z
  7.         .key_in (key_in ),$ k8 N  r; {8 l/ a' y4 N: `
  8.         .key_out(key_out)
    # L8 ]; H- j- w6 t( k$ Y
  9.     );    // clock
    . p  `" y% `) H
  10.     initial begin6 H0 s! M; ?  b( @1 O. N% m  F
  11.         clk = 1;        forever #(T/2) clk = ~clk;    end
    2 v, R  T' J' G* Q; L

  12. + {0 r9 c- a% ~( w. C
  13.     // reset
    $ g* x$ r! C& ?
  14.     initial begin
    3 j  L5 W* A2 K% ~. D. y
  15.         nrst = 1;+ a) C- V' W9 G/ B
  16.         @(negedge clk) nrst = 0;
    . p; G) r) \1 W; m
  17.         @(negedge clk) nrst = 1;    end/ m3 V1 r0 F5 |, `0 i7 j4 z) S

  18. - x5 @5 L- Y* B* t* d
  19.     // key_in
    8 c. i. s2 Z& J' \' y8 w& E$ j
  20.     initial begin3 Z3 M% s/ l" W+ H
  21.         // initial value/ C3 {: o, q& D
  22.         key_in = 0;        
    : i/ [  b5 {9 |9 M! F
  23.         // wait reset* B0 ?4 u4 Q8 t. |& V
  24.         repeat(3) @(negedge clk);        
    0 p( ]0 n5 _7 M/ p
  25.         // no bounce        // key down4 W. o2 G- V1 b- D! {/ p, i8 x2 S$ s
  26.         key_in = 1;        // last 60ms' g; [1 f0 X1 x# V! Z& P
  27.         repeat(3000) @(negedge clk);        // key up
    7 c* U: d8 \  h3 c) P! j. K1 s
  28.         key_in = 0;        // wait 50ms
    - ^/ [7 R+ c- ^' j
  29.         repeat(2500) @(negedge clk);        // down 5ms, up 15ms        // key down, bounce 5ms
    4 t- ^3 d& S) V* [( p; L& G7 F1 x/ H
  30.         repeat(251) @(negedge clk) key_in = ~key_in;        // last 60ms
    : e2 t0 ~" Y9 W
  31.         repeat(3000) @(negedge clk);        // key up, bounce 15ms
    - o- [5 k0 }6 g" t( R, M2 ~& M5 ]% [
  32.         repeat(751) @(negedge clk) key_in = ~key_in;        // wait 50ms
    # d, Y( A! ^7 M6 @" |
  33.         repeat(2500) @(negedge clk);        // down 19ms, up 19ms        // key down, bounce 19ms
    0 X# [! a& d* y5 [
  34.         repeat(951) @(negedge clk) key_in = ~key_in;        // last 60ms, ^0 m" U; i/ v8 X
  35.         repeat(3000) @(negedge clk);        // key up, bounce 19ms
    4 S9 i% i$ T( R* H' E, a, P
  36.         repeat(951) @(negedge clk) key_in = ~key_in;        // wait 50ms; B/ w$ Q2 ?4 f( }1 @
  37.         repeat(2500) @(negedge clk);        
    0 P+ d+ G/ H- [) i- Q8 K- C
  38.         // additional, this situation shoud not ever happen        // down 25ms, up 25ms        // key down, bounce 25ms
    ! I3 D4 ~& @& W# o4 }1 `
  39.         repeat(1251) @(negedge clk) key_in = ~key_in;        // last 60ms
    ( t- F; k% t6 S# P+ \
  40.         repeat(3000) @(negedge clk);        // key up, bounce 25ms8 J6 L7 f% S6 B- Z8 y
  41.         repeat(1251) @(negedge clk) key_in = ~key_in;        // wait 50ms
    * l, [7 y  W  C
  42.         repeat(2500) @(negedge clk);        // stop        $stop;    endendmodule
复制代码

& T# \- B! C1 p. U' K4 K放在最后的,并不一定是最不重要的. H# O6 o- B& l5 ~" a8 p- n. c% b

" y" i/ [! e1 M. R/ N
对于上面的三种方案,我比较喜欢第三种方案,它更贴合实际的按键状态,以上的代码我都做过modelsim仿真,但还没有在实际的项目中验证。在整理准备这个博客的时候,我又想到了一个感觉是更巧妙的方案,具体是这样的:在第三个方案的基础上,因为按键输入有变化的第一时刻,输出就已经改变了,在这种情况下,我可以把计时的时长改为一个很小的值,该值只要比抖动中的最长高低电平变化时间长即可。但想想也没这个必要,且这个抖动的高低电平变化时长我也很难去给它界定一个值。
赞 收藏 评论0 发布时间:2020-3-20 14:10

举报

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