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

【经验分享】STM32H7之GPIO的HAL库API

[复制链接]
STMCU小助手 发布时间:2021-10-29 23:33
17.1 初学者重要提示  }5 t( @" R: m: ]
1、  如何阅读HAL库源码的问题0 G  B) h% L/ m6 H/ x; o( t# c. \: T
/ F# n6 W( a# h/ z7 j
HAL库实现的函数有复杂的,也有简单的,简单的可以直接阅读代码。复杂的代码阅读起来比较耗时间,如果再配合参考手册抠每个寄存器的配置,那就更消耗时间了。所以对于这种函数,用户仅需了解每个部分实行的功能即可,而且HAL库都做了关键注释,以说明这部分实现的功能。所以用户没有必要去抠每个配置是如何实现的,仅需知道实现了什么功能。以后工程项目有需要了解具体配置时,再看即可。
; o1 X; X$ b/ I
7 z" b& ^- L, [  z5 `8 Y2、  学习本章节前,务必保证已经学习了第15章。
) }" B$ ~% m+ r; P+ l7 z$ [
1 v/ _& ?, p' O$ d17.2 GPIO涉及到的寄存器# B5 E% b, I& v" l: e
GPIO外设涉及到的寄存器比较少,也容易理解,推荐大家阅读GPIO源码的时候将参考手册中对应的寄存器功能做一个了解。
' {& Y6 A, r7 W5 d
; B: k: W, T& J) H% X9 ?很多时候,我们会直接调用GPIO的寄存器进行配置,而不使用HAL进行调用,以提高执行效率,特别是中断里面执行时。
! S5 Z' N% C/ s! @, l/ B  a$ n/ M4 ^* J+ Y
17.3 源文件stm32h7xx_hal_gpio.c
) c9 q! c3 D+ R. V. p" X这个文件主要是实现GPIO的引脚配置,学习这个文件注意事项:
6 X$ J! a* \. D6 i5 ]+ [6 H) a
- d$ W7 d6 q& O  H" n  系统上电后,引脚默认状态是模拟模式。# v& z6 G0 t0 _  n; d
  所有的引脚有弱上拉和弱下拉电阻,阻值范围30-50KΩ。其中配置为模拟模式时,上拉和下拉被硬件禁止,其它的输入、输出和复用都可以配置上拉和下拉。- E/ [3 d+ ?" ~2 L+ r
  在输出或者复用模式,每个引脚可以配置成推挽或者开漏,且有GPIO速度等级可配置。另外注意,不同的供电范围,实际速度等级是有些区别的。0 u0 j/ i, X& l9 W
  每个GPIO都可以配置成外部中断/事件模式,但要特别注意,引脚要配置成输入模式,在芯片的内部有个多路选择器,选择引脚与16个外部中断/事件EXTI0 - EXTI15中的那个导通。这就决定了,每个外部中断/事件只能与一个引脚导通,如果用户配置了多个引脚PA0,PB0,PC0等,那么只有一个能够与EXTI0导通。  }% u5 L/ N$ G
