STM32HAL库 -- 3.介绍HAL库中操作GPIO的函数

目录

1.简介

2.函数总览

3.GPIO初始化函数 -- HAL_GPIO_Init()

4.GPIO反初始化函数 -- HAL_GPIO_Deinit()

5.GPIO读取数据函数 -- HAL_GPIO_ReadPin()

6.GPIO设置电平状态函数 -- HAL_GPIO_WritePin()

7.GPIO翻转电平状态函数 -- HAL_GPIO_TogglePin()

8.GPIO锁定引脚配置函数 -- HAL_GPIO_LockPin()

1.简介

        直击HAL库中操作GPIO的函数,不介绍寄存器。

2.函数总览

        我们打开Keil5软件,查看HAL库中有常用的操作GPIO的函数。操作GPIO的函数基本都在封装在stm32f1xx_hal_gpio.c和stm32f1xx_hal_gpio_ex.c文件中,如下图所示:

        下面将分章节介绍常用于操作GPIO的HAL库函数,不会涉及到函数要操作的寄存器,主要介绍函数的功能、参数和返回值,非常友好。

3.GPIO初始化函数 -- HAL_GPIO_Init()

        函数原型

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

        函数功能:初始化指定的GPIO端口和引脚。

        函数参数:GPIOx:GPIO端口,如GPIOA, GPIOB, GPIOC等。

                          GPIO_Init:指向GPIO_InitTypeDef结构体的指针,该结构体包含了GPIO的配置信息,如引脚号、模式、上拉/下拉、速度等。

        仔细地说一下GPIO_InitTypeDef 结构体,结构体的成员如下图所示:

        1.Pin:指的是具体的GPIO引脚,可以使用的选值,可以使用‘|’或运算符一次性配置多个引脚,如(GPIO_PIN_1 | GPIO_PIN_2)。引脚定义如下图所示:

        2.Mode:指的是GPIO的模式,有4种输入和四种输出模式,定义如下图所示:

        上面的宏定义对应的模式如下表所示:

GPIO_MODE_INPUT输入模式
GPIO_MODE_AF_INPUT输入复用模式
GPIO_MODE_ANALOG模拟输入模式
GPIO_MODE_OUTPUT_PP 推挽输出模式
GPIO_MODE_OUTPUT_OD开漏输出模式
GPIO_MODE_AF_PP推挽复用模式
GPIO_MODE_AF_OD开漏复用模式

        3.Pull:指定GPIO引脚的上拉或下拉电阻配置。可选的选项如下表和表所示:

GPIO_NOPULL既不上拉也不下拉
GPIO_PULLUP上拉模式
GPIO_PULLDOWN下拉模式

        4.Speed:指定GPIO引脚的输出速度,可选值有低速、中速和高速。输出速度的意义如下表介绍:

低速(2 MHz)用于对性能要求不高的场景,例如连接至低频外设或简单的指示灯控制
中速(25 MHz)中速适用于大多数常规通信接口,比如USART串口工作在较高波特率下(如115.2 kbps),或者某些慢速SPI应用
高速(50 MHz)更快速的数据传输需求,高频UART、I²C总线运行于快速模式(400 kHz)及以上的情况

        可选的选项如下图和表所示:

GPIO_SPEED_FREQ_LOW低速模式
GPIO_SPEED_FREQ_MEDIUM中速模式
GPIO_SPEED_FREQ_HIGH高速模式

         初始化GPIO代码示例,比如配置输出模式:

void GPIO_Output_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* 使能GPIOA时钟 */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* 配置GPIOA引脚0 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

        上面的代码配置了GPIOA的GPIO_PIN_11引脚,推挽输出模式,上拉模式,高速模式。

        配置输入模式:

void GPIO_Input_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* 使能GPIOB时钟 */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /* 配置GPIOB引脚1 */
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

        上面的代码配置了GPIOB的GPIO_PIN_1引脚,输入模式,不设置上下拉电阻,为浮空输入。注:在输入模式下可以不用配置Speed。

        该函数没有返回值。

4.GPIO反初始化函数 -- HAL_GPIO_Deinit()

        函数原型

void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin);

        函数功能:用于将 GPIO 引脚恢复到默认状态的函数。它会清除 GPIO 引脚的模式、速度、上拉、下拉等配置,将其设置为复位后的默认值(通常为模拟模式)。还会禁用 GPIO 引脚的复用功能。

        函数参数

        1.GPIOx:用于指定要反初始化的 GPIO 端口。GPIOx 可以是 GPIOA、GPIOB、GPIOC 等。

        2.GPIO_Pin:用于指定要反初始化的 GPIO 引脚,可以是单个引脚(如 GPIO_PIN_0、GPIO_PIN_1 等),也可以使用按位或运算符组合多个引脚(如 GPIO_PIN_0 | GPIO_PIN_1)。

        该函数没有返回值。

        代码示例:

void GPIO_Deinit_Single_Pin(void)
{
    // 反初始化 GPIOA 的引脚 0
    HAL_GPIO_Deinit(GPIOA, GPIO_PIN_0);
}

