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
- void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)7 h- a- x+ J; T" U o
- {
0 F; X, h: O8 e% c+ \6 B - /* 部分省略未写 */. i9 }# i& `3 y: w/ T
- ( Q/ j, O' @6 h% N' G5 P* N
- /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */& t5 H- l z% P. y: p( e
- for(position = 0; position < GPIO_NUMBER; position++)( I2 f* V, H9 m$ A& {( n' A
- {
! W' f6 [* n" j/ R8 l - /* 部分省略未写 */( j; J6 Q! I( n+ c) b
- if(iocurrent == ioposition)
! U) e8 R9 s, V# U* p# @$ m* [- h - {
$ T1 e8 @& l. Q. E2 \; x U, h - /*--------------------- GPIO模式配置 ------------------------*/! ?! D* L8 S" A4 G$ v
$ I# o. v8 t. \8 L' U- L- /*--------------------- EXTI模式配置 ------------------------*/( f- r! [$ F! H3 k6 i( T2 J
# [7 d/ i0 B* R: h! S- }3 H. w1 d' i9 a2 }' M
- }. j' [; }( v# L; J4 Q+ V. c
- }
复制代码
" 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
- #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
, l7 d: u" ?" b( z; I/ @4 | - #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
4 |; F* Z a* T) k5 r& @- a# F5 W, t# ^ - #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)* C& ]8 j' F4 s% p$ g( D; ^
- #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
! n4 A% J6 m+ c. d! U7 k - #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
) w0 j- K9 `6 ]( [ - #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)# x8 e8 a7 H9 d% ~5 w1 ?" v
- #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)) B6 I) a- Z0 P, L2 e3 T$ b
- #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)" R% I1 g; ?+ ~" ?1 \' J
- #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)6 ~) K7 ^5 V4 i6 p
- #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) ^ k; o) o7 h9 m/ b
- #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
复制代码
4 @* ^* _. ~7 R; P# H% t 第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:" |4 h; d; o, g
- typedef struct5 i* i0 y% R' u% W; @2 c
- {
8 i1 c# n: O' F: w - uint32_t Pin; O3 R; \( ^# @& {1 X2 s- S) U. _
- uint32_t Mode;
5 s- T a- l" k8 J. i - uint32_t Pull;
! P! r# N: x4 F5 B - uint32_t Speed; 0 v: f# O# x$ C4 T- V5 D
- uint32_t Alternate;
7 t' N S1 G, c - }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- GPIO_MODE_INPUT /* 输入模式 */
2 \5 j6 h% s; p1 ^4 t+ ?! b - GPIO_MODE_OUTPUT_PP /* 推挽输出 */
. ?* I+ \- h& I - GPIO_MODE_OUTPUT_OD /* 开漏输出 */) H3 w9 r8 c4 n! _" d$ |3 }( k
- GPIO_MODE_AF_PP /* 复用推挽 */
! M& S, n% f c: g, S3 t/ J d - GPIO_MODE_AF_OD /* 复用开漏 */0 h; c! |* P O! T7 T
9 N; _" H- E" }, D/ y- GPIO_MODE_ANALOG /* 模拟模式 */
, N6 Q* d, }9 A7 ?# o0 P2 q# C - GPIO_MODE_IT_RISING /* 外部中断,上升沿触发检测 */; g5 q' v7 X. b% S
- GPIO_MODE_IT_FALLING /* 外部中断,下降沿触发检测 */9 F. q9 R* u3 D$ o
- GPIO_MODE_IT_RISING_FALLING /* 外部中断,双沿触发检测 */
/ o. h3 U: O- I! p' h* [% e6 n
! J5 r: @5 r& T: S8 r- GPIO_MODE_EVT_RISING /* 外部事件模式,上升沿触发检测 */
& r, S5 B# [ m - GPIO_MODE_EVT_FALLING /* 外部事件模式,下降沿触发检测 */7 O' j: y- H& h2 R( n
- GPIO_MODE_EVT_RISING_FALLING /* 外部事件模式,双沿触发检测 */
复制代码
& I" P3 F8 M" k! V! M2 E 成员Pull用于配置上拉下拉电阻:4 U+ @' d- F: B* N% Z
- GPIO_NOPULL /* 无上拉和下拉电阻 */" H5 K* n+ n7 d5 E. F1 y O) B
- GPIO_PULLUP /* 带上拉电阻 */
0 }& x+ {% x' Z$ M* ^- k - GPIO_PULLDOWN /* 带下拉电阻 */7 ^5 o4 p3 C; p/ T. Y
- 成员Speed用于配置GPIO速度等级,有下面四种可选:
5 o1 l7 X; D# [- K" o+ z5 H - GPIO_SPEED_FREQ_LOW /* 低速 */
6 x. @! C& m, c% A - GPIO_SPEED_FREQ_MEDIUM /* 中等速度 */
) G9 F! v3 B1 J4 ~; M9 [4 K0 G - GPIO_SPEED_FREQ_HIGH /* 快速 */
2 A; T5 |7 I" r; \9 x - 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
- GPIO_AF7_USART1
: `* \2 j2 W" A3 O - GPIO_AF7_USART2 0 Y3 V7 P3 C2 J- C( Y. c* M+ L
- GPIO_AF7_USART3
4 g4 z8 K6 [2 m+ C - GPIO_AF7_USART6 : C, Q* F( Y8 k7 k. z3 x
- 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
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
. _: N* y+ y$ ]# L - GPIO_InitStruct.Pull = GPIO_NOPULL; 1 |* A) R, `1 W+ n. [' O
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; ) e$ v' W7 ~& ]3 {4 {
- GPIO_InitStruct.Pin = GPIO_PIN_0;) ?, r1 w$ s1 a! E" C
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询! r$ \3 p& j+ `7 _; ~* r! i
- 7 F+ f+ K) L+ V8 c/ ~ p* {
- GPIO_InitStruct.Pin = GPIO_PIN_1;
! I' i" |" F; s9 @6 d2 r) l - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询
/ H- X5 f4 ~) {) J6 o - 5 K* Y7 u) K9 i4 \2 B+ |* V" _* i
- GPIO_InitStruct.Pin = GPIO_PIN_2;
$ A, x! d: _0 ?5 e1 ^( {4 s - 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 {
- GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ;
9 N u) Q# f. Y- W* x - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
! C4 n! z z+ O8 [4 P; g% u; | - GPIO_InitStruct.Pull = GPIO_NOPULL; ; s. I6 F* k: e j
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- S5 g; ~" s) U# |# |. f/ [4 O7 X - . C5 a* M6 e. u: V0 `
- 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
- GPIO_InitTypeDef GPIO_InitStruct;( ?1 @9 A* T. A$ D' m/ G7 V3 q
4 g. |& X, Y) T; p" Q. S- GPIO_InitStruct.Pin = GPIO_PIN_0;/ e: d" Q* x. U1 W U& F
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ % T) C6 \/ b: U$ y* ]5 C
- GPIO_InitStruct.Pull = GPIO_NOPULL; /* 无上拉和下拉电阻 */
' g- X: e+ C6 ^! e& U |- r - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */
. K* J$ i6 k. S- m& h7 s' Q: d
$ r; d( L) I, N& F, O- 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
- void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)) f4 A3 F2 c7 i0 I; \/ b P
- {
2 I7 F2 M. `! b& ^% g - for(position = 0; position < GPIO_NUMBER; position++)8 T4 y3 N$ w; A7 Z
- {; x' Z' a. J: c
- /* 部分省略未写 */
; s) j) l, T$ @1 p6 W J - if(iocurrent == ioposition)& `/ R% R5 p* N% k2 W/ w9 C' d
- {! U" u4 G4 ?) K* |
- /*------------------------- GPIO Mode Configuration --------------------*/( b) R) h5 J% `
- /* 配置为模拟模式 */! p0 R& a( }: O
- GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2));
! R1 w3 y! U: J
5 U R! r" z s% d7 U, i; z1 J, M- /* 配置复用模式为AF0,即作为通用IO */2 p. H* z% \, W9 j
- GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;
+ S+ \0 `3 m9 d$ d% c5 T - ' w9 B' U. @2 W) E# R7 U
- /* 配置到最低速度 */
3 g% _! @) \! }6 P - GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2));
2 l+ ?( c" t& R3 H6 i2 [" Q, u$ N0 B - 0 S% Q' |( D: b5 R
- /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */
: H+ g4 |! Z( V4 P" g4 E - GPIOx->OTYPER &= ~(GPIO_OTYPER_OT_0 << position) ;
% y( o4 k; O2 T - / V ^1 t* N8 r2 S, W- D9 H
- /* 无上拉和下拉电阻 */8 L5 g1 J" x/ g, q% z
- GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2));4 i( z/ f0 y0 M
- , Y. B. b% b! b2 {" O4 P! f
- /*------------------------- EXTI模式配置 --------------------*/& S P7 v) j0 A# j% ]7 w
( g; ] U" S$ O- }( t/ \6 B" m/ Q7 z
- }
( p% p, y8 u. C! A" K" @" w - }
复制代码
: 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& ^% ~- #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
; K" R0 F/ Z/ z) _, r X - #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
! o2 k' l: c1 O! E1 }; q2 x - #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
7 h( M5 o: X C0 Q& |4 p - #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
8 z% J8 ]4 {* O6 V7 |$ x- S. w, u - #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)# b y8 n5 H3 g7 |1 W
- #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
& t: C! [- Z) ^! n- M5 c - #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
& L1 `" X, S& t1 `: I - #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)3 A6 }6 X8 V0 l# }4 e/ Y" L5 @
- #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE)% J$ \, E! `7 I2 S3 K2 }: W. V
- #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE)' P3 u% t7 C* H: [! X
- #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- GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- P w8 k* \; F' K - {3 o6 n: ^) d) I" I
- GPIO_PinState bitstatus;
G) q1 b: ^; g+ o' G5 X
2 C1 }6 L( T# V. M$ k- /* Check the parameters */2 _8 j P. c' {. a2 L
- assert_param(IS_GPIO_PIN(GPIO_Pin));$ ~6 ~8 T+ D0 e* H" d+ L8 o$ v: X
- ) _4 G. t. o7 r( f' f4 `. A+ k% i
- if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)0 [( M% d1 K% P% w% k; V
- {+ B! c# d: n4 T, M
- bitstatus = GPIO_PIN_SET;: R1 Q! g- u1 p S7 r1 P
- }$ q0 f9 Y* \; s
- else9 ~- ^5 l$ t" U- m; t
- {
2 M8 C7 v" R: b; o' q - bitstatus = GPIO_PIN_RESET;
/ P+ h7 X. a% g7 _8 O - }
: x1 ~. F2 s+ y, } - return bitstatus;
/ m5 w* f& }6 Y( L4 I t" o - }
复制代码 ; 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- void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
7 [- X7 v$ |/ M8 U% V - {
2 V! O9 Z$ g/ J, n - /* Check the parameters */8 `' Q2 Q G1 E- D$ ^" G! {
- assert_param(IS_GPIO_PIN(GPIO_Pin));; S T1 e5 G; T, J1 n
- assert_param(IS_GPIO_PIN_ACTION(PinState));9 g; h+ h7 R4 h1 \
- 3 E! m( n& I" r) }
- if(PinState != GPIO_PIN_RESET)
2 I3 c1 Z8 O( t - {' a) y: y) Y' d% H1 n4 S+ t
- GPIOx->BSRRL = GPIO_Pin;
' R& Q1 n, G% b# I# y! R, Z& r - }% P& R3 v( [9 `8 R% D% N1 ]6 V& H* d
- else
& z0 x/ V7 k1 s' O2 V - {. l# \, X2 H3 {6 U( V
- GPIOx->BSRRH = GPIO_Pin ;) d, ^" U3 `, X5 B P! T
- }2 X" g0 y5 ^: l4 i
- }
复制代码 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
- void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
8 U1 [2 D" w; V' A) F4 x) {# h7 N - {* Q& T, M8 U. b! t* r2 P
- /* Check the parameters */
9 o$ k' D/ g. S( l1 b# @2 Z2 E - assert_param(IS_GPIO_PIN(GPIO_Pin));
( O' }5 s# B% H1 t7 Z6 N( L, K - 7 b5 w* B- ?- q/ m
- GPIOx->ODR ^= GPIO_Pin;
9 d" j% l8 O) k- F - }
复制代码
- 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" @- HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
! q- j9 W" P+ a! B - {
. u1 q7 p5 c% `8 p - __IO uint32_t tmp = GPIO_LCKR_LCKK;+ [* z$ i( z3 \
- ; \/ n" c4 j( y2 w" f* s
- assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));
* D* |9 I" [% O! m) G: X - assert_param(IS_GPIO_PIN(GPIO_Pin));1 ]- Y+ d0 r7 n& i& }4 Y
- + Y6 l' U/ ^5 y9 w3 N* ^$ L4 f
- /* 应用IO锁的写入顺序 */. U/ u+ k; i( }7 Q- Q. ^
- tmp |= GPIO_Pin;0 b: g B" _ @, w$ }2 f
- /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */: P1 r3 d$ v2 w7 u) W! a' L0 l
- GPIOx->LCKR = tmp;
" z" ]2 d" y8 |; Q% m; b% v/ | - /* 复位 LCKx bit(s): LCKK='0' + LCK[15-0] */
- K( \% c; [0 Q2 O9 O2 ^& q$ N - GPIOx->LCKR = GPIO_Pin;. D6 l1 I# Z8 H; h' u4 j, O5 V9 a k
- /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
/ p. t6 `* t$ P3 |; E% {5 O - GPIOx->LCKR = tmp;/ V4 a6 I3 p9 B
- /* 复位 LCKK bit*/, F7 Q+ {$ [9 m* a
- tmp = GPIOx->LCKR;
6 D2 d2 b+ c& h+ w& f% p3 Z
p/ Z; e0 T, H3 i( q7 M- if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
% o$ w1 ?$ e5 ]& {: ?( Q- @ - {
7 Q# E+ E* z$ I% ^* j8 y - return HAL_OK;7 h6 E0 D6 R7 |
- }
5 E- ^. r7 A. R9 n4 s0 t9 Y. y - else' Y$ ^. [' R* o5 x0 H3 K& @
- {
% A8 E) i; j# [8 F: p - return HAL_ERROR;
% h) A; U* j' A' q - }
! c' c' | x) [% B! d& R0 r& M - }
复制代码 : 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
|