基于STM32的按键扫描测试程序(学习记录):
) y/ L5 ?8 E9 i( _) ?$ Y; H0 l6 W6 O
% d" t+ ]) G/ I0 D0 A) ]0 q, m0 s p1 F; \# m+ w
1、粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编译器打开,而不是kei;最后keil会正常显示中文字符;# G! l& d) o2 [ `2 x
2、程序使用嵌套循环实现4x4按键扫描,如果IO口设置过乱时,将循环拆分即可;
3 O' N! h# Y; ^) e9 jkey4x4.h源文件
8 f& H! n. U, m$ [; h' ?- #ifndef __KEY4x4_H. U2 S B* g7 Q" t0 G
- #define __KEY4x4_H ) a( D) V) C$ W: `0 X/ Q2 |8 T
- #include "sys.h"
8 X$ r: l, t) o9 R: e - - p) x4 F/ \/ f9 k
- #define KEY_X(X) PEin(X) // 尽量避免使用PD以上GPIO,便于移植;4 C+ _6 U8 Y, I( w' G
- #define KEY_Y(Y) PEout(Y)
- F# { b* L) Y8 F7 B& p - 7 u; z: @% I, k3 a! ?# g
- #define ALL_DOWN_KEY_Y { KEY_Y(11) = 0; KEY_Y(12) = 0; KEY_Y(13) = 0; KEY_Y(14) = 0;}8 K! v( q& k4 W, m) h' k
-
( P k5 W9 H1 X, y# u. H - void KEY4x4_Init(void); // IO口初始化9 Z/ D5 {4 j5 U# @5 j2 y4 t
- u8 KEY4x4_Scan(void); // 矩阵按键反馈函数 // 可配置任意矩阵按键使用!!
4 v6 Y% ?% h0 U* w5 ^1 z: ^2 u8 t( | - u8 KEY16_Scan(void);* g" o# f" X' a9 w
- 3 n* E! X7 Y: P4 w
- /**************
& f& @ s/ ]- s: E - 按键扫描方案二:
L6 b, z. l$ ^4 E/ d! R# K4 o - 按键使用8个输入IO口, 且所有IO口一端接VCC/VSS;
. u: n+ U( C2 V0 Z8 D0 i
. r! ?2 t% P3 G/ N$ R/ O- 程序框架:
: z; M/ n( n* S n. U - 扫描检测哪两个IO口输入高/低电平;VCC/VSS4 Y/ _: c5 L4 c, N; p# B
- 根据对应坐标,return对应编号;8 X" v$ c1 F; |, G; z( |- J4 S
- **************/
9 q# w! L* {, m
" n& r( X* s, m8 r4 }/ M& F- #endif
+ z3 z' w- ~7 [. m
复制代码 key4x4.c源文件
8 { }% t4 s0 V' {- #include "stm32f10x.h"
9 L# }2 a7 H' K+ ?% Q - #include "key4x4.h"6 b$ I# H" M$ {& A
- #include "sys.h" 9 @2 H7 I& o @6 ]
- #include "delay.h"
# C) @: J( i" x7 R6 N/ q) {) | - #include "usart.h"
. e; k3 q, C0 Q( ]+ A! w - / f& t4 b e2 C9 M% Q4 h
- % H0 v+ J: E' a" t& v& t2 t
- /*************************5 y! ?! I' e1 I% {; y) w+ ]
- X:
i0 [0 c: w/ G2 U* I4 Y( ? - PEin 7-10
$ Z; I6 e3 ]. C+ m# Q( T - Y:4 z% s ~$ U0 s; W+ n6 e
- PEout 11-14/ S* ]( J: q2 `$ g; L E
- *************************/' D' H5 I" A! `2 j& H
, W( x, N, C) k- m+ T2 x" s" \
) \, u4 M/ u% c; X- // 矩阵按键初始化函数
3 y- x$ \+ I& t! o - void KEY4x4_Init(void)
1 Y# N/ i& I: x# _ - {+ w; Z* e# x' H& q3 h7 J4 Z
- GPIO_InitTypeDef GPIO_InitStructure;# M( f( R* Y0 Q! {
- 2 d# E' n. J4 ]: \- S2 b; H' b: i
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
& X) u5 [% g4 C- }1 M; {
4 \8 e3 W/ u+ ^# I8 Z- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//KEY0-KEY1
; r. M! w5 W: B6 ` - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入
4 V; _8 W! T, X! | - GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE1 B# i, N& R# {. u6 V7 ~
0 E0 _( l* W5 M6 B' `- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14; //LED0-->PB.5 端口配置
6 _! N' f V; L$ L0 m - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出# i" V& q4 y" X1 e$ U1 t. t) l% ?
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz" m9 j; f( [ K5 j& J
- GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE/ z* h; w9 @: a* s7 l8 h9 ~
- }
' k; O( y. Q: p' m$ O; T; w3 C
6 \. ?/ R, `2 n- Z7 u( _6 u- // 使所有输出拉低;初始化; // 可以使用define宏定义,执行速度更快,不用跳转函数!!
9 q F( }5 _. @* q `! P! S/ i/ { - static void KEY_ALL_Pull_Down(void)( d" f7 f# U( O8 x! c
- {
; U# [' e- u6 H, j/ a6 j' g - KEY_Y(11) = 0;' c" H& Y% R \$ j, D: Q# Z. c
- KEY_Y(12) = 0;& n/ T3 j. J1 ~: v, e
- KEY_Y(13) = 0;
7 V% V* k! ^, h - KEY_Y(14) = 0;
3 O. b; q6 `9 b. S" V& B6 D. I4 V, M - }7 u# t& t& Z6 M) q& C& T5 a- B. J$ H
- ! Z' f) W+ m' t y; G: Q |
- // 4x4按键扫描函数+ M4 ~9 W4 t: S3 r" O" w1 A( `
- // 使用一个嵌套循环,完成扫描X和Y;0 @) F' V' v0 ]+ V; m
- // 原理: 通过Y 依次输出高电平,
9 p. c: Y6 s u( c. A$ K - // 扫描X 那个IO口输入高电平;3 o8 i3 Z9 U$ [% R
- // X轴:输入检查、扫描;
2 r5 n- \- S$ z7 h9 C& J8 C- U8 e - // Y轴:设置输出状态;
6 d( ~' }6 R& b t: A - // 该函数适合任意排列矩阵按键,只需修改循环中对应编号!!
( U7 }& Z& x" J - // 注意: 1. 改变IO口时,该函数还需要改变循环中IO口编号;(可以定义好IO口编号,便于修改)& A; P/ R" G- I; E% B& ?, F
- // 2. 该函数同样有优先级问题! 即同时按下时,只反馈先被扫描到的(0-15标号最小的)+ u' c. Q% K, N6 n0 g
- // 3. 函数同一坐标轴的IO口编号必须相邻或能有 规律递增可寻;(否则无法用循环判断)
3 X/ ?3 g) O0 c1 Z4 C( F1 m! x+ D* o - // 4. 暂时未添加功能(模式): 按键复用 和 长按;6 ?' R0 }" z L# S
- // 2020-11-13 返回值调整为:1-16
( M3 S: E. M+ E3 r6 F - // 11/14 不支持连按;解决重复反馈的问题;
0 f7 d0 N# Z2 Y# h/ D - u8 KEY4x4_Scan(void)
F# H& [' R' |1 g' z# o- w - {
" _; Z* h7 K# ]( W: J - int i,j;" @8 j0 S" E2 }% l' ` b1 G3 M
- u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键; V9 Q \+ j" R; |/ ]3 w
- static key_up = 1;
5 k& u4 I _' H$ w% Q+ ?, i5 w - / B$ u& D5 G1 L5 v* F3 E
- //KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低;
6 v; R1 F: `0 i5 z+ S: ] - 9 _: M7 p+ Z% C* p+ b: ?
- for (i = 11; i <= 14; i++) // Y输出IO口编号;8 c/ z% J" \% q" M; k
- for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);
# s2 X. b# h K: z3 C( ? - {' @. \. ^' A% E& F
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;
; Z0 B' C1 |; X2 v. P -
! m' q% K" L0 Y9 z. p; [ - if (KEY_Y(i)) // 扫描Y输出高电平, 可以取消此判断; 因为必须输出高电平时,才能正常判断!!
: I, k' n* [' {+ w( s3 j; A8 p - {$ |. {4 y9 F+ r) J
- if (KEY_X(j) && key_up) // 当输入检查到高电平时;
4 |3 n- d, `: Z: d - {$ j3 i8 E; X: Q6 ?7 M& x/ R. i
- key_up = 0;4 V S' Z9 S. |8 U2 P* ]) W( y
- printf("按键扫描反馈: %d \r\n",IO_Sign);
p3 n/ K7 ~5 _: e3 R+ q; J - return IO_Sign;
7 D$ F% h+ r" f/ S% W - }
- Y4 X( q; ?1 X5 m1 w - else if (!KEY_X(7)&&!KEY_X(8)&&!KEY_X(9)&&!KEY_X(10)) // 当所有输入检测到低电平时,再次使能按键; 防止重复反馈
3 y1 D$ Z' |' M; p; k6 y - {, k0 B' q$ ^" _6 {) O
- key_up = 1;; A1 |$ q0 k _( z( P
- KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低(低电平);必须在此位置;
; B6 q4 c' D- L1 [) L - }; E) |7 ~, z0 L& P# w* N5 c) J
- }
) T/ z! T9 s* S, ~! v$ W+ a - else
4 C" J( `$ ]: ?% n - printf("PEout:IO口%d输出异常! \r\n ", i);
' T( K' [, W6 E+ J: T* [ - }6 ?; ~) b/ h+ w# v; W& ^! q: s
-
5 I' N& |4 N3 N* `1 V- w/ ^ - delay_ms(10); // 按键防抖;; j7 a. d( W& T, N$ m, h3 P7 R j
- }
2 P. g- N$ u) I% W2 N
$ k) c( W3 z, W" [2 Z4 V9 G- , y8 D2 s6 p( i- z% ^$ r
- /******************************************标准扫描函数*********************************************/
( f8 f( ` z1 E9 z( R) V3 ^) ^4 I - // 支持连按* Z. y3 a, I' L; y; U$ z
- u8 KEY16_Scan(void)
/ J7 X; l% `* r$ k$ n- s/ z - {
/ S j4 v) ?7 A6 b: b - int i,j;( \: N" D: H- z. I5 F5 E/ s
- u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键
, C P1 c) I4 `7 O - # Y& E6 U$ r# T$ G0 A* E- y4 @5 U
- ALL_DOWN_KEY_Y // 初始化所有按键输出拉低;* w8 @1 n" g& a) f5 F
-
8 P) l b0 p0 L7 u - for (i = 11; i <= 14; i++) // Y输出IO口编号;/ K% I. L7 C: B j
- for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);9 m/ }! v& D: E: t, e% {2 h
- {; O; }! {7 g, s8 _5 ]7 D
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;9 B+ k3 t! a3 I# V3 _5 f
- 2 A0 r* y6 }% I0 A& `
- if (KEY_X(j)) // 当输入检查到高电平时;8 |5 ~& c1 [/ S# i2 B
- return IO_Sign;
7 O! l, Z. [# J! ? R! Z - }
0 y2 e. ~! t( k -
/ D% m* c) i ^9 {8 N' S0 \" Y( r; A - delay_ms(20); // 按键防抖; // 连按延时;+ \4 @7 M' H5 {5 V; K
- }
" v' O, A2 u5 B# I% E2 j' g
/ U) T! F; s4 V" _
复制代码
; b4 ?: v, ]! n3 c. k. I9 V# `+ \
3 `; c; W; @+ m1 K4 S5 n; }*4x4按键原理图" u& s6 m* a5 Q5 M4 J+ o6 S
% Z; q- g8 U6 k8 ?8 Y" ]2 j7 Q# ^
3、按键工作原理:
1 l! i+ i5 S, N5 ?' I, P当按键被按下时,相连的电路导通;即S1按下时,位于0行0列的线短路,使标号4和5对应的IO口相连(导通);
! d* K6 B1 N* E' c- `) I' A8 o最后通过扫描检测输入输出状态即可判断出对应按键被按下;5 d) I" Y0 b: X7 _4 y
& F" _; t& g' k# \6 ~1 H' x; C
8 ?* y- ^$ M* H# S: O4、程序设计思路:
. o) _) l) E6 [# |* V! j3 g$ x程序通过依次使标号1、2、3、4(列0-3)输出高电平,判断5/6/7/8(行0-3)状态;完成按键扫描;# F8 S+ s, ~$ j. @" Q# P7 s( a
5 e! r3 K5 f; ?4 z% }3 r
& d) z; @1 h2 Q5 {6 M0 r, m$ E6 N; P
4 x' ?3 g O* _. O
|