基于STM32的按键扫描测试程序(学习记录):% P+ l& O+ `- \; f# k8 C% c; M9 Q
4 W9 f& n. ^. _" n* I3 I) i5 S8 ]
/ M7 v2 A" |: V6 L1、粘贴代码时,粘贴在源文件存放的位置中(如:HARDWARE中的.c和.h文件),用C++编译器打开,而不是kei;最后keil会正常显示中文字符;: W& ]( o# A: V9 @
2、程序使用嵌套循环实现4x4按键扫描,如果IO口设置过乱时,将循环拆分即可;
2 G# C; I& S5 ] h/ l1 ekey4x4.h源文件
9 ~# Y: r% x* c. ~" ^- #ifndef __KEY4x4_H! d6 [) e. v1 N0 q7 J
- #define __KEY4x4_H
2 V! a# T P6 x: U" ]) } - #include "sys.h". V+ j& f+ k1 \/ m5 i
-
$ C3 c% K! o# t* y - #define KEY_X(X) PEin(X) // 尽量避免使用PD以上GPIO,便于移植; e2 S# g5 a l0 p. a! G
- #define KEY_Y(Y) PEout(Y)
0 w9 C: S: c, T' ]+ h1 M# \/ h" T9 p
5 ?% C# `6 x& H( T$ T, o- #define ALL_DOWN_KEY_Y { KEY_Y(11) = 0; KEY_Y(12) = 0; KEY_Y(13) = 0; KEY_Y(14) = 0;}* G7 p' }* Y( d _( R, d) o
- - G. R% p8 _. B+ [: L2 }
- void KEY4x4_Init(void); // IO口初始化# K8 B4 K- N7 F( p9 Q9 S! c
- u8 KEY4x4_Scan(void); // 矩阵按键反馈函数 // 可配置任意矩阵按键使用!!
0 G; V* Q% g, O; d! A' B { - u8 KEY16_Scan(void);
3 k, { \. ?2 p
6 }7 T4 ?! ^. A6 T8 l$ v- /**************
& i0 a0 [8 Z+ | - 按键扫描方案二:3 g' T8 O M( G- ^, O" p
- 按键使用8个输入IO口, 且所有IO口一端接VCC/VSS;
# O6 n5 j3 ?6 l2 V5 g2 v
! x% a ?" X' S. C+ {- 程序框架:
* {) Z! f3 g- A, E) j9 A# q - 扫描检测哪两个IO口输入高/低电平;VCC/VSS8 T, u) U. q2 I: H
- 根据对应坐标,return对应编号;
$ O+ |! N, a$ \ - **************/; N# N( [8 i& ]# Y6 U- E1 x7 o- {% u- m
: b, R7 C3 A7 C) ]; O- #endif" H" G1 k3 W$ @- _1 U
复制代码 key4x4.c源文件
8 K, ]6 c5 m1 B- #include "stm32f10x.h"+ |! s0 f: n+ Q
- #include "key4x4.h"
. K9 d7 V" g) ?: G6 R* N3 |% s/ R - #include "sys.h" # j( [. g {6 Y3 `$ ~
- #include "delay.h"$ ~5 V! f' c) G% H* w- v
- #include "usart.h"$ ~; J6 a+ R* u. Q5 p' z
- 6 r0 }1 b( F9 _2 J2 Z
- ; L* `# \3 f8 h: [( h. S
- /*************************
* g# e( B) m% L+ S7 A - X:
) s6 F$ {- m1 U# @ - PEin 7-10
9 J& q7 M1 e/ {( T/ d( H - Y:
7 K* x) Y z" h' P! [ - PEout 11-14; ^- i) B8 [) v- a5 y' N- T
- *************************/0 W4 l7 c' M: I9 L; F. |
' L: U& N. Y5 W4 Y; o- 3 |( u4 B* Y& z6 g
- // 矩阵按键初始化函数% O5 U" }* x' ?. {3 }
- void KEY4x4_Init(void)" l3 q- w0 Q4 C# b
- {
4 n B! b" C: n$ K# ? - GPIO_InitTypeDef GPIO_InitStructure;
* \2 c8 _$ F0 W; J }) z - ! ]8 _6 Q& o! ?$ v$ r
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);. B* H# ?& D* A3 \# u6 e- K
1 Q v3 U8 V( I# ]# |- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;//KEY0-KEY1
S4 a, a7 E7 h7 h/ N. Q - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入
/ p* n* e; ` v5 w5 b# | - GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE
( q6 f- T0 r6 k- D& G - 5 a5 a8 G5 G, }, f
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14; //LED0-->PB.5 端口配置+ V! f+ J/ @- `
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出* j& s$ s& B5 g$ U# p
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
" y: {$ k/ v$ a$ K' T- M+ C! M' z1 x - GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE L7 |" B/ Q4 ~/ d. X4 N
- }6 n5 e( _% V% t/ l% I
- ; b, y ~# p* V5 N
- // 使所有输出拉低;初始化; // 可以使用define宏定义,执行速度更快,不用跳转函数!!
( s* N5 T7 t- m2 ] - static void KEY_ALL_Pull_Down(void)
# r5 U: H- y9 q( I; g - {
; I# a9 P# { X - KEY_Y(11) = 0;
6 d, c% D* A$ A" ?# ^7 v - KEY_Y(12) = 0;; w. L$ R/ V/ f, K6 _/ j7 w
- KEY_Y(13) = 0;# f/ ~4 g& Q* U2 U( P. V& S
- KEY_Y(14) = 0;! x3 P* ]- l) I; I, _. O$ a( l6 I
- }
% j/ \5 O. s0 R& ?, i g - % u U5 z) l5 h" r& c, k
- // 4x4按键扫描函数1 c/ Q! p2 h& z. H
- // 使用一个嵌套循环,完成扫描X和Y;3 L$ x4 L! r* o3 p6 I. I
- // 原理: 通过Y 依次输出高电平,6 p/ l7 \0 e S0 Y& @4 Y6 l8 S
- // 扫描X 那个IO口输入高电平;
9 ], u* M0 r1 Q# U7 o - // X轴:输入检查、扫描;
: P1 e9 h( @+ K - // Y轴:设置输出状态;' C9 v. L9 i# }5 ?/ q
- // 该函数适合任意排列矩阵按键,只需修改循环中对应编号!!! z9 B: Z, r Q; b
- // 注意: 1. 改变IO口时,该函数还需要改变循环中IO口编号;(可以定义好IO口编号,便于修改)- W+ f( o6 Q9 |& L! G
- // 2. 该函数同样有优先级问题! 即同时按下时,只反馈先被扫描到的(0-15标号最小的)
( x+ C) x E0 h, n% E - // 3. 函数同一坐标轴的IO口编号必须相邻或能有 规律递增可寻;(否则无法用循环判断)
2 O4 @, x! W3 J' b% c: y - // 4. 暂时未添加功能(模式): 按键复用 和 长按;: M+ Z% n# T r( B( M1 K: g. J3 v
- // 2020-11-13 返回值调整为:1-16- D( K1 t& n& A/ i
- // 11/14 不支持连按;解决重复反馈的问题;
+ {5 `& S: Q) A/ k8 f& n - u8 KEY4x4_Scan(void)
$ `; d1 m0 r+ [ - {, K, }0 `! |5 s. g! }
- int i,j;
P* a. F6 Q: s - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键# Z' B: c+ I' n
- static key_up = 1;
6 P# U5 c1 G! J# W8 H -
/ v |7 _# z( T6 M y - //KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低;& U0 h5 k; w# D# w4 G- R
-
N3 @* ?# y1 ~- d) b D* \ - for (i = 11; i <= 14; i++) // Y输出IO口编号;
, {3 S9 i( N) |. i - for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);
& \( q: `: f5 L5 {$ d% k8 d$ d - {; G a( u/ W) ]
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;
. k6 v) `' ]3 |. d! f -
9 \' T$ G6 |7 R9 X - if (KEY_Y(i)) // 扫描Y输出高电平, 可以取消此判断; 因为必须输出高电平时,才能正常判断!!! F$ N! Y$ h$ _2 H2 G. k
- {
: R) k# Z6 m% r: `. i4 h - if (KEY_X(j) && key_up) // 当输入检查到高电平时;
- j, ? E- r! C# W/ j8 S0 \ - {
" B, p: B/ v C- ~3 ^0 c! r ~ - key_up = 0;
' k7 c2 \* w5 I* j7 P$ Y - printf("按键扫描反馈: %d \r\n",IO_Sign); j p* f6 y, O" v, V
- return IO_Sign;& N4 Y) g/ t. Q# j0 S8 a# n
- }
9 f. Y- Z$ K( | - else if (!KEY_X(7)&&!KEY_X(8)&&!KEY_X(9)&&!KEY_X(10)) // 当所有输入检测到低电平时,再次使能按键; 防止重复反馈
7 Y1 _2 j u9 ], y2 }% t - {5 L% N) S, K: T M
- key_up = 1;
/ [3 J o! k- k+ A6 ?* p - KEY_ALL_Pull_Down(); // 初始化所有按键输出拉低(低电平);必须在此位置;
9 T7 A- \) C- ]' Q - }
$ v6 S9 r& ?7 q( c8 `' @; n - }( [# N! g0 T5 H% z7 }
- else
5 O n, T* \% b# P1 l8 ?3 Q - printf("PEout:IO口%d输出异常! \r\n ", i);' I0 |8 a N4 r
- }9 K$ H' z% C5 n2 S X
- ) i3 h0 `' j8 h4 X* I; x$ F! j
- delay_ms(10); // 按键防抖;
# Y: G! Z+ E, `" T - }
/ \0 y9 u% ]8 l/ u6 W# _! z: l
% {! B7 }4 q, L: Z* s- z- E& ` X) O8 F( C' y
- /******************************************标准扫描函数*********************************************/
1 X: W" c( q# R3 ?* j2 A8 _ - // 支持连按9 P' z( c6 u( L0 Y/ D, B# d/ [; W
- u8 KEY16_Scan(void)
6 o, d; ]3 @7 x" v8 W( z; g" A7 S: g - {, z: j6 {+ z$ o/ T& _6 j
- int i,j;
5 u) X7 P+ l1 ]) y t7 k3 V - u8 IO_Sign = 0; // 很简便的一个IO反馈方式标签!!! 且嵌套循环特有!! // 能返回0-15,适合任意排列矩阵按键
" R2 H. M3 E+ |0 w3 f- u - 3 Z( V5 i1 l1 I& f! M$ l3 p
- ALL_DOWN_KEY_Y // 初始化所有按键输出拉低;
' b! ?: U3 _" r; z" T2 k2 P -
8 r2 m! @" I5 y# X/ h - for (i = 11; i <= 14; i++) // Y输出IO口编号;$ A, L) E0 v7 ^! {6 X
- for (j = 7; j <= 10; j++,IO_Sign++) // X输出IO口编号; // 递增IO_Sign,反馈对应值(0-15);
% }1 M* `: }- s: P: t - {- R( c" k- W- {6 i
- KEY_Y(i) = 1; // 使PEout(i)输出高电平;
' [2 D" _5 Q% Q8 U4 S
; E& z" N) p9 X& y( Y5 S7 v) N9 L- if (KEY_X(j)) // 当输入检查到高电平时;. X: d+ d' v. t% o5 z* w% y* R
- return IO_Sign;
) Y2 K0 h0 z3 d, ~" q' }+ ?( a - }% j/ d$ ^! Q% b) @% V
- 3 M2 ~6 M2 |. H- ^! f: T- P
- delay_ms(20); // 按键防抖; // 连按延时;
) B' l' V7 ?3 ] - }
' q, [3 D; }8 H! F% M
$ B' H$ ~# A: H0 x! S* y+ U
复制代码 - ^4 l5 H2 k3 L7 v4 \6 _# z- b
" F! u% B4 M' R% k+ ^) f5 n*4x4按键原理图2 m- l8 X1 I& c7 l) Q( d
. a# S" [5 `( s, b: R# f# n3、按键工作原理:
) H1 j7 H% }$ F当按键被按下时,相连的电路导通;即S1按下时,位于0行0列的线短路,使标号4和5对应的IO口相连(导通);( t. v% c; c2 v; M# ^* O* W8 t6 _7 X
最后通过扫描检测输入输出状态即可判断出对应按键被按下;( b. z0 k3 m2 U$ E
* O7 g6 I) k2 k1 p' r: _: i3 Y
6 O9 }1 y5 K4 E- ]* v+ t; w4、程序设计思路:
( M1 f' }8 U5 z1 z程序通过依次使标号1、2、3、4(列0-3)输出高电平,判断5/6/7/8(行0-3)状态;完成按键扫描;
* S% p* \/ X$ t6 z6 x
% V! |7 K. w/ N0 U: z$ P' h/ j. e, z+ P1 a
) c/ t8 W8 F" Z" M- z& Q
4 X- ^4 M2 w. @; l7 R
|