你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32CUBEMX+MDK5实现按键以及LED灯

[复制链接]
STMCU小助手 发布时间:2022-5-6 10:47
1 概述- X: B- `$ ^# {; B" S. b
1.1 资源概述

' \0 j; s( J- [% S! I- a  E开发板:正点原子STM32F103 Nano开发板, B' H+ a. H) H* n' w% b- Z. Z
CUBEMX版本:1.3.0
7 g9 V) C1 v) v' u. Y' [+ [0 sMDK版本:5.23
6 P" |5 f4 P6 W+ z" z* R8 r. n8 }2 c主控芯片型号:STM32F103RBT6" _% B7 j9 }/ K% M; Y' ~9 Z
ZS`RD5LNO2DBRH(2M@_V.png / u8 O; L' Q/ _& G8 o* B

+ g6 F% h) g2 e1.2 实现功能
" P6 \. |; t! `$ G$ i4 `使用KEY0~KEY3分别控制LED0到LED3的亮灭,KEY_UP控制LED4的亮灭.按一次按键对应的灯亮,再按一次对应的灯灭。另外长按KEY0可以实现蜂鸣器的鸣响。$ {3 s' d7 [$ w1 @5 r0 k3 t

0 o( r. _  }0 @7 J# z2 CUBEMX的配置0 W# c4 i, A3 p! Q
2.1 GPIO口配置
% }5 \! p( t4 O0 |% ~& q2.1.1 端口配置图
9 w# B9 A; w) ?: ]9 ?
(部分引脚配置此次实验未用,但是不影响程序的编译,不会报错)* \; X6 }9 M) ~/ Z
; H: F2 h6 b4 u: @
5HZ[J_8UQATS(}_(DF]0L.png
! j- k$ F1 W  w" e5 y, D5 t+ ]+ J8 ?
4 r/ k0 B/ l4 e- Y( S" {. K' k2.1.2 端口资源汇总表  s1 h2 X, [' A% a7 f
查阅开发板的电路图确认实验用到的GPIO口配置资源如下。- H5 ]3 v; ]7 l& \+ B! |: t' S

% m7 W' s! g8 Z7 Z: _4 Z- }  k 6AJ(ZJA]8LQ2}TJ}79FYM.png
/ z; f8 ?1 [8 u+ g2 w. N2 t5 ]8 s& y2 K+ L/ f
2.1.3 通用GPIO口的配置
7 P- V% V) v' e+ F7 bKEY_UP是输出高有效,因此初始化时,配置输入下拉。其余LED端口或者按键均是低有效,所以配置为上拉。这里定义User Label(用户标签),方便后续程序的移植。
& ^& C4 L3 O6 L* N" X" h# E+ J6 K% K2 L0 y0 u% T
X2[OWJ']04K)LR00U{W22.png
6 V7 e3 i9 w9 i, m$ L% P" D" u. Z# e6 [$ o
2.2 时钟配置
! F% c; t" `; R, ]! _+ n) [& S2 H2.2.1时钟RCC配置图* p. _* X9 a9 i2 c
使用外部HSE,PLL选择9倍频,系统时钟选择PLL,HCLK配置为72MHz,APB1设置为2分频。+ _% v3 h* c# @! U
7 I% I1 ?) j& |! O5 d
45$~GD1JD1A1KZC]LE_86IP.png
8 B" i6 R5 i$ C) j) X: W8 p& H  f5 E$ q" [1 Y
2.2.2晶振选择8 Y+ l1 B0 O3 V+ o0 S" G6 z
选择外部晶振,高速晶振为8MHz,低速晶振为32.768KHz。/ e% i) G* B4 U# b" Y: H# b& \

