05. 通用GPIO

一、GPIO概述

  GPIO 是控制或者采集外部器件的信息的外设,即负责输入输出。它按组分配存在,每组最多 16 个 IO 口,组数视芯片而定。比如:STM32F407ZGT6 芯片是 144 脚的芯片,分为 7 组,分别是:GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF 和 GPIOG,其中共有 112 个 IO 口可供我们编程使用。

  STM32F407 的绝大部分 IO 口,都兼容 5V,至于到底哪些是兼容 5V 的,请看 STM32F407ZG 的数据手册(注意是数据手册,不是中文参考手册),见表 5 大容量 STM32F40xxx 引脚定义,凡是有 FT 标志的,都是兼容 5V 电平的 IO 口,可以直接接 5V 的外设(注意:如果引脚设置的是模拟输入模式,则不能接 5V!),凡是不带 FT 标志的,就建议大家不要接 5V 了,可能烧坏 MCU。

二、GPIO基本结构

GPIO的基本结构图

  如上图所示,可以看到右边只有 I/O 引脚,这个 I/O 引脚就是我们可以看到的芯片实物的引脚,其他部分都是 GPIO 的内部结构。

【1】、保护二极管

  保护二极管共有两个,用于保护引脚外部过高或过低的电压输入。当引脚输入电压高于 VDD 时,上面的二极管导通,当引脚输入电压低于 VSS 时,下面的二极管导通,从而使输入芯片内部的电压处于比较稳定的值。

  虽然有二极管的保护,但这样的保护却很有限,大电压大电流的接入很容易烧坏芯片。所以在实际的设计中我们要考虑设计引脚的保护电路。

【2】、上拉、下拉电阻

  它们阻值大概在 30~50K 欧之间,可以通过上、下两个对应的开关控制,这两个开关由寄存器控制

  当引脚外部的器件没有干扰引脚的电压时,即没有外部的上、下拉电压,引脚的电平由引脚内部上、下拉决定,开启内部上拉电阻工作,引脚电平为高,开启内部下拉电阻工作,则引脚电平为低。同样,如果内部上、下拉电阻都不开启,这种情况就是我们所说的浮空模式。

  浮空模式下,引脚的电平是不可确定的。引脚的电平可以由外部的上、下拉电平决定。需要注意的是,STM32 的内部上拉是一种“弱上拉”,这样的上拉电流很弱,如果有要求大电流还是得外部上拉。

【3】、施密特触发器

  对于标准施密特触发器,当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;当输入在正负向阈值电压之间,输出不改变,也就是说输出由高电准位翻转为低电准位,或是由低电准位翻转为高电准位对应的阈值电压是不同的。只有当输入电压发生足够的变化时,输出才会变化,因此将这种元件命名为触发器。这种双阈值动作被称为迟滞现象,表明施密特触发器有记忆性。从本质上来说,施密特触发器是一种双稳态多谐振荡器。

  施密特触发器可作为波形整形电路,能将模拟信号波形整形为数字电路能够处理的方波波形,而且由于施密特触发器具有滞回特性,所以可用于抗干扰,其应用包括在开回路配置中用于抗扰,以及在闭回路正回授/负回授配置中用于实现多谐振荡器。

【4】、P-MOS 管和 N-MOS 管

  这个结构控制 GPIO 的开漏输出和推挽输出两种模式。开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。推挽输出:这两只对称的 MOS 管每次只有一只导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载拉电流。推拉式输出既能提高电路的负载能力,又能提高开关速度。

三、GPIO工作模式

3.1、输入浮空

  上拉/下拉电阻为断开状态,施密特触发器打开,输出被禁止。输入浮空模式下,IO 口的电平完全是由外部电路决定。如果 IO 引脚没有连接其他的设备,那么检测其输入电平是不确定的。该模式可以用于按键检测等场景。

输入浮空模式

空闲时,IO 状态是不确定的,由外部环境决定;

