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

基于STM32CubeIDE的GPIO原理和底层源码经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-4 23:05
一、GPIO介绍( l8 r% J, D7 H% A  I8 H
        1.1 GPIO 简述
  \" }) H9 I/ u1 M! W+ ^2 B
        GPIO(General purpose input/output,通用型输入输出),一个引脚可以用于输入、输出或其他特殊功能,PIN脚依现实需要可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),并多个GPIO一起组合可实现诸如UART、SPI等外设功能。% k- D# T+ x& T
. ?0 M/ i  Q$ e7 \+ d& I. d
        GPIO是通过寄存器操作来实现电平信号输入输出的,对于输入,通过读取输入数据寄存器(IDR,Input Data Register)来确定引脚电位的高低;对于输出,通过写入输出数据寄存器(ODR,Output Data Register)来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。GPIO的输出通常可以输出0/1信号,用来实现如LED灯、继电器、蜂鸣器等控制,而GPIO的输入可识别0/1信号,用来实现开关、按键等等动作或状态判定。
4 C# V" m% J6 \% y& T
& o& A# u  N: V5 P
449d07eb68a648aeac94e93fee1899c8.png 6 u7 z0 u& ]7 M: y
6 y# s1 x& d) m

9 l, E7 @8 G$ L8 ~+ n       1.2 STM32 GPIO寄存器9 n5 x" p* s. Q4 w! Z
         STM32中GPIO的寄存器是MCU分配的一个连续存储区域,按存储地址划分出不同功能寄存器,如下图所示,有Port mode寄存器、Output Type寄存器等等。
% Q/ D' Z6 u  r: _0 Q. T! U9 d7 x5 b. X+ q4 |
9 [' Q2 @  f1 T+ ?6 q
a525eff9cbda4bc586eb33d752192f3d.png
) M& S- V+ X5 Z/ |
% ^2 z4 [$ D" c4 T3 z2 b
        在HLA库中,这些寄存器的数值(宏定义)通常会放在/Drivers/CMSIS/Device/ST/STM32L4xx/Include/stm32XXX.h头文件中,供开发者调用,例如STM32L496VGTx的开发板,该头文件具体名是stm32l496xx.h,自8977行起到10008行,具是按上述表宏定义了GPIO各寄存器的各个数据位可设置数据值(偏移(POS)及相应数值)。
, T: O: t0 ?. @# A) `
# Z1 Z2 @& H/ q* \: y# ~' \
f9707b3a86ea43709bbd4febde18a379.png
9 Q& w1 t2 A7 C6 x2 T
0 C: T9 C: V+ H' i        1.3 GPIO引脚与标记
: h" g9 c1 [! I/ L         在一个STM32 MCU芯片中,所有引脚本质都是GPIO引脚,只是鉴于全GPIO引脚开发者使用起来比较不方便,于是才通过各个GPIO组合形成针对各个外设的IO接口。在CubeMX中,以STM32L496VGTx芯片为例,可以直观看到该芯片有100个引脚:LQFP100,LQFP是封装,100指的是引脚数。; g+ g; J, [+ r
* y; E5 g- i  }$ e
cd47979d01fe420bbb0a16478916dd41.png
* k1 V! ?* N$ O/ b! d4 T

5 I4 W( T; k" Q; z- K* e# p- X, k" I0 ?        STM32的MCU标记一般有两种,直插IC以半圆条口为标记,贴片IC以小圆点为标记,也有缺口及小点都有标记的。通常将标记圆点或缺口正面朝左上角,左上角的下方脚为第一脚,依次围绕芯片中心逆时针计数,在旋转360度以后,左上角右方角为最后一脚。如上图所示,STM32L496VGTx引脚序号如蓝色字体及箭头所示标记。STM32的MCU的引脚标记有三种方式:! O* O  I% F3 M5 E! i% C

2 p/ I: j) ^3 K& N7 C1 M
        一种是基于上面所述的100个序号排序P1、Pn等,这种标记目前一般是在直接操作寄存器地址或使用标准库中使用,目前HLA库、LL库已经不提供直接地址偏移方式的API了,而是给没一组分组提供了独立寄存器来处理该组引脚;
