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

【经验分享】进程同步与互斥实验代码_正点原子 STM32F4/F7水星 开发板资料连载第八章 按键输入实验...

[复制链接]
STMCU小助手 发布时间:2021-12-15 11:00

第八章 按键输入实验

使用 STM32F7 的 IO 口作为输入用。在本章中,我们将利用板载的 3 个按键,来控制板载的两个 LED 的亮灭。通过本章的学习,你将了解到 STM32F7 的 IO 口作为输入口的使用方法。本章分为如下几个小节:


7 T  q8 \5 G' O0 b& m, W( k6 o

8.1 STM32F7 IO 口简介

STM32F7 的 IO 口在上一章已经有了比较详细的介绍,这里我们不再多说。STM32F7 的 IO

口做输入使用的时候,是通过调用函数 HAL_GPIO_ReadPin ()来读取 IO 口的状态的。了解了这

点,就可以开始我们的代码编写了。

这一章,我们将通过 ALIENTEK 水星 STM32 开发板上载有的 4 个按钮(KEY_UP、KEY0、

KEY1 和 KEY2),来控制板上的 2 个 LED(DS0 和 DS1),其中 KEY_UP 控制 DS0,DS1 互斥

点亮;KEY2 控制 DS0,按一次亮,再按一次灭;KEY1 控制 DS1,效果同 KEY2;KEY0 则同

时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。

8.2 硬件设计

本实验用到的硬件资源有:

1) 指示灯 DS0、DS1。

2) 4 个按键:KEY0、KEY1、KEY2、和 KEY_UP。

DS0、DS1 和 STM32F767 的连接在上一章已经介绍过了,在水星 STM32 开发板上的按键

KEY0 连接在 PH3 上、KEY1 连接在 PH2 上、KEY2 连接在 PC13 上、KEY_UP 连接在 PA0 上。

如图 7.2.1 所示:

$ v# M6 g5 L5 f0 ?  z( O

2 Z5 p, x% r. ^  f

图 8.2.1 按键与 STM32F767 连接原理图

这里需要注意的是:KEY0 和 KEY1 是低电平有效的,而 KEY_UP 是高电平有效的,并且

外部都没有上下拉电阻,所以,需要在 STM32F767 内部设置上下拉。

8.3 软件设计

从这章开始,我们的软件设计主要是通过直接打开我们光盘的实验工程,而不再讲解怎么

加入文件和头文件目录。工程中添加相关文件的方法在我们前面实验已经讲解非常详细。

打开按键实验工程可以看到,工程引入了 key.c 文件以及头文件 key.h。下面我们首先打开

key.c 文件,关键代码如下:

  1. #include "key.h"
    ( ~5 g7 A' J2 b6 J
  2. #include "delay.h"( e6 |5 R% I6 Q0 [" y$ T3 v
  3. //按键初始化函数+ m8 v* ?. b9 z, N
  4. void KEY_Init(void)
    4 O& P+ G+ x9 N. A/ |6 Q
  5. {
    : D( R0 v3 o: H: W2 e  j! B3 m! }5 X
  6. GPIO_InitTypeDef GPIO_Initure;
    , J. L* O9 d0 D: N0 I1 t  N0 P. b
  7. __HAL_RCC_GPIOA_CLK_ENABLE();# u. a% q- U+ D1 G0 b" A
  8. //开启 GPIOA 时钟9 n4 S/ o: M$ W/ b
  9. __HAL_RCC_GPIOC_CLK_ENABLE();
    0 g# y/ n4 C/ r5 U3 e8 b, Y
  10. //开启 GPIOC 时钟
      r" L* Z' [8 m' F3 W
  11. __HAL_RCC_GPIOH_CLK_ENABLE();
    ( W" Q/ N* ~; b( K! h: W
  12. //开启 GPIOH 时钟; C. }: L3 u: ~
  13. GPIO_Initure.Pin=GPIO_PIN_0;, e6 l9 Y4 s2 T) X
  14. //PA0
      |- q+ k! g, ?' l+ t  r) K0 D7 ~
  15. GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入) S$ v5 J* V5 U7 Z( M+ @( W
  16. GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
    . Y! H- m& s% l3 r0 A1 S% x: J
  17. GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速2 M3 h4 J  p* \
  18. HAL_GPIO_Init(GPIOA,&GPIO_Initure);
    3 M9 a* B3 d0 |3 n
  19. GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3" J4 J4 v+ ]  ]# t$ y3 @
  20. GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
    6 d3 c' G1 A* r. O" p& p
  21. GPIO_Initure.Pull=GPIO_PULLUP; //上拉
    $ K# }3 b9 I% |0 m9 d7 `" s; Y
  22. GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速6 X7 B$ C; ]1 U3 m+ N) q1 y
  23. HAL_GPIO_Init(GPIOH,&GPIO_Initure);7 e+ T5 f4 D9 C8 }& O
  24. }- {. q. `( ^  c
  25. //按键处理函数
    , ?" q% w$ B. N9 r
  26. //返回按键值
    3 h+ W. I& A1 Y& I* l: Z+ ^; J
  27. //mode:0,不支持连续按;1,支持连续按;- A0 f' ~4 |" m2 n0 G# Q2 ]
  28. //0,没有任何按键按下 1,WKUP 按下 WK_UP
    % `# h  m# L$ ^  h% e" k* N
  29. //注意此函数有响应优先级,KEY0>KEY1>WK_UP!!
    # ^8 }5 c  i6 y- q' P
  30. u8 KEY_Scan(u8 mode)6 i3 H, @( _% T" q, S8 }! S3 v, _
  31. {/ ]' [# n/ ]1 G  q( @7 E$ D9 J
  32. static u8 key_up=1;/ p. Q* j8 Y' g* q( @7 P
  33. //按键松开标志' t: T4 V* J+ w* T+ {) H
  34. if(mode==1)key_up=1; //支持连按9 W3 C7 \  j, N, r/ i
  35. if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
    ) ^/ Q0 z! ~- g2 l7 }8 g4 K
  36. {2 |! p/ X$ g, e* a
  37. delay_ms(10);9 O& S5 V. g5 u
  38. key_up=0;4 Q7 A$ ~, O3 z2 J7 W7 r5 @
  39. if(KEY0==0) return KEY0_PRES;
    % F7 P9 l2 [- \7 Q- {
  40. else if(KEY1==0) return KEY1_PRES;
    6 U7 A! F9 Q5 X7 ?! M  S* u
  41. else if(WK_UP==1) return WKUP_PRES;
    7 L) Y1 Q" N; I
  42. }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;
    " J: }2 f) y% ?
  43. return 0; //无按键按下
    # A# n/ z  P% t0 p' b
  44. }
