stm32 hal库 GPIO初始化函数MX | 您所在的位置:网站首页 › 中断函数和中断回调函数 › stm32 hal库 GPIO初始化函数MX |
目录 一、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时表示引脚为低电平。 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寄存器判断是否产生了中断,调用中断回调函数,清除中断标志位。 __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 实验室设备网 版权所有 |