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

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

[复制链接]
STMCU小助手 发布时间:2021-10-29 23:33
17.1 初学者重要提示+ ?2 a0 p! J7 E& V5 i5 {
1、  如何阅读HAL库源码的问题
% k- F" l" x! e; S/ N8 E+ v4 Y' _
HAL库实现的函数有复杂的,也有简单的,简单的可以直接阅读代码。复杂的代码阅读起来比较耗时间,如果再配合参考手册抠每个寄存器的配置,那就更消耗时间了。所以对于这种函数,用户仅需了解每个部分实行的功能即可,而且HAL库都做了关键注释,以说明这部分实现的功能。所以用户没有必要去抠每个配置是如何实现的,仅需知道实现了什么功能。以后工程项目有需要了解具体配置时,再看即可。; g6 e! @2 m# N5 P

$ _" T% t7 U( c" u1 x/ E( z2、  学习本章节前,务必保证已经学习了第15章。
' F: F, L/ L9 W: a9 T6 D+ [7 ?
" k! P& y: O3 K. t/ ^/ B17.2 GPIO涉及到的寄存器
. F+ I! {/ ?( i; K, d# ZGPIO外设涉及到的寄存器比较少,也容易理解,推荐大家阅读GPIO源码的时候将参考手册中对应的寄存器功能做一个了解。
( b" K, z. ^' C
: y6 @4 M  W6 N6 z* J很多时候,我们会直接调用GPIO的寄存器进行配置,而不使用HAL进行调用,以提高执行效率,特别是中断里面执行时。
" Z* b8 Q+ {' S3 L! K  c" w& E
: w$ j4 `9 U; X$ z1 I: }. w17.3 源文件stm32h7xx_hal_gpio.c
# I( ^* u# C2 X6 s! C6 `8 d, m2 k* i4 F这个文件主要是实现GPIO的引脚配置,学习这个文件注意事项:2 h1 a/ N( F7 H+ k( q

1 B3 |9 K9 K0 _% `( W1 c. q% X3 W  系统上电后,引脚默认状态是模拟模式。
/ C* C3 Q6 n* c- l# k. {  所有的引脚有弱上拉和弱下拉电阻,阻值范围30-50KΩ。其中配置为模拟模式时,上拉和下拉被硬件禁止,其它的输入、输出和复用都可以配置上拉和下拉。
( _0 f5 \3 g+ _5 r; x/ U, Z  在输出或者复用模式,每个引脚可以配置成推挽或者开漏,且有GPIO速度等级可配置。另外注意,不同的供电范围,实际速度等级是有些区别的。/ @& G$ p- d; {
  每个GPIO都可以配置成外部中断/事件模式,但要特别注意,引脚要配置成输入模式,在芯片的内部有个多路选择器,选择引脚与16个外部中断/事件EXTI0 - EXTI15中的那个导通。这就决定了,每个外部中断/事件只能与一个引脚导通,如果用户配置了多个引脚PA0,PB0,PC0等,那么只有一个能够与EXTI0导通。
, q* B* s- N* u1 g7 Y17.3.1 函数HAL_GPIO_Init
+ @$ i* p5 c- v: q函数原型:6 x( ?- \! H6 U; z; y5 O% e: M* S

4 M( q# o: @" d% ~+ ]; y: A! m
  1. void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
    ' T' X2 k9 @( f% R
  2. {
    & }" p. f5 O" t$ M0 g- O$ L
  3. /* 部分省略未写 */: ?( X) c$ P7 @1 O+ Z, O% M: v
  4. 7 s  z2 R1 t! u' v+ b
  5.   /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */* T1 [3 I' w& c9 e" w
  6.   for(position = 0; position < GPIO_NUMBER; position++)7 k! M, L& d2 l
  7.   {" T/ p6 t6 q1 @7 {$ o
  8.      /* 部分省略未写 */
    4 e( p# R# C1 L. \! [
  9.     if(iocurrent == ioposition)2 E' L9 O. y8 N& K
  10.     {6 l+ X' t5 e; ]! R: n( i
  11.       /*--------------------- GPIO模式配置 ------------------------*/, ^1 T. Q4 M. n$ k5 v. [

  12. 8 F( P' W# o" `# U9 L( L! \* y
  13.       /*--------------------- EXTI模式配置 ------------------------*/0 C( D+ W! }6 y$ f+ T( s3 B0 U

  14. 2 Y0 U1 g+ p+ k3 W; B; ^- u
  15.     }
    4 B; R9 j" ?0 o# J# y9 \7 b5 c
  16.   }- o' r- P' T/ T" G6 H, E
  17. }
复制代码
3 H' y' ?7 _. C3 F6 F' P
函数描述:
- O& }0 v, `, c& o4 h: w4 Q" T8 ]% \9 I- T  N6 w/ {- ]
此函数用于初始化GPIO,此函数主要实现如下功能:$ {' H5 Z4 L( b$ @! Q3 V2 X* K

* T% B  Q, y( z1 c& p( q. }  GPIO功能配置。
7 ^' J+ w* [6 Z$ ~! z- _! U  设置EXTI功能。
* p& q) `2 A% T) e: W函数参数:
4 o# B3 }3 C# ?- D  [
4 ]6 E% Q( B# ]  第1个参数用于填写使用的端口号,可以是:" i4 v4 V! A( s2 I( c
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)
    % c, ?; K7 ?% |' O
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
    % f' K2 ?. e# c) U- s5 M" C
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)
    9 o# p+ H5 u$ g/ Y* c: t
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)
    1 g9 |( ~0 L% x) Z9 A: h* y
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)
    * s) D( B# Q9 ?  Y! L, ~. ^9 h
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)
    5 \, }1 e% z. {
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)3 a1 f6 T! I5 ^8 }9 e5 i
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)
    9 G" ]$ J: a# Z- f6 o
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)( X- z2 T+ A3 ^+ ^
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)
    , n3 Y$ S! e' D; j# ?4 ~# a& D; l
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码