17.3.1 函数HAL_GPIO_Init! A% j7 b7 _9 M( ?
函数原型:
, L9 n7 k* e4 e6 j5 l: z  I+ u4 ~+ N& ?- c6 u2 _/ j
  1. void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)7 h- a- x+ J; T" U  o
  2. {
    0 F; X, h: O8 e% c+ \6 B
  3. /* 部分省略未写 */. i9 }# i& `3 y: w/ T
  4. ( Q/ j, O' @6 h% N' G5 P* N
  5.   /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */& t5 H- l  z% P. y: p( e
  6.   for(position = 0; position < GPIO_NUMBER; position++)( I2 f* V, H9 m$ A& {( n' A
  7.   {
    ! W' f6 [* n" j/ R8 l
  8.      /* 部分省略未写 */( j; J6 Q! I( n+ c) b
  9.     if(iocurrent == ioposition)
    ! U) e8 R9 s, V# U* p# @$ m* [- h
  10.     {
    $ T1 e8 @& l. Q. E2 \; x  U, h
  11.       /*--------------------- GPIO模式配置 ------------------------*/! ?! D* L8 S" A4 G$ v

  12. $ I# o. v8 t. \8 L' U- L
  13.       /*--------------------- EXTI模式配置 ------------------------*/( f- r! [$ F! H3 k6 i( T2 J

  14. # [7 d/ i0 B* R: h! S
  15.     }3 H. w1 d' i9 a2 }' M
  16.   }. j' [; }( v# L; J4 Q+ V. c
  17. }
复制代码

" K8 O; D5 S* O2 p函数描述:
: H, s/ u" X; D0 ]) \% l/ w4 C" R5 g. Z/ b8 n% E3 Q
此函数用于初始化GPIO,此函数主要实现如下功能:/ ?# I; V; L3 J& u  T

+ Z- Z8 d6 r& K$ t/ m  GPIO功能配置。
: Q1 p; t$ [( Q& V) S3 z& v3 i, }  设置EXTI功能。9 }' |* }( r  y. s. O
函数参数:; j/ ~1 L$ I; m+ i+ ^$ v; t

- v+ V2 c+ m$ \7 s  第1个参数用于填写使用的端口号,可以是:% T% b4 y% Q& B' j$ u) A) I
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)
    , l7 d: u" ?" b( z; I/ @4 |
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
    4 |; F* Z  a* T) k5 r& @- a# F5 W, t# ^
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)* C& ]8 j' F4 s% p$ g( D; ^
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)
    ! n4 A% J6 m+ c. d! U7 k
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)
    ) w0 j- K9 `6 ]( [
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)# x8 e8 a7 H9 d% ~5 w1 ?" v
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)) B6 I) a- Z0 P, L2 e3 T$ b
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)" R% I1 g; ?+ ~" ?1 \' J
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)6 ~) K7 ^5 V4 i6 p
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)  ^  k; o) o7 h9 m/ b
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码

4 @* ^* _. ~7 R; P# H% t  第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:" |4 h; d; o, g
  1. typedef struct5 i* i0 y% R' u% W; @2 c
  2. {
    8 i1 c# n: O' F: w
  3.   uint32_t Pin;      O3 R; \( ^# @& {1 X2 s- S) U. _
  4.   uint32_t Mode;
    5 s- T  a- l" k8 J. i
  5.   uint32_t Pull;     
    ! P! r# N: x4 F5 B
  6.   uint32_t Speed;   0 v: f# O# x$ C4 T- V5 D
  7.   uint32_t Alternate;
    7 t' N  S1 G, c
  8. }GPIO_InitTypeDef;
复制代码
7 U9 B# O. `' \7 s) v  `4 O. g- G
下面将结构体每个成员做个说明:" J  ^3 u9 A0 J# e) I, \1 R: I  J

, d, ^7 X& r# b  成员Pin用于配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15,额外还可以选择GPIO_PIN_All和GPIO_PIN_MASK。
& T* Z' q8 N4 ^5 ]6 e& V5 X  成员Mode可以选择:
/ b  o" e% T: u+ w2 Y# Z/ _1 s
  1. GPIO_MODE_INPUT             /* 输入模式  */
    2 \5 j6 h% s; p1 ^4 t+ ?! b
  2. GPIO_MODE_OUTPUT_PP        /* 推挽输出  */
    . ?* I+ \- h& I
  3. GPIO_MODE_OUTPUT_OD       /* 开漏输出  */) H3 w9 r8 c4 n! _" d$ |3 }( k
  4. GPIO_MODE_AF_PP             /* 复用推挽  */
    ! M& S, n% f  c: g, S3 t/ J  d
  5. GPIO_MODE_AF_OD            /* 复用开漏  */0 h; c! |* P  O! T7 T

  6. 9 N; _" H- E" }, D/ y
  7. GPIO_MODE_ANALOG          /* 模拟模式  */
    , N6 Q* d, }9 A7 ?# o0 P2 q# C
  8. GPIO_MODE_IT_RISING         /* 外部中断,上升沿触发检测 */; g5 q' v7 X. b% S
  9. GPIO_MODE_IT_FALLING       /* 外部中断,下降沿触发检测 */9 F. q9 R* u3 D$ o
  10. GPIO_MODE_IT_RISING_FALLING    /* 外部中断,双沿触发检测   */
    / o. h3 U: O- I! p' h* [% e6 n

  11. ! J5 r: @5 r& T: S8 r
  12. GPIO_MODE_EVT_RISING           /* 外部事件模式,上升沿触发检测  */
    & r, S5 B# [  m
  13. GPIO_MODE_EVT_FALLING          /* 外部事件模式,下降沿触发检测  */7 O' j: y- H& h2 R( n
  14. GPIO_MODE_EVT_RISING_FALLING  /* 外部事件模式,双沿触发检测 */