3.2、输入上拉

  上拉电阻导通,施密特触发器打开,输出被禁止。在需要外部上拉电阻的时候,可以使用内部上拉电阻,这样可以节省一个外部电阻,但是内部上拉电阻的阻值较大,所以只是“弱上拉”,不适合做电流型驱动。

输入上拉模式

空闲时,IO 呈现高电平;

3.3、输入下拉

  下拉电阻导通,施密特触发器打开,输出被禁止。在需要外部下拉电阻的时候,可以使用内部下拉电阻,这样可以节省一个外部电阻,但是内部下拉电阻的阻值较大,所以不适合做电流型驱动。

输入下拉模式

空闲时,IO 呈现低电平;

3.4、模拟功能

  上下拉电阻断开,施密特触发器关闭,双 MOS 管也关闭。该模式用于 ADC 采集或者 DAC 输出,或者低功耗下省电。

模拟功能

专门用于模拟信号输入或输出,如 ADC 和 DAC;

3.5、开漏输出

  STM32 的开漏输出模式是数字电路输出的一种,从结果上看它只能输出低电平 VSS 或者高阻态。

  开漏模式下,P-MOS 管是一直截止的,所以 P-MOS 管的栅极一直接 VSS。如果输出数据寄存器设置为 0 时,经过“输出控制”的逻辑非操作后,输出逻辑 1 到 N-MOS 管的栅极,这时 N-MOS 管就会导通,使得 I/O 引脚接到 VSS,即输出低电平。

  如果输出数据寄存器设置为 1 时,经过“输出控制器”的逻辑非操作后,输出逻辑 0 到 NMOS 管的栅极,这时 N-MOS 管就会截止。因为 P-MOS 管是一直截止的,使得 I/O 引脚呈现高阻态,即不输出低电平,也不输出高电平。因此要 I/O 引脚输出高电平就必须接上拉电阻。这时可以接内部上拉电阻,或者接一个外部上拉电阻。由于内部上拉电阻的阻值较大,所以只是“弱上拉”。需要大电流驱动,请接外部的上拉电阻。

  此外,上拉电阻具有线与特性,即如果有很多开漏模式的引脚连在一起的时候,只有当所有引脚都输出高阻态,电平才为 1,只要有其中一个为低电平时,就等于接地,使得整条线路都为低电平 0。

  另外在开漏输出模式下,施密特触发器是打开的,所以 IO 口引脚的电平状态会被采集到输入数据寄存器中,如果对输入数据寄存器进行读访问可以得到 IO 口的状态。也就是说开漏输出模式下,我们可以对 IO 口进行读数据。

开漏输出模式

不能暑促高电平,必须有外部(或内部)上拉才能输出高电平;

3.6、推挽输出

  STM32 的推挽输出模式,从结果上看它会输出低电平 VSS 或者高电平 VDD。推挽输出跟开漏输出不同的是,推挽输出模式 P-MOS 管和 N-MOS 管都用上。

  如果输出数据寄存器设置为 0 时,经过“输出控制”的逻辑非操作后,输出逻辑 1 到 P-MOS 管的栅极,这时 P-MOS 管就会截止,同时也会输出逻辑 1 到 N-MOS 管的栅极,这时 N-MOS 管就会导通,使得 I/O 引脚接到 VSS,即输出低电平。

  如果输出数据寄存器设置为 1 时,经过“输出控制”的逻辑非操作后,输出逻辑 0 到 N-MOS 管的栅极,这时 N-MOS 管就会截止,同时也会输出逻辑 0 到 P-MOS 管的栅极,这时 PMOS 管就会导通,使得 I/O 引脚接到 VDD,即输出高电平。

  推挽输出模式下,P-MOS 管和 N-MOS 管同一时间只能有一个 MOS管是导通的。当引脚高低电平切换时,两个管子轮流导通,一个负责灌电流,一个负责拉电流,使其负载能力和开关速度都有很大的提高。

 另外在推挽输出模式下,施密特触发器也是打开的,我们可以读取 IO 口的电平状态。由于推挽输出模式输出高电平时,是直接连接 VDD ,所以驱动能力较强,可以做电流型驱动,驱动电流最大可达 25mA。该模式也是最常用的输出模式。