1 R  x2 i. i$ A
& l* T1 o& s4 G2 F; N+ _        第二种是标准库或HLA库宏定义了引脚分组,一般为A、B、C、...,按大写字母次序网线,目前支持到16组类别,每组为PX0~PX15,其标记就是分组名+0~15序号,例如GPIOB+GPIO_PIN_3。
& n6 G5 C0 D. J- E5 A, y# N/ ^- m( Q
- V% {& ^4 h8 C" K" g        注意,目前STM32的MCU设计是最大16组类别,支持到256引脚(16*16),通过STM32命名规范可以得知,STM32最做引脚支持到256个,但一般会少于引脚数/16的组数,因为不少引脚在芯片设计时就特别支持了其引脚标识,如VSS、VDD等。另外也不是每组必须0~15序号全用完,分组主要是方便设计及使用时进行引脚区分而已。
9 o1 _/ G( q: H5 m% l) Q: T8 I! l9 Y5 \0 K: e: n
daa905e0158b4400a1341f3edc6203d3.png
% r) Z; I& r- `1 F2 }- |5 Y! a( V7 `8 P: n" N) ~0 b$ w, n4 A
         目前HLA库主要就是这种方式,例如来看看GPIO读取数据的API(HAL_GPIO_ReadPin)实现,主要就是根据引脚序号(0~15)来读取IDR寄存器内对应数据位的数值。
