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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-4 23:05
一、GPIO介绍
# v* |% I* Y* F) S- [7 E) z        1.1 GPIO 简述
4 e* J% W( p5 x
        GPIO(General purpose input/output,通用型输入输出),一个引脚可以用于输入、输出或其他特殊功能,PIN脚依现实需要可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),并多个GPIO一起组合可实现诸如UART、SPI等外设功能。; b) e8 \* |7 }& D7 }
6 C3 V6 n  d! l. u& u2 ], P
        GPIO是通过寄存器操作来实现电平信号输入输出的,对于输入,通过读取输入数据寄存器(IDR,Input Data Register)来确定引脚电位的高低;对于输出,通过写入输出数据寄存器(ODR,Output Data Register)来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。GPIO的输出通常可以输出0/1信号,用来实现如LED灯、继电器、蜂鸣器等控制,而GPIO的输入可识别0/1信号,用来实现开关、按键等等动作或状态判定。
2 o2 B3 W/ Z3 ~5 O$ W" L3 K4 \3 R& e0 E$ B
449d07eb68a648aeac94e93fee1899c8.png & ^; q: O; ?: g4 E& `% W
. }" x0 k4 ?9 A3 T
' z# n( o0 h( B* P2 ?& M3 L; p
       1.2 STM32 GPIO寄存器0 j- _5 {6 E  g2 [! ~+ q! V
         STM32中GPIO的寄存器是MCU分配的一个连续存储区域,按存储地址划分出不同功能寄存器,如下图所示,有Port mode寄存器、Output Type寄存器等等。& I, \! x& p+ v4 Q6 R0 r2 @8 m4 r

+ ?9 Z: |$ J* ~% g7 C' R
( L7 q2 k& M3 {
a525eff9cbda4bc586eb33d752192f3d.png 5 E# U9 a+ W4 f* s( f3 h- d
! s1 y% G. M: _3 A. ^% [* @1 \
        在HLA库中,这些寄存器的数值(宏定义)通常会放在/Drivers/CMSIS/Device/ST/STM32L4xx/Include/stm32XXX.h头文件中,供开发者调用,例如STM32L496VGTx的开发板,该头文件具体名是stm32l496xx.h,自8977行起到10008行,具是按上述表宏定义了GPIO各寄存器的各个数据位可设置数据值(偏移(POS)及相应数值)。% \' }; F/ i+ D  Y. l5 v  Y
3 d. [5 L5 J0 H6 r1 V; s  L' ]' g
f9707b3a86ea43709bbd4febde18a379.png : M% l9 u! _4 y+ M. `' \( S: a

  F  R  D0 \+ g! |$ n9 N" d        1.3 GPIO引脚与标记
% ~: ]) ?+ z  R9 G5 q" k/ g         在一个STM32 MCU芯片中,所有引脚本质都是GPIO引脚,只是鉴于全GPIO引脚开发者使用起来比较不方便,于是才通过各个GPIO组合形成针对各个外设的IO接口。在CubeMX中,以STM32L496VGTx芯片为例,可以直观看到该芯片有100个引脚:LQFP100,LQFP是封装,100指的是引脚数。* N0 j& U$ V+ y# g6 m

9 p7 F% z3 A- V6 H! g* Q: }
cd47979d01fe420bbb0a16478916dd41.png $ V- j. A9 _; j, C# G4 G

- I2 R" }: s( b+ T# I8 R# J        STM32的MCU标记一般有两种,直插IC以半圆条口为标记,贴片IC以小圆点为标记,也有缺口及小点都有标记的。通常将标记圆点或缺口正面朝左上角,左上角的下方脚为第一脚,依次围绕芯片中心逆时针计数,在旋转360度以后,左上角右方角为最后一脚。如上图所示,STM32L496VGTx引脚序号如蓝色字体及箭头所示标记。STM32的MCU的引脚标记有三种方式:
2 D* A" j7 s  l4 y3 x+ k$ i3 h# e) V3 k7 p/ X& @. q
        一种是基于上面所述的100个序号排序P1、Pn等,这种标记目前一般是在直接操作寄存器地址或使用标准库中使用,目前HLA库、LL库已经不提供直接地址偏移方式的API了,而是给没一组分组提供了独立寄存器来处理该组引脚;