复制代码
  Z+ _8 E+ m7 k( l! E6 c- O! n

这段代码包含 2 个函数,void KEY_Init(void)和 u8 KEY_Scan(u8 mode),KEY_Init 是用来

初始化按键输入的 IO 口的。实现 PA0、PC13、PH2 和 PH3 的输入设置,这里和第六章的输出

配置差不多,只是这里用来设置成的是输入而第六章是输出。

KEY_Scan 函数,则是用来扫描这 4 个 IO 口是否有按键按下。KEY_Scan 函数,支持两种

扫描方式,通过 mode 参数来设置。

当 mode 为 0 的时候,KEY_Scan 函数将不支持连续按,扫描某个按键,该按键按下之后必

须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次

触发,而坏处就是在需要长按的时候比较不合适。

当 mode 为 1 的时候,KEY_Scan 函数将支持连续按,如果某个按键一直按下,则会一直返

回这个按键的键值,这样可以方便的实现长按检测。

有了 mode 这个参数,大家就可以根据自己的需要,选择不同的方式。这里要提醒大家,

因为该函数里面有 static 变量,所以该函数不是一个可重入函数,在有 OS 的情况下,这个大家

要留意下。同时还有一点要注意的就是,该函数的按键扫描是有优先级的,最优先的是 KEY0,

第二优先的是 KEY1,最后是 KEY_UP 按键。该函数有返回值,如果有按键按下,则返回非 0

值,如果没有或者按键不正确,则返回 0。

接下来我们看看头文件 key.h 里面的代码:

  1. #ifndef _KEY_H" y9 H6 e) s- T+ L$ n4 l& y: S
  2. #define _KEY_H; P% o5 |: m. I9 y
  3. #include "sys.h"; }, ^+ j( W% W5 J7 p% O
  4. /*下面的方式是通过直接操作 HAL 库函数方式读取 IO*/0 E, o* D! z$ \
  5. #define KEY0 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) //KEY0 按键 PH3
      V7 _7 J0 A. W, n& e2 l
  6. #define KEY1 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) //KEY1 按键 PH2- p& c7 w! O7 O  ~( O- ]1 s* j
  7. #define WK_UP HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) //WKUP 按键 PA0* B9 B2 ]- a" o7 z! ^0 G
  8. #define KEY0_PRES 1. u/ B1 g/ Y2 v, F2 |7 h
  9. #define KEY1_PRES0 A* T( p8 C# Q7 r8 e: x& i- C
  10. 2' \; ~( z( H, @
  11. #define KEY2_PRES
    6 h# w- Z8 _0 W1 ?- W1 k! ~
  12. 3
    & B! r: `2 d- B& `6 _# _
  13. void KEY_Init(void);
    8 p( G4 u4 g8 v: L9 m/ j
  14. u8 KEY_Scan(u8 mode);
    # R" b  t. _6 D; j
  15. #endif
复制代码
2 h& a8 \( K6 q% t& `

这段代码里面最关键就是 4 个宏定义:

#define KEY0 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) //KEY0 按键 PH3

#define KEY1 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) //KEY1 按键 PH2

#define WK_UP HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) //WKUP 按键 PA0

这里使用的是调用 HAL 库函数 HAL_GPIO_ReadPin 来实现读取某个 IO 口的输入电平。函

数 HAL_GPIO_ReadPin 的使用方法在上一章跑马灯实验我们已经讲解,这里我们就不累赘了。

在 key.h 中,我们还定义了 KEY0_PRES / KEY1_PRES//WKUP_PRESS 等 4 个宏定义,分

别对应开发板四个按键(KEY0/KEY1/KEY_UP)按键按下时 KEY_Scan 返回的值。通过宏定义

的方式判断返回值,方便大家记忆和使用。

最后,我们看看 main.c 里面编写的主函数代码如下:

  1. int main(void)1 W: [9 z2 D- E# @5 [( ~8 V
  2. {( c2 X6 P4 O2 k
  3. u8 key;
    4 s" _" D1 d# R0 ?  q# V' ?7 W
  4. u8 led0sta=1,led1sta=1;. M6 ~7 \4 v  V5 S6 D  l
  5. //LED0,LED1 的当前状态
    & t9 ]  E( ]7 ?& X6 Y1 i6 G5 p
  6. Cache_Enable(); //打开 L1-Cache# H9 O/ t. u) D
  7. HAL_Init();
    5 Y4 e$ {( |$ Y) {. ~7 H
  8. //初始化 HAL 库
    2 Q0 K7 x, p; Q, j' t8 U9 M1 Q
  9. Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz/ [- x; ~! |9 m/ I5 X
  10. delay_init(216); //延时初始化
    - h- w, P0 @- v( z  P$ G9 l+ m
  11. uart_init(115200);
    , e! B- A7 g4 O# F/ N9 A7 u1 `
  12. //串口初始化- |- V! Y" {% U& F: H
  13. LED_Init(); //初始化 LED6 z( f* S1 V, U
  14. KEY_Init(); //按键初始化  E- ?1 |0 j9 L
  15. while(1)& ]% b4 ]' d, X7 w
  16. {
    " J% y" [" |5 K6 w
  17. key=KEY_Scan(0);
    ' o; l: q/ w) e: i# M) m  O4 C
  18. //得到键值" |- W; k3 E0 u5 J$ B/ `
  19. if(key)5 I. T3 _# A0 ^4 x/ l
  20. {
    2 e) K8 S; H! V! w5 J
  21. switch(key)7 p6 T' ^' \1 F
  22. {
    % Y+ d- I/ h. F) a0 t
  23. case WKUP_PRES:
    " n1 i( q- w2 f. @
  24. //控制 LED0,LED1 互斥点亮+ u2 D7 B4 a0 T& L
  25. led1sta=!led1sta;- G. k' i( k. s4 c$ K
  26. led0sta=!led1sta;' C7 g, T! M# B& s: T
  27. break;
    ! R7 W6 g2 I4 }8 s
  28. case KEY0_PRES: //控制 LED0 翻转
    % g. G5 B+ b+ V
  29. led0sta=!led0sta;
    ! y' A) V! {! K% z6 h  D* S" Q
  30. break;
    3 ~: j! J2 ]- x8 q9 R, [
  31. case KEY1_PRES: //控制 LED1 翻转9 [' f1 T' ]9 o# W3 G2 q+ n
  32. led1sta=!led1sta;
    ( c* j, _: _- F
  33. break;% D! |# ~! B, w2 ]# @& l( ~5 i
  34. }& c2 c7 R9 r2 \
  35. LED0(led0sta);) C" {! o1 f+ y; P" D4 ]! }
  36. //控制 LED0 状态+ H1 a8 ]% f+ G/ ~& @8 Y* H
  37. LED1(led1sta);# K: t: E; B8 D
  38. //控制 LED1 状态2 [# n: w+ T0 J  E: Z
  39. }else delay_ms(10);
    7 a; ], I' l. f1 I2 [0 m
  40. }
    % A) {% A2 }$ ~8 G8 O* ]* H' e! O
  41. }
