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- void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
' T' X2 k9 @( f% R - {
& }" p. f5 O" t$ M0 g- O$ L - /* 部分省略未写 */: ?( X) c$ P7 @1 O+ Z, O% M: v
- 7 s z2 R1 t! u' v+ b
- /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */* T1 [3 I' w& c9 e" w
- for(position = 0; position < GPIO_NUMBER; position++)7 k! M, L& d2 l
- {" T/ p6 t6 q1 @7 {$ o
- /* 部分省略未写 */
4 e( p# R# C1 L. \! [ - if(iocurrent == ioposition)2 E' L9 O. y8 N& K
- {6 l+ X' t5 e; ]! R: n( i
- /*--------------------- GPIO模式配置 ------------------------*/, ^1 T. Q4 M. n$ k5 v. [
8 F( P' W# o" `# U9 L( L! \* y- /*--------------------- EXTI模式配置 ------------------------*/0 C( D+ W! }6 y$ f+ T( s3 B0 U
2 Y0 U1 g+ p+ k3 W; B; ^- u- }
4 B; R9 j" ?0 o# J# y9 \7 b5 c - }- o' r- P' T/ T" G6 H, E
- }
复制代码 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
- #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
% c, ?; K7 ?% |' O - #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
% f' K2 ?. e# c) U- s5 M" C - #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
9 o# p+ H5 u$ g/ Y* c: t - #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
1 g9 |( ~0 L% x) Z9 A: h* y - #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
* s) D( B# Q9 ? Y! L, ~. ^9 h - #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
5 \, }1 e% z. { - #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)3 a1 f6 T! I5 ^8 }9 e5 i
- #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
9 G" ]$ J: a# Z- f6 o - #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)( X- z2 T+ A3 ^+ ^
- #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
, n3 Y$ S! e' D; j# ?4 ~# a& D; l - #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
复制代码
3 ?- U" c, p E+ t' ^7 F. F. z 第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:$ A$ S. P" l) h% \
- typedef struct
; M7 e$ T; w2 c5 p" `' e - {/ H: X9 ]" ?* x; c' z
- uint32_t Pin;
: o8 V0 O% N/ O9 ? - uint32_t Mode;
, S; N7 ?0 E4 i9 ~# c - uint32_t Pull; 3 j* j* i4 I5 @" f5 _( j! a8 R
- uint32_t Speed;
( f! x) @) Q1 O# G) W8 w+ x ` - uint32_t Alternate;
1 c; c7 v& v( y. N, Q2 R5 ?; I - }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- GPIO_MODE_INPUT /* 输入模式 */3 C) U6 ?7 k- r/ j2 m4 M; S
- GPIO_MODE_OUTPUT_PP /* 推挽输出 */
8 h3 C: {! X6 _ - GPIO_MODE_OUTPUT_OD /* 开漏输出 */
6 A+ k3 i6 F' u4 d+ u- `; j+ f5 q; p - GPIO_MODE_AF_PP /* 复用推挽 */; m1 Q$ @9 R; x$ h
- GPIO_MODE_AF_OD /* 复用开漏 */
7 n: G% a( C3 }* ^* l! `
7 ^/ G3 {% g/ |9 G1 c/ t0 r7 Z$ J1 @- GPIO_MODE_ANALOG /* 模拟模式 */: ` O$ N/ U7 D4 @4 L; j
- GPIO_MODE_IT_RISING /* 外部中断,上升沿触发检测 */
: R" m# L$ I7 v - GPIO_MODE_IT_FALLING /* 外部中断,下降沿触发检测 */
' ]1 ^3 }6 k; y) e4 X4 R0 u4 [9 f$ \ - GPIO_MODE_IT_RISING_FALLING /* 外部中断,双沿触发检测 */8 m" Q' s9 s0 A( a6 e0 J, D
+ J3 _7 }( V5 r3 |. o {) |$ j4 L- GPIO_MODE_EVT_RISING /* 外部事件模式,上升沿触发检测 */
& l) ~# B9 N) z5 q7 l1 ` - GPIO_MODE_EVT_FALLING /* 外部事件模式,下降沿触发检测 */4 Y, F+ Z# w& c2 ?$ p1 Q
- GPIO_MODE_EVT_RISING_FALLING /* 外部事件模式,双沿触发检测 */
复制代码 7 ~7 D2 w* \* F6 I& F: T
成员Pull用于配置上拉下拉电阻:/ D. u% a+ p, d6 M. h( [
- GPIO_NOPULL /* 无上拉和下拉电阻 */8 k ~$ u4 y. u( K0 v
- GPIO_PULLUP /* 带上拉电阻 */; H4 r/ C+ I9 ~; Y1 q
- GPIO_PULLDOWN /* 带下拉电阻 */) B6 \- d+ Q0 Q/ A9 C7 P* Z# l
- 成员Speed用于配置GPIO速度等级,有下面四种可选:% `- ^+ ]: P: u9 `% ?9 ^* W
- GPIO_SPEED_FREQ_LOW /* 低速 */* P3 ~8 r- n2 ]$ S
- GPIO_SPEED_FREQ_MEDIUM /* 中等速度 */1 V- s6 u# W8 Q$ y2 k6 M' q- W
- GPIO_SPEED_FREQ_HIGH /* 快速 */
* K3 r; ?# `, A* o" G, M, s; @' I - GPIO_SPEED_FREQ_VERY_HIGH /* 高速 */
复制代码
' x# ]! U3 B0 u$ F1 _ 成员Alternate用于配置引脚复用,可选择的复用方式在文件stm32h7xx_hal_gpio_ex.h里面进行了定义,比如串口复用:) f7 I" {3 L! O5 ^; X# y' r
- GPIO_AF7_USART1
6 E* G: l5 x& \ - GPIO_AF7_USART2
. v. O; l A4 j7 S) J, P - GPIO_AF7_USART3 + ~' U; J( m5 {" z
- GPIO_AF7_USART6
8 |! R% j+ H2 L0 `* v& Y - 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- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; # f+ ]- y+ E: B$ E, f; A
- GPIO_InitStruct.Pull = GPIO_NOPULL;
# T2 o4 T; `% \3 P# t - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
4 ?8 d# M+ l$ G6 \) ^. ] - GPIO_InitStruct.Pin = GPIO_PIN_0;
, p3 w) O' `$ | - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询" L0 ^6 t5 J9 y- Q; @
- ' r* Q: {1 N% r( h: U. Z
- GPIO_InitStruct.Pin = GPIO_PIN_1;" d# Z0 L/ z& v! H1 @
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询
3 n8 q1 c- M4 v1 H. g - 9 E* U3 _4 t5 {; M! {
- GPIO_InitStruct.Pin = GPIO_PIN_2;
. `9 v" o; D3 I; Q" p, \6 L1 S; b - 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
- GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ;0 |9 E$ ~/ m* U, Y0 }- b3 G
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; & ~& B: V6 h. |; R" I: _- e
- GPIO_InitStruct.Pull = GPIO_NOPULL;
9 ~) D& i. m! X' e( b - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; 5 g, \9 a: ?2 a3 r6 k
- ; i' c: d9 @/ {0 k
- 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- GPIO_InitTypeDef GPIO_InitStruct;
+ R" k) P1 G! e3 Q7 S( N - 2 P6 X5 h0 t% [+ {" n$ ~
- GPIO_InitStruct.Pin = GPIO_PIN_0;4 A' P/ l0 k& R! R2 e% \
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
! v5 z: E5 L/ G - GPIO_InitStruct.Pull = GPIO_NOPULL; /* 无上拉和下拉电阻 */
$ K9 G; y4 {& j/ I0 Q# {) P! s7 C - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */) v+ s# ]" y0 V% C
- 4 b1 y* u4 I2 h) k% m* ? `& Y
- 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- void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
8 J# m2 @2 E/ f( o- J* h - { $ C* f6 n7 h V
- for(position = 0; position < GPIO_NUMBER; position++)7 J1 L! i. v2 Y4 _! q2 E- h4 \
- {
! e9 f4 a( c/ y' Q2 g0 ? - /* 部分省略未写 */
: f$ I" d4 c. X% x - if(iocurrent == ioposition)
9 w p! J# ?2 }, i) S - {* |8 k0 B: m% b3 f1 x. |
- /*------------------------- GPIO Mode Configuration --------------------*/
7 G/ ^$ Q3 v* i! x) k! } N - /* 配置为模拟模式 */
; g, d5 B2 y. x) }0 U, e- Q/ X$ S - GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2));$ J4 y5 B/ T& i* S! m
- 8 n4 t2 v S+ Q* T5 [
- /* 配置复用模式为AF0,即作为通用IO */: ~9 I0 @1 f: G4 F
- GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;% R& `3 G1 ?6 P z! Y3 }. K/ n
- * z, C2 U7 R0 D0 o
- /* 配置到最低速度 */7 d& |! {$ E9 S+ L0 [1 M
- GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2));. n6 g* ]1 V- f8 b
- " t% x, K( M' X# m( i- D, r
- /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */0 q9 M4 P' L3 S$ ]' L9 S
- GPIOx->OTYPER &= ~(GPIO_OTYPER_OT_0 << position) ;1 }8 @* N& }2 Q) ]
0 \7 c: P$ o. \7 a3 w! x+ V- /* 无上拉和下拉电阻 */
- S$ H4 w5 C% x& [: F" t! r5 i* y - GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2));
+ o2 y4 M( a7 @7 y, e( @
1 \% I* P. v8 Z" U9 y8 f7 J- /*------------------------- EXTI模式配置 --------------------*/9 s! S C( b5 A9 q0 Y; T+ O: M
- ( f7 ]/ Q% J% O+ d$ u0 q. f
- }1 |- ~* ]3 P: {6 I
- }2 `4 V1 p. I9 ~. i1 ^" q
- }
复制代码
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 ?
- #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)+ b9 I/ m5 y1 Z* j S6 n
- #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
, u, Y: c% G S1 y - #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)7 t. U1 e0 k$ A5 b! D/ X
- #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)* W/ s: E" @0 z! f4 ?2 m
- #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
( X' a- E8 O9 Y7 x1 M% S* V* F7 W - #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)4 O6 U# C/ j7 I1 q( h
- #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
6 A* ?4 {/ d; X/ y$ J- k - #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
- ?% _# n8 r" I% x - #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)
$ r( ^( ~. f+ y7 |) r6 Y$ P2 i6 u - #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)
: {% L4 K" L/ R6 Q - #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
- GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)# M8 _0 j: B6 M; F
- {3 \. j' b+ q- ^/ Z
- GPIO_PinState bitstatus;
6 D$ r/ J0 c5 T" M5 P - ' k* ~( b* X3 W( I
- /* Check the parameters */
3 G, R- n% w) m9 [! q - assert_param(IS_GPIO_PIN(GPIO_Pin));
8 `6 w% e2 l2 J7 B( } - $ Y1 q( i+ Q5 ?7 _
- if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
9 L% L+ w7 O* ? - {, }3 q) ~. a3 u0 y/ A- o
- bitstatus = GPIO_PIN_SET;4 k5 k3 [! k5 f* u8 G; N
- }6 k4 \4 }2 x) ]# H8 k* U
- else( Q& Z' Y7 h' C. p$ X
- {
! i/ w- B* q5 E+ F2 O, G; B# \ - bitstatus = GPIO_PIN_RESET;% a0 F' J8 _* |3 O9 q6 ?
- }
% @0 Q; I, p) a7 z - return bitstatus;
, Y& p, a. V* e; Z7 w+ D; g - }
复制代码
# 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
- void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
% ~+ [& j. C) `7 N - {6 ]1 Y! r7 K1 L
- /* Check the parameters */2 y; t. t: m: y( i" G& A% d& G
- assert_param(IS_GPIO_PIN(GPIO_Pin));) n& Q( S# X3 ~% H1 t
- assert_param(IS_GPIO_PIN_ACTION(PinState)); \5 b$ H2 H$ h* ?9 S' E" z' ^/ E2 P4 v
- O9 z& c) X2 r/ J2 E
- if(PinState != GPIO_PIN_RESET)8 E0 m4 g6 w$ |! `6 n& D
- {
, g# h, m" t9 C" g- h - GPIOx->BSRRL = GPIO_Pin;$ ~( e3 x: P4 {/ {( w
- }: B+ W! m; S. g7 F
- else; X6 V' T8 O* U* X0 Q% u" ?) w8 d
- {
1 @8 D+ a# I+ F$ e8 N - GPIOx->BSRRH = GPIO_Pin ;. H q4 B% _8 Q! \" d
- }
2 Y! [9 S1 J& T0 s - }
复制代码 & 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. }- void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
) x3 v0 D1 v. e8 x, j; { - {+ ~, s% p+ K" W; f# `2 V9 n: N7 S
- /* Check the parameters */
?( a. L! U+ x9 L# s5 ]4 R7 ]% v. W - assert_param(IS_GPIO_PIN(GPIO_Pin));
) ^4 w$ ]! @1 @- m0 R; @ - 0 @3 t Y4 l6 e% m7 V2 M/ T0 Q7 t
- GPIOx->ODR ^= GPIO_Pin;
. s Z0 Q0 c0 t F, s d1 E: S - }
复制代码 / 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- HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
: Q' M. w% l+ I* u x - {
" r# Z1 Z# k; R% @7 |# t/ x - __IO uint32_t tmp = GPIO_LCKR_LCKK;4 W) x a) H, B4 h6 _
+ r9 C' Q) v+ L- v9 s" @- assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));: }, K5 `+ R+ E8 t+ Q# q
- assert_param(IS_GPIO_PIN(GPIO_Pin)); A+ m0 u- r1 H& R- L
- ' j: \& d4 m6 n: s8 t: K
- /* 应用IO锁的写入顺序 */
) w/ m8 q# R/ K8 q$ T3 b, b1 U - tmp |= GPIO_Pin;# f3 D) m3 }% r+ [5 Z/ [
- /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */1 [2 Z E4 f1 f# ~4 c; Q
- GPIOx->LCKR = tmp;& m% C9 L' S' n1 b# r( C
- /* 复位 LCKx bit(s): LCKK='0' + LCK[15-0] */
! |% H9 _* Y/ ?2 ^$ X: Q5 s - GPIOx->LCKR = GPIO_Pin;$ i8 t5 K$ a/ K! n# X
- /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
% i) H0 W" w1 }5 P: o2 c - GPIOx->LCKR = tmp;( g [" `* f1 B0 N
- /* 复位 LCKK bit*/
* Z0 @; b" l' w" M- [+ N) o5 M - tmp = GPIOx->LCKR;
1 g6 M+ g0 `0 I5 O - * o0 [* U0 D3 L+ H5 h3 h! l, S
- if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
. z1 b1 Y/ `& W# k6 e: j! r9 E - {
- N: z8 m3 q" s O" f; @* Z - return HAL_OK;5 b b P: h0 P7 [5 D( t% m' X
- }
: M5 x# V5 c) e) S - else
3 f: A, W* g- Z/ l, F7 } - {
, n3 }2 l3 d3 T. D - return HAL_ERROR;2 w8 W/ ^9 w/ ]0 s
- }
) ^& E' ^$ y; c+ ]0 W/ b - }
复制代码
& 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
|