基于STM32的按键扫描测试程序(学习记录):
# N( q. N( r6 u4 P
* |5 f6 M* f/ ?7 }2 c9 s# k9 h, p" U/ ~' Q$ n5 }; L# ^& ~
1、粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编译器打开,而不是kei;最后keil会正常显示中文字符;
# W- G4 x/ K. S2、程序使用嵌套循环实现4x4按键扫描,如果IO口设置过乱时,将循环拆分即可;
3 ?* Q7 l: T' E$ h& ?9 ^key4x4.h源文件
4 ^1 }. i0 k" y9 }! j8 a) |- #ifndef __KEY4x4_H
7 D9 I2 H( q2 a7 W8 G' T2 l - #define __KEY4x4_H
5 x6 G2 {* x" U0 x! X3 U - #include "sys.h"
) q0 i1 G4 C) k1 B - ! v, E8 I! @ G7 h, z1 x& n
- #define KEY_X(X) PEin(X) // 尽量避免使用PD以上GPIO,便于移植;
1 E9 a* r7 P0 E9 a" Q" T - #define KEY_Y(Y) PEout(Y)% c& r2 F! [+ U; I7 h5 s [
+ E$ H! L O1 M1 C1 D5 N- #define ALL_DOWN_KEY_Y { KEY_Y(11) = 0; KEY_Y(12) = 0; KEY_Y(13) = 0; KEY_Y(14) = 0;}
2 z' L3 c! w) M: Z# D -
% [) Q' {9 i S7 h3 [ - void KEY4x4_Init(void); // IO口初始化/ b, M/ l7 j& c& K
- u8 KEY4x4_Scan(void); // 矩阵按键反馈函数 // 可配置任意矩阵按键使用!!
0 j8 |% p( a9 z2 K1 s - u8 KEY16_Scan(void);
) W' y5 @$ x( x. _! d- Y2 V - 3 H3 { g% [) B9 j0 g$ @
- /**************. q6 A& m% Y4 x( [$ N1 Y
- 按键扫描方案二:2 p; ?3 E: w3 n/ J/ c, K6 c
- 按键使用8个输入IO口, 且所有IO口一端接VCC/VSS;
+ M6 H; e6 x7 V0 V) v - 2 u5 F% g+ O4 h( _# n7 X- H
- 程序框架:
. c; |" x: z! S( a; L+ Z - 扫描检测哪两个IO口输入高/低电平;VCC/VSS; m9 y8 r9 C/ z2 K j. j
- 根据对应坐标,return对应编号;7 V3 S" W7 N0 A, R# V8 B f2 j
- **************/' P" n o, K; z/ J! {1 [3 \
4 S! ` m/ x% D/ k4 F3 ~- #endif
4 P7 o: F/ G/ h
复制代码 key4x4.c源文件! A$ h0 O* D) {
- #include "stm32f10x.h"2 y! p; m3 r+ s6 A/ ^, L% _$ \! h8 R
- #include "key4x4.h"
2 d" V$ t1 R3 r. }* ~ - #include "sys.h" 9 | `! R( E6 c$ Q
- #include "delay.h"4 ?+ K) a8 F* N( R$ j
- #include "usart.h"6 W6 L) t; i8 ~5 V' _/ C" w. ?5 t
) Q% ]7 r, H0 e- s- 3 m! B# ^" `: U4 V" \" h% L% s2 y
- /*************************1 v8 b0 p8 a6 G" F
- X:* G6 z. f( k9 M
- PEin 7-10- P3 V) N) f/ Q. k: Z) w
- Y:/ l; {& l1 G& R7 r/ }
- PEout 11-14* J' k- U O: G' v' R
- *************************/
* C% W2 i( x; O& i. z+ j0 ~! N
; v8 j8 v$ e8 z7 s5 A- % ?# s* u6 F( Q7 B- K* f
- // 矩阵按键初始化函数$ F* ?# Z) c3 ?) P) c
- void KEY4x4_Init(void)- n3 E- V/ Z% w( _/ Q+ H
- {
1 |- u8 [* h5 e2 P" [* X" q& f8 e - GPIO_InitTypeDef GPIO_InitStructure;
/ F% M: l6 f2 a, v& m
+ V. h _" P l, F+ r* y% F- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
, ~0 p; p: l! g k, h) [# [8 l
$ @$ c& i0 o3 n" [2 F I- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//KEY0-KEY1
+ V; ^; ]/ q' F: }/ w( ~ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入2 \, F# M# K [9 [+ G! ]
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE% S: f- [* K, t( w) }# f, u
l0 Q# L& D0 G7 p( O- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14; //LED0-->PB.5 端口配置# E+ k1 \5 Y1 ^4 z8 E0 C1 B
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
; E; e& u# s9 v" k9 i \) z - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
% e2 E" N/ G. }: P) L - GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE6 F5 K0 n6 e a- j% V. q' g' ^; g
- }9 ~: n3 g0 y6 {* j% e
4 Q) i* }% W- ^; d- // 使所有输出拉低;初始化; // 可以使用define宏定义,执行速度更快,不用跳转函数!!
" P: a% i8 ]1 [ - static void KEY_ALL_Pull_Down(void)1 o4 L( x2 k; K0 n# p+ K+ ]
- {& [$ G/ m, q* C: l( S% G) T8 `" _
- KEY_Y(11) = 0;5 Y; _& ^- R7 `
- KEY_Y(12) = 0;
- }7 a+ d2 v- ^4 J - KEY_Y(13) = 0;8 `$ g4 |/ ]! e
- KEY_Y(14) = 0;
0 o% Z/ w# @; [- R3 z - }4 |. n$ Z! i( ?0 h; n, K( W
$ X- x. N7 z0 x5 V9 i- // 4x4按键扫描函数
9 f7 u9 I5 s0 S( \& ^! a1 P - // 使用一个嵌套循环,完成扫描X和Y;
. |8 W6 }2 Q _+ L; x8 J8 K - // 原理: 通过Y 依次输出高电平,
& B, j* H4 H' S2 c# t - // 扫描X 那个IO口输入高电平;- j# X; N) M/ D3 U+ `
- // X轴:输入检查、扫描;
+ R9 [/ n' k# Q, M# a# W5 l - // Y轴:设置输出状态;
* I" X% O% b4 w! a: c1 S1 l - // 该函数适合任意排列矩阵按键,只需修改循环中对应编号!!
! l9 d& l: `' N. x, W7 y2 o# \ - // 注意: 1. 改变IO口时,该函数还需要改变循环中IO口编号;(可以定义好IO口编号,便于修改)9 g }& ?, l G1 {5 i, g* t
- // 2. 该函数同样有优先级问题! 即同时按下时,只反馈先被扫描到的(0-15标号最小的)
( \' b: x/ O) Q4 W& ]' M# j - // 3. 函数同一坐标轴的IO口编号必须相邻或能有 规律递增可寻;(否则无法用循环判断), c. _) I# E3 F+ B
- // 4. 暂时未添加功能(模式): 按键复用 和 长按;
, P* K: w/ B: |5 L; c& E' A - // 2020-11-13 返回值调整为:1-16! f% b1 x5 q: ?0 @9 P
- // 11/14 不支持连按;解决重复反馈的问题;
$ N& L: f: ]! Y9 ^ - u8 KEY4x4_Scan(void)
- o; E5 C8 @8 |/ V6 j - {
3 W5 Q" ~3 ~/ \: K. y - int i,j;
; O8 n) q) o+ ]# n& Z. Q( S! ?( O - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键0 Q/ x: H4 l1 u" b
- static key_up = 1;% U" G3 i; y) Z8 I3 j1 q: B( I4 r& U) c: K
-
% s' h6 P* k) q# Y' k - //KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低;
# s2 G# {8 X) v6 @' \( P# C: ] - ( ^4 E ?* G9 E
- for (i = 11; i <= 14; i++) // Y输出IO口编号;
; |3 ^% ~ F* e - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);
2 M9 C6 H) U- J' ?; K$ O - {5 M1 I$ O% O1 L( Q
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;% `* l! h4 Y; R) N
- / [# p" P4 g( E8 x( F/ t
- if (KEY_Y(i)) // 扫描Y输出高电平, 可以取消此判断; 因为必须输出高电平时,才能正常判断!!0 ?7 A, I% K0 ?* S4 @ n( R
- {
- z `& @3 V$ H8 x. _" ? [ - if (KEY_X(j) && key_up) // 当输入检查到高电平时;
- Z* W4 D6 J2 h - {8 C; N& e% V- d. o; q0 s' f$ J
- key_up = 0;
% C& ]5 v2 A8 J. J - printf("按键扫描反馈: %d \r\n",IO_Sign);' H) b; H' l& I1 Z* W8 v
- return IO_Sign;# J- V. \# b2 b8 p6 Q
- }$ ^% M1 ]/ r C) l. W* u5 u) r' v+ H$ o
- else if (!KEY_X(7)&&!KEY_X(8)&&!KEY_X(9)&&!KEY_X(10)) // 当所有输入检测到低电平时,再次使能按键; 防止重复反馈
, [) ]; ~6 ?" |& D. C9 T - {
2 q4 G9 K# }# F- [. u. r( ^ - key_up = 1;) ]3 y' z0 P) o" v1 Z+ m
- KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低(低电平);必须在此位置;
7 C# h8 R6 ~8 `) | - }( ?) ~3 x1 ~; I
- }
; p+ C/ f$ J3 d" z6 [ - else/ C7 m( n/ j0 O, c
- printf("PEout:IO口%d输出异常! \r\n ", i);2 u& u2 f: r$ D
- }# A" {; P$ l# i* i
- , }/ ~# V" L* i3 F) O% n
- delay_ms(10); // 按键防抖;
7 X. J% [# g* E3 l4 z5 J - } d; v4 R+ K" X. e
- ; q! d a8 C" }( _2 ]3 U
, j( r O* ]. P1 P9 x- /******************************************标准扫描函数*********************************************/
& _9 M! Y+ z8 [, y - // 支持连按# ]6 U$ G% }' T+ a
- u8 KEY16_Scan(void): |- s6 l8 \; u) f: K
- {
9 e: N% @" n, y& h+ B |0 n% H, X - int i,j;
1 E+ ~& ?3 L5 h6 x7 E5 h, F - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键# _) M' V" U3 J
-
( f3 d5 l+ `6 d! P - ALL_DOWN_KEY_Y // 初始化所有按键输出拉低;8 n7 M# k3 `% p$ H3 A
-
; A9 R* A9 Y' @5 V t3 {9 r - for (i = 11; i <= 14; i++) // Y输出IO口编号;
) Y9 t; u& k7 a2 f6 ` - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);7 r7 o: B, q# n
- {9 G2 e! v1 ]( M( Y+ U" U4 C3 U( V
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;
5 c- g$ j6 W/ Z/ \ C9 _ - 0 s* R+ I, H! d( M/ [" n& N
- if (KEY_X(j)) // 当输入检查到高电平时;
. c; ^" _5 s4 A9 g _) g& ~' x - return IO_Sign;
1 [( f" E9 A& c% k) t - }+ o, I6 [1 g* | @2 r# h% d
- & ~( H; l- V+ J% H
- delay_ms(20); // 按键防抖; // 连按延时;
' `9 h& T1 G: } - }
1 z$ F; k: R3 @ p! N- C
3 C, Y' B1 P9 F) t4 t3 y0 a
复制代码
$ w9 T" L; o8 ~3 ]) r; ]* ~
+ F- D; R/ Y6 F" O9 p*4x4按键原理图* o d8 \5 V( x. a! Z( P" [2 F
0 H- _" r7 K: U% Q/ P4 B3、按键工作原理:1 i5 ~+ k! B) d1 S, u) r
当按键被按下时,相连的电路导通;即S1按下时,位于0行0列的线短路,使标号4和5对应的IO口相连(导通);
+ O& S9 n" I: L) n# d: h最后通过扫描检测输入输出状态即可判断出对应按键被按下;% E( b" k& A. h: U6 E/ G
) U' x0 i& D$ ~
1 I9 t/ A! t' ~( t6 `" _9 I' [
4、程序设计思路:
- Y8 E N- s% y, E7 ^7 }程序通过依次使标号1、2、3、4(列0-3)输出高电平,判断5/6/7/8(行0-3)状态;完成按键扫描;
" k/ P, Y T/ C- z" u* R& q
$ D" e* {/ y" k5 g4 \4 f+ X* x4 J7 s A
, y8 E8 u( A" ^6 J6 {- J6 F- U2 o
2 l2 n# t/ N' l9 ~0 N6 k0 a |