8 h. f2 b9 w3 |; D* y$ L3 g" E
' Y* y9 U$ o, V3 j- y
6a026a585a054cb6bd5f0a0bf30d0769.png
& e7 S3 T; D! }+ d6 r, A
0 y1 c+ h6 N1 ]         GPIO分组引脚地址在HLA宏定义数值如下。
6 o, P5 _: w2 D6 P8 T; g7 {( K
  1. #define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
    ; C' F: m/ q3 X
  2. #define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */: r! O3 x& f( c; z
  3. #define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */& ~( Y; A) c2 z9 A/ s3 D" I4 {
  4. #define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */, C7 Z9 K% b" q$ a6 |+ s
  5. #define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
    4 R, [1 R; a* U1 c* G2 m) W/ ]5 B
  6. #define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
    $ B6 U! {. T, b! g" x0 H6 q9 p! F9 w
  7. #define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */; G# n2 L& d4 H9 d& H3 U
  8. #define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
    , q8 h8 `0 Y* [, ^4 p( F$ n
  9. #define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */* x4 U9 [6 F% x- q" @
  10. #define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
    7 t7 h" M7 |; `, A
  11. #define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
    7 ?# w/ v5 M; j
  12. #define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
    9 _7 t5 I, f0 y& A
  13. #define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
    8 }  D8 F: T4 |: {( ]: D$ a$ Z# {
  14. #define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
    / z4 g$ t/ L# P6 c& C7 H' y  x
  15. #define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */# g9 }; P; T6 Z0 ]7 A8 K
  16. #define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */$ p- y* a1 @4 N2 l) A# a6 T- a
  17. #define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */
复制代码
2 V. U1 t0 N; L$ K+ b' D. _8 A/ R- D
        而GPIO各个分组的IDR寄存器为32bit宽度的数据,目前只用到低16位字段。$ Y8 `- u; Z" j9 ]; w
  1. typedef struct% ~9 M# c. g  B. ~
  2. {
    5 o6 h& E$ h# Y4 H* m
  3.   __IO uint32_t MODER;       /*!< GPIO port mode register,               Address offset: 0x00      */
      }7 `" a  \, ~  m
  4.   __IO uint32_t OTYPER;      /*!< GPIO port output type register,        Address offset: 0x04      */$ a7 w  ~1 P( U# D, N& r
  5.   __IO uint32_t OSPEEDR;     /*!< GPIO port output speed register,       Address offset: 0x08      */
    : @, W4 Y8 _6 m5 D' M; E
  6.   __IO uint32_t PUPDR;       /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
    # ?# f6 y: F: D: M# |3 o/ g. j
  7.   __IO uint32_t IDR;         /*!< GPIO port input data register,         Address offset: 0x10      */
    6 t  b$ z  t5 N5 N7 q1 {
  8.   __IO uint32_t ODR;         /*!< GPIO port output data register,        Address offset: 0x14      */
    : Z7 z' ~! Y8 \" L
  9.   __IO uint32_t BSRR;        /*!< GPIO port bit set/reset  register,     Address offset: 0x18      */- q7 B) H5 ^6 @: \
  10.   __IO uint32_t LCKR;        /*!< GPIO port configuration lock register, Address offset: 0x1C      */! |( V; G9 N" E+ t( \
  11.   __IO uint32_t AFR[2];      /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    6 U) i' q. w$ e6 X
  12.   __IO uint32_t BRR;         /*!< GPIO Bit Reset register,               Address offset: 0x28      */% M; |  U& b+ l) L& s/ N

  13. / C" Z) x! w. I. Z9 l' B0 D- C
  14. } GPIO_TypeDef;
复制代码
1 P4 g7 Y  Y; Z/ M, B9 m
        第三种引脚标记其实就是在第二种方式基础上,为引脚设置别名,在cubeMX设置引脚mode后,会开启该引脚,在引脚参数设置列表可以设置该引脚标识名,例如下图,设置PE3(P2)为输出模式(GPIO_Output),的别名为LED1。- K2 t' }: l" B" p- h) x

: G/ F/ Z+ m( J9 [& \! e- O# U2 v
1908ec9006fa426faac892a293dd6106.png 1 g* |8 Q; ?0 D0 `( L& S8 ?

8 d- U3 K7 L6 X1 {, q$ f         那么在生成输出代码时,会通过#define实现标识名替换。- ?0 ]: ]# J2 v: K2 h- D6 k
  m  P2 y$ K: F0 u
b9d7b04778e74d8b80a5ec7608bbc59d.png 2 p9 h- H3 V, C7 n/ ]

. G+ B9 D, f/ d6 `
6 Y! a8 E4 d* `) o  t& W/ t/ Q- [" }
         1.4 GPIO原理框图及CubeMX配置
. @0 G5 K: x, I# H/ G
        进一步,再看看GPIO结构原理图,它支持4种GPIO输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和4种GPIO输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)。因此才能通过多种输入输出模式组合来实现更复杂的其他外设功能。
! z* z( V+ s: Z* L+ k, ?5 q2 D4 `
; V# {. ~: E& ?0 n  p
ad01c8e1a5f443f897e519e3299fd597.png , W8 D, X( n7 r' ?6 Y" i) ]

" z, B7 @4 P5 M( ^: B7 b& [         在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输入模式以及其他参数设置。
- L4 y3 i9 U2 @7 C! M2 x- T
% G+ M: a  v5 h9 ^2 n
cc3f4397f52e4e3191f4f3611e0c0b07.png : k$ q1 R; P" ]1 n( U

8 `" [* G8 T, @( n0 ?0 G) }* Z, }         同样地,在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输出模式以及其他参数设置。: R# A9 L, Z5 L6 Y3 c  C

* |  s% m! r4 I( R4 G( F% |: R
e9a0da7fcdbc4c0dbea4ecc489aaeeff.png
$ W) g9 y* Q7 F; ^0 y( Y0 y8 `. Y( ]
3 r1 f$ l4 g* y( h0 a        另外各种基于GPIO组合实现的外设,也可在GPIO栏的各个外设页面下,单独设置各个引脚的GPIO输入或输出模式。
1 O9 D/ J! A1 O8 ~* Z. P- h
9 p7 [- c' K+ ~) U, @: O
40f1f5a7010642cdb1fa273713535630.png ) m5 W3 z9 H( N
$ P5 q4 `) a4 Y2 s8 e0 q
二、GPIO的HLA源码分析
$ y3 V$ b& Y9 j: @$ B        2.1 HLA库GPIO初始化

/ v) Q% ]* d' D, N2 ?+ u, o        通过cubeMX生成输出源码,如果配置GPIO引脚功能,则cubeMX生成代码时,设置了为每个外设生成独立.h/.c文件时,会在Core源码目录下的Inc及Src目录,分别生成gpio.h和gpio.c驱动文件,否则将会GPOI相关输出函数放置main.h和main.c中。
/ l- w& U& E$ @" ]# i! x5 r4 W' q' z  M/ c9 n" @( X
        在gpio.c文件中,主要定义了MX_GPIO_Init函数,该主要做三件事情,一是启动各个GPIO分组寄存器的时钟,二是对于输出GPIO引脚实现初始值写入,三是将在cubeMX上配置引脚参数写入到参数缓存区域,并依据缓存参数,调用HLA库的HAL_GPIO_Init来实现真正的初始化设定。如下面代码所示。+ p8 D. B( r2 ^* R" ~6 A! w3 i% r
$ K* \  j& y: q# I+ A
  1. //main.h0 \- M  p4 v: q4 H& R9 [
  2. /* Private defines -----------------------------------------------------------*/
    ( w% B6 Z  z8 e! T
  3. #define LED1_Pin GPIO_PIN_33 a$ {7 j7 C  a1 @7 n
  4. #define LED1_GPIO_Port GPIOE
    , _- x0 O& d% i: M# P& d: `
  5. #define KEY2_Pin GPIO_PIN_10
    3 X3 X: j/ P: V1 z0 y* o. s% G
  6. #define KEY2_GPIO_Port GPIOE
    , J7 w) p0 z( l  A1 }
  7. #define KEY0_Pin GPIO_PIN_11
      e7 T3 U3 f# V% x' z5 E
  8. #define KEY0_GPIO_Port GPIOE
    5 l: Y" B3 x8 U% j$ O
  9. #define KEY1_Pin GPIO_PIN_14
    $ F$ ~  V* C( o7 R4 F$ I) K( X! L
  10. #define KEY1_GPIO_Port GPIOE
    * H8 p" Z/ i" t
  11. #define LED2_Pin GPIO_PIN_15
    3 |8 N( R7 R1 H# [* B1 G' L4 R; l* g
  12. #define LED2_GPIO_Port GPIOD+ i; B+ i7 Q  J1 O( h8 e! e3 w* g
  13. #define LED0_Pin GPIO_PIN_6- {% w, e5 [! Y! {4 Q( N
  14. #define LED0_GPIO_Port GPIOB
    " q) k$ ]% T* i. s1 _0 D0 h: t
  15. /* USER CODE BEGIN Private defines */% w8 n) ^5 q. _: Z
  16. //gpio.c
    ; O5 W" w% j& k0 o, n: l! w# O0 a
  17. void MX_GPIO_Init(void)6 h* Y- {0 _$ P) w3 l. _
  18. {
    / a5 K5 }9 f' {$ n9 c8 Z

  19. % \8 _  n" e, w7 r3 e( A6 Q
  20.   GPIO_InitTypeDef GPIO_InitStruct = {0};) j1 |& e' U" v6 t5 e9 ]' x
  21. / X2 H. Y) \% |8 e. e
  22.   /* GPIO Ports Clock Enable */
    # b2 p/ p9 y3 C. f7 g: a' {. C* k
  23.   __HAL_RCC_GPIOE_CLK_ENABLE();
    . f3 ]0 Y7 z  y, e" f
  24.   __HAL_RCC_GPIOC_CLK_ENABLE();
    7 g+ c; j0 V" L7 `2 v2 H
  25.   __HAL_RCC_GPIOH_CLK_ENABLE();& h2 i4 ], Y& s7 U: i# ]' C, I
  26.   __HAL_RCC_GPIOB_CLK_ENABLE();
    2 b& x. J3 N% X8 G
  27.   __HAL_RCC_GPIOD_CLK_ENABLE();
    0 o- C9 S8 D( m. _7 l- s
  28.   __HAL_RCC_GPIOA_CLK_ENABLE();% S  X: c  C& t/ K9 C, D+ {

  29. 6 [7 e6 J$ u5 R. X* |$ Y2 v
  30.   /*Configure GPIO pin Output Level */
    4 ?7 @; F  T4 K# n' V% I' J, T6 C
  31.   HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);. _$ a* |2 Z) [3 y$ K! e+ j

  32. " u1 `* W/ I- @8 v
  33.   /*Configure GPIO pin Output Level */
    + \  W( h/ z/ a0 J, K
  34.   HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);4 X2 F2 w; K' k0 u1 e; p
  35. 4 d7 E# n8 U: ?" a- f3 ~' h1 `: t
  36.   /*Configure GPIO pin Output Level */
    + N$ e. |3 I4 d1 P; M, _0 O. h
  37.   HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);0 w$ V/ }! ]% k# y. M
  38. % O% L/ }* q4 ~- k% |
  39.   /*Configure GPIO pin : PtPin */$ P; A. h0 W+ @' c& b
  40.   GPIO_InitStruct.Pin = LED1_Pin;
    & X0 D( }' T4 {/ U/ w+ m4 H& G
  41.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;, }# C  k' p9 A6 [" b6 [$ A
  42.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    3 H2 Y: f; ~+ m4 s* H4 ?: M
  43.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    / n, n( B5 R9 Y3 y" I8 S  ]2 K( `
  44.   HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);
    ! v. B9 h; ^7 ~: M
  45. . k- C) b4 e# h. V- ]' c
  46.   /*Configure GPIO pins : PEPin PEPin PEPin */6 W( B, `$ }% D
  47.   GPIO_InitStruct.Pin = KEY2_Pin|KEY0_Pin|KEY1_Pin;
    ! |: I' c8 F- S
  48.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    ! G! a4 ?5 X) ~" ~9 X! Q' P
  49.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    & Q; J, I- u0 ^: D3 A: o0 T
  50.   HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);8 W, S$ V7 B7 B5 G7 ^$ l

  51. & l+ H/ q5 {/ s: I+ v
  52.   /*Configure GPIO pin : PtPin */# R" k. ^; o* h- _5 `3 |# v% d
  53.   GPIO_InitStruct.Pin = LED2_Pin;
    & y5 I& l6 H9 Y0 ]( l
  54.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    * z3 U: r/ p4 E: B3 Z& R
  55.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    9 v1 q# t, \3 n% P/ g. m
  56.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    . c+ G6 a5 b: X# ~( w/ V
  57.   HAL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);  k& |7 X; T3 T8 n' i
  58. ; }8 B/ H: P1 \) a
  59.   /*Configure GPIO pin : PtPin */) m$ N- T; T5 u: U9 s
  60.   GPIO_InitStruct.Pin = LED0_Pin;
    5 _' F  E8 j) ~- Y% E, x; K! i: r8 J5 M6 _5 t
  61.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    + |* M& `* a. N
  62.   GPIO_InitStruct.Pull = GPIO_NOPULL;6 P  i/ g2 \* n8 w
  63.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;2 s+ o5 C; z, \1 ^( r) @( j2 B
  64.   HAL_GPIO_Init(LED0_GPIO_Port, &GPIO_InitStruct);
    ; z& P3 G2 r3 q# ^, m

  65. 7 u2 J2 U: v) K8 e1 f& Y! V
  66. }
复制代码
, O3 H1 Q7 Z$ A& z: m
        在stm32l4xx_hal_gpio.c源文件中定义了HAL_GPIO_Init函数,它做以下事情:1)诊断先前缓存配置参数是否合规,2)再将缓存配置参数写入到GPIO的寄存器内,完成GPIO的初始化设定(GPIO 的Mode、Pull、Analog、Alternate、Direction、EXTI等)。0 j; A: d  I5 U

8 E$ I# P# c  K  B3 Z+ j( x, y
a6544e520b0d46f9b58ac56b87ac4c1a.png 7 P( ?3 v! @$ Q8 M& H
! m; D' W; q# s0 J1 ~( I
        2.2 GPIO的开发应用接口API简述
5 s1 T) {9 i& B7 [6 V        除了提供GPIO初始化API外,STM32的HLA针对GPIO还外开发这提供如下API:
/ ]8 \- B( u! f/ Z$ m8 H' a1 |; C3 h1 X8 K) K
d2a594213a664287863277625ef6c4c9.png ' O) o; J; s2 [! d' ^! h
  y9 t$ O4 h$ L, _4 a
         GPIO对数据的读取主要是HAL_GPIO_ReadPin函数,写入主要是HAL_GPIO_WritePin、HAL_GPIO_TogglePin函数,其参数都是GPIO_TypeDef* GPIOx和 uint16_t GPIO_Pin,前者就是前面参数的分组如GPIOA、GPIOB等,后者就是Pin0~Pin15,例如GPIO_PIN_3。; t7 N' `) o8 z

