当前位置:   article > 正文

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

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配置如下:

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

代码如下:

  1. void MX_GPIO_Init(void)
  2. {
  3. GPIO_InitTypeDef GPIO_InitStruct = {0};
  4. /* GPIO Ports Clock Enable */
  5. __HAL_RCC_GPIOC_CLK_ENABLE();
  6. __HAL_RCC_GPIOH_CLK_ENABLE();
  7. __HAL_RCC_GPIOA_CLK_ENABLE();
  8. __HAL_RCC_GPIOB_CLK_ENABLE();
  9. __HAL_RCC_GPIOD_CLK_ENABLE();
  10. /*Configure GPIO pin Output Level */
  11. HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
  12. /*Configure GPIO pin : PtPin */
  13. GPIO_InitStruct.Pin = LED1_Pin;
  14. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  15. GPIO_InitStruct.Pull = GPIO_NOPULL;
  16. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  17. HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);
  18. /*Configure GPIO pin : PD6 */
  19. GPIO_InitStruct.Pin = GPIO_PIN_6;
  20. GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  21. GPIO_InitStruct.Pull = GPIO_NOPULL;
  22. HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  23. /* EXTI interrupt init*/
  24. HAL_NVIC_SetPriority(EXTI9_5_IRQn, 2, 0);
  25. HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
  26. }

 有两个关于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和系统烧录会用到。

二、配置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);

  1. void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
  2. {
  3. uint32_t position;
  4. uint32_t ioposition = 0x00U;
  5. uint32_t iocurrent = 0x00U;
  6. uint32_t temp = 0x00U;
  7. /* Check the parameters */
  8. assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
  9. assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
  10. assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
  11. assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
  12. /* Configure the port pins */
  13. for(position = 0U; position < GPIO_NUMBER; position++)
  14. {
  15. /* Get the IO position */
  16. ioposition = 0x01U << position;
  17. /* Get the current IO position */
  18. iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
  19. if(iocurrent == ioposition)
  20. {
  21. /*--------------------- GPIO Mode Configuration ------------------------*/
  22. /* In case of Output or Alternate function mode selection */
  23. if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
  24. (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
  25. {
  26. /* Check the Speed parameter */
  27. assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
  28. /* Configure the IO Speed */
  29. temp = GPIOx->OSPEEDR;
  30. temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
  31. temp |= (GPIO_Init->Speed << (position * 2U));
  32. GPIOx->OSPEEDR = temp;
  33. /* Configure the IO Output Type */
  34. temp = GPIOx->OTYPER;
  35. temp &= ~(GPIO_OTYPER_OT_0 << position) ;
  36. temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position);
  37. GPIOx->OTYPER = temp;
  38. }
  39. /* Activate the Pull-up or Pull down resistor for the current IO */
  40. temp = GPIOx->PUPDR;
  41. temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
  42. temp |= ((GPIO_Init->Pull) << (position * 2U));
  43. GPIOx->PUPDR = temp;
  44. /* In case of Alternate function mode selection */
  45. if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
  46. {
  47. /* Check the Alternate function parameter */
  48. assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
  49. /* Configure Alternate function mapped with the current IO */
  50. temp = GPIOx->AFR[position >> 3U];
  51. temp &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ;
  52. temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & 0x07U) * 4U));
  53. GPIOx->AFR[position >> 3U] = temp;
  54. }
  55. /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
  56. temp = GPIOx->MODER;
  57. temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
  58. temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));
  59. GPIOx->MODER = temp;
  60. /*--------------------- EXTI Mode Configuration ------------------------*/
  61. /* Configure the External Interrupt or event for the current IO */
  62. if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
  63. {
  64. /* Enable SYSCFG Clock */
  65. __HAL_RCC_SYSCFG_CLK_ENABLE();
  66. temp = SYSCFG->EXTICR[position >> 2U];
  67. temp &= ~(0x0FU << (4U * (position & 0x03U)));
  68. temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
  69. SYSCFG->EXTICR[position >> 2U] = temp;
  70. /* Clear EXTI line configuration */
  71. temp = EXTI->IMR;
  72. temp &= ~((uint32_t)iocurrent);
  73. if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
  74. {
  75. temp |= iocurrent;
  76. }
  77. EXTI->IMR = temp;
  78. temp = EXTI->EMR;
  79. temp &= ~((uint32_t)iocurrent);
  80. if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
  81. {
  82. temp |= iocurrent;
  83. }
  84. EXTI->EMR = temp;
  85. /* Clear Rising Falling edge configuration */
  86. temp = EXTI->RTSR;
  87. temp &= ~((uint32_t)iocurrent);
  88. if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
  89. {
  90. temp |= iocurrent;
  91. }
  92. EXTI->RTSR = temp;
  93. temp = EXTI->FTSR;
  94. temp &= ~((uint32_t)iocurrent);
  95. if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
  96. {
  97. temp |= iocurrent;
  98. }
  99. EXTI->FTSR = temp;
  100. }
  101. }
  102. }
  103. }

 首先是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;

         /* 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寄存器里。

       /* 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里

2.2.2 外部中断的配置

  1. /*--------------------- EXTI Mode Configuration ------------------------*/
  2. /* Configure the External Interrupt or event for the current IO */
  3. if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
  4. {
  5. /* Enable SYSCFG Clock */
  6. __HAL_RCC_SYSCFG_CLK_ENABLE();
  7. temp = SYSCFG->EXTICR[position >> 2U];
  8. temp &= ~(0x0FU << (4U * (position & 0x03U)));
  9. temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
  10. SYSCFG->EXTICR[position >> 2U] = temp;
  11. /* Clear EXTI line configuration */
  12. temp = EXTI->IMR;
  13. temp &= ~((uint32_t)iocurrent);
  14. if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
  15. {
  16. temp |= iocurrent;
  17. }
  18. EXTI->IMR = temp;
  19. temp = EXTI->EMR;
  20. temp &= ~((uint32_t)iocurrent);
  21. if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
  22. {
  23. temp |= iocurrent;
  24. }
  25. EXTI->EMR = temp;
  26. /* Clear Rising Falling edge configuration */
  27. temp = EXTI->RTSR;
  28. temp &= ~((uint32_t)iocurrent);
  29. if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
  30. {
  31. temp |= iocurrent;
  32. }
  33. EXTI->RTSR = temp;
  34. temp = EXTI->FTSR;
  35. temp &= ~((uint32_t)iocurrent);
  36. if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
  37. {
  38. temp |= iocurrent;
  39. }
  40. EXTI->FTSR = temp;
  41. }

  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,即使能系统配置控制器时钟;

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

后来又翻阅英文资料如下

 就是在休眠模式下使能(关闭)这个时钟。猜测和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。

        /* 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寄存器控制是否开放某条线上的中断。事件的配置和中断一样。

        /* 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寄存器对应位赋值。

 值得注意的点是,上升下降沿不能有毛刺信号;

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

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

三、相关知识分析

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时表示引脚为低电平。

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;

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赋值。

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赋值,直接看手册:

 所以在锁定时有一个键写序列,不能有错误,锁定后只有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寄存器判断是否产生了中断,调用中断回调函数,清除中断标志位。

 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 剩余寄存器了解

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

 四、总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/516511
推荐阅读
相关标签
  

闽ICP备14008679号