目录
一、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 <<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);
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;
/* 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 外部中断的配置
/*--------------------- 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,即使能系统配置控制器时钟;
这儿解释的不是很好,半天没看懂为什么要使能这个时钟,而又查询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的复用功能,后面使用其他外设时会使用。文章来源:https://uudwc.com/A/6zjaE
四、总结
GPIO是单片机中的基础了,hal库的方法和库函数差不多,都是定义结构体再通过函数写入到对应的寄存器中。和之前一样,通过梳理了一遍GPIO初始化函数的逻辑,我对可以使用的GPIO函数更加熟悉,知其然而知其所以然,以后使用hal库或者标准库会更顺手。文章来源地址https://uudwc.com/A/6zjaE