赞
踩
一、什么是中断?(了解)
打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断
中断的作用和意义
中断的意义:高效处理紧急程序,不会一直占用CPU资源
STM32 GPIO外部中断简图
二、NVIC(熟悉)
2.1,NVIC基本概念
2.2,NVIC相关寄存器介绍
2.3,NVIC工作原理
2.4,STM32中断优先级基本概念
2.5,STM32中断优先级分组
2.6,STM32 NVIC的使用
2.1、NVIC基本概念
Nested vectored interrupt controller,嵌套向量中断控制器,属于内核(M3/4/7)
NVIC支持:256个中断(16内核 + 240外部),支持:256个优先级,允许裁剪!
什么是中断向量表?
定义一块固定的内存,以4字节对齐,存放各个中断服务函数程序的首地址
中断向量表定义在启动文件,当发生中断,CPU会自动执行对应的中断服务函数
2.2、NVIC相关寄存器介绍
NVIC还有:中断挂起,解挂,激活标志等非常用功能,不做介绍!
2.3、NVIC工作原理
2.4、STM32中断优先级基本概念
1,抢占优先级(pre):高抢占优先级可以打断正在执行的低抢占优先级中断
2,响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能互相打断
3,抢占和响应都相同的情况下,自然优先级越高的,先执行
4,自然优先级:中断向量表的优先级
5,数值越小,表示优先级越高
2.5、 STM32中断优先级分组
STM32中断优先级举例(假设分组是2)
EXTI1和RTC可以打断:EXTI0和Systick的中断,获得优先执行!
2.6、 STM32 NVIC的使用
三、EXTI(熟悉)
3.1,EXTI基本概念
3.2,EXTI主要特性
3.3,EXTI工作原理(F1/F4/F7)
3.1、EXTI基本概念
External(Extended) interrupt/event Controller,外部(扩展)中断事件控制器
包含20个产生事件/中断请求的边沿检测器,即总共:20条EXTI线(F1)
中断和事件的理解:
中断:要进入NVIC,有相应的中断服务函数,需要CPU处理
事件:不进入NVIC,仅用于内部硬件自动控制的,如:TIM、DMA、ADC
EXTI支持的外部中断/事件请求
3.2、EXTI主要特性
F1/F4/F7系列
每条EXTI线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位
3.3、EXTI工作原理(F1/F4/F7系列)
①,边沿检测
②,软件触发
③,中断屏蔽/清除
④,事件屏蔽
四、EXTI和IO映射关系(熟悉)
4.1,AFIO简介(F1)
Alternate Function IO,即复用功能IO,主要用于重映射和外部中断映射配置
4.2,SYSCFG简介(F4/F7/H7)
System configuration controller,即系统配置控制器,用于外部中断映射配置等
4.3,EXTI 与 IO对应关系
五、如何使用中断(掌握)
STM32 EXTI的HAL库设置步骤(外部中断)
__HAL_RCC_GPIOx_CLK_ENABLE
HAL_GPIO_Init
HAL_NVIC_SetPriorityGrouping
HAL_NVIC_SetPriority
HAL_NVIC_EnableIRQ
EXTIx_IRQHandler
STM32仅有:EXTI0~4、EXTI9_5、EXTI15_10,7个外部中断服务函数
六、HAL库中断回调处理机制介绍(掌握)
七、demo程序——通过外部中断控制一个灯亮灭(掌握)
exit.c
#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/BEEP/beep.h" #include "./BSP/KEY/key.h" #include "./BSP/EXTI/exti.h" /** * @brief 外部中断初始化程序 * @param 无 * @retval 无 */ void extix_init(void) { GPIO_InitTypeDef gpio_init_struct; KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */ KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */ KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */ WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */ gpio_init_struct.Pin = KEY0_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct); /* KEY0配置为下降沿触发中断 */ gpio_init_struct.Pin = KEY1_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct); /* KEY1配置为下降沿触发中断 */ gpio_init_struct.Pin = KEY2_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct); /* KEY2配置为下降沿触发中断 */ gpio_init_struct.Pin = WKUP_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿触发 */ gpio_init_struct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP配置为上升沿触发中断 */ HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2); /* 抢占0,子优先级2 */ HAL_NVIC_EnableIRQ(KEY0_INT_IRQn); /* 使能中断线1 */ HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2); /* 抢占1,子优先级2 */ HAL_NVIC_EnableIRQ(KEY1_INT_IRQn); /* 使能中断线15 */ HAL_NVIC_SetPriority(KEY2_INT_IRQn, 2, 2); /* 抢占2,子优先级2 */ HAL_NVIC_EnableIRQ(KEY2_INT_IRQn); /* 使能中断线15 */ HAL_NVIC_SetPriority(WKUP_INT_IRQn, 3, 2); /* 抢占3,子优先级2 */ HAL_NVIC_EnableIRQ(WKUP_INT_IRQn); /* 使能中断线0 */ } /** * @brief KEY0 外部中断服务程序 * @param 无 * @retval 无 */ void KEY0_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief KEY1 外部中断服务程序 * @param 无 * @retval 无 */ void KEY1_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY1所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief KEY2 外部中断服务程序 * @param 无 * @retval 无 */ void KEY2_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY2_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY2所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief WK_UP 外部中断服务程序 * @param 无 * @retval 无 */ void WKUP_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY_UP所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */ __HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief 中断服务程序中需要做的事情 在HAL库中所有的外部中断服务函数都会调用此函数 * @param GPIO_Pin:中断引脚号 * @retval 无 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { delay_ms(20); /* 消抖 */ switch(GPIO_Pin) { case KEY0_INT_GPIO_PIN: if (KEY0 == 0) { LED0_TOGGLE(); /* LED0 状态取反 */ LED1_TOGGLE(); /* LED1 状态取反 */ } break; case KEY1_INT_GPIO_PIN: if (KEY1 == 0) { LED0_TOGGLE(); /* LED0 状态取反 */ } break; case KEY2_INT_GPIO_PIN: if (KEY2 == 0) { LED1_TOGGLE(); /* LED1 状态取反 */ } break; case WKUP_INT_GPIO_PIN: if (WK_UP == 1) { BEEP_TOGGLE(); /* 蜂鸣器状态取反 */ } break; } }
exti.h
#ifndef __EXTI_H #define __EXTI_H #include "./SYSTEM/sys/sys.h" /******************************************************************************************/ /* 引脚 和 中断编号 & 中断服务函数 定义 */ #define KEY0_INT_GPIO_PORT GPIOA #define KEY0_INT_GPIO_PIN GPIO_PIN_4 #define KEY0_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define KEY0_INT_IRQn EXTI4_IRQn #define KEY0_INT_IRQHandler EXTI4_IRQHandler #define KEY1_INT_GPIO_PORT GPIOA #define KEY1_INT_GPIO_PIN GPIO_PIN_3 #define KEY1_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define KEY1_INT_IRQn EXTI3_IRQn #define KEY1_INT_IRQHandler EXTI3_IRQHandler #define KEY2_INT_GPIO_PORT GPIOA #define KEY2_INT_GPIO_PIN GPIO_PIN_2 #define KEY2_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define KEY2_INT_IRQn EXTI2_IRQn #define KEY2_INT_IRQHandler EXTI2_IRQHandler #define WKUP_INT_GPIO_PORT GPIOA #define WKUP_INT_GPIO_PIN GPIO_PIN_0 #define WKUP_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */ #define WKUP_INT_IRQn EXTI0_IRQn #define WKUP_INT_IRQHandler EXTI0_IRQHandler /******************************************************************************************/ void extix_init(void); /* 外部中断初始化 */ #endif
led.c
#include "./BSP/LED/led.h" /** * @brief 初始化LED相关IO口, 并使能时钟 * @param 无 * @retval 无 */ void led_init(void) { GPIO_InitTypeDef gpio_init_struct; LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */ LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */ gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */ gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */ gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */ HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */ LED0(1); /* 关闭 LED0 */ LED1(1); /* 关闭 LED1 */ }
led.h
#ifndef _LED_H #define _LED_H #include "./SYSTEM/sys/sys.h" /******************************************************************************************/ /* 引脚 定义 */ #define LED0_GPIO_PORT GPIOB #define LED0_GPIO_PIN GPIO_PIN_5 #define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ #define LED1_GPIO_PORT GPIOB #define LED1_GPIO_PIN GPIO_PIN_6 #define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ /******************************************************************************************/ /* LED端口定义 */ #define LED0(x) do{ x ? \ HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) /* LED0翻转 */ #define LED1(x) do{ x ? \ HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) /* LED1翻转 */ /* LED取反定义 */ #define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0) /* 翻转LED0 */ #define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* 翻转LED1 */ /******************************************************************************************/ /* 外部接口函数*/ void led_init(void); /* 初始化 */ #endif
beep.c
#include "./BSP/BEEP/beep.h" /** * @brief 初始化BEEP相关IO口, 并使能时钟 * @param 无 * @retval 无 */ void beep_init(void) { GPIO_InitTypeDef gpio_init_struct; BEEP_GPIO_CLK_ENABLE(); /* BEEP时钟使能 */ gpio_init_struct.Pin = BEEP_GPIO_PIN; /* 蜂鸣器引脚 */ gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(BEEP_GPIO_PORT, &gpio_init_struct); /* 初始化蜂鸣器引脚 */ BEEP(0); /* 关闭蜂鸣器 */ }
beep.h
#ifndef __BEEP_H #define __BEEP_H #include "./SYSTEM/sys/sys.h" /******************************************************************************************/ /* 引脚 定义 */ #define BEEP_GPIO_PORT GPIOB #define BEEP_GPIO_PIN GPIO_PIN_8 #define BEEP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ /******************************************************************************************/ /* 蜂鸣器控制 */ #define BEEP(x) do{ x ? \ HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_SET) : \ HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_RESET); \ }while(0) /* BEEP状态翻转 */ #define BEEP_TOGGLE() do{ HAL_GPIO_TogglePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN); }while(0) /* BEEP = !BEEP */ void beep_init(void); /* 初始化蜂鸣器 */ #endif
key.c
#include "./BSP/KEY/key.h" #include "./SYSTEM/delay/delay.h" /** * @brief 按键初始化函数 * @param 无 * @retval 无 */ void key_init(void) { GPIO_InitTypeDef gpio_init_struct; KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */ KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */ KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */ WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */ gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */ gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */ gpio_init_struct.Pin = KEY2_GPIO_PIN; /* KEY2引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct); /* KEY2引脚模式设置,上拉输入 */ gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */ } /** * @brief 按键扫描函数 * @note 该函数有响应优先级(同时按下多个按键): WK_UP > KEY2 > KEY1 > KEY0!! * @param mode:0 / 1, 具体含义如下: * @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值, * 必须松开以后, 再次按下才会返回其他键值) * @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值) * @retval 键值, 定义如下: * KEY0_PRES, 1, KEY0按下 * KEY1_PRES, 2, KEY1按下 * KEY2_PRES, 3, KEY2按下 * WKUP_PRES, 4, WKUP按下 */ uint8_t key_scan(uint8_t mode) { static uint8_t key_up = 1; /* 按键按松开标志 */ uint8_t keyval = 0; if (mode) key_up = 1; /* 支持连按 */ if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */ { delay_ms(10); /* 去抖动 */ key_up = 0; if (KEY0 == 0) keyval = KEY0_PRES; if (KEY1 == 0) keyval = KEY1_PRES; if (KEY2 == 0) keyval = KEY2_PRES; if (WK_UP == 1) keyval = WKUP_PRES; } else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */ { key_up = 1; } return keyval; /* 返回键值 */ }
key.h
#ifndef __KEY_H #define __KEY_H #include "./SYSTEM/sys/sys.h" /******************************************************************************************/ /* 引脚 定义 */ #define KEY0_GPIO_PORT GPIOA #define KEY0_GPIO_PIN GPIO_PIN_4 #define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define KEY1_GPIO_PORT GPIOA #define KEY1_GPIO_PIN GPIO_PIN_3 #define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define KEY2_GPIO_PORT GPIOE #define KEY2_GPIO_PIN GPIO_PIN_2 #define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */ #define WKUP_GPIO_PORT GPIOA #define WKUP_GPIO_PIN GPIO_PIN_0 #define WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */ /******************************************************************************************/ #define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */ #define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */ #define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) /* 读取KEY2引脚 */ #define WK_UP HAL_GPIO_ReadPin(WKUP_GPIO_PORT, WKUP_GPIO_PIN) /* 读取WKUP引脚 */ #define KEY0_PRES 1 /* KEY0按下 */ #define KEY1_PRES 2 /* KEY1按下 */ #define KEY2_PRES 3 /* KEY2按下 */ #define WKUP_PRES 4 /* KEY_UP按下(即WK_UP) */ void key_init(void); /* 按键初始化函数 */ uint8_t key_scan(uint8_t mode); /* 按键扫描函数 */ #endif
main.c
#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/BEEP/beep.h" #include "./BSP/EXTI/exti.h" int main(void) { HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ led_init(); /* 初始化LED */ beep_init(); /* 初始化蜂鸣器 */ extix_init(); /* 初始化外部中断输入 */ LED0(0); /* 先点亮红灯 */ while (1) { delay_ms(1000); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。