《STM32 HAL库》GPIO初始化系列函数详尽解析 您所在的位置:网站首页 简述函数gpio_write 《STM32 HAL库》GPIO初始化系列函数详尽解析

《STM32 HAL库》GPIO初始化系列函数详尽解析

2024-06-03 08:50| 来源: 网络整理| 查看: 265

《STM32 HAL库》GPIO初始化系列函数详尽解析 景皓 分类:嵌入式开发 发布时间 2024.02.28阅读数 1090 评论数 0

观前提醒:本文详尽分析了HAL库中GPIO配置的相关函数,包括MX_GPIO_Init()、HAL_GPIO_WritePin()、HAL_GPIO_Init()。文末附f1系列GPIO口相关寄存器说明

MX_GPIO_Init()

先看源码:

void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = LED4_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED4_GPIO_Port, &GPIO_InitStruct); }

下面我们来逐行分析代码

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

这里定义了一个名为 MX_GPIO_Init 的函数,没有输入参数,也没有返回值。同时创建了一个 GPIO_InitTypeDef 类型的结构体变量 GPIO_InitStruct 并初始化为零。

__HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE();

通过调用 __HAL_RCC_GPIOD_CLK_ENABLE()、__HAL_RCC_GPIOC_CLK_ENABLE() 和 __HAL_RCC_GPIOA_CLK_ENABLE() 函数,使 GPIOD、GPIOC 和 GPIOA 端口的时钟被启用。

HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET);

这行代码将 LED4_Pin 引脚的输出电平设置为高电平(GPIO_PIN_SET)。它使用了 HAL 库提供的 HAL_GPIO_WritePin 函数。

GPIO_InitStruct.Pin = LED4_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED4_GPIO_Port, &GPIO_InitStruct);

这几行代码配置了引脚 LED4_Pin 的模式、上下拉和速度。首先,将 LED4_Pin 赋值给 GPIO_InitStruct.Pin,然后将模式设置为输出模式(GPIO_MODE_OUTPUT_PP),无上下拉(GPIO_NOPULL),低速(GPIO_SPEED_FREQ_LOW)。最后,通过调用 HAL_GPIO_Init 函数,将配置应用到 LED4_GPIO_Port 上。

总结一下,以上代码的主要功能是初始化指定的 GPIO 引脚。它启用了相应端口的时钟,设置了一个引脚的输出电平,然后配置了该引脚的模式、上下拉和速度。这样,在调用该函数后,相关的 GPIO 引脚就被正确地初始化,可以进行后续的输入输出操作。有聪明的小伙伴又要问了:为啥上述代码先配置IO口的电平后设置IO口的模式?答:在初始化 IO 口之前,我们需要确保 IO 口的电平是正确的,以避免在切换为输出模式后产生意外的电平变化。如果我们先设置模式再设置电平,会导致在切换为输出模式之前,IO 口的电平发生瞬时变化,从而引起不必要的干扰或错误。

HAL_GPIO_WritePin()