% c- W1 V3 F5 i4 `* b+ q 6QJ]9AV`DL$S@`NWU9YT12G.png
8 l* N5 X4 b; Y2 C4 v4 n" J& F
3 [+ N. \3 B4 w4 K" ]2.3 调试端口
4 f! N3 s5 g  P7 N' @! g  W采用SW方式进行调试烧录。* y) O. C; s0 W( Z' w& @0 k) K+ J$ S0 r
& H5 V* v( E7 t" l1 d
60RV@X1OQ[W63WY[OVR[DRM.png
2 e) X+ i4 W" E7 N+ o$ A  T2 i$ G
% x0 C4 N" C- T* i* v. L2 ]6 E2.4 CUBEMX工程管理配置! Y( z* Y4 H# L* Z0 I
1、选择Advanced、MDK-ARM、V5。
" C9 d8 b- c8 K7 D& `$ t! [7 V5 P4 f2 r! [+ D( j
BGZHEWX~8NUI8DA8`T1IUYI.png
$ Q+ i7 q* O; S1 V" c  @7 D! i
% M7 ?" C3 g/ d( ~2、代码生成配置4 Z$ W. p2 `7 E. V
勾选生成外设C文件和H文件。否则这些端口的配置都会存在于main函数中。  D) O7 }5 F' t" J: |( j, P% x
, Q. Q$ ]/ O4 V6 t  c- _& i2 \
RCD2K]7IVJ7RK3N]P8IJI.png
5 J# s0 K8 ]6 V# C5 \: H
6 H+ H4 k4 F( \+ j* Q' x6 M3、高级设置
, N. s: n, W  y, n) l全部勾选HAL函数。HAL函数的全称是Hardware abstract layer(硬件抽象层),这里还可以选择LL即Low layer(底层),函数和HAL完全不一样,优点是直接操作底层,程序占用空间小。咨询ST原厂FAE工程师,后续新开发芯片均不在支持标准库函数,仅支持HAL和LL函数。比如最新的H7系列芯片,就没有标准库函数的支持。
/ h6 X) _1 p* Q5 R9 y( R  J- W7 l  @% M. s: {
QTIALM`}$`@M{F26MQZ}K(6.png : B! G9 P( R$ p- N- o9 g4 ]

5 I6 r5 V* p% d2.3 选择生成代码
4 G" Y4 f; p$ H6 d$ l% ^/ S9 _( y点击Generate code,生成软件代码,在之后的提示框中点击打开工程。! r& x/ ?9 D4 ~. \  E) ~5 m
$ W3 {) k! u8 y% K( R
20200328173226350.png
1 F2 ]+ b; {9 c: |) D) b* \  K" i$ p0 [9 b
3 main函数增加代码: b; A# Q& z: A0 k
3.1 端口初始化函数

7 ?$ E: ~) @& {% ]9 X# X) `- g在生成的main函数中增加相应的功能函数,端口初始化函数,给各个GPIO口赋值初值,防止上电时状态不确定。写入串口打印函数,提示信息。! w. e% _# J6 f

2 d8 }" q& W/ u9 H7 P9 m3 H$ T- w
  1. /* USER CODE BEGIN 2 */
    7 O6 j. U0 J; j/ F% _( z. }
  2. HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);
    % q6 J) L1 l, f3 \% f5 }, b5 O* G
  3. HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);* v/ l* W) v) _- g
  4. HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);7 u3 H$ C$ I- ?9 x4 [
  5. HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
    . u9 d9 x" r2 g5 I5 }' u
  6. HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
    5 _  ~5 U" X: ^7 I# |( I1 M* q8 R
  7. HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
    0 b" H, K+ L# ~& l5 }% i# T
  8. HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);  a8 }' {& f3 f4 a( Q; T" m
  9. HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);//初始化LED灯的状态,全部为灭
    8 C, n' t+ Z; ]+ z7 `8 u
  10. HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//初始化BEEP,不响
    / z5 F4 E' j6 W% x

  11. - u+ X5 f9 f, `7 s3 p& c( t
  12. uint8_t temp[]="Press a key\r\n ";//向串口输入提示信息% Q4 R. h9 n9 {8 @, F1 I! v4 @
  13. 6 q) ]5 {  c! b% R
  14.   /* USER CODE END 2 */
复制代码

2 S! d- w4 G8 v( W; ?; w" h& t% \3.2 功能实现函数
9 u7 ^) c( N  ]& D3 H( e% k在while(1)函数中增加下述函数,实现不间断的按键检测
+ }5 r) [$ S' K! f2 U# p; l  z7 V3 g! |7 S
  1. while (1)
    ; J% @0 w; D2 G+ ]
  2. {2 E& \3 x' s6 z4 x
  3.         /* USER CODE END WHILE */
    $ d. a$ ]/ U2 l) ]6 B- @9 q
  4. / }" ]3 H7 T5 x7 B9 p' [  x
  5.         /* USER CODE BEGIN 3 */$ q9 |2 o- u$ J9 G

  6. ( m: q1 b" a* \2 Q+ b
  7.         if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET)//判定KEY0按键是否有按下& N* i6 Z# C" j
  8.         {6 ~: [- Y1 q  k/ k3 @0 d
  9.                 HAL_Delay(10);                                                                //延时,用于按键消抖1 e! F4 X. l% ]* o1 X- p' L) ~
  10.                 if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //判定KEY0按键是否有按下/ `0 w! C! H. n8 q; \8 ~
  11.                 {# T" F& b  p$ X3 o6 M6 J
  12.                         HAL_Delay(500);//判定KEY0按键是否长按) Q8 i# S) [8 M0 p
  13.                         if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //KEY0按键被长按
    - w. h6 t& n! L" h* k
  14.                         {* j7 k- ]! ], k+ \! H  c4 m: ^
  15.                                 HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_RESET);//开启蜂鸣器) `4 ~% Q9 W' T. t" |
  16.                                 HAL_Delay(1000);//开启蜂鸣器1000ms
    8 n( }: K4 z/ D6 B
  17.                                 HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);//向串口输出提示信息
    9 n" P+ B' i1 t6 d' x* t
  18.                                 HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//关闭蜂鸣器
    ( `3 X' {" D7 X
  19.                         }* B' _, D0 N( r
  20.                         else         
    8 o9 S+ G1 ]! e1 j- ~& `. B
  21.                           HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);//KEY0按键为短按,LED0灯状态翻转
    ( Q1 P8 m, T! S
  22.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);) q0 K" W) a% J1 U0 J2 h/ j0 D: S( y
  23.                 }% C8 h- {: U% F, g
  24.         }
    & j: }4 j; M! `3 y& h. l
  25.         else  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
    & l; t/ H4 Q! p- w* a
  26.         {
    4 ?+ e; Z% k# ?& b* s7 L
  27.                 HAL_Delay(10);' v7 P" B& h6 V0 X0 i# j4 m
  28.                 if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
    " i2 h3 a5 E) w/ b2 h2 c1 k) Z# g  D
  29.                 {
      _# W; K3 {5 A* g: ?7 ?0 n
  30.                         HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
    3 C+ I0 ?( s" ?0 a- k) r# H0 K
  31.                         HAL_Delay(200);
    $ k: b2 H' F$ G9 N# Y; C
  32.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);$ B/ ^' d) N. `: f
  33.                 }
      M/ M3 n* V- Z, P) I1 C
  34.         }
    * x" X( T% ?" P/ U: W
  35.         else   if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
    / K3 x; T6 d2 {/ M3 ~' _
  36.         {0 ], w4 |- i3 _
  37.                 HAL_Delay(10);
    ; Y$ J# L6 p0 `% _5 S" G; b
  38.                 if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
    4 h9 I8 G/ M0 L
  39.                 {
    * ~- S" @( R0 [8 T
  40.                         HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
    1 e0 q: p+ o# P4 `9 @, b
  41.                         HAL_Delay(200);
    + i. g+ t8 {; X0 }) D
  42.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
    9 B# n* n2 }  M3 G! ]
  43.                 }4 X3 p; A/ p, n3 k/ k
  44.         }
    ; {% g7 ~* d! m5 K2 X6 J, V4 {
  45.         else   if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)
    0 E& `( e3 M+ f  U( y
  46.         {
    , U( d  H: M9 x6 ~6 _
  47.                 HAL_Delay(10);
    & j" U+ h$ w' i; `* e4 S; ?
  48.                 if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)9 z2 ^) L# v6 I! w/ X2 m$ F1 z
  49.                 {
    5 y# r3 [8 M6 ?- b$ X1 Y
  50.                         HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);! j' J# Z7 N, l$ q: y# X+ r; a
  51.                         HAL_Delay(200);! i& {& u. j; n# B3 O% ?( ~' t, m- o
  52.                         HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);  m6 ]( q' z* Z& j- U  R' T5 B- _
  53.                 }6 w% o- d" I. X! [5 A7 z  l
  54.         }* [6 @3 f" L( @1 u
  55. }7 R9 W  O: S8 ~
  56. /* USER CODE END 3 */