3 ?- U" c, p  E+ t' ^7 F. F. z  第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:$ A$ S. P" l) h% \
  1. typedef struct
    ; M7 e$ T; w2 c5 p" `' e
  2. {/ H: X9 ]" ?* x; c' z
  3.   uint32_t Pin;   
    : o8 V0 O% N/ O9 ?
  4.   uint32_t Mode;
    , S; N7 ?0 E4 i9 ~# c
  5.   uint32_t Pull;     3 j* j* i4 I5 @" f5 _( j! a8 R
  6.   uint32_t Speed;   
    ( f! x) @) Q1 O# G) W8 w+ x  `
  7.   uint32_t Alternate;
    1 c; c7 v& v( y. N, Q2 R5 ?; I
  8. }GPIO_InitTypeDef;
复制代码
2 h' I4 K2 |  j/ a3 L0 M
下面将结构体每个成员做个说明:
' W7 u6 A( I$ R& n- Z
% R& Y9 c7 y2 n! L7 W  成员Pin用于配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15,额外还可以选择GPIO_PIN_All和GPIO_PIN_MASK。- ?; e; r) |. z/ r0 O  |2 j
  成员Mode可以选择:
! T) G- S3 N" _" {8 H
  1. GPIO_MODE_INPUT             /* 输入模式  */3 C) U6 ?7 k- r/ j2 m4 M; S
  2. GPIO_MODE_OUTPUT_PP        /* 推挽输出  */
    8 h3 C: {! X6 _
  3. GPIO_MODE_OUTPUT_OD       /* 开漏输出  */
    6 A+ k3 i6 F' u4 d+ u- `; j+ f5 q; p
  4. GPIO_MODE_AF_PP             /* 复用推挽  */; m1 Q$ @9 R; x$ h
  5. GPIO_MODE_AF_OD            /* 复用开漏  */
    7 n: G% a( C3 }* ^* l! `

  6. 7 ^/ G3 {% g/ |9 G1 c/ t0 r7 Z$ J1 @
  7. GPIO_MODE_ANALOG          /* 模拟模式  */: `  O$ N/ U7 D4 @4 L; j
  8. GPIO_MODE_IT_RISING         /* 外部中断,上升沿触发检测 */
    : R" m# L$ I7 v
  9. GPIO_MODE_IT_FALLING       /* 外部中断,下降沿触发检测 */
    ' ]1 ^3 }6 k; y) e4 X4 R0 u4 [9 f$ \
  10. GPIO_MODE_IT_RISING_FALLING    /* 外部中断,双沿触发检测   */8 m" Q' s9 s0 A( a6 e0 J, D

  11. + J3 _7 }( V5 r3 |. o  {) |$ j4 L
  12. GPIO_MODE_EVT_RISING           /* 外部事件模式,上升沿触发检测  */
    & l) ~# B9 N) z5 q7 l1 `
  13. GPIO_MODE_EVT_FALLING          /* 外部事件模式,下降沿触发检测  */4 Y, F+ Z# w& c2 ?$ p1 Q
  14. GPIO_MODE_EVT_RISING_FALLING  /* 外部事件模式,双沿触发检测 */
复制代码
7 ~7 D2 w* \* F6 I& F: T
  成员Pull用于配置上拉下拉电阻:/ D. u% a+ p, d6 M. h( [
  1. GPIO_NOPULL         /* 无上拉和下拉电阻 */8 k  ~$ u4 y. u( K0 v
  2. GPIO_PULLUP          /* 带上拉电阻  */; H4 r/ C+ I9 ~; Y1 q
  3. GPIO_PULLDOWN    /* 带下拉电阻  */) B6 \- d+ Q0 Q/ A9 C7 P* Z# l
  4.   成员Speed用于配置GPIO速度等级,有下面四种可选:% `- ^+ ]: P: u9 `% ?9 ^* W
  5. GPIO_SPEED_FREQ_LOW          /* 低速 */* P3 ~8 r- n2 ]$ S
  6. GPIO_SPEED_FREQ_MEDIUM      /* 中等速度 */1 V- s6 u# W8 Q$ y2 k6 M' q- W
  7. GPIO_SPEED_FREQ_HIGH         /* 快速 */
    * K3 r; ?# `, A* o" G, M, s; @' I
  8. GPIO_SPEED_FREQ_VERY_HIGH   /* 高速  */
复制代码

' x# ]! U3 B0 u$ F1 _  成员Alternate用于配置引脚复用,可选择的复用方式在文件stm32h7xx_hal_gpio_ex.h里面进行了定义,比如串口复用:) f7 I" {3 L! O5 ^; X# y' r
  1. GPIO_AF7_USART1   
    6 E* G: l5 x& \
  2. GPIO_AF7_USART2   
    . v. O; l  A4 j7 S) J, P
  3. GPIO_AF7_USART3       + ~' U; J( m5 {" z
  4. GPIO_AF7_USART6   
    8 |! R% j+ H2 L0 `* v& Y
  5. GPIO_AF7_UART7
复制代码
' x, e3 F5 T; y
注意事项:; X9 {* S4 r9 [* b

5 ?' B; o. h, s1 E; X' P与F1,F4系列的标准库不同,H7的HAL库已经没有单独的EXTI外部中断设置文件,是将其整合到此函数里面了。9 h( {0 L$ J6 {; j
函数HAL_GPIO_Init对引脚的初始化是把同组16个引脚for循环检测了一遍,效率稍低。所以不推荐下面这种初始化:
2 h' h7 P% D" o/ ?, `1 F5 q
  1. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                   # f+ ]- y+ E: B$ E, f; A
  2. GPIO_InitStruct.Pull = GPIO_NOPULL;              
    # T2 o4 T; `% \3 P# t
  3. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  
    4 ?8 d# M+ l$ G6 \) ^. ]
  4. GPIO_InitStruct.Pin = GPIO_PIN_0;
    , p3 w) O' `$ |
  5. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询" L0 ^6 t5 J9 y- Q; @
  6. ' r* Q: {1 N% r( h: U. Z
  7. GPIO_InitStruct.Pin = GPIO_PIN_1;" d# Z0 L/ z& v! H1 @
  8. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询
    3 n8 q1 c- M4 v1 H. g
  9. 9 E* U3 _4 t5 {; M! {
  10. GPIO_InitStruct.Pin = GPIO_PIN_2;
    . `9 v" o; D3 I; Q" p, \6 L1 S; b
  11. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码

. p. X0 C$ i) N! B5 w; }' `! V4 `& Q3 Z如果是程序运行期间的引脚状态切换,最好采用下面的方式或者直接寄存器操作:- v" G! B5 j; `' x$ L
, F; e/ K; u9 o! Q( @6 [& D: I" x
  1. GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ;0 |9 E$ ~/ m* U, Y0 }- b3 G
  2. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                   & ~& B: V6 h. |; R" I: _- e
  3. GPIO_InitStruct.Pull = GPIO_NOPULL;              
    9 ~) D& i. m! X' e( b
  4. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  5 g, \9 a: ?2 a3 r6 k
  5. ; i' c: d9 @/ {0 k
  6. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码

" X3 W1 [3 v' G+ ^  n+ [使用举例:' F8 f& a+ D% `; T, O

8 y3 q+ Y/ F# j: _# K- k
  1. GPIO_InitTypeDef  GPIO_InitStruct;
    + R" k) P1 G! e3 Q7 S( N
  2. 2 P6 X5 h0 t% [+ {" n$ ~
  3. GPIO_InitStruct.Pin = GPIO_PIN_0;4 A' P/ l0 k& R! R2 e% \
  4. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */         
    ! v5 z: E5 L/ G
  5. GPIO_InitStruct.Pull = GPIO_NOPULL;                /* 无上拉和下拉电阻 */
    $ K9 G; y4 {& j/ I0 Q# {) P! s7 C
  6. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */) v+ s# ]" y0 V% C
  7. 4 b1 y* u4 I2 h) k% m* ?  `& Y
  8. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
复制代码

) D( g4 \3 d' R$ _17.3.2 函数HAL_GPIO_DeInit
# m1 `+ ?9 \  L& E/ I- d. m# k0 r函数原型:" A" O8 Q+ `; b5 C6 c

  \- J. [& g. d3 B0 u: n
  1. void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)
    8 J# m2 @2 E/ f( o- J* h
  2. {  $ C* f6 n7 h  V
  3.   for(position = 0; position < GPIO_NUMBER; position++)7 J1 L! i. v2 Y4 _! q2 E- h4 \
  4.   {
    ! e9 f4 a( c/ y' Q2 g0 ?
  5.      /* 部分省略未写 */
    : f$ I" d4 c. X% x
  6.     if(iocurrent == ioposition)
    9 w  p! J# ?2 }, i) S
  7.     {* |8 k0 B: m% b3 f1 x. |
  8.       /*------------------------- GPIO Mode Configuration --------------------*/
    7 G/ ^$ Q3 v* i! x) k! }  N
  9.       /* 配置为模拟模式 */
    ; g, d5 B2 y. x) }0 U, e- Q/ X$ S
  10.       GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2));$ J4 y5 B/ T& i* S! m
  11. 8 n4 t2 v  S+ Q* T5 [
  12.       /* 配置复用模式为AF0,即作为通用IO */: ~9 I0 @1 f: G4 F
  13.       GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;% R& `3 G1 ?6 P  z! Y3 }. K/ n
  14. * z, C2 U7 R0 D0 o
  15.       /* 配置到最低速度 */7 d& |! {$ E9 S+ L0 [1 M
  16.       GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2));. n6 g* ]1 V- f8 b
  17. " t% x, K( M' X# m( i- D, r
  18.       /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */0 q9 M4 P' L3 S$ ]' L9 S
  19.       GPIOx->OTYPER  &= ~(GPIO_OTYPER_OT_0 << position) ;1 }8 @* N& }2 Q) ]

  20. 0 \7 c: P$ o. \7 a3 w! x+ V
  21.       /* 无上拉和下拉电阻 */
    - S$ H4 w5 C% x& [: F" t! r5 i* y
  22.       GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2));
    + o2 y4 M( a7 @7 y, e( @

  23. 1 \% I* P. v8 Z" U9 y8 f7 J
  24.       /*------------------------- EXTI模式配置 --------------------*/9 s! S  C( b5 A9 q0 Y; T+ O: M
  25. ( f7 ]/ Q% J% O+ d$ u0 q. f
  26.     }1 |- ~* ]3 P: {6 I
  27.   }2 `4 V1 p. I9 ~. i1 ^" q
  28. }
复制代码

5 v* M- z$ K# S5 H; q4 ]函数描述:
" Y5 I9 {3 k1 k0 V  x" {* S( ^
3 d; p3 Y* Q. K+ B2 A此函数用于复位IO到初始化状态,具体状态看函数原型中的注释即可。
1 r5 g. n' C8 Z7 M  x. C5 O7 Z7 X. J: L2 j* h
函数参数:# H1 |; z. D! m0 j6 V6 i) O
8 l9 S1 `5 P6 V! O) y
  第1个参数用于填写使用的端口号,可以是:: ^0 T5 Y- ^2 ?
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)+ b9 I/ m5 y1 Z* j  S6 n
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
    , u, Y: c% G  S1 y
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)7 t. U1 e0 k$ A5 b! D/ X
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)* W/ s: E" @0 z! f4 ?2 m
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)
    ( X' a- E8 O9 Y7 x1 M% S* V* F7 W
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)4 O6 U# C/ j7 I1 q( h
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)
    6 A* ?4 {/ d; X/ y$ J- k
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)
    - ?% _# n8 r" I% x
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)
    $ r( ^( ~. f+ y7 |) r6 Y$ P2 i6 u
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)
    : {% L4 K" L/ R6 Q
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码
- E- h( L& G8 e5 K2 ]  N1 Q+ K, C
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
- j* Q3 ~3 M4 O( L# C使用举例:! U! L- x1 D1 ]* M) ^4 e
/ s# G9 A$ ^" `8 j
此函数的使用比较简单,需要调用的时候直接调用即可。
' S7 s% x" j0 U) m# s, J% O, W- h% }5 {! ^7 C
17.3.3 函数HAL_GPIO_ReadPin
, ?; w7 Z; O  p$ X: x函数原型:
% o7 H# A: F0 b  I9 ?" U" m, S1 Z- b! P+ g
  1. GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)# M8 _0 j: B6 M; F
  2. {3 \. j' b+ q- ^/ Z
  3.   GPIO_PinState bitstatus;
    6 D$ r/ J0 c5 T" M5 P
  4. ' k* ~( b* X3 W( I
  5.   /* Check the parameters */
    3 G, R- n% w) m9 [! q
  6.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    8 `6 w% e2 l2 J7 B( }
  7. $ Y1 q( i+ Q5 ?7 _
  8.   if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
    9 L% L+ w7 O* ?
  9.   {, }3 q) ~. a3 u0 y/ A- o
  10.     bitstatus = GPIO_PIN_SET;4 k5 k3 [! k5 f* u8 G; N
  11.   }6 k4 \4 }2 x) ]# H8 k* U
  12.   else( Q& Z' Y7 h' C. p$ X
  13.   {
    ! i/ w- B* q5 E+ F2 O, G; B# \
  14.     bitstatus = GPIO_PIN_RESET;% a0 F' J8 _* |3 O9 q6 ?
  15.   }
    % @0 Q; I, p) a7 z
  16.   return bitstatus;
    , Y& p, a. V* e; Z7 w+ D; g
  17. }
复制代码

# f0 y9 K6 s) N函数描述:* m- @( d! e# h+ V

0 l: b' R4 O6 G8 \) X  F此函数用于读取引脚状态,通过GPIO的IDR寄存器读取。1 e! \* g' |7 N- k5 Y. a, i
7 @7 w3 ]% w3 ]2 N/ V! O
函数参数:8 ?+ k) \, _' b6 S1 u4 ^

6 k; P; s( B6 n+ e1 |  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。) g3 b9 l1 ~5 o
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
2 I6 B& \" ?% ]5 j7 D9 |# j) T使用举例:
' a1 v- Z' v9 k$ a7 c
7 H6 n5 C) ~7 |8 t此函数的使用比较简单,需要调用的时候直接调用即可。
6 {3 {7 P0 @4 p; a' F0 z  Z; s' I! h, F" N# H
17.3.4 函数HAL_GPIO_WritePin) o5 ]0 u' S- ^4 K; R) A1 |
函数原型:4 Q" P2 s2 W, O! C8 `. Q
, |5 i- g$ y, l7 d3 c' e: J
  1. void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    % ~+ [& j. C) `7 N
  2. {6 ]1 Y! r7 K1 L
  3.   /* Check the parameters */2 y; t. t: m: y( i" G& A% d& G
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));) n& Q( S# X3 ~% H1 t
  5.   assert_param(IS_GPIO_PIN_ACTION(PinState));  \5 b$ H2 H$ h* ?9 S' E" z' ^/ E2 P4 v
  6.   O9 z& c) X2 r/ J2 E
  7.   if(PinState != GPIO_PIN_RESET)8 E0 m4 g6 w$ |! `6 n& D
  8.   {
    , g# h, m" t9 C" g- h
  9.     GPIOx->BSRRL = GPIO_Pin;$ ~( e3 x: P4 {/ {( w
  10.   }: B+ W! m; S. g7 F
  11.   else; X6 V' T8 O* U* X0 Q% u" ?) w8 d
  12.   {
    1 @8 D+ a# I+ F$ e8 N
  13.     GPIOx->BSRRH = GPIO_Pin ;. H  q4 B% _8 Q! \" d
  14.   }
    2 Y! [9 S1 J& T0 s
  15. }
复制代码
& f4 g; r9 W9 r6 n
函数描述:6 k  @- F8 P6 _4 e" k' X3 F) J  x
6 ?. d$ g) o: {, M5 a' e1 u
此函数用于设置引脚输出高电平或者低电平。使用GPIO的BSRR寄存器进行设置,使用这个寄存器的好处是支持原子操作,由硬件支持的。原子操作的含义是操作过程不会被中断打断,而我们使用GPIO中另一个设置输出的寄存ODR是会被中断打断的。大家看下寄存器赋值操作对应的反汇编,是由多条汇编指令组成的。
% Q$ s3 Q6 M3 B" O
: s) A+ @7 f5 \9 B7 w8 p函数参数:7 W$ v5 N6 e) Z, u" Q% M0 _

# ]5 \$ A3 L- o# o2 J  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。) e+ ]/ T8 C! f. d  V1 E
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
9 ^. P# r2 D7 U  第3个参数用于设置引脚输出高电平还是低电平,GPIO_PIN_RESET表示低电平,GPIO_PIN_SET表示高电平。1 {  H8 x% J( P; ?- e) Y
使用举例:
- C+ X  J7 n  c( S* }& C% V8 J8 _) U- ]/ M; b
此函数的使用比较简单,需要调用的时候直接调用即可。3 u. z0 ~- P* a" S9 {7 Y% \

7 C  e4 P, Y0 g8 q- w17.3.5 函数HAL_GPIO_TogglePin; B5 Y9 O) X3 d
函数原型:7 ]( s/ ~# Q$ x# D$ X4 s

8 l* A; V1 t$ [) C. }
  1. void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    ) x3 v0 D1 v. e8 x, j; {
  2. {+ ~, s% p+ K" W; f# `2 V9 n: N7 S
  3.   /* Check the parameters */
      ?( a. L! U+ x9 L# s5 ]4 R7 ]% v. W
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    ) ^4 w$ ]! @1 @- m0 R; @
  5. 0 @3 t  Y4 l6 e% m7 V2 M/ T0 Q7 t
  6.   GPIOx->ODR ^= GPIO_Pin;
    . s  Z0 Q0 c0 t  F, s  d1 E: S
  7. }
