基于STM32的按键扫描测试程序(学习记录):
1 @0 h+ J3 A/ F+ t, s" t6 v+ f
4 i3 X2 z' r, l5 t" |+ C' f
! c+ u' K s+ e3 X! f* L: s& L1、粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编译器打开,而不是kei;最后keil会正常显示中文字符;1 B/ k$ A( o5 z* B
2、程序使用嵌套循环实现4x4按键扫描,如果IO口设置过乱时,将循环拆分即可;8 I2 q0 m& I3 L( @5 [8 O0 e
key4x4.h源文件' r8 f7 y! T1 j p
- #ifndef __KEY4x4_H
( |5 m( i" ]" E9 d# U: l0 |4 F - #define __KEY4x4_H
2 w: i5 q7 w% V0 G& J& B2 i* J# X - #include "sys.h"
% ^& b; N, v; W2 {+ } - + j2 X" f( m- C; D+ u, |
- #define KEY_X(X) PEin(X) // 尽量避免使用PD以上GPIO,便于移植;
! n2 O, E2 g- Y/ j8 R - #define KEY_Y(Y) PEout(Y)' w! {% o' U. f2 \. j3 C4 [
- 1 r' {+ J* y$ h6 s; n7 ~1 ]
- #define ALL_DOWN_KEY_Y { KEY_Y(11) = 0; KEY_Y(12) = 0; KEY_Y(13) = 0; KEY_Y(14) = 0;}
0 C) V0 N. _! {' _7 p- X - 2 ]) H" B5 x* i& v) U2 B
- void KEY4x4_Init(void); // IO口初始化
. p& r) V" l# A; k2 s. f; B - u8 KEY4x4_Scan(void); // 矩阵按键反馈函数 // 可配置任意矩阵按键使用!!
5 h) F5 W* o0 S# d - u8 KEY16_Scan(void);2 w2 Z" h$ u. _" j% h
- - _- r# h- v7 | Y/ _
- /**************
; o! x9 Z9 Y; }1 s- O6 X; f; J - 按键扫描方案二:+ q+ ?: Z* l9 n8 O$ T
- 按键使用8个输入IO口, 且所有IO口一端接VCC/VSS;3 @9 Q: D5 v* N7 D
) D- H0 [- c$ Q' t6 k+ ^- 程序框架: 6 f8 f v: O0 e! F$ C
- 扫描检测哪两个IO口输入高/低电平;VCC/VSS
" d& [* P% g2 D |" ^ - 根据对应坐标,return对应编号;' U% U" w d, `
- **************/
% K8 ]- O& @% r) ^ - $ C& B$ o& X" G: }* W8 J4 @3 X
- #endif
5 [2 R3 m; q! t6 W* W1 r2 C
复制代码 key4x4.c源文件
5 K# \. \3 a6 j: {- #include "stm32f10x.h"
7 g+ J: q# F% }0 O* U* ?7 l - #include "key4x4.h"8 X0 ~9 L6 q$ e8 b7 @
- #include "sys.h" / U2 B3 q3 Z# U5 E# C
- #include "delay.h": a; h r/ Z3 F
- #include "usart.h"8 ]3 s. D* c+ b. u3 X; M
- 6 W6 @- B1 l4 v, f# F7 g' H3 o) l- k" K
' Z2 V9 Y( f( q: j; m- /*************************7 \, V6 Y6 U7 z9 H# b
- X: g0 M M4 n$ b% u+ k6 }. t! x
- PEin 7-10
4 H+ A! E# X- u0 n" z* t6 L - Y:
' c j" G' Z7 w- f3 G - PEout 11-146 V: V0 S# [0 u& p
- *************************/9 C8 e3 Y. m- U0 @9 `7 l
- : ?: n3 C) w- ^8 I
2 T# p8 }; Y) b' W- // 矩阵按键初始化函数" c/ i- ~1 \2 \+ F. B2 U& b
- void KEY4x4_Init(void), Q J! F. k2 w4 C4 Q9 t
- {
B/ z/ J: C0 i) O1 w - GPIO_InitTypeDef GPIO_InitStructure;6 A3 A' M6 [0 G3 |' j% M
- 9 F: m3 b' M/ L3 ~) R6 B$ k
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);5 Q4 b; B. E0 m5 w0 V; _2 A9 Z
+ Y W, Y" b& p) v- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//KEY0-KEY1( ?& e, s) o' W# u
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入
8 S" M2 v; B/ W0 K1 F# N6 D - GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE- i* V9 N h* D5 c; ]* M
- - |: W$ V* o- _8 B, S4 R' w6 @
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14; //LED0-->PB.5 端口配置8 |8 Z: d# W7 W# c
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出: l4 @1 _/ U1 b6 ]
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
& O, t! X" h, j# b( A4 n# E; N - GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE% a! I" i) {$ e1 a" [0 M
- }
4 V' Y9 X5 e, S$ ~ - ! G1 b: _3 _4 q0 m# \5 T
- // 使所有输出拉低;初始化; // 可以使用define宏定义,执行速度更快,不用跳转函数!!# n3 q* R! D( d# i* k
- static void KEY_ALL_Pull_Down(void)9 I* W& @3 O8 r* h" F
- {# {, s: o8 e X4 y ]1 D
- KEY_Y(11) = 0;0 N6 W# h/ J. N% [: F; C
- KEY_Y(12) = 0;
0 W& n4 o5 f; K/ D, B6 A - KEY_Y(13) = 0;
1 |0 F1 \1 J& }, b% | x& D - KEY_Y(14) = 0;# d4 _! {; w8 `3 }
- }
* z7 p" \" o5 Q. e; t" e6 T - ; y2 h3 k$ V9 G0 J. s: ~
- // 4x4按键扫描函数3 F& l) @5 F6 r0 @( g N3 D
- // 使用一个嵌套循环,完成扫描X和Y;# x# K$ ?( v7 g
- // 原理: 通过Y 依次输出高电平,% Z f7 s3 [* d5 V; G8 B
- // 扫描X 那个IO口输入高电平;
# `; M& ~: j) s7 Q - // X轴:输入检查、扫描;
9 g, t! U s- }# I1 [0 Y+ E - // Y轴:设置输出状态;+ X" }! M, l! ~* }8 t+ M
- // 该函数适合任意排列矩阵按键,只需修改循环中对应编号!!
# I4 |/ t9 N! ^6 r7 j2 D - // 注意: 1. 改变IO口时,该函数还需要改变循环中IO口编号;(可以定义好IO口编号,便于修改)
+ D+ E+ Q6 u5 I - // 2. 该函数同样有优先级问题! 即同时按下时,只反馈先被扫描到的(0-15标号最小的)
& w5 B3 g/ ^; }& O6 E2 ^ - // 3. 函数同一坐标轴的IO口编号必须相邻或能有 规律递增可寻;(否则无法用循环判断)
' I) H' Q- j8 f+ {& Y - // 4. 暂时未添加功能(模式): 按键复用 和 长按;
* y+ G6 x, L' `. f - // 2020-11-13 返回值调整为:1-16+ g( E- P1 I# ~0 O9 s# N
- // 11/14 不支持连按;解决重复反馈的问题;
* z% m' A7 Y1 X C/ K - u8 KEY4x4_Scan(void)3 D! @, |& z) P' O% w9 H. d7 o
- {6 _' o$ s7 s7 f- Z
- int i,j;
$ I6 z7 i4 D, a+ N - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键
! R w+ r; l2 A' E0 L& ^( P - static key_up = 1;
6 B& ]$ ?" w+ `7 ^ - 5 L: Q3 p; r( T4 X$ c
- //KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低;
% n7 w' B9 u& `; K5 J -
Q& p! J. m9 G& x) a - for (i = 11; i <= 14; i++) // Y输出IO口编号;
3 g, Y4 T$ g% S - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);
9 [8 G- B' i* {! Q4 O7 K2 k - {6 N" d7 ^ I2 q& L A: _
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;6 S- L1 n0 ] `3 {- F+ U* w' B
- 6 [7 t1 s) v! h5 f
- if (KEY_Y(i)) // 扫描Y输出高电平, 可以取消此判断; 因为必须输出高电平时,才能正常判断!!2 w9 @- j) L. o4 O1 P* n
- {3 r4 f: k% H" d
- if (KEY_X(j) && key_up) // 当输入检查到高电平时;, |( \6 v" T0 q1 H: @9 ?
- {( }, h7 W% K. [1 a; x. D
- key_up = 0;
2 R1 O$ t8 y+ u3 |8 h3 T - printf("按键扫描反馈: %d \r\n",IO_Sign);
9 k* c; ?8 f$ P9 S2 Q - return IO_Sign;. a, P2 d9 Y! X2 }
- }
+ K6 I. d7 X0 h6 P - else if (!KEY_X(7)&&!KEY_X(8)&&!KEY_X(9)&&!KEY_X(10)) // 当所有输入检测到低电平时,再次使能按键; 防止重复反馈8 M3 V7 E% }% s, ]
- {
8 @3 y$ }% p) a9 ?, p+ O - key_up = 1;
2 n4 [& Y! c7 i8 [( c+ T - KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低(低电平);必须在此位置;! L2 Q0 q' _7 N
- }0 L3 h$ L# a: C4 ?
- }3 ~) s, h0 Y: y6 v0 ?
- else) M" M |* Z) i4 P: c
- printf("PEout:IO口%d输出异常! \r\n ", i);
' h/ k; h7 w- z+ ]7 M - }
5 {9 m8 |/ c& p9 M" Y+ [% @. ^# o - % J! ~9 n! d6 a5 z& @$ s% `
- delay_ms(10); // 按键防抖;1 f% i+ O+ p' r! J5 o3 |
- }
+ B9 m( B* ?# F
1 ?: y6 b0 Q6 L! I3 t, V
$ [ h4 d6 Y$ C2 _8 ?( h/ ?! V- /******************************************标准扫描函数*********************************************/
0 ?3 T$ R, a% h - // 支持连按( Z. t0 L/ I- `) k# V9 n2 i# J
- u8 KEY16_Scan(void)
0 ^9 T2 B& ~; K; h. @, b: d) g - {
5 K+ x5 H. N. }5 @ - int i,j;
: Q! {8 p" p& d$ P - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键+ h& |0 N. ~) a
- 7 \2 [: f8 d. p" n& u; J) d
- ALL_DOWN_KEY_Y // 初始化所有按键输出拉低;& W! Q3 O0 y5 X. @0 k* s( t- u
-
. O% B8 M u6 m- [$ X - for (i = 11; i <= 14; i++) // Y输出IO口编号;
" v& x2 J6 a, o* @1 L* v% q% p - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);5 H' f8 O m1 i; Q: O3 S9 P
- {7 G- x4 [' R, ^0 k3 |5 {
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;# t5 m& n% d2 g/ l
- 8 ^ ~& ], g1 S" J$ z# I1 R
- if (KEY_X(j)) // 当输入检查到高电平时;
1 s2 s$ g9 i, } - return IO_Sign;
; v8 [* B9 A% S# @. ^! c - }
" l! p3 ^2 x0 m. G - 2 T) \% k0 Y2 N+ C8 e% c' x
- delay_ms(20); // 按键防抖; // 连按延时;' o0 M# ~4 E$ @2 a
- }
; P3 i. W. N3 |5 C: ?/ W5 |+ j
1 n7 u; O! O0 ~" R( a
复制代码 " H3 C) C1 Z, L" p
8 ]$ \- @& k; T' @
*4x4按键原理图9 H4 J( H, w: d3 }3 E
, { D( H! u1 J- o( ?3、按键工作原理:0 K! f4 V8 ]7 P; D6 w
当按键被按下时,相连的电路导通;即S1按下时,位于0行0列的线短路,使标号4和5对应的IO口相连(导通);
* k- W6 }" l* f; Z% Y8 F4 w$ `3 y1 o最后通过扫描检测输入输出状态即可判断出对应按键被按下;- F7 i( y. d1 t8 E2 c
$ K# x9 \- `; Y) O) W) r
: z' p5 X/ F x% e- H) S
4、程序设计思路:
x: d0 r6 [2 m3 ?程序通过依次使标号1、2、3、4(列0-3)输出高电平,判断5/6/7/8(行0-3)状态;完成按键扫描;
2 f( W3 Q& Q4 H& n& v% `# n. @/ S, X0 u( ?; X: l% K
8 x- [* u6 I$ `- f( A
# G% f8 p- i# L; p" D' g. r( q
' {, w1 r) c Q' x) y! `2 p! ~ |