基于STM32的按键扫描测试程序(学习记录):4 q N/ D# g; Q; K
+ m; _4 f1 N4 k, W7 v$ w" F4 e$ g* Q" J+ W4 B
1、粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编译器打开,而不是kei;最后keil会正常显示中文字符;& Y3 q3 C, K: Y" J0 ]
2、程序使用嵌套循环实现4x4按键扫描,如果IO口设置过乱时,将循环拆分即可;
) ]0 O* b: z; \( Okey4x4.h源文件# i, o) Z# L6 w/ l
- #ifndef __KEY4x4_H& V6 k9 h$ ?7 c
- #define __KEY4x4_H
; A' i. n- M6 \1 T - #include "sys.h"
/ S' H6 L0 }; R - ( S3 E* f( b& `2 d
- #define KEY_X(X) PEin(X) // 尽量避免使用PD以上GPIO,便于移植;0 k3 R0 B. Q0 F; r* R
- #define KEY_Y(Y) PEout(Y)1 Y- N: U; k& B3 j# [
- - _2 {' y" v2 v- B4 {
- #define ALL_DOWN_KEY_Y { KEY_Y(11) = 0; KEY_Y(12) = 0; KEY_Y(13) = 0; KEY_Y(14) = 0;}
T6 p$ m8 q# @7 w, Z7 L -
( y: G& O( p% n% Q; | - void KEY4x4_Init(void); // IO口初始化
1 D+ ~; J! w/ ~: l! v - u8 KEY4x4_Scan(void); // 矩阵按键反馈函数 // 可配置任意矩阵按键使用!!
6 x' P: F/ X& L" z1 P- y7 ] - u8 KEY16_Scan(void);8 g- d3 I( U5 P' ^# e4 H
$ ?4 ]) x' g5 @- /**************
; \8 Q; b. w( L1 o3 l3 j - 按键扫描方案二:
& d! u% z) R. _ O+ S5 u! i - 按键使用8个输入IO口, 且所有IO口一端接VCC/VSS;: F9 L1 |4 n F( y8 v
- ! I: i# {) Q2 {# d& C$ V
- 程序框架: 5 [' z9 \& G7 C4 c& v. W0 e* f
- 扫描检测哪两个IO口输入高/低电平;VCC/VSS$ c' F5 L. O x& f. }7 ^
- 根据对应坐标,return对应编号;
/ q4 a, d+ N9 `8 T" ?' j( K - **************/
" g& ]7 X$ l$ v1 Y# T" N$ K - % ?" g* ^5 T( ]3 F
- #endif% B3 k: C8 c) \5 S
复制代码 key4x4.c源文件3 J g% i! {, ?3 ]
- #include "stm32f10x.h"* u k; _( C r- q: K
- #include "key4x4.h"; {( }0 p7 |1 g2 Z' @, o
- #include "sys.h" @+ Q7 E0 |' S* `( ^2 K" l/ g
- #include "delay.h"
( {2 z+ G6 l3 a: b0 w# y0 [ - #include "usart.h"3 x* \* Y4 x2 d! p# u+ l& i5 K6 G/ `% j
- 0 x; t- r4 ?4 R* O+ l
, i1 U q2 ?0 [3 p- /*************************6 v ^2 q+ l5 g; d: j f
- X:+ s9 t+ Z8 \; U1 _! V+ w
- PEin 7-10; i( _8 F& a0 x) B1 H, T
- Y:
5 n R! X5 [. Z( ? - PEout 11-14
: a/ I( ]3 ]" C9 w - *************************/9 q. G( Q/ I' n# K5 |* M; K+ \! V
- 3 g' I, t h5 H9 Z
- + N2 Y6 m- x8 {5 x* x& M
- // 矩阵按键初始化函数4 r, o3 y. C' n+ E2 i: S
- void KEY4x4_Init(void)
9 R, k6 |" v" o - {
$ Y1 P' @! V6 |8 Y - GPIO_InitTypeDef GPIO_InitStructure;
; p8 Z3 N: s4 C2 S, C' b, D$ n
8 F0 \: b/ h+ x5 J- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);' [$ }8 }: V0 g7 V# G! F. x @
- ! X" F2 k( h' Q4 o' g
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//KEY0-KEY1
- u& J/ e; K9 F; v) }. @ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入1 |1 D y( x8 j$ x9 r( [, E; P
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE
- c. D8 a. D3 y; X/ d F
/ _0 Q' |0 J5 A, D! c/ Q- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14; //LED0-->PB.5 端口配置# W+ y' K+ G7 S, v b" X
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
0 `, |7 ~6 h" ] - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz, R( L9 I w0 Q) M* C+ E
- GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE3 a* `( j- m8 x( N8 m; L1 u
- }
6 ^ B- [5 b3 [8 u& M: x - 3 R% o: \, f; v, v5 E
- // 使所有输出拉低;初始化; // 可以使用define宏定义,执行速度更快,不用跳转函数!!
3 c! }( @; K/ w- p$ ] - static void KEY_ALL_Pull_Down(void) o, X z- F* y8 c# m+ g
- {: w; I8 ^2 t9 |, P1 O: s
- KEY_Y(11) = 0;8 k" L9 |- q$ N7 m9 v! [
- KEY_Y(12) = 0;. N: }7 l" h6 Q a% Q# P4 c
- KEY_Y(13) = 0;
) ~3 v/ M+ \/ C3 h7 j% l" K - KEY_Y(14) = 0;
" Z7 @- v. g# L7 S% Y2 o! x: t. v" E - }+ Q- s% ?) E! |# u' M' |( O+ M
4 O$ G6 E! u6 g/ X# e' M; R- // 4x4按键扫描函数# s7 i1 u& H% V( n) s2 {/ |' G: v: B
- // 使用一个嵌套循环,完成扫描X和Y;, \- q/ Q. h/ P
- // 原理: 通过Y 依次输出高电平,: O( }8 G" o+ n7 c0 W/ C1 n5 T P! t% s* n
- // 扫描X 那个IO口输入高电平;" V& p: y' n6 b- z: {. C8 \1 e
- // X轴:输入检查、扫描;
/ t: V9 q) {4 D3 Y! P- C - // Y轴:设置输出状态;
$ | c6 B4 d- w2 F - // 该函数适合任意排列矩阵按键,只需修改循环中对应编号!!
' M9 |" h& U- g+ x% g8 M - // 注意: 1. 改变IO口时,该函数还需要改变循环中IO口编号;(可以定义好IO口编号,便于修改)0 u" F* J! l6 f I7 O2 f* j
- // 2. 该函数同样有优先级问题! 即同时按下时,只反馈先被扫描到的(0-15标号最小的)
4 m* R! n5 |# `: h/ h3 Q8 v - // 3. 函数同一坐标轴的IO口编号必须相邻或能有 规律递增可寻;(否则无法用循环判断)
% ?) \" ?; {2 [% i* o - // 4. 暂时未添加功能(模式): 按键复用 和 长按;
7 [5 F# ?8 o0 L: ?+ E" w) C - // 2020-11-13 返回值调整为:1-16
. ^* z. U% u- o; \" C( R8 X3 U0 ?7 V - // 11/14 不支持连按;解决重复反馈的问题;
8 r6 g7 D( n# I d! z, Z - u8 KEY4x4_Scan(void)- r- y, u) d) @5 @6 @
- {/ O( C: ~2 v3 s1 U& U
- int i,j;
/ e; b1 R# e8 J! y - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键
, o* s" v0 R0 T4 n' r$ {) ?3 m - static key_up = 1;
% A4 n4 y# J! N( r7 I, @ - 5 {* j# q* L F& C
- //KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低;; A% l8 A$ l& \9 V- |( t* ~. t
-
9 w4 m8 h6 l1 J1 e p7 `( Q - for (i = 11; i <= 14; i++) // Y输出IO口编号;" c( x& h& I5 U5 a" c
- for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);; c% d) [3 ]5 O X
- {
# m6 F& }; \, ^: p4 b9 a; ~2 [$ H9 w - KEY_Y(i) = 1; // 使PEout(i)输出高电平;
; C9 j- V* f2 G -
# Q3 {" k! n. K2 ~1 |* @5 k - if (KEY_Y(i)) // 扫描Y输出高电平, 可以取消此判断; 因为必须输出高电平时,才能正常判断!!
- l2 v9 s8 a' ~- c/ ]; e$ l d# v - {9 N6 y6 Q4 A+ Z8 P* n- t
- if (KEY_X(j) && key_up) // 当输入检查到高电平时;& B V+ e& X H: S
- {1 D8 X! r6 t" \( a2 E" j( l( p- `: C
- key_up = 0;+ d. F, [4 b: w! @6 K/ y7 ?$ \& c& k
- printf("按键扫描反馈: %d \r\n",IO_Sign);
% {- V: K# _- r$ d2 {6 Y - return IO_Sign;; i6 B6 ~# o% n1 l; t5 I( n
- }# y1 _6 u+ q) E
- else if (!KEY_X(7)&&!KEY_X(8)&&!KEY_X(9)&&!KEY_X(10)) // 当所有输入检测到低电平时,再次使能按键; 防止重复反馈
" [2 p; {7 t0 W( ? - {
7 e6 Y& F+ {9 y( J& C3 K* T* ? - key_up = 1;) ^) C# o; Y5 _4 ~( S% F. U& K* V
- KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低(低电平);必须在此位置;
( l8 Q3 M- u- f6 d" u - }0 `. f m+ M1 B/ e- l
- }+ u5 p) t# f" w: ~% l2 _
- else
1 I: q7 {0 }- n - printf("PEout:IO口%d输出异常! \r\n ", i);
6 n8 W' J' O }( z - }
% A! z9 w0 Z5 [% |/ a9 m" } -
: d0 Y( f# w8 w+ i$ s - delay_ms(10); // 按键防抖;- M1 r# U% K: |: S& V6 A
- }- A% {$ t4 Q7 f W1 r* L
, y9 K1 q6 R" C9 u, H
) `2 I3 z8 _9 `) G8 `4 B: g- /******************************************标准扫描函数*********************************************/
1 y/ M6 u' y3 r% |; [: C - // 支持连按
9 k4 s3 h( x! Q& f: _. l - u8 KEY16_Scan(void)% h. p) a9 A5 i5 B3 K8 [/ N
- {
" x: X# g1 x4 C3 {: O - int i,j;
r8 L% ?3 B# o. {0 Z$ J - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键
( |" j" a% T& N& L3 I' ^ - 8 V( r* R F: A4 h2 x! L! G0 Q
- ALL_DOWN_KEY_Y // 初始化所有按键输出拉低;
$ z( K, Q: T6 z7 X: C" I - & t+ `. W+ ]+ X. X
- for (i = 11; i <= 14; i++) // Y输出IO口编号;
, T0 |* N7 s/ N1 D7 p - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);) J( @3 L. d( P
- {* \" q2 W: l" W
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;/ f) O# J( U8 B% d+ A( S
* e! s* ^( M% _+ z" S! d& c- if (KEY_X(j)) // 当输入检查到高电平时;
5 r2 ~; i9 y4 o8 ]# X, F8 p/ G - return IO_Sign;
: s6 `' P; K+ g8 l% l* y9 I/ ~( C - }
' @5 V/ Q e- V' i - ( R! U/ J7 s( N/ M) X: U3 I
- delay_ms(20); // 按键防抖; // 连按延时;
- z$ |- q7 P8 ? - }
0 U$ ?4 R6 F. ^" D' g - 9 }$ w2 j8 x) e/ v
复制代码
$ A) v8 K9 I6 `( F
9 N- g5 `. e2 |8 z. d$ f2 M. C$ o*4x4按键原理图
+ U! e7 H% v# ~( V% D* s" o) G
5 ], a6 T) u2 v. g2 d
3、按键工作原理:* M1 z! z D/ r/ A4 S
当按键被按下时,相连的电路导通;即S1按下时,位于0行0列的线短路,使标号4和5对应的IO口相连(导通);* V8 ]) T4 x& `3 k. \
最后通过扫描检测输入输出状态即可判断出对应按键被按下;
7 @9 c U" r% U* r5 P
" ^! @% e ]# K9 J. l/ _# ^, e9 }7 b: g. [
4、程序设计思路:
( Q- I1 a! @2 e1 q6 |8 ~程序通过依次使标号1、2、3、4(列0-3)输出高电平,判断5/6/7/8(行0-3)状态;完成按键扫描;
1 P# B' f: w+ x. D! Q; c* l5 m$ I2 X X* w$ n( x- m: s' f! c
$ t/ g0 A: ?* ^+ V
( I) j9 C# s! M
) d- T$ [" O$ D3 D N( h |