引言
上篇帖子STM32中断系统学习笔记(1)是理论,这篇帖子开始实战,目标是通过按键实现LED的控制。
1.工程建立
以正点原子HAL库 实验1 跑马灯实验为基础,复制工程,在“Drivers--BSP”目录下建立EXTI文件夹,并创建exti.c和exti.h文件;
2.导入C文件
第一步:
第二步:
第三步:添加exti.c
第四步:跳转到exti.h
添加这部分代码
#ifndef _EXTI_H
#define _EXTI_H#include "./SYSTEM/sys/sys.h"#endif
3.查看原理图
我们想要最终实现的效果是按下KEY0使LED0灯翻转,按下KEY1使LED1翻转。
配置LED0和LED1引脚为推挽输出模式(之前工程已经配置过),KEY0和KEY1引脚为中断输入模式;
4.编写代码
按照正点原子给的步骤一步步来配置,最重要的是学习配置思路,一通百通!!
4.1 外部中断初始化
void exti_init(void)
{GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能PA引脚时钟 */__HAL_RCC_GPIOC_CLK_ENABLE(); /* 使能PC引脚时钟 */gpio_init_struct.Pin = GPIO_PIN_15; /* 按键 KEY1 引脚 */gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 外部中断下降沿触发 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 配置初始化 */gpio_init_struct.Pin = GPIO_PIN_5; /* 按键 KEY0 引脚 */gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 外部中断下降沿触发 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */HAL_GPIO_Init(GPIOC, &gpio_init_struct); /* 配置初始化 */HAL_NVIC_SetPriority(EXTI15_10_IRQn,1,0); /* 配置抢占优先级和响应优先级 */HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); /* 使能中断 */HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0); /* 配置抢占优先级和响应优先级 */HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); /* 使能中断 */
}
代码解释:
①由原理图得知KEY0对应PC5,KEY1对应PA15,那么首先我们要使能PA和PC端口的时钟 ;
GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能PA引脚时钟 */__HAL_RCC_GPIOC_CLK_ENABLE(); /* 使能PC引脚时钟 */
②配置引脚号,引脚模式,有无上下拉电阻,以及配置完成初始化,这里需要注意一点的就是引脚模式配置为外部中断下降沿触发 ;
gpio_init_struct.Pin = GPIO_PIN_15; /* 按键 KEY1 引脚 */gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 外部中断下降沿触发 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */HAL_GPIO_Init(GPIOA, &gpio_init_struct); /* 配置初始化 */gpio_init_struct.Pin = GPIO_PIN_5; /* 按键 KEY0 引脚 */gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 外部中断下降沿触发 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */HAL_GPIO_Init(GPIOC, &gpio_init_struct); /* 配置初始化 */
为什么要配置为上拉电阻:由原理图知,当我们按下KEY0时,GND导通,此时KEY0是低电平,我们接一个内置上拉电阻,那么KEY0在空闲状态时就是高电平。当按键按下时,KEY0的电平从高---->低,就是一个下降沿,这时我们配置引脚模式为下降沿触发,就能让外部中断信号顺利进入NVIC中断管家里。
③ 配置中断分组,中断优先级,使能中断
在stm32f1xx_hal.c里HAL_Init函数里,已经默认配置好了中断分组为2,所以我们只需要配置中断优先级以及使能中断就行;
HAL_NVIC_SetPriority(EXTI15_10_IRQn,1,0); /* 配置抢占优先级和响应优先级 */HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); /* 使能中断 */HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0); /* 配置抢占优先级和响应优先级 */HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); /* 使能中断 */
HAL_NVIC_SetPriority,第一个参数是中断向量,选中它后按F12就能来到枚举类型的中断向量。
由EXTI和IO的重映射关系我们得知,PC5对应的EXTI线是EXTI5,PA5对应的EXTI线是EXTI15,所以我们要在IRQn_Type里找到我们所需要的中断向量, EXTI0~EXTI4有各自对应的中断向量,而EXTI5~EXTI9共用一个中断向量EXTI9_5_IRQn,EXTI10~EXTI15共用一个中断向量EXTI15_10_IRQn;
之后再配置抢占优先级和响应优先级即可,还有使能中断。
4.2 中断处理函数
思路:由HAL库中断回调处理机制得知,当发生中断后,首先进入到对应的中断服务函数里,比如KEY0(对应PC5引脚)按键按下,就会进入EXTI9_5_IRQHandler函数里,并调用HAL库中断处理公用函数HAL_GPIO_EXTI_IRQHandle(),在中断处理公用函数里会进行清中断标志位,调用中断回调函数HAL_GPIO_EXTI_Callback()的操作。
/* KEY1按键中断处理函数 */
void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15); /* 公共处理函数 *//* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_15);
}/* KEY0按键中断处理函数 */
void EXTI9_5_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5); /* 公共处理函数 *//* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_5);
}void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(20); /* 延时消除按键抖动 */switch(GPIO_Pin){case GPIO_PIN_15 :if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15) == 0) /* 检测KEY1是否按下 */{ HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8); /* 翻转LED0 */ }break;case GPIO_PIN_5 :if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_5) == 0) /* 检测KEY0是否按下 */{ HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2); /* 翻转LED1 */ }break;default:break; }
}
而 HAL_GPIO_EXTI_Callback()是虚函数,最后按照我们的思路编写HAL_GPIO_EXTI_Callback()就好了。
main函数里只添加exti_init()外部中断初始化。
OK,外部中断的原理和实验到此结束,完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。
本篇完。
本人博客仅代表个人见解方便记录成长笔记。
若有不足,请指出,感谢您的阅读!