stm32 hal库 GPIO初始化函数MX_GPIO_Init()梳理分析、初步细致学习(二)

这篇具有很好参考价值的文章主要介绍了stm32 hal库 GPIO初始化函数MX_GPIO_Init()梳理分析、初步细致学习(二)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、GPIO外设时钟初始化

二、配置GPIO

2.1 配置 GPIO_InitTypeDef结构体成员变量

 2.2 把参数写到对应寄存器

2.2.1 io口的配置

2.2.2 外部中断的配置

三、相关知识分析

3.1 hal_gpio其他函数简单分析

3.1.1 HAL_GPIO_DeInit();

3.1.2  HAL_GPIO_ReadPin();

3.1.3  HAL_GPIO_WritePin();

3.1.4  HAL_GPIO_TogglePin();

3.1.5  HAL_GPIO_LockPin();

3.1.6 HAL_GPIO_EXTI_IRQHandler()

 3.1.7 HAL_GPIO_EXTI_Callback();

 3.2 剩余寄存器了解

 四、总结

本人使用的单片机stm32f407vg,代码来源stm32CubeMx。

GPIO配置如下:

hal_gpio_init,stm32,单片机,学习

 其中PB16是普通的开漏输出io口;PD16是外部中断输入;

代码如下:

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PD6 */
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

}

 有两个关于NVIC的初始化,下次再写。

一、GPIO外设时钟初始化

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

#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 RCC_AHB1ENR_GPIOCEN_Pos       (2U)                                
#define RCC_AHB1ENR_GPIOCEN_Msk      (0x1UL <<RCC_AHB1ENR_GPIOCEN_Pos)  #define RCC_AHB1ENR_GPIOCEN               RCC_AHB1ENR_GPIOCEN_Msk 

也就是给RCC_AHB1ENR第2位赋1,使能GPIOC时钟;

这里初始化了其他几个GPIO因为RCC和系统烧录会用到。

hal_gpio_init,stm32,单片机,学习

二、配置GPIO

2.1 配置 GPIO_InitTypeDef结构体成员变量

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PD6 */
  GPIO_InitStruct.Pin = GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
这里配置了4个成员,分别是pin即第几位

Mode,GPIO的工作模式,常用有开漏、推挽输出,输入,模拟,复用开漏、推挽,中断上升沿触发、下降沿触发、上下都触发,事件上升沿触发、下降沿触发、上下都触发;

Pull,配置io口上拉、下拉、无上拉和下拉;GPIO的速度(低\中\\很高)。

 2.2 把参数写到对应寄存器

通过函数HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
  uint32_t position;
  uint32_t ioposition = 0x00U;
  uint32_t iocurrent = 0x00U;
  uint32_t temp = 0x00U;

  /* 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));
  assert_param(IS_GPIO_PULL(GPIO_Init->Pull));

  /* Configure the port pins */
  for(position = 0U; position < GPIO_NUMBER; position++)
  {
    /* Get the IO position */
    ioposition = 0x01U << position;
    /* Get the current IO position */
    iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;

    if(iocurrent == ioposition)
    {
      /*--------------------- GPIO Mode Configuration ------------------------*/
      /* In case of Output or Alternate function mode selection */
      if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
         (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
      {
        /* Check the Speed parameter */
        assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
        /* Configure the IO Speed */
        temp = GPIOx->OSPEEDR; 
        temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
        temp |= (GPIO_Init->Speed << (position * 2U));
        GPIOx->OSPEEDR = temp;

        /* Configure the IO Output Type */
        temp = GPIOx->OTYPER;
        temp &= ~(GPIO_OTYPER_OT_0 << position) ;
        temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
        GPIOx->OTYPER = temp;
       }

      /* Activate the Pull-up or Pull down resistor for the current IO */
      temp = GPIOx->PUPDR;
      temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
      temp |= ((GPIO_Init->Pull) << (position * 2U));
      GPIOx->PUPDR = temp;

      /* In case of Alternate function mode selection */
      if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
      {
        /* Check the Alternate function parameter */
        assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
        /* Configure Alternate function mapped with the current IO */
        temp = GPIOx->AFR[position >> 3U];
        temp &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ;
        temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & 0x07U) * 4U));
        GPIOx->AFR[position >> 3U] = temp;
      }

      /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
      temp = GPIOx->MODER;
      temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
      temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));
      GPIOx->MODER = temp;

      /*--------------------- EXTI Mode Configuration ------------------------*/
      /* Configure the External Interrupt or event for the current IO */
      if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
      {
        /* Enable SYSCFG Clock */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        temp = SYSCFG->EXTICR[position >> 2U];
        temp &= ~(0x0FU << (4U * (position & 0x03U)));
        temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
        SYSCFG->EXTICR[position >> 2U] = temp;

        /* Clear EXTI line configuration */
        temp = EXTI->IMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
        {
          temp |= iocurrent;
        }
        EXTI->IMR = temp;

        temp = EXTI->EMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
        {
          temp |= iocurrent;
        }
        EXTI->EMR = temp;

        /* Clear Rising Falling edge configuration */
        temp = EXTI->RTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->RTSR = temp;

        temp = EXTI->FTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->FTSR = temp;
      }
    }
  }
}

 首先是for循环,position 可以看成第几位,是整形变量;ioposition,iocurrent可以看成验证的。循环时position一直+,当1左移po2.2sition位和欲操作的位相同时,进行后面的操作。