复制代码
/ L& x+ s+ U, D
函数描述:
5 r: V" z& t8 |  n
% {( v  `5 J# A4 b此函数用于设置引脚的电平翻转,使用GPIO的ODR寄存器进行设置。0 y8 J/ B9 N$ X. C& B# I" w

8 }/ y. q4 ~# W, X函数参数:
0 I* S. [, {! N) O% V7 z  d* U+ T( E( u6 Z3 i8 s
  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。6 |$ a5 t) [7 O- D( a
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。' q* C: _1 e+ n0 y3 R) c
使用举例:
- J2 r- r8 N  o8 M) S; _0 X- u
; S$ I, r  S% a% e& F% d此函数的使用比较简单,需要调用的时候直接调用即可。! v/ d; V+ H$ r9 g) K0 a  e  l, C

) o- P* O3 A, p, ]$ b17.3.6 函数HAL_GPIO_LockPin+ d$ ~- v3 Z# f! A* i; y1 B8 d3 m
函数原型:
$ b5 Y; o7 ]# P
/ C( }; q$ ~8 d  b  E
  1. HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    : Q' M. w% l+ I* u  x
  2. {
    " r# Z1 Z# k; R% @7 |# t/ x
  3.   __IO uint32_t tmp = GPIO_LCKR_LCKK;4 W) x  a) H, B4 h6 _

  4. + r9 C' Q) v+ L- v9 s" @
  5.   assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));: }, K5 `+ R+ E8 t+ Q# q
  6.   assert_param(IS_GPIO_PIN(GPIO_Pin));  A+ m0 u- r1 H& R- L
  7. ' j: \& d4 m6 n: s8 t: K
  8.   /* 应用IO锁的写入顺序 */
    ) w/ m8 q# R/ K8 q$ T3 b, b1 U
  9.   tmp |= GPIO_Pin;# f3 D) m3 }% r+ [5 Z/ [
  10.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */1 [2 Z  E4 f1 f# ~4 c; Q
  11.   GPIOx->LCKR = tmp;& m% C9 L' S' n1 b# r( C
  12.   /* 复位 LCKx bit(s): LCKK='0' + LCK[15-0] */
    ! |% H9 _* Y/ ?2 ^$ X: Q5 s
  13.   GPIOx->LCKR = GPIO_Pin;$ i8 t5 K$ a/ K! n# X
  14.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
    % i) H0 W" w1 }5 P: o2 c
  15.   GPIOx->LCKR = tmp;( g  [" `* f1 B0 N
  16.   /* 复位 LCKK bit*/
    * Z0 @; b" l' w" M- [+ N) o5 M
  17.   tmp = GPIOx->LCKR;
    1 g6 M+ g0 `0 I5 O
  18. * o0 [* U0 D3 L+ H5 h3 h! l, S
  19. if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
    . z1 b1 Y/ `& W# k6 e: j! r9 E
  20.   {
    - N: z8 m3 q" s  O" f; @* Z
  21.     return HAL_OK;5 b  b  P: h0 P7 [5 D( t% m' X
  22.   }
    : M5 x# V5 c) e) S
  23.   else
    3 f: A, W* g- Z/ l, F7 }
  24.   {
    , n3 }2 l3 d3 T. D
  25.     return HAL_ERROR;2 w8 W/ ^9 w/ ]0 s
  26.   }
    ) ^& E' ^$ y; c+ ]0 W/ b
  27. }