( m% }. W! |5 N4 _. N! P        关于外部中断回调函数HAL_GPIO_EXTI_Callback,该函数是弱函数
/ E3 m& t: u" F8 X# m7 N1 k
. \. x9 I) |4 T. ]9 Y
4003e2f80e2f4b69ad20a5d7d7d78398.png
4 V" N) m. P' U7 x' H6 c; l* m; U4 q7 z3 F+ B* W; E5 x* u. d
        若需要使用该函数,使其生效,首先需要在cubeMX配置界面,将GPIO引脚调整为GPIO_EXTI类型. {+ v: T* n( h/ u
* U1 d. \6 X3 x9 Q5 w
f7622ff6361c46139b2ce314069d95ce.png
1 c% ^3 G1 p4 e* B' ]8 Q6 N( J/ ]( d$ x$ h. _
        并开启该引脚的中断支持0 a; [# z  l1 s+ P2 A" R

( ~2 ^) G6 q4 [
c22186f18cca4e1d9d30f35b2add8333.png 8 M+ H+ v3 R0 W! |, Q

" c% c9 Z0 P2 d# U. v- W         就可以重新实现stm32l4xx_hal_gpio.c文件中的HAL_GPIO_EXTI_Callback函数来实现GPIO外部中断回调功能,例如设置如下,则实现按键KEY0按下异常,触发回调函数HAL_GPIO_EXTI_Callback调用,从而实现LED0灯状态反转(点亮或熄灭):% y$ o. k  W6 _
  1. /* USER CODE BEGIN 0 */
    $ l: t0 x8 x' x+ f7 z
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)3 _. w  x1 w, M' s$ y; M6 L
  3. {2 x8 x  }, b% v7 B9 p# q
  4.         if(GPIO_Pin==KEY0_Pin); Y, k3 @( {/ M# A' X# |/ o! Z5 S* N
  5.         {8 k' d6 W3 a2 z
  6.                 static uint32_t key0_count = 0;, k+ I% |* `& X. D$ N
  7.                 printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
    % a" a! C8 b$ C0 ]8 s$ W/ u$ _
  8.                 Toggle_led0();//LED0等状态反转一次
    : \' j* b) w8 F* @! s
  9.         }
    1 s, N5 B! Y; ?
  10. }
    4 e0 @4 ]4 k2 t0 f  H5 \
  11. /* USER CODE END 0 */
复制代码
8 n9 {+ o+ U6 y! ~8 \1 G
         同样地对于外部中断请求函数HAL_GPIO_EXTI_IRQHandler也可以类似地进行调整实现GPIO外部中断触发器调用,从而实现我们需要的业务应用。  L9 j1 W# m. x- z
! G0 H3 G8 W$ w
        GPIO的HLA库中,HAL_GPIO_LockPin函数,主要是操作lock寄存器数值的,lock寄存器会针对GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR等GPIO寄存器配置对应的状态标记,而其他寄存器操作时,会先去lock寄存器读取其使用状态来确定是否能进行相关操作,从而确保防止冲突及达成一致性。: ^( H2 L- m( B9 q9 M
————————————————+ s7 {8 j- `1 z3 |" ^# I
版权声明:py_free-物联智能
3 b1 a( Z- r* z( w, C如有侵权请联系删除
7 f6 P4 M% ]% q3 ?
收藏 评论0 发布时间:2023-4-4 23:05

举报

0个回答

所属标签

相似分享

官网相关资源

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