2.2.1 io口的配置

首先要判断配置的功能是什么,如果不是开漏、推挽输出、开漏推挽复用就不进行后面操作。

        /* Check the Speed parameter */
        assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
        /* Configure the IO Speed */
        temp = GPIOx->OSPEEDR
        temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
        temp |= (GPIO_Init->Speed << (position * 2U));
        GPIOx->OSPEEDR = temp;

#define GPIO_OSPEEDER_OSPEEDR0           GPIO_OSPEEDR_OSPEED0

#define GPIO_OSPEEDR_OSPEED0_Pos   (0U)                                  
#define GPIO_OSPEEDR_OSPEED0_Msk  (0x3UL <<GPIO_OSPEEDR_OSPEED0_Pos) 
#define GPIO_OSPEEDR_OSPEED0           GPIO_OSPEEDR_OSPEED0_Msk  

#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 

首先要把GPIO_OSPEEDR寄存器里的值读出来;

再与等于后面一堆,就是把position*2和position*2+1两位置0(和0相与等于0);

或等于就是把想写入的值写进去(和0相或等于本身)

最后把这一个io口配置速度写入GPIOx_OSPEEDR;

hal_gpio_init,stm32,单片机,学习

         /* Configure the IO Output Type */
        temp = GPIOx->OTYPER;
        temp &= ~(GPIO_OTYPER_OT_0 << position) ;
        temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
        GPIOx->OTYPER = temp;

#define GPIO_OTYPER_OT_0                 GPIO_OTYPER_OT0

#define GPIO_OTYPER_OT0_Pos              (0U)                                  
#define GPIO_OTYPER_OT0_Msk              (0x1UL << GPIO_OTYPER_OT0_Pos)      

                                                                                        /*!< 0x00000001 */
#define GPIO_OTYPER_OT0                  GPIO_OTYPER_OT0_Msk

#define GPIO_OUTPUT_TYPE      0x00000010U

#define  GPIO_MODE_OUTPUT_PP                    0x00000001U  
#define  GPIO_MODE_OUTPUT_OD                    0x00000011U

#define  GPIO_MODE_AF_PP                        0x00000002U   
#define  GPIO_MODE_AF_OD                        0x00000012U

这儿用了GPIO_OUTPUT_TYPE主要是把普通io和复用区分开;

代码的逻辑和写入速度是一样的,把数据写入到GPIOx_OTYPER寄存器里。

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习

       /* Activate the Pull-up or Pull down resistor for the current IO */
      temp = GPIOx->PUPDR;
      temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
      temp |= ((GPIO_Init->Pull) << (position * 2U));
      GPIOx->PUPDR = temp;