复制代码

& I" P3 F8 M" k! V! M2 E  成员Pull用于配置上拉下拉电阻:4 U+ @' d- F: B* N% Z
  1. GPIO_NOPULL         /* 无上拉和下拉电阻 */" H5 K* n+ n7 d5 E. F1 y  O) B
  2. GPIO_PULLUP          /* 带上拉电阻  */
    0 }& x+ {% x' Z$ M* ^- k
  3. GPIO_PULLDOWN    /* 带下拉电阻  */7 ^5 o4 p3 C; p/ T. Y
  4.   成员Speed用于配置GPIO速度等级,有下面四种可选:
    5 o1 l7 X; D# [- K" o+ z5 H
  5. GPIO_SPEED_FREQ_LOW          /* 低速 */
    6 x. @! C& m, c% A
  6. GPIO_SPEED_FREQ_MEDIUM      /* 中等速度 */
    ) G9 F! v3 B1 J4 ~; M9 [4 K0 G
  7. GPIO_SPEED_FREQ_HIGH         /* 快速 */
    2 A; T5 |7 I" r; \9 x
  8. GPIO_SPEED_FREQ_VERY_HIGH   /* 高速  */
复制代码

/ P; r0 J+ m, _' M  成员Alternate用于配置引脚复用,可选择的复用方式在文件stm32h7xx_hal_gpio_ex.h里面进行了定义,比如串口复用:9 S1 H/ Y/ u) B" q8 ]2 n
  1. GPIO_AF7_USART1   
    : `* \2 j2 W" A3 O
  2. GPIO_AF7_USART2    0 Y3 V7 P3 C2 J- C( Y. c* M+ L
  3. GPIO_AF7_USART3      
    4 g4 z8 K6 [2 m+ C
  4. GPIO_AF7_USART6    : C, Q* F( Y8 k7 k. z3 x
  5. GPIO_AF7_UART7
复制代码
& H1 O0 f4 N, ^- q/ G0 U. s. g8 Z
注意事项:
- D. H* t" X8 y. [+ h$ T) Z. w, U* |& Z  [6 V! u$ M3 [+ b5 ?
与F1,F4系列的标准库不同,H7的HAL库已经没有单独的EXTI外部中断设置文件,是将其整合到此函数里面了。& z: V2 M7 i0 y; w
函数HAL_GPIO_Init对引脚的初始化是把同组16个引脚for循环检测了一遍,效率稍低。所以不推荐下面这种初始化:+ u! S8 _+ P" }  P; @0 @) Q. i) D
  1. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                  
    . _: N* y+ y$ ]# L
  2. GPIO_InitStruct.Pull = GPIO_NOPULL;              1 |* A) R, `1 W+ n. [' O
  3. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  ) e$ v' W7 ~& ]3 {4 {
  4. GPIO_InitStruct.Pin = GPIO_PIN_0;) ?, r1 w$ s1 a! E" C
  5. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询! r$ \3 p& j+ `7 _; ~* r! i
  6. 7 F+ f+ K) L+ V8 c/ ~  p* {
  7. GPIO_InitStruct.Pin = GPIO_PIN_1;
    ! I' i" |" F; s9 @6 d2 r) l
  8. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询
    / H- X5 f4 ~) {) J6 o
  9. 5 K* Y7 u) K9 i4 \2 B+ |* V" _* i
  10. GPIO_InitStruct.Pin = GPIO_PIN_2;
    $ A, x! d: _0 ?5 e1 ^( {4 s
  11. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码

5 b- d1 U$ [3 P7 i+ s如果是程序运行期间的引脚状态切换,最好采用下面的方式或者直接寄存器操作:
5 e# l# m/ y% g  n4 Y. r( u6 c. C9 h6 b1 f" e- l# |2 W0 f6 z2 {
  1. GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ;
    9 N  u) Q# f. Y- W* x
  2. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                  
    ! C4 n! z  z+ O8 [4 P; g% u; |
  3. GPIO_InitStruct.Pull = GPIO_NOPULL;              ; s. I6 F* k: e  j
  4. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  
    - S5 g; ~" s) U# |# |. f/ [4 O7 X
  5. . C5 a* M6 e. u: V0 `
  6. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码
