嵌入式之中断的简单使用
一 什么是中断举一个比较形象的例子你是一名初中生今天是星期六你和同学约定好要一起开黑打王者但是快要临近中午了妈妈喊你回家吃饭但是此时你已经开局10分钟而且还是优势局但如果你此时回去吃饭所以你不得不乖乖的听你妈妈的话因为你妈妈的话很有分量比和你同学玩还有含金量没办法你只能感觉你回家把饭快点吃完因为你还要接着回来和同学再接着玩游戏这个过程就叫做中断。二 中断的分类系统中断和外部中断本节内容主要讲解外部中断但无论是哪种中断都是由ARM Cortex-M内核定义并统一由嵌套向量中断控制器 (NVIC)管理基于Cortex-M内核的中断编号体系ARM Cortex-M内核为所有中断和异常分配了一个统一的中断编号 (Interrupt Number)这构成了中断向量表的基础编号 1 到 15分配给系统异常。例如编号-3对应复位-2对应NMI。编号 16 及以上分配给外部中断。例如EXTI0、TIM1_UP等。管理机制中断优先级STM32的中断优先级管理非常灵活主要体现在以下两个方面优先级分组STM32将可编程优先级4位共16级进一步划分为抢占优先级 (Preemption Priority) 和子优先级 (Subpriority)。通过配置AIRCR寄存器可以设定这两种优先级的位数分配。抢占优先级决定是否发生中断嵌套。高抢占优先级的中断可以打断低抢占优先级的中断。子优先级当两个中断的抢占优先级相同时子优先级决定它们的响应顺序但不会发生嵌套。优先级数值STM32中优先级数值越小表示优先级越高。‘所以在编程实现中我们需要配置优先级分组和抢占优先级三 编程实现案例使用按钮KEY控制LED灯的亮灭编程的步骤如下所示1 LED初始化/** * brief 初始化 LED 对应的 GPIO 引脚 * param 无 * retval 无 */ void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 1. 使能 LED 所在 GPIO 端口GPIOC的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 2. 配置 GPIO 引脚模式 GPIO_InitStructure.GPIO_Pin GPIO_Pin_13; // 选择 PC13 引脚 GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 推挽输出模式 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;// 输出速度为 50MHz // 3. 调用初始化函数 GPIO_Init(GPIOC, GPIO_InitStructure); }2 KEY初始化/** * brief 初始化按键 KEY 对应的 GPIO 引脚为中断输入模式 * param 无 * retval 无 */ void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 1. 使能 KEY 所在 GPIO 端口GPIOA和 AFIO 复用功能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 2. 配置 GPIO 引脚模式 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; // 选择 PA0 引脚 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入模式[reference:1][reference:2] GPIO_Init(GPIOA, GPIO_InitStructure); }3 外部中断初始化/** * brief 初始化外部中断线 (EXTI) * param 无 * retval 无 */ void EXTI_Init(void) { EXTI_InitTypeDef EXTI_InitStructure; // 1. 将 GPIOA Pin0 映射到 EXTI Line0[reference:3][reference:4] GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 2. 配置 EXTI 结构体 EXTI_InitStructure.EXTI_Line EXTI_Line0; // 选择 EXTI Line0 EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; // 设置为中断模式[reference:5] EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; // 下降沿触发[reference:6][reference:7] EXTI_InitStructure.EXTI_LineCmd ENABLE; // 使能该中断线[reference:8] // 3. 调用初始化函数 EXTI_Init(EXTI_InitStructure); }4 中断向量NVIC初始化/** * brief 初始化嵌套向量中断控制器 (NVIC) * param 无 * retval 无 */ void NVIC_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; // 1. 设置中断优先级分组整个工程只需设置一次 // 这里设置为分组22位抢占优先级2位子优先级[reference:9] NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2. 配置 NVIC 结构体 NVIC_InitStructure.NVIC_IRQChannel EXTI0_IRQn; // 选择 EXTI0 中断通道[reference:10] NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 抢占优先级为 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 子优先级为 1 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; // 使能该中断通道[reference:11] // 3. 调用初始化函数 NVIC_Init(NVIC_InitStructure); } /** * brief EXTI0 中断服务函数 * param 无 * retval 无 */ void EXTI0_IRQHandler(void) { // 1. 检查 EXTI Line0 的中断标志位是否被置位[reference:12] if (EXTI_GetITStatus(EXTI_Line0) ! RESET) { // 2. 简单的软件消抖延时 10ms 后再次检测引脚状态[reference:13] // 注意实际项目推荐使用定时器或状态机方式进行消抖 uint32_t i 100000; // 一个简单的延时循环 while (i--); // 3. 再次检测按键是否确实被按下低电平有效 if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) RESET) { // 4. 翻转 LED (PC13) 的状态[reference:14] SetBits ^ (114); //根据开发板手册使用位运算 来进行对应寄存器置0置1操作 } // 5. 清除 EXTI Line0 的中断挂起标志位[reference:15][reference:16] EXTI_ClearITPendingBit(EXTI_Line0); } }5 主函数编写/** * brief 主函数 * param 无 * retval 无 */ int main(void) { // 1. 初始化 LED LED_Init(); // 2. 初始化 KEY (GPIO) KEY_Init(); // 3. 初始化 EXTI EXTI_Init(); // 4. 初始化 NVIC NVIC_Init(); // 5. 主循环为空等待中断触发 while (1) { // 主循环可以执行其他非阻塞任务 } }