#define GPIO_PUPDR_PUPDR0                GPIO_PUPDR_PUPD0

#define GPIO_PUPDR_PUPD0_Pos             (0U)                                  
#define GPIO_PUPDR_PUPD0_Msk             (0x3UL << GPIO_PUPDR_PUPD0_Pos)                                                                                                        /*!< 0x00000003 */
#define GPIO_PUPDR_PUPD0                 GPIO_PUPDR_PUPD0_Msk

#define  GPIO_NOPULL        0x00000000U   /*!< No Pull-up or Pull-down activation  */
#define  GPIO_PULLUP        0x00000001U   /*!< Pull-up activation                  */
#define  GPIO_PULLDOWN      0x00000002U   /*!< Pull-down activation 

上下拉或者不上下拉,主要是把参数写入到GPIOx_PUPDR里

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习

2.2.2 外部中断的配置

/*--------------------- EXTI Mode Configuration ------------------------*/
      /* Configure the External Interrupt or event for the current IO */
      if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
      {
        /* Enable SYSCFG Clock */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        temp = SYSCFG->EXTICR[position >> 2U];
        temp &= ~(0x0FU << (4U * (position & 0x03U)));
        temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
        SYSCFG->EXTICR[position >> 2U] = temp;

        /* Clear EXTI line configuration */
        temp = EXTI->IMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
        {
          temp |= iocurrent;
        }
        EXTI->IMR = temp;

        temp = EXTI->EMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
        {
          temp |= iocurrent;
        }
        EXTI->EMR = temp;

        /* Clear Rising Falling edge configuration */
        temp = EXTI->RTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->RTSR = temp;

        temp = EXTI->FTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->FTSR = temp;
      }

  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

#define  GPIO_MODE_IT_RISING                    0x10110000U   
#define  GPIO_MODE_IT_FALLING                   0x10210000U   
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000U

#define EXTI_MODE             0x10000000U

先是判断配置的GPIO模式是不是中断的,再进行后面操作;

通过EXTI_MOOD和GPIO_Init.Mode,相与判断是否等于EXTI_MOOD来判断;

 先是使能系统配置控制器时钟;

        /* Enable SYSCFG Clock */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

#define __HAL_RCC_SYSCFG_CLK_ENABLE()   do { \
                             __IO uint32_t tmpreg = 0x00U; \
                             SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN);\
                              /* Delay after an RCC peripheral clock enabling */ \
                             tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SYSCFGEN);\
                             UNUSED(tmpreg); \
                                          } while(0U)

#define RCC_APB2ENR_SYSCFGEN_Pos           (14U)                               
#define RCC_APB2ENR_SYSCFGEN_Msk(0x1UL << RCC_APB2ENR_SYSCFGEN_Pos)                                                                                  /*!< 0x00004000 */
#define RCC_APB2ENR_SYSCFGEN               RCC_APB2ENR_SYSCFGEN_Msk 

这儿是对RCC_APB2ENR第14位置1,即使能系统配置控制器时钟;

hal_gpio_init,stm32,单片机,学习

 这儿解释的不是很好,半天没看懂为什么要使能这个时钟,而又查询f103的资料,发现其RCC_APB2ENR寄存器中没有SYSCFGEN位。

后来又翻阅英文资料如下

hal_gpio_init,stm32,单片机,学习

 就是在休眠模式下使能(关闭)这个时钟。猜测和GPIO外部中断有关,使能没毛病。

         temp = SYSCFG->EXTICR[position >> 2U];
        temp &= ~(0x0FU << (4U * (position & 0x03U)));
        temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
        SYSCFG->EXTICR[position >> 2U] = temp;

#define LED1_GPIO_Port GPIOB

#define GPIO_GET_INDEX(__GPIOx__)    (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\
                                               ((__GPIOx__) == (GPIOB))? 1U :\
                                               ((__GPIOx__) == (GPIOC))? 2U :\
                                               ((__GPIOx__) == (GPIOD))? 3U :\
                                               ((__GPIOx__) == (GPIOE))? 4U :\
                                               ((__GPIOx__) == (GPIOF))? 5U :\
                                               ((__GPIOx__) == (GPIOG))? 6U :\
                                               ((__GPIOx__) == (GPIOH))? 7U : 8U)