$ S; ^7 e. c" g# ]) G- u6 W; m
使用举例:
# ^+ x5 y2 h+ B6 E: k  x" e2 F- X6 V* w2 S5 S# \8 ^& T  G
  1. GPIO_InitTypeDef  GPIO_InitStruct;( ?1 @9 A* T. A$ D' m/ G7 V3 q

  2. 4 g. |& X, Y) T; p" Q. S
  3. GPIO_InitStruct.Pin = GPIO_PIN_0;/ e: d" Q* x. U1 W  U& F
  4. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */         % T) C6 \/ b: U$ y* ]5 C
  5. GPIO_InitStruct.Pull = GPIO_NOPULL;                /* 无上拉和下拉电阻 */
    ' g- X: e+ C6 ^! e& U  |- r
  6. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */
    . K* J$ i6 k. S- m& h7 s' Q: d

  7. $ r; d( L) I, N& F, O
  8. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
复制代码

: L0 t4 d  f  |17.3.2 函数HAL_GPIO_DeInit
# F8 {) I% T6 C9 D" i$ n& M函数原型:1 _6 x! S3 I: C' T* c/ O
6 {( `: P) z6 k6 Z1 j
  1. void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)) f4 A3 F2 c7 i0 I; \/ b  P
  2. {  
    2 I7 F2 M. `! b& ^% g
  3.   for(position = 0; position < GPIO_NUMBER; position++)8 T4 y3 N$ w; A7 Z
  4.   {; x' Z' a. J: c
  5.      /* 部分省略未写 */
    ; s) j) l, T$ @1 p6 W  J
  6.     if(iocurrent == ioposition)& `/ R% R5 p* N% k2 W/ w9 C' d
  7.     {! U" u4 G4 ?) K* |
  8.       /*------------------------- GPIO Mode Configuration --------------------*/( b) R) h5 J% `
  9.       /* 配置为模拟模式 */! p0 R& a( }: O
  10.       GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2));
    ! R1 w3 y! U: J

  11. 5 U  R! r" z  s% d7 U, i; z1 J, M
  12.       /* 配置复用模式为AF0,即作为通用IO */2 p. H* z% \, W9 j
  13.       GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;
    + S+ \0 `3 m9 d$ d% c5 T
  14. ' w9 B' U. @2 W) E# R7 U
  15.       /* 配置到最低速度 */
    3 g% _! @) \! }6 P
  16.       GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2));
    2 l+ ?( c" t& R3 H6 i2 [" Q, u$ N0 B
  17. 0 S% Q' |( D: b5 R
  18.       /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */
    : H+ g4 |! Z( V4 P" g4 E
  19.       GPIOx->OTYPER  &= ~(GPIO_OTYPER_OT_0 << position) ;
    % y( o4 k; O2 T
  20. / V  ^1 t* N8 r2 S, W- D9 H
  21.       /* 无上拉和下拉电阻 */8 L5 g1 J" x/ g, q% z
  22.       GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2));4 i( z/ f0 y0 M
  23. , Y. B. b% b! b2 {" O4 P! f
  24.       /*------------------------- EXTI模式配置 --------------------*/& S  P7 v) j0 A# j% ]7 w

  25. ( g; ]  U" S$ O
  26.     }( t/ \6 B" m/ Q7 z
  27.   }
    ( p% p, y8 u. C! A" K" @" w
  28. }
复制代码

: q3 E+ s  G( W% ^% e* ~函数描述:
( ^7 M8 \1 d* b8 H3 [. W/ Q' S# k, j* d1 [8 p
此函数用于复位IO到初始化状态,具体状态看函数原型中的注释即可。* v3 ?. X2 h9 g. V; J8 w7 l
: w+ Z* ~( p0 P! `  w% e. \
函数参数:( O+ D: k$ P0 U+ d) r
( k0 a, Q# H6 j- ]3 Q( X) O
  第1个参数用于填写使用的端口号,可以是:
' o& V" p! X& @) M& ^% ~
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)
    ; K" R0 F/ Z/ z) _, r  X
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
    ! o2 k' l: c1 O! E1 }; q2 x
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)
    7 h( M5 o: X  C0 Q& |4 p
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)
    8 z% J8 ]4 {* O6 V7 |$ x- S. w, u
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)# b  y8 n5 H3 g7 |1 W
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)
    & t: C! [- Z) ^! n- M5 c
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)
    & L1 `" X, S& t1 `: I
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)3 A6 }6 X8 V0 l# }4 e/ Y" L5 @
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)% J$ \, E! `7 I2 S3 K2 }: W. V
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)' P3 u% t7 C* H: [! X
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码
4 I) u  i0 ]8 o/ D  k
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。- z9 G5 A" n% w4 d  D
使用举例:
# O  S$ K# _5 p4 [1 J7 q1 W! G5 H( r% O7 j3 O  h+ r  V
此函数的使用比较简单,需要调用的时候直接调用即可。' j2 p' E- m; t
9 [: C3 s1 b# o/ k
17.3.3 函数HAL_GPIO_ReadPin. q, I2 s2 {5 H5 q7 g5 b( o
函数原型:
' e5 V8 A# p4 I( B) w! R4 `0 R
/ a% y1 }3 e$ N4 K' p
  1. GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    - P  w8 k* \; F' K
  2. {3 o6 n: ^) d) I" I
  3.   GPIO_PinState bitstatus;
      G) q1 b: ^; g+ o' G5 X

  4. 2 C1 }6 L( T# V. M$ k
  5.   /* Check the parameters */2 _8 j  P. c' {. a2 L
  6.   assert_param(IS_GPIO_PIN(GPIO_Pin));$ ~6 ~8 T+ D0 e* H" d+ L8 o$ v: X
  7. ) _4 G. t. o7 r( f' f4 `. A+ k% i
  8.   if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)0 [( M% d1 K% P% w% k; V
  9.   {+ B! c# d: n4 T, M
  10.     bitstatus = GPIO_PIN_SET;: R1 Q! g- u1 p  S7 r1 P
  11.   }$ q0 f9 Y* \; s
  12.   else9 ~- ^5 l$ t" U- m; t
  13.   {
    2 M8 C7 v" R: b; o' q
  14.     bitstatus = GPIO_PIN_RESET;
    / P+ h7 X. a% g7 _8 O
  15.   }
    : x1 ~. F2 s+ y, }
  16.   return bitstatus;
    / m5 w* f& }6 Y( L4 I  t" o
  17. }