复制代码

4 m& j$ ?* P3 e& l. [$ T  ~6 X% _4 实验结果
5 c; K  G1 K' F2 ~' x9 X4.1 串口打印

2 Q4 Q3 }* J1 X9 r3 _! G使用串口调试助手,按下一次按键后,串口会打印一个Press a key的字符' K- D) N  L. _) I& y
  u2 R# l1 Z; i8 i+ W
U)4HDWF~8FROB$G)2H`FX}2.png $ e$ X1 H! q/ i4 p+ P. a4 w5 L+ o
$ v: e! R! k, E1 `- Z9 ?4 W
4.2 硬件实现/ }& U8 t' e9 F
将程序下载到开发板中,验证软件功能。全部功能实现完成。
6 }" K) Y* i3 u# J  \8 j通过按键将全部的灯点亮
( [7 w9 y2 g. p0 X* J
, c, X& |: U/ T 1WXQ[AWLGY5`{~KCSH8MS5X.png
  Q. I1 ^5 h% E& J( n5 G4 f) w% d+ T. Z# Q0 @% X) W, L
通过按键熄灭LED1和LED2
; o: A3 G) _: ?: k1 Y, H, i8 W, M6 F8 \3 u7 i) F% h* M( {
@KLY}71TJ%ERC@NVSD9SN(4.png
: s  [: C$ q; P* D# U
3 r6 l7 V% J3 H' G5 按键检测程序之魔鬼数字优化  \' ~( e& |$ X& _- ^0 ]
参考正点原子的官方例程,按键这一段是这么写的% \- Q; q9 H$ m- w. Q$ z

) G) B1 C* ^' r; Q" o) [! n* \
  1. u8 KEY_Scan(u8 mode)* A. ~7 r2 R8 X3 a, e/ g
  2. {         
    5 c# S: I! `* Y3 r. ?; u
  3.         static u8 key_up=1;//按键按松开标志
    0 Q8 e/ n* }- [% y" s
  4.         if(mode)key_up=1;  //支持连按                  
    * X! L4 h+ B5 i+ L7 A1 S
  5.         if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))+ J8 a# \5 L9 a( n
  6.         {
    # H9 C* J- k, g' o  A7 Z& n
  7.                 delay_ms(10);//去抖动 . p1 F$ l" M8 S8 n% C3 K
  8.                 key_up=0;
    0 E% n- w+ ~# A
  9.                 if(KEY0==0)return KEY0_PRES;
    8 [/ P7 q" E! ^: N
  10.                 else if(KEY1==0)return KEY1_PRES;
    & e5 v( T$ ]) {0 k
  11.                 else if(KEY2==0)return KEY2_PRES;/ C5 C; X/ N9 |( J# Y; l+ {
  12.                 else if(WK_UP==1)return WKUP_PRES;
    4 _, _# W& s0 Z( E
  13.         }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;              
    - N) s* H6 y5 C, c  G; Q4 p1 k
  14.         return 0;// 无按键按下( R0 B  V9 q# _  [* V+ ?, M; _  N
  15. }
复制代码
1 N# _" q9 A- k: x% `! T- U1 U
初次看感觉非常绕,云里雾里,一会0一会1的,传递的全部是数值,非常不利于阅读,于是增加了一些宏定义,消除掉这些魔鬼数字。将key_up的变量名进行了更改。- {, m# f0 S* ]4 r8 u5 K1 _% D
增加的宏定义
3 W* W  n( m3 l
( y! k: K$ ]* v  x
  1. #define KEYOFF 1* @" s; B; t0 S5 o( V
  2. #define KEYON 0+ O0 l% \$ A+ R  o7 b
  3. ; f4 P; _2 T8 S, }  d& @, D
  4. #define SINGLE_CLICK 0
    . R; ~) M# ]$ x5 ]% C7 w4 `9 g0 C
  5. #define CONTINUOUS_CLICK 1* \# j0 g: i& i  B; @7 A
  6. #define NOKEY_PRESS 0
复制代码

, F8 b. W3 T! g3 b. \$ O8 r* S7 ~依据宏定义改后的程序3 w% L, g7 m; E1 g; j1 v

, V# w) n' V: w8 d: o
  1. u8 KEY_Scan(u8 mode)5 \$ k' E! m* ^8 Q  U4 W
  2. {         
    ) S( B6 `( V3 z3 F  N9 t2 H5 T
  3.         static u8 keyState=KEYOFF;//按键状态为OFF+ C/ O: I- I* D7 P' I* t# H. K' j, p* b
  4.         if(mode==CONTINUOUS_CLICK)4 j! n; k6 _/ ?) q' m* u
  5.                 keyState=KEYOFF;  //按键初始态为OFF9 K: ?- u- V# x# o  @4 S
  6.         if((keyState==KEYOFF)&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
    , @5 o, p' Z0 ?2 t/ Q' ]
  7.         {
    ' L3 k3 }. l; ^% u, V  n7 F
  8.                 delay_ms(10);//去抖动
    0 ]6 ^1 r" B2 C5 p5 n
  9.                 keyState=KEYON;
    4 m' _' B: H9 y
  10.                 if(KEY0==0)* E) |4 T1 |" r9 L* {
  11.                         return KEY0_PRES;
    9 Z" C3 ?1 k' a4 l: D+ Z
  12.                 else if(KEY1==0)
    ( I9 k- ~# y0 Z, ~! o. S. r1 W8 w
  13.                         return KEY1_PRES;, t, z; ?1 B" e) M
  14.                 else if(WK_UP==1): ]9 [- _3 d/ E7 L' j" f
  15.                         return WKUP_PRES; ' E4 d" u- W# [/ T  w3 b3 K  H
  16.                 else if(KEY2==0)
    & ^' u. U: Z( h7 |; ?
  17.                         return KEY2_PRES;
    $ j/ E6 k' ^( L& s# M' a/ D. r
  18.         }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0) ! h. K* i, O9 @- {3 S$ x$ y7 j
  19.          {
    2 V# o# W  c3 f% G% o
  20.                  keyState=KEYOFF; 4 W5 U$ s1 m7 d' z: s% Q
  21.          }   
    ! r5 I3 B  B; P; _
  22.         return NOKEY_PRESS;// 无按键按下
复制代码

3 w+ M5 W" R8 L/ L5 v主函数中引用的地方原来为; E4 r; d. J/ G% ~3 B

5 M* X. u( I( Y1 X" O
  1. key=KEY_Scan(0;                    //扫描按键" W" [8 s& f, r3 e2 V9 L5 v/ I
  2.         if((Key_Queue!=NULL)&&key)           //消息队列Key_Queue创建成功,并且按键被按下
复制代码
) f$ G. D% B) y/ @0 R$ ^
更改后为& |: T* g$ |+ N

3 m2 S0 P& C# j
  1. key=KEY_Scan(SINGLE_CLICK);                    //扫描按键8 l0 }8 _6 g7 x1 o/ C# z
  2.         if((Key_Queue!=NULL)&&(key!=NOKEY_PRESS))           //消息队列Key_Queue创建成功,并且按键被按下
复制代码
3 p4 p+ u" X! Q9 d7 M
这样就可以知道函数在主函数中调用方式为单次按有效,按键函数的逻辑也很清楚了,就是初次进入函数,静态变量keystate为keyoff,第二项条件式不满足,跳转到按键检测中,当有按键被按下时,keystate为keyon,并返回键值。如果按键没有松手,那么第二次进入这个函数时,if((keyStateKEYOFF)&&(KEY00||KEY10||KEY20||WK_UP==1))条件不满足,无法对按键进行检测。直到按键松手,keyState状态为Keyoff后,才可再次进去按键检测状态。同理可以理解连续按时的实现逻辑。
0 v0 l: o* N+ E! {; A: \* `$ a* m/ r- T7 m2 @( ~3 W5 `
整个按键的功能的实现如下流程图
# Y, ~# r' W1 M2 r" i
# }" r4 i! H0 [' G: F MRP$(UPVY_1EL03~ZWW4L_V.png & U8 D! L* F1 A- k6 i+ c) v

( p" l# [6 k* G7 x7 s
0 h  F, z1 {. N2 `8 P9 T7 [+ u3 b
赞 收藏 评论0 发布时间:2022-5-6 10:47

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版