Talk is cheap. Show me the code

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); assert_param(IS_GPIO_PIN_ACTION(PinState)); if (PinState != GPIO_PIN_RESET) { GPIOx->BSRR = GPIO_Pin; } else { GPIOx->BSRR = (uint32_t)GPIO_Pin BSRR = GPIO_Pin; } else { GPIOx->BSRR = (uint32_t)GPIO_Pin BSRR 寄存器的对应引脚位置为 1,从而将引脚的输出电平设置为高电平。

如果 PinState 等于 GPIO_PIN_RESET,即要设置的输出电平为低电平,那么将 GPIOx->BSRR 寄存器的对应引脚位置为 1(设置引脚为低电平),同时将 GPIOx->BSRR 寄存器的高 16 位位置为 1(清除引脚为高电平),从而将引脚的输出电平设置为低电平。

通过这样的逻辑,根据 PinState 的值,可实现对 GPIO 引脚的输出电平设置。

总结:以上代码实现了 HAL_GPIO_WritePin 函数,用于设置指定 GPIO 引脚的输出电平。通过判断 PinState 的值,将对应的 GPIO 寄存器的位设置为高或低电平,实现了输出电平的设置。

HAL_GPIO_Init()

鉴于本函数过长,源码将放置在分析之后,省略了无关紧要的部分

uint32_t position = 0x00u; uint32_t ioposition; uint32_t iocurrent; uint32_t temp; uint32_t config = 0x00u; __IO uint32_t *configregister; uint32_t registeroffset; position:用于追踪当前处理的引脚位置。在循环中递增,以处理下一个引脚。 ioposition:用于获取当前引脚的位位置。通过对 position 进行移位操作得到。 iocurrent:通过将 GPIO_Init->Pin 与 ioposition 进行位与操作,用于检查当前引脚是否需要初始化。如果 iocurrent 等于 ioposition,则说明当前引脚需要初始化。 temp:一个临时变量 config:用于存储引脚的配置值。根据不同的模式和参数,将相应的配置值写入该变量。 configregister:用于存储指向 CRL 或 CRH 寄存器的指针,具体取决于引脚号的范围。在后续的代码中,将使用该指针来修改寄存器的值。 registeroffset:在计算 CNF 和 MODE 位在 CRL 或 CRH 寄存器中的位置时使用的偏移量。 while (((GPIO_Init->Pin) >> position) != 0x00u) { /* Get the IO position */ ioposition = (0x01uL Pin) & ioposition; if (iocurrent == ioposition) { /* Check the Alternate function parameters */ assert_param(IS_GPIO_AF_INSTANCE(GPIOx)); /* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */ switch (GPIO_Init->Mode) { /* If we are configuring the pin in OUTPUT push-pull mode */ case GPIO_MODE_OUTPUT_PP: assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP; break; ....

让我们逐行分析以上代码段:

while (((GPIO_Init->Pin) >> position) != 0x00u)

这是一个循环的起始。它通过判断 (GPIO_Init->Pin) 右移 position 位后的值是否不等于 0,来确定是否继续执行循环。GPIO_Init->Pin 是一个表示引脚状态的位掩码,通过右移 position 位来检查每个引脚的状态。

ioposition = (0x01uL Pin) & ioposition;

这行代码获取当前引脚的状态。它将 GPIO_Init->Pin 强制转换为 uint32_t 类型,然后与 ioposition 进行按位与运算,并将结果存储在 iocurrent 变量中。这样可以判断当前引脚的状态是否为 1。

if (iocurrent == ioposition)

这是一个条件语句,用于检查当前引脚是否需要进行初始化配置。如果当前引脚的状态等于 ioposition 的值,即当前引脚的状态为 1,则进入条件判断。

assert_param(IS_GPIO_AF_INSTANCE(GPIOx));

这是一个断言宏,用于检查 GPIOx 是否是有效的 GPIO 外设实例。如果 GPIOx 不满足条件,即不是有效的 GPIO 外设实例,则会触发断言错误。

switch (GPIO_Init->Mode)

这是一个 switch 语句,根据 GPIO_Init->Mode 的值进行多重分支判断。GPIO_Init->Mode 表示当前引脚的模式。

case GPIO_MODE_OUTPUT_PP: assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP; break;

这是一个分支语句,用于处理引脚配置为输出推挽模式(GPIO_MODE_OUTPUT_PP)的情况。在这个分支中,会检查GPIO的速度参数,然后根据速度参数和相应的控制寄存器配置值计算出 config 的值。

总结一下:它们共同组成了一段循环结构,用于遍历一组引脚的状态,并根据需要进行初始化配置。在每个循环迭代中,会获取当前引脚的位置和状态,并根据状态进行条件判断和配置操作。

/* Check if the current bit belongs to first half or last half of the pin count number in order to address CRH or CRL register*/ configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH; registeroffset = (iocurrent < GPIO_PIN_8) ? (position Mode & RISING_EDGE) == RISING_EDGE) { SET_BIT(EXTI->RTSR, iocurrent); } else { CLEAR_BIT(EXTI->RTSR, iocurrent); } /* Enable or disable the falling trigger */ if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE) { SET_BIT(EXTI->FTSR, iocurrent); } else { CLEAR_BIT(EXTI->FTSR, iocurrent); } /* Configure the event mask */ if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT) { SET_BIT(EXTI->EMR, iocurrent); } else { CLEAR_BIT(EXTI->EMR, iocurrent); } /* Configure the interrupt mask */ if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT) { SET_BIT(EXTI->IMR, iocurrent); } else { CLEAR_BIT(EXTI->IMR, iocurrent); } } }

这段代码是用于配置GPIO外部中断的相关设置。让我们逐行分析这段代码的功能:

首先,通过判断GPIO_Init->Mode与EXTI_MODE的按位与运算结果是否等于EXTI_MODE,来确定是否需要配置外部中断。EXTI_MODE是一个宏定义,表示外部中断模式。

如果需要配置外部中断,首先使能AFIO(Alternate Function I/O)时钟。AFIO是一个外设,用于管理GPIO的复用功能。

接下来,从AFIO的EXTICR寄存器中读取当前位置对应的值。

根据GPIOx(GPIO端口号)和position(GPIO引脚号)的值,计算出需要设置的值,并将其设置到temp中。

将temp的值写入到AFIO的EXTICR寄存器中,完成对外部中断的配置。