. L. V+ l4 Q3 J% Y* X7 j. U3 k4 h- [  z
        第二种是标准库或HLA库宏定义了引脚分组,一般为A、B、C、...,按大写字母次序网线,目前支持到16组类别,每组为PX0~PX15,其标记就是分组名+0~15序号,例如GPIOB+GPIO_PIN_3。) p/ q1 m* G- I3 \7 n; D0 i+ y

8 x, F/ _. [$ L! u4 y        注意,目前STM32的MCU设计是最大16组类别,支持到256引脚(16*16),通过STM32命名规范可以得知,STM32最做引脚支持到256个,但一般会少于引脚数/16的组数,因为不少引脚在芯片设计时就特别支持了其引脚标识,如VSS、VDD等。另外也不是每组必须0~15序号全用完,分组主要是方便设计及使用时进行引脚区分而已。+ K, c( e* p# y4 s

/ q: `5 L3 B2 ?/ \& t# Y" J' d
daa905e0158b4400a1341f3edc6203d3.png
! e* o3 i4 h, j4 ^8 @/ `+ u4 U6 S; Q* N! l2 ^: e# D
         目前HLA库主要就是这种方式,例如来看看GPIO读取数据的API(HAL_GPIO_ReadPin)实现,主要就是根据引脚序号(0~15)来读取IDR寄存器内对应数据位的数值。
, ^0 i4 m  Y. i+ Y
; C  H! C' V- }! K% Y1 N7 e
6a026a585a054cb6bd5f0a0bf30d0769.png
9 Z$ }! B: J, w3 z/ ~7 w8 b' b0 E8 N" W+ q8 s: P. f$ ?' h% d
         GPIO分组引脚地址在HLA宏定义数值如下。
$ I+ p* B" `1 p4 ?. {4 U
  1. #define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
    , J" T! ?( b5 x9 n, A
  2. #define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */: e6 k( \7 E( S: m& h
  3. #define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    *// T/ X4 k& M6 p. Y% M9 ?
  4. #define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
    - x" P# s) g$ e( \8 x+ {
  5. #define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
    5 J! V: c0 |8 V* t  C) s5 |
  6. #define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
    : j! g( L  W8 g( t% M
  7. #define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
    ) w  c8 X5 g2 [& k5 a
  8. #define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
    ' N; M4 _3 n+ `6 l1 N
  9. #define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
    + M) L& x3 M( f: q( Y3 u
  10. #define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
    - I  a. A, M4 X
  11. #define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */9 h; r0 G3 O4 v( w
  12. #define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */# N' p% D. w4 N" o) f2 }6 e
  13. #define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
    8 K  i3 D0 P/ W1 w  e0 R
  14. #define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
    5 t+ X. d6 S* H7 J# @) a9 V5 d
  15. #define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */; O5 T4 e4 l- f8 d4 E) u8 e
  16. #define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
    2 l6 n' F( M8 T# k1 Z) `; ]
  17. #define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */
复制代码

" }9 b3 P$ f4 p  Q        而GPIO各个分组的IDR寄存器为32bit宽度的数据,目前只用到低16位字段。
- ]% s  y" g: F" _0 [2 \# X; q0 ^
  1. typedef struct" O) s3 A: ]( o0 w
  2. {( i5 v) N" V5 T7 `* e9 L: {" q! k
  3.   __IO uint32_t MODER;       /*!< GPIO port mode register,               Address offset: 0x00      */
    , q2 y5 w$ {! s& L! ]
  4.   __IO uint32_t OTYPER;      /*!< GPIO port output type register,        Address offset: 0x04      */, ~2 [2 H+ {, o$ r! o5 Q
  5.   __IO uint32_t OSPEEDR;     /*!< GPIO port output speed register,       Address offset: 0x08      */
    # r: c8 D( ^4 R. m; j5 e+ g4 B
  6.   __IO uint32_t PUPDR;       /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */7 J/ K! t$ ]! d* J
  7.   __IO uint32_t IDR;         /*!< GPIO port input data register,         Address offset: 0x10      */
    0 C. S# }7 S" `; t3 {
  8.   __IO uint32_t ODR;         /*!< GPIO port output data register,        Address offset: 0x14      */
    ) c' |9 b; r5 a3 ~  @( J
  9.   __IO uint32_t BSRR;        /*!< GPIO port bit set/reset  register,     Address offset: 0x18      */* _$ Q6 I5 k! M/ I9 ]: ~
  10.   __IO uint32_t LCKR;        /*!< GPIO port configuration lock register, Address offset: 0x1C      */5 Q& y9 p: q( `: N2 r1 I
  11.   __IO uint32_t AFR[2];      /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    ; R, e6 P$ w6 O/ n  Z! n+ d
  12.   __IO uint32_t BRR;         /*!< GPIO Bit Reset register,               Address offset: 0x28      */, v5 f! o2 I* m# _, h

  13. / O6 @# I* V' [/ _+ ]; k! d
  14. } GPIO_TypeDef;
复制代码
, g' s3 T! f$ m) [, }2 q3 e
        第三种引脚标记其实就是在第二种方式基础上,为引脚设置别名,在cubeMX设置引脚mode后,会开启该引脚,在引脚参数设置列表可以设置该引脚标识名,例如下图,设置PE3(P2)为输出模式(GPIO_Output),的别名为LED1。* V5 L7 _- ?$ a; p' A! s/ l0 G
( B, k, ~6 Q! p. F* M; X3 f
1908ec9006fa426faac892a293dd6106.png 1 `/ ^3 z9 \" @+ H
) W6 F  J) `1 N8 X% J
         那么在生成输出代码时,会通过#define实现标识名替换。% w# n6 I6 |1 x/ A

4 |- `' A5 Y/ T5 G+ V* g
b9d7b04778e74d8b80a5ec7608bbc59d.png , e: a9 M2 a( I1 L0 n; U2 g

+ X" y$ V1 {: u6 M. |2 L. w3 @/ p
. \' \9 b9 z/ N7 r7 Y/ `
         1.4 GPIO原理框图及CubeMX配置

& a: C5 Z3 y6 ^) Y3 {        进一步,再看看GPIO结构原理图,它支持4种GPIO输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和4种GPIO输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)。因此才能通过多种输入输出模式组合来实现更复杂的其他外设功能。
/ P8 g" S5 y  W" X. L( ]) Q
3 k0 [% k0 C& z! r+ M) Y* w2 o
ad01c8e1a5f443f897e519e3299fd597.png
; ^' j8 @# c( d) \
) L6 G6 ?+ c+ J1 a+ g         在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输入模式以及其他参数设置。
$ K  a" h9 g8 m. q/ G' N
$ `9 I8 c( N0 K# f! P3 J& @
cc3f4397f52e4e3191f4f3611e0c0b07.png 6 Z$ R% G! Z9 M6 _' T& t/ U

1 F) i5 x) j3 X, c0 o5 k         同样地,在cubeMX中在GPIO配置界面中,可以配置GPIO引脚的GPIO输出模式以及其他参数设置。
* E! Z% @6 [$ F5 v* D/ @4 \% S: s( r5 }
e9a0da7fcdbc4c0dbea4ecc489aaeeff.png 8 _% J  m6 S, h7 }6 x
% u0 Q' z. }/ {1 W; p' N
        另外各种基于GPIO组合实现的外设,也可在GPIO栏的各个外设页面下,单独设置各个引脚的GPIO输入或输出模式。
% @/ \* V$ E# m8 {+ |' R7 ]2 u: f1 K" S
40f1f5a7010642cdb1fa273713535630.png / m/ b, h& U5 F7 r- J, w1 K! C

" v* Z5 \/ f) a3 f$ }9 E 二、GPIO的HLA源码分析
* z4 q' r' [, ^9 `7 z        2.1 HLA库GPIO初始化
4 C6 H& X  L' W8 u- s/ S' M: _
        通过cubeMX生成输出源码,如果配置GPIO引脚功能,则cubeMX生成代码时,设置了为每个外设生成独立.h/.c文件时,会在Core源码目录下的Inc及Src目录,分别生成gpio.h和gpio.c驱动文件,否则将会GPOI相关输出函数放置main.h和main.c中。
0 l5 @/ ~0 ?8 `2 P- w+ S' Z- S: O! y
        在gpio.c文件中,主要定义了MX_GPIO_Init函数,该主要做三件事情,一是启动各个GPIO分组寄存器的时钟,二是对于输出GPIO引脚实现初始值写入,三是将在cubeMX上配置引脚参数写入到参数缓存区域,并依据缓存参数,调用HLA库的HAL_GPIO_Init来实现真正的初始化设定。如下面代码所示。) x1 G: B7 K- Y( X: D: q( d* F
" [9 q/ N/ J+ Q& z
  1. //main.h6 J# |" t1 g9 ~: C2 R( O+ {& Y. C
  2. /* Private defines -----------------------------------------------------------*/
    $ J: K! e! p1 m. w
  3. #define LED1_Pin GPIO_PIN_34 y" x! t, \+ \* l7 T- v: D
  4. #define LED1_GPIO_Port GPIOE) @/ f: B" P$ z
  5. #define KEY2_Pin GPIO_PIN_103 B. p+ T3 W8 |
  6. #define KEY2_GPIO_Port GPIOE2 [/ m& y! I; q( s' p
  7. #define KEY0_Pin GPIO_PIN_11
    ) ?9 v0 k* z; J
  8. #define KEY0_GPIO_Port GPIOE! b4 U" l! G6 L' t
  9. #define KEY1_Pin GPIO_PIN_149 C& t* R( A6 k6 D7 q: s, `2 K
  10. #define KEY1_GPIO_Port GPIOE
    7 b5 A) o1 u) C8 W' T6 w: G
  11. #define LED2_Pin GPIO_PIN_158 q( }/ [  R: L5 h' p6 ?
  12. #define LED2_GPIO_Port GPIOD3 Q; g4 T/ _7 ?6 a) q$ |
  13. #define LED0_Pin GPIO_PIN_6, g5 N" S# J' ^
  14. #define LED0_GPIO_Port GPIOB
    * S/ b" ~% O1 m+ q8 |* x
  15. /* USER CODE BEGIN Private defines */
    7 s8 o0 ^. }# S9 ^1 t
  16. //gpio.c! d! ?  E) q# ]
  17. void MX_GPIO_Init(void)5 e% v3 p! G( z1 |$ j. I$ m
  18. {. Y! i4 k6 P$ a) X$ e8 N. ?

  19. . i: \+ I$ {. u# [* G  U: Y
  20.   GPIO_InitTypeDef GPIO_InitStruct = {0};8 s5 x9 G" R8 {% ~! I; ^/ c3 r  L6 X

  21. + @% r- w( |/ D  Y. n
  22.   /* GPIO Ports Clock Enable */
    / z. g/ m; A5 Y5 C8 V. w3 g$ F
  23.   __HAL_RCC_GPIOE_CLK_ENABLE();9 p2 P0 R! u2 m8 j7 I7 _8 h
  24.   __HAL_RCC_GPIOC_CLK_ENABLE();$ h" q, v5 k! j: \- }* ^! X
  25.   __HAL_RCC_GPIOH_CLK_ENABLE();
    9 j8 K) ^" t$ ^# D  e3 B" ]) l
  26.   __HAL_RCC_GPIOB_CLK_ENABLE();6 O5 K2 C2 P8 v
  27.   __HAL_RCC_GPIOD_CLK_ENABLE();3 t8 n( C! n* f5 K
  28.   __HAL_RCC_GPIOA_CLK_ENABLE();
    / h: J/ f7 M3 x! P$ E
  29. 3 y" r9 l& ]. X6 G
  30.   /*Configure GPIO pin Output Level */1 _/ |" j8 S% O" r! U( o3 p" K3 i
  31.   HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    $ g! d1 u0 k/ x5 b3 o! E1 O8 [! D

  32. ! O5 c, ^  U9 _
  33.   /*Configure GPIO pin Output Level */) c2 h8 `  T! ]/ T$ h7 _
  34.   HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);9 P6 z* w5 {: a! O0 a0 J5 t, Q

  35. ! p# x+ F! ?5 d9 q3 B- `- u. G+ w
  36.   /*Configure GPIO pin Output Level */2 E6 R  M) M; p  z, Y
  37.   HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
    . n* m% B7 Z% }+ y) [& R

  38. $ I) d, g- f/ e
  39.   /*Configure GPIO pin : PtPin */
    - X4 \- K& }0 Z% @; p
  40.   GPIO_InitStruct.Pin = LED1_Pin;
    6 e# P9 g9 I8 S# R3 y
  41.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  a$ ^8 l3 Z) k
  42.   GPIO_InitStruct.Pull = GPIO_NOPULL;, ^6 j* s% T3 y
  43.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;: z. J* ~5 L/ K1 M  ~$ t+ K/ g# F2 Z
  44.   HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);& Z, L7 e  {  ~6 V6 ]6 A! c

  45. 8 |. P/ p9 g! M* g' l: ^% @) [4 R
  46.   /*Configure GPIO pins : PEPin PEPin PEPin */
    : G. ~# K$ \, ^) r* _; r# ?: t
  47.   GPIO_InitStruct.Pin = KEY2_Pin|KEY0_Pin|KEY1_Pin;
    * x3 E- e0 k7 K/ @6 r2 y
  48.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    % z& v; Z" F1 p6 ]
  49.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    " }+ I  [& x- m2 _" ]9 l
  50.   HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);( w3 w3 b7 w; e
  51. , i+ c8 U8 {2 K& ^, p* {
  52.   /*Configure GPIO pin : PtPin */
    $ Z: `( G6 ^, a8 u0 |
  53.   GPIO_InitStruct.Pin = LED2_Pin;
    3 @% a' h$ K( O9 T$ T, }
  54.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    9 p( l9 o: r9 D! `! E
  55.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    $ I/ y$ ^2 {1 ?8 J* t2 M% `* n/ r
  56.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;1 S3 [0 I; ?1 v) T; a  t
  57.   HAL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);# |( O! n* d1 g2 K6 G$ b- E7 j

  58. 7 T0 O: z3 T7 z: Q( {# {
  59.   /*Configure GPIO pin : PtPin */
    6 n; \# S: b" ?( @& i1 \  y/ _
  60.   GPIO_InitStruct.Pin = LED0_Pin;
    6 f) k  N# S' s( w, A6 C$ x
  61.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;: G4 K: b% c4 y1 h% {" h
  62.   GPIO_InitStruct.Pull = GPIO_NOPULL;
    . t3 ~6 P( L4 d2 k& W& M
  63.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    ; }. L- c/ q$ v2 r
  64.   HAL_GPIO_Init(LED0_GPIO_Port, &GPIO_InitStruct);, @; B! Q/ i( h, z9 h, R0 y0 b# y
  65. & t2 e: P, F1 B# }8 F# Q6 n% y
  66. }
复制代码

. n+ T" E3 s0 E- Y" [( _# S3 M+ Z        在stm32l4xx_hal_gpio.c源文件中定义了HAL_GPIO_Init函数,它做以下事情:1)诊断先前缓存配置参数是否合规,2)再将缓存配置参数写入到GPIO的寄存器内,完成GPIO的初始化设定(GPIO 的Mode、Pull、Analog、Alternate、Direction、EXTI等)。6 M# r- h6 _) a( G) u* e

& U& H8 @% |: V$ D* u9 f7 R
a6544e520b0d46f9b58ac56b87ac4c1a.png ' O" r% x/ h$ o& |9 Q
7 U' V/ A4 G3 E6 z- H: X6 Q
        2.2 GPIO的开发应用接口API简述$ N$ ]/ I: j% Z/ c# o6 D9 M
        除了提供GPIO初始化API外,STM32的HLA针对GPIO还外开发这提供如下API:7 r8 N5 A# D0 j# c1 V; s; ]

, U0 n  \8 o! x/ ^( \& ~0 q
d2a594213a664287863277625ef6c4c9.png
9 d: b- k+ }6 R  ?9 B/ f
, y9 n0 I" g' e) X: ~         GPIO对数据的读取主要是HAL_GPIO_ReadPin函数,写入主要是HAL_GPIO_WritePin、HAL_GPIO_TogglePin函数,其参数都是GPIO_TypeDef* GPIOx和 uint16_t GPIO_Pin,前者就是前面参数的分组如GPIOA、GPIOB等,后者就是Pin0~Pin15,例如GPIO_PIN_3。
: l5 x2 e7 ?/ x2 K; B, C3 p) V- O( b
; B% B$ @# u* `% ?# {
        关于外部中断回调函数HAL_GPIO_EXTI_Callback,该函数是弱函数
9 t" n1 U# }$ A" R% e) h6 y/ M. d# R
4003e2f80e2f4b69ad20a5d7d7d78398.png - Y" s- j, }. b) p

( F, m2 _  u5 E0 y, C        若需要使用该函数,使其生效,首先需要在cubeMX配置界面,将GPIO引脚调整为GPIO_EXTI类型
  {( U9 E2 P3 y' Z. C0 _5 y2 d, T- ~" j( J7 X' Q2 F- @
f7622ff6361c46139b2ce314069d95ce.png 5 `9 J; I9 I1 m! Z8 V+ G: [
" \0 j6 k5 r! ~% _
        并开启该引脚的中断支持
% R5 Z) J/ s, S& ^. P* o* e+ _0 H: H( f
c22186f18cca4e1d9d30f35b2add8333.png
- R9 M/ C0 `+ B* r2 `0 \
- k* l2 a; ^3 l/ m& v1 R* y. M% y
         就可以重新实现stm32l4xx_hal_gpio.c文件中的HAL_GPIO_EXTI_Callback函数来实现GPIO外部中断回调功能,例如设置如下,则实现按键KEY0按下异常,触发回调函数HAL_GPIO_EXTI_Callback调用,从而实现LED0灯状态反转(点亮或熄灭):
% e) ~: j4 m3 u9 d5 L8 N
  1. /* USER CODE BEGIN 0 */8 \9 I$ }/ N4 }' g7 p3 R
  2. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    : }7 a  K/ E3 n" u) a% P: z
  3. {) t$ G  l. v: h* v! c5 f- _% o
  4.         if(GPIO_Pin==KEY0_Pin)
    0 G0 `6 |7 x" G7 t$ {! \9 \
  5.         {
    - E8 U, a  d. Y% }' |
  6.                 static uint32_t key0_count = 0;' b( K% ~; D: u6 |: R
  7.                 printf("this is %lu count for HAL_GPIO_EXTI_Callback!\r\n",key0_count++);
    . u' n% W3 O: v1 F
  8.                 Toggle_led0();//LED0等状态反转一次3 Q8 o' f% @) v- ^. w
  9.         }
    6 B# S) m% \. i; V. U% g' K3 K! M
  10. }
    5 W3 o6 c$ Q" D0 \9 O  m2 F
  11. /* USER CODE END 0 */
复制代码

; c, b8 l3 a; b! @         同样地对于外部中断请求函数HAL_GPIO_EXTI_IRQHandler也可以类似地进行调整实现GPIO外部中断触发器调用,从而实现我们需要的业务应用。
, U- f; r0 M4 K7 y. [, E
3 w9 c: I2 @! j; n! M2 I
        GPIO的HLA库中,HAL_GPIO_LockPin函数,主要是操作lock寄存器数值的,lock寄存器会针对GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR等GPIO寄存器配置对应的状态标记,而其他寄存器操作时,会先去lock寄存器读取其使用状态来确定是否能进行相关操作,从而确保防止冲突及达成一致性。
) I( }! Y# G! U0 |————————————————
0 M# Y; f2 y9 [5 I! f版权声明:py_free-物联智能( e: l  [  ]0 C, W$ _  I4 R6 n! |
如有侵权请联系删除
/ z9 s+ g  l9 ?% w: a2 k* n
赞 收藏 评论0 发布时间:2023-4-4 23:05

举报

0个回答

所属标签

相似技术帖

官网相关资源

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