这儿第一个position右移两位就是除以4,把16位分为了4组,每组由一个寄存器控制。

寄存器的作用是为16个外部中断线选择中断源,例子是exti6。

hal_gpio_init,stm32,单片机,学习

        /* Clear EXTI line configuration */
        temp = EXTI->IMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
        {
          temp |= iocurrent;
        }
        EXTI->IMR = temp;

        temp = EXTI->EMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
        {
          temp |= iocurrent;
        }
        EXTI->EMR = temp;

 #define  GPIO_MODE_IT_RISING                    0x10110000U   
#define  GPIO_MODE_IT_FALLING                   0x10210000U   
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000U 

#define GPIO_MODE_IT          0x00010000U

iocurrent的值是1左移6位的整形值。

EXTI_IMR寄存器控制是否开放某条线上的中断。事件的配置和中断一样。

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习

        /* Clear Rising Falling edge configuration */
        temp = EXTI->RTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->RTSR = temp;

        temp = EXTI->FTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
        {
          temp |= iocurrent;
        }
        EXTI->FTSR = temp;

#define  GPIO_MODE_IT_RISING                    0x10110000U   
#define  GPIO_MODE_IT_FALLING                   0x10210000U  
#define  GPIO_MODE_IT_RISING_FALLING            0x10310000U  

#define RISING_EDGE           0x00100000U
#define FALLING_EDGE          0x00200000U

逻辑和前面一样,给EXTI_RTSR和EXTI_FTSR寄存器对应位赋值。

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习 值得注意的点是,上升下降沿不能有毛刺信号;

如果在给寄存器赋值时产生了上升下降沿,中断挂起位不会置位,即不会提示产生中断;

可以设置上升沿下降沿都触发,写入结构体的值就是两者相或。

三、相关知识分析

3.1 hal_gpio其他函数简单分析

3.1.1 HAL_GPIO_DeInit();

void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)

作用和HAL_GPIO_Init相反,即取消初始化。

完成逻辑是,把每个成员的默认值作为结构体成员,赋值给对应寄存器。但是相比初始化,不用保护原寄存器的值,所以简单很多。

3.1.2  HAL_GPIO_ReadPin();

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  GPIO_PinState bitstatus;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
  {
    bitstatus = GPIO_PIN_SET;
  }
  else
  {
    bitstatus = GPIO_PIN_RESET;
  }
  return bitstatus;
}

先访问寄存器得到的数和GPIO_Pin相与,得到的数就是给GPIO输入的数据(也可以理解为引脚的电平),相与后的值不一定为1,所以用if语句判断时条件是不等于0;

不等于0时表示引脚为高电平,等于0时表示引脚为低电平。

hal_gpio_init,stm32,单片机,学习

3.1.3  HAL_GPIO_WritePin();

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 << 16U;
  }
}

给GPIO某一位写入值比较简单,直接给GPIOx_BSRR写就行了。

高16位是清零,低16位是置1;

hal_gpio_init,stm32,单片机,学习

3.1.4  HAL_GPIO_TogglePin();

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if ((GPIOx->ODR & GPIO_Pin) == GPIO_Pin)
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << GPIO_NUMBER;
  }
  else
  {
    GPIOx->BSRR = GPIO_Pin;
  }
}

此函数的作用是反转引脚的电平。即若此时引脚为低电平则会输出高电平,反之亦然。

实现是通过读取GPIO_ODR寄存器,即读取输出的数据,再取反输出就可以了,输出的方法是给BSRR赋值。

hal_gpio_init,stm32,单片机,学习

3.1.5  HAL_GPIO_LockPin();

HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  __IO uint32_t tmp = GPIO_LCKR_LCKK;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  /* Apply lock key write sequence */
  tmp |= GPIO_Pin;
  /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */
  GPIOx->LCKR = tmp;
  /* Reset LCKx bit(s): LCKK='0' + LCK[15-0] */
  GPIOx->LCKR = GPIO_Pin;
  /* Set LCKx bit(s): LCKK='1' + LCK[15-0] */
  GPIOx->LCKR = tmp;
  /* Read LCKR register. This read is mandatory to complete key lock sequence */
  tmp = GPIOx->LCKR;

  /* Read again in order to confirm lock is active */
 if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
  {
    return HAL_OK;
  }
  else
  {
    return HAL_ERROR;
  }
}

#define GPIO_LCKR_LCKK_Pos               (16U)                                 
#define GPIO_LCKR_LCKK_Msk               (0x1UL << GPIO_LCKR_LCKK_Pos)        

                                                                                         /*!< 0x00010000 */
#define GPIO_LCKR_LCKK                   GPIO_LCKR_LCKK_Msk 

作用是锁定GPIO某一位,实现方法是给GPIOx_LCKR赋值,直接看手册:

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习

 所以在锁定时有一个键写序列,不能有错误,锁定后只有cpu复位时才能恢复。

3.1.6 HAL_GPIO_EXTI_IRQHandler()

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }

void EXTI9_5_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI9_5_IRQn 0 */

  /* USER CODE END EXTI9_5_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
  /* USER CODE BEGIN EXTI9_5_IRQn 1 */

  /* USER CODE END EXTI9_5_IRQn 1 */
}

#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR = (__EXTI_LINE__)

使用hal库时,stm32f4xx.it.c里面调用了这个函数,函数形参应该是区分的作用,if判断不同的实参对应不同的语句。可以不用管它,有中断回调函数。

中断的作用是,发生中断时,会调用这个函数,先调用EXTI_PR寄存器判断是否产生了中断,调用中断回调函数,清除中断标志位。

hal_gpio_init,stm32,单片机,学习

 3.1.7 HAL_GPIO_EXTI_Callback();

 __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

中断回调函数,使用中断时可以只使用这一个,中断处理函数不动,把回调函数放到主函数下面就可以了。回调的原理就是中断处理函数中调用了回调函数。

回调函数直接写自己想要的效果就可以了。

 3.2 剩余寄存器了解

hal_gpio_init,stm32,单片机,学习

hal_gpio_init,stm32,单片机,学习

 GPIO的复用功能,后面使用其他外设时会使用。

 四、总结

GPIO是单片机中的基础了,hal库的方法和库函数差不多,都是定义结构体再通过函数写入到对应的寄存器中。和之前一样,通过梳理了一遍GPIO初始化函数的逻辑,我对可以使用的GPIO函数更加熟悉,知其然而知其所以然,以后使用hal库或者标准库会更顺手。文章来源地址https://www.toymoban.com/news/detail-765709.html

