外部中断/事件控制器主要特征:
内部框图
边沿检测电路可以通过配置上升沿触发选择寄存器或者下降触发选择寄存器来检测输入的信号
触发信号可以从边沿检测电路获取到也可以通过配置软件中断事件寄存器来触发中断事件
若有触发信号产生就会请求挂起(配置请求挂起寄存器)
事件屏蔽寄存器配置则会结合触发信号产生脉冲发生器
若配置中断屏蔽寄存器并且有中断挂起就会触发NVIC中断控制产生中断事件
外部中断通路选择
共有16根外部中断线
PA0-PG0引脚都能触发EXTI0中断,当是注意的是,如EXTI0有选择位,选择哪个引脚线上的中断。
换言之,选择PA0触发EXTI0中断,那么接在EXTI0上的其他引脚就不能再用了。 所以在设计原理图的时候一定要注意。
了解STM32 片内资源外部中断。本实验基于STM32F103开发 实现 按键功能。
1、硬件开发准备
主控:STM32F103ZET6
2、软件开发准备
软件开发使用虚拟机 + VScode + STM32Cube 开发STM32,在虚拟机中直接完成编译下载。
该部分可参考:软件开发环境构建
1、STM32CubeMX基本配置
本实验基于 CubeMX详解构建基本框架进行开发。
2、GPIO 配置
本实验以按键为例确定外部触发模式为:下降沿触发
2、NVIC 优先级配置
配置四个按键外部触发等级。(数值越小,优先等级越高)
1、定义按键相关结构体,以及初始化
typedef struct Key_s
{
uint32_t volatile key_flag; // 触发标志位
uint32_t key_press; // 单机按键
uint32_t key_hold; // 长按按键
void (*Key_Monitoring_Function)(void); // 按键监测函数
} Key_t;
extern Key_t Key1;
extern Key_t Key2;
extern Key_t Key3;
extern Key_t Key4;
Key_t Key1 = {FALSE, FALSE, FALSE, Key1_Monitoring_Function };
Key_t Key2 = {FALSE, FALSE, FALSE, Key2_Monitoring_Function };
Key_t Key3 = {FALSE, FALSE, FALSE, Key3_Monitoring_Function };
Key_t Key4 = {FALSE, FALSE, FALSE, Key4_Monitoring_Function };
2、按键具体监测函数(以一个key1为例)
static void Key1_Monitoring_Function(void)
{
if(Key1.key_flag == TRUE) // 监测按键按下
{
Key1.key_press = FALSE; // 初始化按键状态:长按状态
Key1.key_hold = TRUE;
for(int i=0; i<200; i++) // 循环2s,如果中间没有检测到高电平(按键松开),即长按状态
{
HAL_Delay(10);
if(GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)) // 监测到按键松开,即单击按键
{
Key1.key_press = TRUE; // 配置单击按键状态,跳出循环
Key1.key_hold = FALSE;
break;
}
}
}
if( Key1.key_press == TRUE ) // 判断为单击按键,做的具体动作(这里继电器翻转)
{
Relay.Filp();
}
if( Key1.key_hold == TRUE ) // 判断为长按按键,做的具体动作(这里继电器开关一次)
{
Relay.On();
HAL_Delay(500);
Relay.Off();
}
Key1.key_flag = FALSE; // 按键状态初始化
Key1.key_press = FALSE;
Key1.key_hold = FALSE;
}
3、外部中断触发,改变按键flag状态 (先了解下外部触发中断处理函数,以一个key1为例)
// Core/Src/stm32f1xx_it.c
void EXTI0_IRQHandler(void) // 外部触发函数
{
/* USER CODE BEGIN EXTI0_IRQn 0 */
/* USER CODE END EXTI0_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
/* USER CODE BEGIN EXTI0_IRQn 1 */
/* USER CODE END EXTI0_IRQn 1 */
}
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u) // 外部中断线检测
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); // 检测到外部中断线后,先清除中断标志位
HAL_GPIO_EXTI_Callback(GPIO_Pin); // 调用回调函数(具体实现)
}
}
__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
*/
}
以上所诉,回调函数是弱函数,可以重新构建。重构之后的回调函数会被真正被调用。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) // 回调函数重构
{
switch (GPIO_Pin) // 判断是哪个引脚触发的中断,对应设置flag标志位
{
case KEY1_Pin: Key1.key_flag = TRUE; break;
case KEY2_Pin: Key2.key_flag = TRUE; break;
case KEY3_Pin: Key3.key_flag = TRUE; break;
case KEY4_Pin: Key4.key_flag = TRUE; break;
default: break;
}
}
大家可以按照步骤实现功能,实现效果可以在评论区交流学习哦!!!!