复制代码
; T+ a) {: u$ G* F: N- S
函数描述:
5 |  e2 l; u- M8 I$ a3 O
  e1 o2 b% ?# I, r此函数用于读取引脚状态,通过GPIO的IDR寄存器读取。
- K' R8 ^5 s7 f1 @" ]2 r6 X
& h. @0 h, B' s' }0 J9 d函数参数:; d' o- `$ r+ L  H
- v& u9 m/ E# w- E
  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。, m+ F, a; h6 h% m, v, G
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。7 y8 q. q0 A4 a& w( g  h
使用举例:
/ w% y/ c/ O: i- \2 x9 O5 {; j& F# _% A' x/ u4 ^+ V7 H; i+ Z9 j1 l
此函数的使用比较简单,需要调用的时候直接调用即可。9 o9 ?, Y; c2 K  c2 P: \" f6 w: A

  Q. N0 c$ I+ r$ I, ~17.3.4 函数HAL_GPIO_WritePin* f" M6 a" p- `5 ^' u" M
函数原型:& u2 A& ~) z" a

4 w7 H2 ~& T6 D* P
  1. void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    7 [- X7 v$ |/ M8 U% V
  2. {
    2 V! O9 Z$ g/ J, n
  3.   /* Check the parameters */8 `' Q2 Q  G1 E- D$ ^" G! {
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));; S  T1 e5 G; T, J1 n
  5.   assert_param(IS_GPIO_PIN_ACTION(PinState));9 g; h+ h7 R4 h1 \
  6. 3 E! m( n& I" r) }
  7.   if(PinState != GPIO_PIN_RESET)
    2 I3 c1 Z8 O( t
  8.   {' a) y: y) Y' d% H1 n4 S+ t
  9.     GPIOx->BSRRL = GPIO_Pin;
    ' R& Q1 n, G% b# I# y! R, Z& r
  10.   }% P& R3 v( [9 `8 R% D% N1 ]6 V& H* d
  11.   else
    & z0 x/ V7 k1 s' O2 V
  12.   {. l# \, X2 H3 {6 U( V
  13.     GPIOx->BSRRH = GPIO_Pin ;) d, ^" U3 `, X5 B  P! T
  14.   }2 X" g0 y5 ^: l4 i
  15. }
复制代码
4 f' e) k& R$ q3 F- n  j. b
函数描述:& u$ N1 l; y+ c' \. m$ T

+ ^, I6 s! }* f3 X' \此函数用于设置引脚输出高电平或者低电平。使用GPIO的BSRR寄存器进行设置,使用这个寄存器的好处是支持原子操作,由硬件支持的。原子操作的含义是操作过程不会被中断打断,而我们使用GPIO中另一个设置输出的寄存ODR是会被中断打断的。大家看下寄存器赋值操作对应的反汇编,是由多条汇编指令组成的。
+ P6 |, x) y- m: k9 M' i5 l8 x7 @% }! u# d- y' |* ~$ x
函数参数:
1 [( V4 ^% h& b: }- }1 ~8 F  F
  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