void GPIO_Deinit_Multiple_Pins(void)
{
    // 反初始化 GPIOA 的引脚 0 和引脚 1
    HAL_GPIO_Deinit(GPIOA, GPIO_PIN_0 | GPIO_PIN_1);
}

void GPIO_Deinit_Whole_Port(void)
{
    // 反初始化整个 GPIOA 端口的所有引脚
    HAL_GPIO_Deinit(GPIOA, GPIO_PIN_ALL);
}

5.GPIO读取数据函数 -- HAL_GPIO_ReadPin()

        函数原型

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

        函数功能:用于读取指定 GPIOx 端口上指定引脚GPIO_Pin_x的当前电平状态。

        函数参数

        1.GPIOx:用于指定要读取数据的 GPIO 端口。GPIOx 可以是 GPIOA、GPIOB、GPIOC 等。

        2.GPIO_Pin:具体要读取的GPIO引脚。比如GPIO_PIN_1。

        函数返回值:GPIO_PinState 是一个枚举型,如下图所示:

         返回值说明,如下表所示:

GPIO_PIN_RESET读取到的引脚电平为低电平
GPIO_PIN_SET读取到的引脚电平为高电平

        代码示例:

void Read_GPIO_Pin(void)
{
    GPIO_PinState pinState;

    // 读取 GPIOA 的引脚 0 的电平状态
    pinState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

    if (pinState == GPIO_PIN_SET)
    {
        // 引脚为高电平,执行相应操作
    }
    else
    {
        // 引脚为低电平,执行相应操作
    }
}

        注意事项

        1.在调用 HAL_GPIO_ReadPin() 函数之前,需要确保相应的 GPIO 引脚已经正确配置为输入模式。如果引脚配置为输出模式,读取的结果可能不是外部信号的真实电平状态。

        2.该函数一次只能读取一个引脚的状态,如果需要读取多个引脚的状态,需要多次调用该函数。

        3.读取的电平状态是当前瞬间的状态,可能会受到外部干扰的影响。如果需要稳定的电平状态判断,可能需要进行消抖处理。

6.GPIO设置电平状态函数 -- HAL_GPIO_WritePin()

        函数原型

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

        函数功能:将指定 GPIOx 端口的 GPIO_Pin 引脚设置为 PinState 所指定的电平状态。

        函数参数

        1.GPIOx:用于指定要设置电平状态的 GPIO 端口。GPIOx 可以是 GPIOA、GPIOB、GPIOC 等。

        2.GPIO_Pin:具体要设置电平状态的GPIO引脚。可以是单个引脚,也可以是多个引脚。

        3.PinState :这是一个枚举值。如下图所示:

        要写入的引脚电平状态,具体如下表所示:

GPIO_PIN_RESET将引脚电平设置为低电平
GPIO_PIN_SET将引脚电平设置为高电平

        该函数没有返回值。

        代码示例:

void Write_High_To_Single_Pin(void)
{
    // 将GPIOA的引脚5设置为高电平
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}

void Write_Low_To_Multiple_Pins(void)
{
    // 将GPIOA的引脚0和引脚1设置为低电平
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0 | GPIO_PIN_1, GPIO_PIN_RESET);
}

void Toggle_Pin_Level(void)
{
    while (1)
    {
        // 将GPIOA的引脚5设置为高电平
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_Delay(500);  // 延时500ms

        // 将GPIOA的引脚5设置为低电平
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
        HAL_Delay(500);  // 延时500ms
    }
}

        注意事项

        1.在调用 HAL_GPIO_WritePin() 函数之前,需要确保相应的GPIO引脚已经正确配置为输出模式。如果引脚配置为输入模式,写入操作将不会产生预期的效果。

        2.当引脚处于复用功能模式下,如果复用功能的优先级高于普通GPIO输出功能,写入操作可能无法正常改变引脚电平,需要先正确配置复用功能或禁用复用功能。

        3.频繁调用该函数进行电平切换可能会影响系统性能,尤其是在对时间要求严格的应用场景中,需要考虑优化代码逻辑。

7.GPIO翻转电平状态函数 -- HAL_GPIO_TogglePin()

        函数原型

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

        函数功能:将指定 GPIOx 端口的 GPIO_Pin 引脚的当前电平状态进行翻转。即如果引脚当前为高电平,调用该函数后会将其变为低电平;如果引脚当前为低电平,调用该函数后会将其变为高电平。

        函数参数

        1.GPIOx:用于指定要设置电平状态的 GPIO 端口。GPIOx 可以是 GPIOA、GPIOB、GPIOC 等。

        2.GPIO_Pin:具体要设置电平状态的GPIO引脚。可以是单个引脚,也可以是多个引脚。

        该函数没有返回值。

        代码示例:

void Toggle_Single_Pin_Periodically(void)
{
    while (1)
    {
        // 翻转 GPIOA 的引脚 5 的电平
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
        HAL_Delay(500);  // 延时 500ms
    }
}

