stm32 hal库 GPIO初始化函数MX 您所在的位置:网站首页 中断函数和中断回调函数 stm32 hal库 GPIO初始化函数MX

stm32 hal库 GPIO初始化函数MX

2023-06-26 13:12| 来源: 网络整理| 查看: 265

目录

一、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是外部中断输入;

代码如下:

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 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 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 Speed OSPEEDR = temp; /* Configure the IO Output Type */ temp = GPIOx->OTYPER; temp &= ~(GPIO_OTYPER_OT_0 Mode & GPIO_OUTPUT_TYPE) >> 4U) OTYPER = temp; } /* Activate the Pull-up or Pull down resistor for the current IO */ temp = GPIOx->PUPDR; temp &= ~(GPIO_PUPDR_PUPDR0 Pull) 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 Alternate) AFR[position >> 3U] = temp; } /* Configure IO Direction mode (Input, Output, Alternate or Analog) */ temp = GPIOx->MODER; temp &= ~(GPIO_MODER_MODER0 Mode & GPIO_MODE) 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 > 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 Speed OSPEEDR = temp;

#define GPIO_OSPEEDER_OSPEEDR0           GPIO_OSPEEDR_OSPEED0

#define GPIO_OSPEEDR_OSPEED0_Pos   (0U)                                   #define GPIO_OSPEEDR_OSPEED0_Msk  (0x3UL > 4U) OTYPER = temp;

#define GPIO_OTYPER_OT_0                 GPIO_OTYPER_OT0

#define GPIO_OTYPER_OT0_Pos              (0U)                                   #define GPIO_OTYPER_OT0_Msk              (0x1UL PUPDR;       temp &= ~(GPIO_PUPDR_PUPDR0 Pull) PUPDR = temp;

#define GPIO_PUPDR_PUPDR0                GPIO_PUPDR_PUPD0

#define GPIO_PUPDR_PUPD0_Pos             (0U)                                   #define GPIO_PUPDR_PUPD0_Msk             (0x3UL Mode & EXTI_MODE) == EXTI_MODE) { /* Enable SYSCFG Clock */ __HAL_RCC_SYSCFG_CLK_ENABLE(); temp = SYSCFG->EXTICR[position >> 2U]; temp &= ~(0x0FU > 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 EXTICR[position >> 2U];         temp &= ~(0x0FU > 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     GPIOx->BSRR = (uint32_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     __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库或者标准库会更顺手。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

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