推挽输出模式

可输出高低电平,驱动能力强;

3.7、开漏式复用功能

  一个 IO 口可以是通用的 IO 口功能,还可以是其他外设的特殊功能引脚,这就是 IO 口的复用功能。一个 IO 口可以是多个外设的功能引脚,我们需要选择作为其中一个外设的功能引脚。当选择复用功能时,引脚的状态是由对应的外设控制,而不是输出数据寄存器。除了复用功能外,其他的结构分析请参考开漏输出模式。

  另外在开漏式复用功能模式下,施密特触发器也是打开的,我们可以读取 IO 口的电平状态,同时外设可以读取 IO 口的信息。

开漏式复用功能

不能输出高电平,必须有外部(或内部)上拉才能输出高电平,由其它外设控制输出;

3.8、推挽式复用功能

  复用功能介绍请查看开漏式复用功能,结构分析请参考推挽输出模式。

推挽式复用功能

可输出高电平,驱动能力强,由其它外设控制输出;

四、GPIO常用寄存器

  STM32F4 每组(这里是 A~I)通用 GPIO 口有 7 个 32 位寄存器控制,包括 :

  • 4 个 32 位配置寄存器(MODER、OTYPER、OSPEEDR 和 PUPDR)
  • 2 个 32 位数据寄存器(IDR 和 ODR)
  • 1 个 32 位置位/复位寄存器 (BSRR)
  • 1 个 32 位锁定寄存器 (LCKR)
  • 2 个 32 位复用功能选择寄存器(AFRH 和 AFRL)

GPIO8种工作模式对应的寄存器配置

4.1、GPIO端口模式寄存器

  该寄存器是 GPIO 口模式控制寄存器,用于控制 GPIOx(STM32F4 最多有 9 组 IO,用大写字母表示,即 x=A/B/C/D/E/F/G/H/I,下同)的工作模式,寄存器描述如下图所示。

GPIO端口模式寄存器

4.2、GPIO端口输出类型寄存器

  该寄存器用于控制 GPIOx 的输出类型,寄存器描述如下图所示。

GPIO端口输出类型寄存器

  该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器低16 位有效,每一个位控制一个 IO 口,复位后,该寄存器值均为 0,也就是在输出模式下 IO 口默认为推挽输出。

4.3、GPIO端口输出速度寄存器

GPIO端口输出速度寄存器

  该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11 时)下不起作用。该寄存器低16 位有效,每两个位控制一个 IO 口。

4.4、GPIO端口上拉/下拉寄存器

  该寄存器用于控制 GPIOx 的上拉/下拉,寄存器描述如下图所示。

GPIO端口上拉下拉寄存器

  该寄存器每两个位控制一个 IO 口,用于设置上下拉,复位后,该寄存器值一般为 0,即无上拉或下拉。

4.5、GPIO端口输入数据寄存器

  该寄存器用于获取 GPIOx 的输入高低电平,寄存器描述如下图所示。

GPIO端口输入数据寄存器

  该寄存器低 16 位有效,分别对应每一组 GPIO 的 16 个引脚。当 CPU 访问该寄存器,如果对应的某位为 0(IDRy=0),则说明该 IO 口输入的是低电平,如果是 1(IDRy=1),则表示输入的是高电平,y=0~15。

4.6、GPIO端口输出数据寄存器

GPIO端口输出类型寄存器

  该寄存器低 16 位有效,分别对应每一组 GPIO 的 16 个引脚。当 CPU 写访问该寄存器,如果对应的某位写 0(ODRy=0),则表示设置该 IO 口输出的是低电平,如果写 1(ODRy=1),则表示设置该 IO 口输出的是高电平,y=0~15。

  除了 ODR 寄存器,还有一个寄存器也是用于控制 GPIO 输出的,它就是 BSRR 寄存器。