void Toggle_Multiple_Pins(void)
{
    while (1)
    {
        // 同时翻转 GPIOA 的引脚 0 和引脚 1 的电平
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0 | GPIO_PIN_1);
        HAL_Delay(500);  // 延时 500ms
    }
}

        注意事项

        1.在调用 HAL_GPIO_TogglePin() 函数之前,需要确保相应的 GPIO 引脚已经正确配置为输出模式。如果引脚配置为输入模式,调用该函数不会产生任何效果。

        2.当引脚处于复用功能模式下时,如果复用功能的优先级高于普通 GPIO 输出功能,调用该函数可能无法正常翻转引脚电平。此时需要先正确配置复用功能或禁用复用功能。

        3.频繁调用该函数进行电平切换可能会影响系统性能,尤其是在对时间要求严格的应用场景中,需要考虑优化代码逻辑。

8.GPIO锁定引脚配置函数 -- HAL_GPIO_LockPin()

        函数原型

HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

        函数功能:将指定 GPIOx 端口的 GPIO_Pin 引脚的当前配置(包括模式、速度、上拉/下拉等)进行锁定。一旦引脚配置被锁定,在芯片复位之前,这些配置将无法被修改,从而确保引脚配置的稳定性和可靠性,防止因程序中的意外操作或外部干扰导致引脚配置被改变。

        函数参数

        1.GPIOx:用于指定要设置电平状态的 GPIO 端口。GPIOx 可以是 GPIOA、GPIOB、GPIOC 等。

        2.GPIO_Pin:具体要设置电平状态的GPIO引脚。可以是单个引脚,也可以是多个引脚。

        函数返回值:HAL_StatusTypeDef 是一个枚举型,如下图所示:

         返回值说明,如下表所示:

HAL_OK表示引脚配置锁定成功
HAL_ERROR表示引脚配置锁定失败

        代码示例:

void Lock_GPIO_Pin(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能 GPIOB 时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();

    // 配置 GPIOB 引脚 5 为推挽输出模式
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 锁定 GPIOA 引脚 5 的配置
    HAL_StatusTypeDef status = HAL_GPIO_LockPin(GPIOB, GPIO_PIN_5);

    if (status == HAL_OK)
    {
        // 引脚配置锁定成功,可进行后续操作
    }
    else
    {
        // 引脚配置锁定失败,可进行错误处理
    }
}

        注意事项

        1.锁定不可逆:一旦引脚配置被锁定,在芯片复位之前,无法通过软件方式解锁或修改该引脚的配置。因此,在调用 HAL_GPIO_LockPin() 函数之前,需要确保引脚的配置已经正确设置。

        2.兼容性:不同型号的 STM32 芯片对引脚锁定功能的支持可能存在差异,在使用该函数之前,需要查阅相应芯片的数据手册,确认其支持该功能。

        3.错误处理:在调用 HAL_GPIO_LockPin() 函数后,需要检查返回值,根据返回结果进行相应的处理,以确保引脚配置锁定操作的成功执行。

  对剩余的HAL_GPIO_EXTI_Callback()、HAL_GPIO_EXTI_IRQHandler()以及stm32f1xx_hal_gpio_ex.c中的HAL_GPIOEx_ConfigEventout()、HAL_GPIOEx_DisableEventout和HAL_GPIOEx_EnableEventout()以后使用到再补充说明。

### HAL_GPIO_ReadPin 函数报错解决方案 当 `HAL_GPIO_ReadPin` 函数发生错误时,通常是因为以下几个原因: #### 参数验证失败 在函数内部会调用 `assert_param(IS_GPIO_PIN(GPIO_Pin))` 来检查传入的引脚号是否有效。如果传递了一个非法的引脚编号,则会在调试模式下触发断言[^1]。 ```c /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); ``` #### GPIO外设未初始化 确保目标GPIO已经通过 `HAL_GPIO_Init()` 正确配置并启用相应的时钟资源。未经初始化就尝试访问硬件可能导致不可预测的行为或返回不正确的状态值[^4]。 #### 错误的GPIO端口指针 确认传递给 `HAL_GPIO_ReadPin` 的第一个参数是一个有效的GPIO端口结构体指针(如 `GPIOA`, `GPIOB` 等)。这些宏定义指向具体的寄存器基地址,例如 `#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)`[^2]。 #### 寄存器读取异常 理论上,在正常工作条件下,直接从输入数据寄存器(IDR)读取不会引起错误;但如果存在外部干扰或其他因素影响到了总线通信,可能会间接造成问题。可以通过查看具体应用场景来排查是否有此类情况发生[^5]。 针对上述可能的原因,建议采取如下措施解决问题: - **仔细核对代码逻辑**:确保所有涉及的操作都遵循了ST官方文档中的指导原则。 - **增加必要的错误处理机制**:比如可以在调用前加入额外的日志记录功能,以便更好地追踪潜在的问题源头。 - **利用开发板上的LED灯或者其他可视化手段测试各个IO的功能性**:这有助于快速定位到底是软件层面还是硬件连接方面出现了偏差。 最后提醒一点,对于嵌入式系统的编程而言,良好的编码习惯非常重要——保持清晰简洁的同时也要注重细节之处的安全性和稳定性考量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值