复制代码

& D+ o: V2 R& {' B; _9 m, j函数描述:
8 i7 |5 W) ?/ h
) f+ ~$ _! a$ _* R* h此函数用于锁住GPIO引脚所涉及到的寄存器,这些寄存器包括GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR,GPIOx_PUPDR,GPIOx_AFRL 和 GPIOx_AFRH。8 x( N+ k) |8 g" }$ H, f
+ B1 a  J7 B  H0 D6 L4 X. z
函数参数:4 p" v' J# N& g
# N) s! S# r! @8 Y+ P
  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
! n( n. i6 H  ?3 i) S2 C  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。+ P# H9 v* H9 R. e) v: w+ [
注意事项:* K4 o3 y- M7 |6 W. s% \( g# ~
6 W: f9 V8 L6 a6 z/ {
此函数是锁住用户设置的引脚所对应的寄存器某些位,并不是把整个寄存器都锁住了。5 e7 F; P: v6 H$ `/ k4 M2 d
一旦锁住后,就不能再修改,只有复位后才可以重新配置。" j; T# u7 `$ k0 c7 P4 s6 F2 K# c
使用举例:
1 p$ p% c  j. }8 K+ B
: g4 Z4 o, i, v: V此函数的使用比较简单,需要调用的时候直接调用即可。1 H9 }; ]* c- |2 t. V! C7 F# \. ]
4 [- C1 D* O! b& j; G
17.4 如何使用HAL库的GPIO驱动
) a8 |, \- C+ f4 o4 ^! i* n1 a使用方法由HAL库提供(本章17.3.1小节提供的例子就是这种方式):  V9 A' |, f! w6 ?