4.7、GPIO端口置位/复位寄存器

  该寄存器也用于控制 GPIOx 的输出高电平或者低电平,寄存器描述如下图所示。

GPIO端口置位复位寄存器

  为什么有了 ODR 寄存器,还要这个 BDRR 寄存器呢?这是因为 BSRR 寄存器是只写权限,而 ODR 寄存器是可读可写权限。

  BSRR 寄存器 32 位有效,对于低 16 位(0-15),我们往相应的位写 1(BSy=1),那么对应的 IO 口会输出高电平,往相应的位写 0(BSy=0),对 IO 口没有任何影响,高 16 位(16-31)作用刚好相反,对相应的位写 1(BRy=1)会输出低电平,写 0(BRy=0)没有任何影响,y=0~15。也就是说,对于 BSRR 寄存器,你写 0 的话,对 IO 口电平是没有任何影响的。我们要设置某个 IO 口电平,只需要相关位设置为 1 即可。

  ODR 寄存器,我们要设置某个 IO 口电平,我们首先需要读出来 ODR 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置某个或者某些 IO 口的目的。

  BSRR 寄存器还有一个好处,就是 BSRR 寄存器改变引脚状态的时候,不会被中断打断,而 ODR 寄存器有被中断打断的风险。

五、GPIO配置步骤

5.1、使能时钟

  有关使能 GPIO 时钟的宏定义代码定义在 stm32f4xx_hal_rcc.h 和 stm32f4xx_hal_rcc_ex.h 头文件中。

#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOB_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOC_CLK_ENABLE()  do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)
#define __HAL_RCC_GPIOH_CLK_ENABLE()  do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOHEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOHEN);\
                                        UNUSED(tmpreg); \
                                         } while(0U)
#define __HAL_RCC_GPIOD_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOE_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOEEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
#define __HAL_RCC_GPIOF_CLK_ENABLE()   do { \
                                       __IO uint32_t tmpreg = 0x00U; \
                                       SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
                                       /* Delay after an RCC peripheral clock enabling */ \
                                       tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOFEN);\
                                       UNUSED(tmpreg); \
                                       } while(0U)
#define __HAL_RCC_GPIOG_CLK_ENABLE()   do { \
                                       __IO uint32_t tmpreg = 0x00U; \
                                       SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
                                       /* Delay after an RCC peripheral clock enabling */ \
                                       tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOGEN);\
                                       UNUSED(tmpreg); \
                                       } while(0U)
#define __HAL_RCC_GPIOI_CLK_ENABLE()   do { \
                                       __IO uint32_t tmpreg = 0x00U; \
                                       SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOIEN);\
                                       /* Delay after an RCC peripheral clock enabling */ \
                                       tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOIEN);\
                                       UNUSED(tmpreg); \
                                       } while(0U)

5.2、设置工作模式

  HAL 库中,提供 HAL_GPIO_Init() 函数用于配置 GPIO 功能模式,初始化 GPIO。我们还可以设置 EXTI 功能。该函数的声明如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);

  该函数的第一个形参 GPIOx 用来指定端口号,可选值如下:

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
#define GPIOH               ((GPIO_TypeDef *) GPIOH_BASE)
#define GPIOI               ((GPIO_TypeDef *) GPIOI_BASE)

  第二个参数是 GPIO_InitTypeDef 类型的结构体变量,用来设置 GPIO 的工作模式,其定义如下:

typedef struct
{
  uint32_t Pin;         // 引脚号
  uint32_t Mode;        // 模式设置
  uint32_t Pull;        // 上下拉设置
  uint32_t Speed;       // 速度设置
  uint32_t Alternate;   // 复用功能设置
}GPIO_InitTypeDef;

  成员 Pin 表示 引脚号,范围:GPIO_PIN_0 到 GPIO_PIN_15,另外还有 GPIO_PIN_All 和GPIO_PIN_MASK 可选。

#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected    */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected    */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected    */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected   */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected   */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected   */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected   */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected   */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected   */
#define GPIO_PIN_All               ((uint16_t)0xFFFF)  /* All pins selected */