根据GPIO_Init->Mode与RISING_EDGE的按位与运算结果,判断是否需要使能上升沿触发中断。如果需要,设置EXTI的RTSR寄存器中对应的位。

根据GPIO_Init->Mode与FALLING_EDGE的按位与运算结果,判断是否需要使能下降沿触发中断。如果需要,设置EXTI的FTSR寄存器中对应的位。

根据GPIO_Init->Mode与GPIO_MODE_EVT的按位与运算结果,判断是否需要配置事件屏蔽寄存器。如果需要,设置EXTI的EMR寄存器中对应的位。

根据GPIO_Init->Mode与GPIO_MODE_IT的按位与运算结果,判断是否需要配置中断屏蔽寄存器。如果需要,设置EXTI的IMR寄存器中对应的位。

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) { uint32_t position = 0x00u; uint32_t ioposition; uint32_t iocurrent; uint32_t temp; uint32_t config = 0x00u; __IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */ uint32_t registeroffset; /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */ /* Check the parameters */ assert_param(IS_GPIO_ALL_INSTANCE(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Init->Pin)); assert_param(IS_GPIO_MODE(GPIO_Init->Mode)); /* Configure the port pins */ while (((GPIO_Init->Pin) >> position) != 0x00u) { /* Get the IO position */ ioposition = (0x01uL Pin) & ioposition; if (iocurrent == ioposition) { /* Check the Alternate function parameters */ assert_param(IS_GPIO_AF_INSTANCE(GPIOx)); /* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */ switch (GPIO_Init->Mode) { /* If we are configuring the pin in OUTPUT push-pull mode */ case GPIO_MODE_OUTPUT_PP: /* Check the GPIO speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP; break; /* If we are configuring the pin in OUTPUT open-drain mode */ case GPIO_MODE_OUTPUT_OD: /* Check the GPIO speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD; break; /* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */ case GPIO_MODE_AF_PP: /* Check the GPIO speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP; break; /* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */ case GPIO_MODE_AF_OD: /* Check the GPIO speed parameter */ assert_param(IS_GPIO_SPEED(GPIO_Init->Speed)); config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD; break; /* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */ case GPIO_MODE_INPUT: case GPIO_MODE_IT_RISING: case GPIO_MODE_IT_FALLING: case GPIO_MODE_IT_RISING_FALLING: case GPIO_MODE_EVT_RISING: case GPIO_MODE_EVT_FALLING: case GPIO_MODE_EVT_RISING_FALLING: /* Check the GPIO pull parameter */ assert_param(IS_GPIO_PULL(GPIO_Init->Pull)); if (GPIO_Init->Pull == GPIO_NOPULL) { config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING; } else if (GPIO_Init->Pull == GPIO_PULLUP) { config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD; /* Set the corresponding ODR bit */ GPIOx->BSRR = ioposition; } else /* GPIO_PULLDOWN */ { config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD; /* Reset the corresponding ODR bit */ GPIOx->BRR = ioposition; } break; /* If we are configuring the pin in INPUT analog mode */ case GPIO_MODE_ANALOG: config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG; break; /* Parameters are checked with assert_param */ default: break; } /* Check if the current bit belongs to first half or last half of the pin count number in order to address CRH or CRL register*/ configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH; registeroffset = (iocurrent < GPIO_PIN_8) ? (position 2u]; CLEAR_BIT(temp, (0x0Fu) > 2u] = temp; /* Enable or disable the rising trigger */ if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE) { SET_BIT(EXTI->RTSR, iocurrent); } else { CLEAR_BIT(EXTI->RTSR, iocurrent); } /* Enable or disable the falling trigger */ if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE) { SET_BIT(EXTI->FTSR, iocurrent); } else { CLEAR_BIT(EXTI->FTSR, iocurrent); } /* Configure the event mask */ if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT) { SET_BIT(EXTI->EMR, iocurrent); } else { CLEAR_BIT(EXTI->EMR, iocurrent); } /* Configure the interrupt mask */ if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT) { SET_BIT(EXTI->IMR, iocurrent); } else { CLEAR_BIT(EXTI->IMR, iocurrent); } } } position++; } } GPIO相关寄存器

端口配置寄存器端口输入数据寄存器端口输出数据寄存器端口位设置/清除寄存器端口位清除寄存器端口配置锁定寄存器

STM32GPIO函数分析

原创文章作者:景皓。如若转载,请注明出处:古月居 https://www.guyuehome.com/46136

打赏 0

点赞 1

收藏 0

分享

微信 微博 QQ 图片 上一篇:《STM32 HAL库》中断相关函数详尽解析——外部中断服务函数 下一篇:《STM32 HAL库》RCC 相关系列函数详尽解析—— HAL_RCC_OscConfig()


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有