到了这里,关于stm32 hal库 GPIO初始化函数MX_GPIO_Init()梳理分析、初步细致学习(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 通用输入输出端口GPIO,及其初始化(HAL库)

    我在学习STM32时候呢,是直接先接触的STM32CubeMX软件,更着网上各种教程迷迷糊糊学了一大堆没用的东西,于是先一步步来吧,我总结了很长时间,希望对正在学习相关知识的朋友们有帮助。 可以先去看看STM32CubeMX如何配置:传送门 读完以上我写的文章基本上是蒙的,因为我

    2024年02月09日
    浏览(44)
  • STM32使用HAL库中外设初始化MSP回调机制及中断回调机制详解

    在STM32的HAL库使用中,会发现库函数大都被设计成了一对: HAL_PPP/PPPP_Init HAL_PPP/PPPP_MspInit 而且HAL_PPP/PPPP_MspInit函数的defination前面还会有__weak 上面的PPP/PPPP代表常见外设的名称为3个字符或者4个字符 怎么理解这个设计呢? 2.1 结论 首先说结论: HAL_PPP/PPPP_Init 是与具体芯片

    2024年02月13日
    浏览(58)
  • 【物联网】详解STM32的GPIO八种输入输出模式,GPIO各种输入输出的区别、初始化的步骤详解,看这文章就行了(超详细)_stm32输出模式

    复用开漏输出(AF Open-Drain Output):复用开漏输出模式允许将GPIO引脚用作特定外设功能。在该模式下,引脚只能输出低电平,要输出高电平需要通过外部上拉电阻或其他方式。 浮空输入(Floating Input):浮空输入模式是一种高阻抗输入模式。在该模式下,引脚不连接到外部电路,处

    2024年04月27日
    浏览(66)
  • 详解STM32的GPIO八种输入输出模式,GPIO各种输入输出的区别、初始化的步骤详解,看这文章就行了(超详细)

    在STM32微控制器中,常见的输入输出(GPIO)模式有八种,分别是推挽输出、开漏输出、复用推挽输出、复用开漏输出、浮空输入、上拉输入、下拉输入和模拟输入。下面我将为你解释每种模式的特点和区别,并提供相应的示例代码。 推挽输出(Push-Pull Output):推挽输出模式是最常

    2024年02月15日
    浏览(70)
  • 【物联网】详解STM32的GPIO八种输入输出模式,GPIO各种输入输出的区别、初始化的步骤详解,看这文章就行了(超详细)

    在STM32微控制器中,常见的输入输出(GPIO)模式有八种,分别是推挽输出、开漏输出、复用推挽输出、复用开漏输出、浮空输入、上拉输入、下拉输入和模拟输入。下面我将为你解释每种模式的特点和区别,并提供相应的示例代码。 推挽输出(Push-Pull Output):推挽输出模式是最常

    2024年02月14日
    浏览(66)
  • 【开篇】STM32F103C8T6 含义、命名规则、GPIO原理以及初始化(参考男神江科协,学习交流用)

    目录 目录 一,STM系列命名规则 二.引脚功能 三.电路以及寄存器 1.产品系列:         STM32代表意法半导体的Cortex-Mx系列内核(ARM)32位的MCU 2.产品类型: F-通用型,S-简单型,L-低功耗,H-高性能,AL-汽车应用低功耗型,AF-汽车应用通用型。 3.产品子系列: 103:ARM Cortex-M3内核

    2024年01月17日
    浏览(90)
  • STM32各外设初始化步骤

            1、使能GPIO时钟         2、初始化GPIO的输入/输出模式         3、设置GPIO的输出值或获取GPIO的输入值         1、使能EXTI线所在的GPIO时钟和AFIO复用时钟         2、初始化EXTI线所在的GPIO的输入输出模式         3、将GPIO脚映射到对应的EXTI线上         4、设置

    2024年03月25日
    浏览(60)
  • STM32 cubemx CAN STM32 CAN初始化详解

    接收用到的结构体如下: CAN概念:         全称Controller Area Network,是一种半双工,异步通讯。 物理层:         闭环:允许总线最长40m,最高速1Mbps,规定总线两端各有一个120Ω电阻,闭环        开环:最大传输距离1Km,最高速125Kbps,规定每根线串联一个2.2kΩ的电阻,

    2024年02月13日
    浏览(58)
  • STM32 串口的初始化(内附详细代码)

    首先我们先要根据原理图来确认我们用的串口接到了那个引脚  我这边的串口1为例,接收端是PA10,发送端是PA9首先我们需要配置PA9和PA10. 把接受端配置成浮空输入,完全靠引脚来判断。把发送端配置成复用推挽模式,并打开GPIOA的时钟和复用时钟多的看代码吧,我把注释都写

    2024年02月13日
    浏览(53)
  • STM32—TIM定时器初始化结构体详解

      注:高级控制定时器可以用到所有初始化结构体,通用定时器不能使用 TIM_BDTRInitTypeDef 结构体,基本定时器只能使用时基结构体。    时基结构体TIM_TimeBaseInitTypeDef用于定时器基础参数设置,与TIM_TimeBaseInit函数配合使用完成配置。 (1) TIM_Prescaler:定时器预分频器设置,

    2024年02月02日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包