8 k8 @0 E, |- E2 k& x3 J$ a  第1步:使能GPIO所在总线的AHB时钟,__HAL_RCC_GPIOx_CLK_ENABLE()。6 K+ j* d. A8 {& Y2 K2 ~

+ R# |) h  W" a3 R) j& q- ]  第2步:通过函数HAL_GPIO_Init()配置GPIO。: b+ l4 A2 o' M5 f2 p5 e' `
8 z( j' `4 g) X' I
(1)    通过结构体GPIO_InitTypeDef的成员Mode配置输入、输出、模拟等模式。
1 u: \6 E8 _+ _" |# M9 O, L) j8 ~
5 L4 s  v% e/ Q% H# q2 @) D(2)    通过结构体GPIO_InitTypeDef的成员Pull配置上拉、下拉电阻。
. M7 R* `$ f: W% v# x( I, T& z# }) p4 Q6 L0 e( `
(3)    通过结构体GPIO_InitTypeDef的成员Speed配置GPIO速度等级。# o+ n% [- W5 S, d  z7 K9 z
  P) \! b: M/ T% t2 v+ `9 C: X1 b
(4)    如果选择了复用模式,那么就需要配置结构体GPIO_InitTypeDef的成员Alternate。
; x  b  \# F; K3 X+ K/ n* y- ]# K7 I" B& D4 @! ~' u
(5)    如果引脚功能用于ADC、DAC的话,需要配置引脚为模拟模式。
) `" [( D) P+ a2 u; c1 L7 j& A$ @$ O
(6)    如果是用于外部中断/事件,结构体GPIO_InitTypeDef的成员Mode可以配置相应模式,相应的上升沿、下降沿或者双沿触发也可以选择。* N. u0 A4 F' g; w, o

) D+ e; X( f' B# R+ o: z" {  第3步:如果配置了外部中断/事件,可以通过函数HAL_NVIC_SetPriority设置优先级,然后调用函数HAL_NVIC_EnableIRQ使能此中断。( ~" N; I4 t, t5 Z, f" Q5 m; r

+ Y( z* n7 P; O0 v  第4步:输入模式读取引脚状态可以使用函数HAL_GPIO_ReadPin。
. ~$ o0 p. Y/ {% F0 C- E$ @# {
  第5步:输出模式设置引脚状态可以调用函数HAL_GPIO_WritePin()和HAL_GPIO_TogglePin。* y' |0 g* L8 ~! j7 a- h

+ U  i0 u5 P& p+ `5 N# b$ d* J3 i% p  `- V) d9 W- G2 t$ W& c
7 C1 t# _2 K# d$ O/ U) w
另外注意下面三个问题:
, C" i% @8 N7 ?+ A, v3 H0 k% i7 a$ P( b/ V. W) c8 S5 W8 \) n
  系统上电复位后,GPIO默认是模拟模式,除了JTAG相关引脚。
& ]. [4 a: a( @2 L& D  关闭LSE的话,用到的两个引脚OSC32_IN和OSC32_OUT(分别是PC14,PC15)可以用在通用IO,如果开启了,就不能再做GPIO。
- @5 N" L. J; R7 Z4 g: V  关闭HSE的话,用到的两个引脚OSC_IN和OSC_OUT(分别是PH0,PH1)可以用在通用IO,如果开启了,就不能再做GPIO。4 ?' w. }) U. ~; T4 B3 l5 V3 j
17.5 总结
- h9 k* u5 B, t! v; c& D7 i本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。
7 Y( q* t, u5 E# }2 q* u1 h6 y; z
; f, w) L$ f' O4 p' w5 O' d+ P- r: S# y+ u" Z7 k0 H+ q1 A$ y
赞 收藏 评论0 发布时间:2021-10-29 23:33

举报

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