* E* l6 k* m( Z! Z( X  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
7 ]* K, j/ r$ |% w1 J  第3个参数用于设置引脚输出高电平还是低电平,GPIO_PIN_RESET表示低电平,GPIO_PIN_SET表示高电平。# _( W  Q6 n; d& @8 _* d2 F
使用举例:" E" F2 v. H; Z4 ?- p5 o; U5 @* h
. R. U1 J! f) S( z4 \# O( y
此函数的使用比较简单,需要调用的时候直接调用即可。1 x4 p" F: Y- q: e9 O5 O% T6 |

# O' a6 v; Z  j  T3 u% k  o17.3.5 函数HAL_GPIO_TogglePin
0 f7 P* B: C0 D( g函数原型:# j2 W: d/ q7 V) M+ K
- _, ?: h* l/ I3 v8 J9 q7 W
  1. void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    8 U1 [2 D" w; V' A) F4 x) {# h7 N
  2. {* Q& T, M8 U. b! t* r2 P
  3.   /* Check the parameters */
    9 o$ k' D/ g. S( l1 b# @2 Z2 E
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    ( O' }5 s# B% H1 t7 Z6 N( L, K
  5. 7 b5 w* B- ?- q/ m
  6.   GPIOx->ODR ^= GPIO_Pin;
    9 d" j% l8 O) k- F
  7. }
复制代码

- F! b; m' D3 a3 n! [函数描述:  y( M- K' g, C. a

# r, ^6 P1 p+ w1 m此函数用于设置引脚的电平翻转,使用GPIO的ODR寄存器进行设置。
+ S  N! w8 N7 j1 W& H- a" |) G% @+ B2 F! d; J& J% _
函数参数:
$ i0 {7 {4 S* [% Z( Q; |: }( Y
' \* S- F9 u" F4 g! S+ n  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
, V; H- x/ L* D! U4 n0 |. k  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。# v- A$ n" c; w  V. E! K
使用举例:
7 v9 a$ @5 W5 v' z% J- k
) X) r; G+ }  N3 p0 R0 S8 f' c此函数的使用比较简单,需要调用的时候直接调用即可。
% P6 ]/ [9 g3 `5 `  O! g+ S. m" Z4 b' z  C9 a
17.3.6 函数HAL_GPIO_LockPin& Q9 v6 F% @+ D% W( {6 I+ i3 a
函数原型:
; T' r+ n! M5 R% [( P
0 _) Q& T2 c" @
  1. HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    ! q- j9 W" P+ a! B
  2. {
    . u1 q7 p5 c% `8 p
  3.   __IO uint32_t tmp = GPIO_LCKR_LCKK;+ [* z$ i( z3 \
  4. ; \/ n" c4 j( y2 w" f* s
  5.   assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));
    * D* |9 I" [% O! m) G: X
  6.   assert_param(IS_GPIO_PIN(GPIO_Pin));1 ]- Y+ d0 r7 n& i& }4 Y
  7. + Y6 l' U/ ^5 y9 w3 N* ^$ L4 f
  8.   /* 应用IO锁的写入顺序 */. U/ u+ k; i( }7 Q- Q. ^
  9.   tmp |= GPIO_Pin;0 b: g  B" _  @, w$ }2 f
  10.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */: P1 r3 d$ v2 w7 u) W! a' L0 l
  11.   GPIOx->LCKR = tmp;
    " z" ]2 d" y8 |; Q% m; b% v/ |
  12.   /* 复位 LCKx bit(s): LCKK='0' + LCK[15-0] */
    - K( \% c; [0 Q2 O9 O2 ^& q$ N
  13.   GPIOx->LCKR = GPIO_Pin;. D6 l1 I# Z8 H; h' u4 j, O5 V9 a  k
  14.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
    / p. t6 `* t$ P3 |; E% {5 O
  15.   GPIOx->LCKR = tmp;/ V4 a6 I3 p9 B
  16.   /* 复位 LCKK bit*/, F7 Q+ {$ [9 m* a
  17.   tmp = GPIOx->LCKR;
    6 D2 d2 b+ c& h+ w& f% p3 Z

  18.   p/ Z; e0 T, H3 i( q7 M
  19. if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
    % o$ w1 ?$ e5 ]& {: ?( Q- @
  20.   {
    7 Q# E+ E* z$ I% ^* j8 y
  21.     return HAL_OK;7 h6 E0 D6 R7 |
  22.   }
    5 E- ^. r7 A. R9 n4 s0 t9 Y. y
  23.   else' Y$ ^. [' R* o5 x0 H3 K& @
  24.   {
    % A8 E) i; j# [8 F: p
  25.     return HAL_ERROR;
    % h) A; U* j' A' q
  26.   }
    ! c' c' |  x) [% B! d& R0 r& M
  27. }
复制代码
: n7 I# E6 c( i$ X7 S5 ]& e* H
函数描述:
! u* g3 @5 [' h' p, [. r
! C$ m) ?6 n' Y9 v6 N此函数用于锁住GPIO引脚所涉及到的寄存器,这些寄存器包括GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR,GPIOx_PUPDR,GPIOx_AFRL 和 GPIOx_AFRH。2 d8 O; k* `8 f: }

+ r% A1 h! B3 H( c0 E. L函数参数:
- i. _) d9 J  r1 a
5 z' i- M7 \  J3 y  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。3 s, ]8 j/ U6 B$ W2 c  }. [
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。- \* S% B; y, C* y* W9 m
注意事项:$ b+ c# z  ?& z- M2 i/ X
! P5 V* d' l. Y
此函数是锁住用户设置的引脚所对应的寄存器某些位,并不是把整个寄存器都锁住了。
4 k( P5 l# h* j5 C1 B一旦锁住后,就不能再修改,只有复位后才可以重新配置。
' n1 s  p/ l3 O  i使用举例:
) S! Q$ u6 @0 v  Y) N: |0 s3 [0 o; O$ m4 }0 l
此函数的使用比较简单,需要调用的时候直接调用即可。
5 I" S+ y. A2 t, h/ f2 {% x8 ?* e, i
, N3 @5 f, i& h2 T17.4 如何使用HAL库的GPIO驱动# C" J2 R  y$ h0 C, t
使用方法由HAL库提供(本章17.3.1小节提供的例子就是这种方式):  ~' a# u( J1 t5 T  P6 J

) s! {# ^. V  m, W  v. a4 O  第1步:使能GPIO所在总线的AHB时钟,__HAL_RCC_GPIOx_CLK_ENABLE()。
  U7 a3 v1 O) J' H3 [
1 B% s! K4 r8 a4 S  第2步:通过函数HAL_GPIO_Init()配置GPIO。
/ W# b. Z6 R/ |' T# ^4 A3 J0 Q! @, k8 [9 o- L- `0 R
(1)    通过结构体GPIO_InitTypeDef的成员Mode配置输入、输出、模拟等模式。* [0 h, c4 b( o' B
# q2 R  I6 B  v" V/ W- A2 S, }5 S
(2)    通过结构体GPIO_InitTypeDef的成员Pull配置上拉、下拉电阻。( b  c& |& T# P/ R/ b; D

5 ~0 L* B6 _' ]9 l(3)    通过结构体GPIO_InitTypeDef的成员Speed配置GPIO速度等级。
# |5 j3 @+ C/ J( N; e0 a  j1 e2 w) g  s2 X* ?2 Q
(4)    如果选择了复用模式,那么就需要配置结构体GPIO_InitTypeDef的成员Alternate。0 y. Y* Z$ Z: V, j4 S

2 s5 Z, ]' J" e(5)    如果引脚功能用于ADC、DAC的话,需要配置引脚为模拟模式。
, t# i2 M+ w, x7 E0 Q. u9 k4 W# R. o' L9 N- X. D, U
(6)    如果是用于外部中断/事件,结构体GPIO_InitTypeDef的成员Mode可以配置相应模式,相应的上升沿、下降沿或者双沿触发也可以选择。3 e' Y; I* S# W1 x+ B: }

& `" z* f' t/ T8 h+ N  ?  第3步:如果配置了外部中断/事件,可以通过函数HAL_NVIC_SetPriority设置优先级,然后调用函数HAL_NVIC_EnableIRQ使能此中断。! w$ |# |9 \0 j1 |- R

1 o$ O9 C2 K. Z# {  第4步:输入模式读取引脚状态可以使用函数HAL_GPIO_ReadPin。" u& ]+ e8 y5 ^  w! y
; u. e# H, `  K* ~1 B* ~/ R8 n7 q3 n( y
  第5步:输出模式设置引脚状态可以调用函数HAL_GPIO_WritePin()和HAL_GPIO_TogglePin。# P) b4 r$ @* x0 K' c5 @, s

% r* d( \$ N: H* ~1 x; K
1 r: T+ c- n- \& \3 }. r$ l, [
* j5 y3 V% `$ H% t7 j% L另外注意下面三个问题:
/ p2 x# u2 d9 \( d7 }! I0 A3 @- i" L
  系统上电复位后,GPIO默认是模拟模式,除了JTAG相关引脚。
3 \9 z% W% X2 H. G0 @( k$ |  关闭LSE的话,用到的两个引脚OSC32_IN和OSC32_OUT(分别是PC14,PC15)可以用在通用IO,如果开启了,就不能再做GPIO。
8 A8 F- _2 G7 a2 V  关闭HSE的话,用到的两个引脚OSC_IN和OSC_OUT(分别是PH0,PH1)可以用在通用IO,如果开启了,就不能再做GPIO。
0 O$ D& v# {$ R  ?; U17.5 总结2 u1 V+ ]/ U" y
本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。4 K7 ~& w' O. u

' N: `) v/ ]4 [( a8 u0 X. [5 u9 p- C4 A! L
收藏 评论0 发布时间:2021-10-29 23:33

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版