复制代码
) _% S+ }8 I' ~5 p1 Y/ D

主函数代码比较简单,先进行一系列的初始化操作,然后在死循环中调用按键扫描函数

KEY_Scan()扫描按键值,最后根据按键值控制 LED 的状态。


1 i; z9 w; F2 U* J1 C/ A1 ?

8.4 下载验证

同样,我们还是通过 ST LINK 来下载代码,在下载完之后,我们可以按 KEY0、KEY1 和

KEY_UP 来看看 DS0 和 DS1 的变化,是否和我们预期的结果一致?

至此,我们的本章的学习就结束了。本章,作为 STM32F767 的入门第二个例子,介绍了

STM32F767 的 IO 作为输入的使用方法,同时巩固了前面的学习。希望大家在开发板上实际验

证一下,从而加深印象。


# o1 p, u! t% L. l

8.5 STM32CubeMX 配置 IO 口输出

上一章我们讲解了使用 STM32CubeMX 工具配置 GPIO 的一般方法。本章我们主要教大家

配置 IO 口为输入模式,操作方法和配置 IO 口为输出模式基本一致。这里我们就直接列出 IO

口配置截图,具体方法请参考 4.8 小节和上一章跑马灯实验。

根据 8.2 小节讲解,水星开发板上有 3 个按键,分别连接四个 IO 口 PA0,PH2 和 PH3。其