#define GPIO_PIN_MASK              0x0000FFFFU         /* PIN mask for assert test */

 成员 Mode 是 GPIO 的 模式选择,有以下选择项:

#define  GPIO_MODE_INPUT                        0x00000000U     // 输入模式
#define  GPIO_MODE_OUTPUT_PP                    0x00000001U     // 推挽模式
#define  GPIO_MODE_OUTPUT_OD                    0x00000011U     // 开漏模式
#define  GPIO_MODE_AF_PP                        0x00000002U     // 推挽式复用
#define  GPIO_MODE_AF_OD                        0x00000012U     // 开漏式复用

#define  GPIO_MODE_ANALOG                       0x00000003U     // 模拟模式
  
#define  GPIO_MODE_IT_RISING                    0x10110000U     // 外部中断,上升沿触发检测
#define  GPIO_MODE_IT_FALLING                   0x10210000U     // 外部中断,下降沿触发检测
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000U     // 外部中断,双边沿触发检测
 
#define  GPIO_MODE_EVT_RISING                   0x10120000U     // 外部事件,上升沿触发检测
#define  GPIO_MODE_EVT_FALLING                  0x10220000U     // 外部事件,下降沿触发检测
#define  GPIO_MODE_EVT_RISING_FALLING           0x10320000U     // 外部事件,双边沿触发检测

  成员 Pull 用于 配置上下拉电阻,有以下选择项:

#define  GPIO_NOPULL        0x00000000U     // 无上下拉
#define  GPIO_PULLUP        0x00000001U     // 上拉
#define  GPIO_PULLDOWN      0x00000002U     // 下拉

  成员 Speed 用于 配置 GPIO 的速度,有以下选择项:

#define  GPIO_SPEED_FREQ_LOW         0x00000000U    // 低速
#define  GPIO_SPEED_FREQ_MEDIUM      0x00000001U    // 中速
#define  GPIO_SPEED_FREQ_HIGH        0x00000002U    // 高速
#define  GPIO_SPEED_FREQ_VERY_HIGH   0x00000003U    // 极速

  成员 Alternate 用于 配置具体的复用功能,不同的 GPIO 口可以复用的功能不同,具体可参考数据手册。

5.3、配置输出状态

  我们可以通过 HAL 库提供的 HAL_GPIO_WritePin() 函数配置引脚输出高电平或者低电平,通过 BSRR 寄存器复位或者置位操作。它的声明如下:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

  第一个参数是 端口号,可以选择范围:GPIOA~GPIOG。

  第二个参数是 引脚号,可以选择范围:GPIO_PIN_0 到 GPIO_PIN_15。

  第三个参数是要设置 输出的状态,是枚举型,可选值如下:

typedef enum
{
  GPIO_PIN_RESET = 0,           // 低电平
  GPIO_PIN_SET                  // 高电平
}GPIO_PinState;

  我们还可以通过 HAL 库的 HAL_GPIO_TogglePin() 函数设置引脚的电平翻转,也是通过 BSRR 寄存器复位或者置位操作。其声明如下:

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

  第一个参数是 端口号,可以选择范围:GPIOA~GPIOG。

  第二个参数是 引脚号,可以选择范围:GPIO_PIN_0 到 GPIO_PIN_15。

5.4、读取输入状态

  我们可以通过 HAL 库的 HAL_GPIO_ReadPin() 函数读取 GPIO 引脚状态,通过 IDR 寄存器读取。其声明如下:

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

  第一个参数是 端口号,可以选择范围:GPIOA~GPIOG。

  第二个参数是 引脚号,可以选择范围:GPIO_PIN_0 到 GPIO_PIN_15。

  该函数的返回值是 引脚状态值,是枚举型有两个选择:GPIO_PIN_SET 表示 高电平GPIO_PIN_RESET 表示 低电平

posted @ 2023-10-29 18:05  星光映梦  阅读(58)  评论(0编辑  收藏  举报