中 WK_UP 按键按下后对应的 PA0 输入为高电平,所以默认情况下,该 IO 口(PA0)要初始化

为下拉输入,其他 IO 口初始化为上拉输入即可。

使用 STM32CubeMX 打开光盘工程模板(双击工程目录的 Template.ioc),目录为“4,程

序源码标准例程-库函数版本实验 0-3 Template 工程模板-使用 STM32CubeMX 配置”。我们首

先在 IO 口引脚图上,依次设置四个 IO 口为输入模式 GPIO_Input。这里我们以 PA0 为例,操作

方法如下图 8.5.1 所示:


, e0 U9 }* O# R3 d# a

  Y2 h' O- X4 D) D# F

图 8.5.1 配置 PA0 为输入模式

同样的方法,我们依次配置 PH2 和 PH3 为输入模式。然后我们进入 Configuration->GPIO

配置界面,配置四个 IO 口详细参数。在配置界面点击 PA0 可以发现,当我们在前面设置 IO 口

为输入GPIO_Input之后,其配置参数只剩下模式GPIO Mode和上下拉GPIO Pull-up/Pull-down,

并且模式值中只有输入模式 Input Mode 可选。这里,我们配置 PA0 为下拉输入,其他三个 IO

口配置为上拉输入即可。配置方法如下图 8.5.2 所示:

- W( u1 m1 A' v2 }
  t2 i- B7 L0 ?9 s

图 8.5.2 配置 IO 口详细参数

配置完成 IO 口参数之后,接下来我们同样生成工程。打开生成的工程会发现,main.c 文件

中添加了函数 MX_GPIO_Init 函数,内容如下:

  1. static void MX_GPIO_Init(void)
    / H+ E, }! F8 |* @+ M; S$ y
  2. {
    9 V+ E5 ~; [% g3 P+ S
  3. GPIO_InitTypeDef GPIO_InitStruct;
    + u! r0 Z+ K7 W4 p% t
  4. /* GPIO Ports Clock Enable */
    " r" L, c+ N$ I) D1 e+ }: d# t2 m; f& q
  5. __HAL_RCC_GPIOH_CLK_ENABLE();
    ; Y% W6 |: c% g  V6 ^2 U( s
  6. __HAL_RCC_GPIOA_CLK_ENABLE();
    2 d' V; @1 a& A0 v3 d
  7. /*Configure GPIO pin : PA0 */8 r' V1 s" j* t
  8. GPIO_InitStruct.Pin = GPIO_PIN_0;( M. S5 }. U. f) `/ P
  9. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    - J4 ~2 u1 r& @' e' ^/ ]
  10. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    8 x; g* H  O/ M4 k) ?# ^7 Z' M
  11. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);" F: w9 f+ e8 R2 H( a5 j+ g! o
  12. /*Configure GPIO pins : PH2 PH3 */
    $ }3 [1 e* X# g6 I8 Z. N
  13. GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    7 l: y' ^2 c/ D- K; b. U; \
  14. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    . K! l+ A/ B  z
  15. GPIO_InitStruct.Pull = GPIO_PULLUP;& E+ Z" y; o$ F; V, c. F- u
  16. HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
    5 b. }; v2 W. I4 C7 f
  17. }
复制代码
& O7 |; t$ B5 b7 ^  ~
9 s0 z8 E4 j; i) x# ]) A( q' M

该函数实现的功能和按键输入实验中 KEY_Init 函数实现的功能一模一样。有兴趣的同学可

以直接复制该函数内容替换按键输入实验中的 KEY_Init 函数内容,替换后会发现实现现象完全

一致。

使用 STM32CubeMX 配置 IO 口为输入模式方法就给大家介绍到这里。


! H2 d* h' k4 y$ i# b* P9 R  z( M
赞 收藏 评论0 发布时间:2021-12-15 11:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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