当前位置: 首页 > ai >正文

STM32外部中断(寄存器和hal库实现)

一、STM32的中断

​Cortex-M3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。一般情况下,芯片厂商会对Cortex-M3的中断进行裁剪。

​STM32有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。STM32F103系列70个中断,有10个内核中断和60个可编程的外部中断。

​下面的列表中,灰色背景的是内部中断(或者异常),其他的为外部中断。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、STM32中断体系架构

在这里插入图片描述

如上图所示,STM32中断来源有三种。一是内核相关,即上面列表的灰色部分。二是片上外设,比如定时器,串口等等。二者直接由NVIC来处理中断。三是外部中断。通过AFIO最多选择16个IO中断,IO中断和其他的中断通过EXTI来控制,再交给NVIC。

AFIO的作用是中断源选择,比如PA0和PB0同时触发中断,AFIO只能选择其中一个。

1. NVIC嵌套向量中断控制器

1.1 NVIC介绍

​NVIC(Nested vectored interrupt controller嵌套向量中断控制器)和处理器核的接口紧密相连,可以实现低延迟的中断处理和高效地处理中断。

嵌套向量中断控制器管理着包括内核异常,外部中断等所有中断。由NVIC决定哪个中断的处理程序交给CPU来执行。

​每一个外部中断都可以被使能或者禁止,并且可以被设置为挂起状态或者清除状态。处理器的中断可以是电平形式的,也可以是脉冲形式的,这样中断控制器就可以处理任何中断源。

​16个IO的中断与PVD(电源电压检测),RTC(实时时钟),USB,以太网检测这20个外部中断会通过EXTI来控制,然后交给NVIC。其他中断都是直接交给NVIC来处理。

1.2 中断优先级

​NVIC为了方便管理中断,可以通过软件给每个中断设置优先级。NVIC用4个位来控制优先级,值小的优先级高。把优先级分为两种:抢占优先级和响应优先级。

规则:

  • 优先级值越小,优先级越高。
  • 如果不设置优先级,则默认优先级为0。
  • 先比较抢占优先级。抢占优先级高的可以打断抢占优先级低的。
  • 若抢占优先级一样,再比较响应优先级。但是响应优先级不会导致中断嵌套。
  • 若抢占优先级一样的同时挂起,则优先处理响应抢占优先级高的。
  • 若挂起的优先级(抢占和响应)都一样,则查找中断向量表,值小的先响应。

​优先级寄存器IPR有8位,实际只用到高4位

​具体切割,由AIRCR寄存器控制

优先级分组AIRCR[10:8]抢占优先级响应优先级
01110位 取值 04位 取值0~15
11101位 0~13位 0~7
21012位 0~32位 0~3
31003位 0~71位 0~1
40114位 0~150位 0

2. 外部中断控制器

2.1 EXTI简介

​EXTI 是 External Interrupt 的缩写,表示外部中断事件控制器。EXTI 可以监测指定 GPIO 口的电平信号变化,并在检测到指定条件时,向内核的中断控制器 NVIC 发出中断申请。NVIC 在裁决后,如果满足条件,会中断CPU的主程序,使 CPU 转而执行 EXTI 对应的中断服务程序。

​EXTI 支持的触发方式:上升沿、下降沿、双边沿或软件触发。

​EXTI 支持所有的 GPIO 口,但需要注意的是,相同的 Pin 不能同时触发中断。例如,PA0 和 PB0 不能同时被配置为中断源。

​EXTI 提供了 16 个 GPIO_Pin 的中断线,以及额外的中断线如 PVD 输出、RTC 闹钟、USB 唤醒和以太网唤醒。

​通过适当的配置,EXTI可以实现丰富多样的功能,如响应按键的按下、传感器的状态变化等外部事件。

2.2 中断/事件

​中断会打断CPU当前正在执行的程序,转而去执行中断服务程序,待中断服务程序执行完毕后,CPU会返回到原来的程序执行点继续执行。

​事件只是简单地表示某个动作或状态的变化,而不会打断CPU当前正在执行的程序。当事件发生时,它会根据配置来决定是否触发相应的中断。如果开放了对应的中断屏蔽位,事件就可以触发相应的中断,否则事件只会作为一个信号存在,不会被CPU处理。

2.3 基本结构

在这里插入图片描述

​要产生中断,必须先配置好并使能中断线。根据需要的边沿检测设置2个触发寄存器,同时在中断屏蔽寄存器的相应位写’1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个中断请求,对应的挂起位也随之被置’1’。在挂起寄存器的对应位写’1’,将清除该中断请求。

​如果需要产生事件,必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存器,同时在事件屏蔽寄存器的相应位写’1’允许事件请求。当事件线上发生了需要的边沿时,将产生一个事件请求脉冲,对应的挂起位不被置’1’。

​通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求。

在这里插入图片描述

2.4 AFIO

​AFIO 是 Alternate Function Input/Output 的缩写,表示复用功能 IO,主要用于实现 I/O 端口的复用功能以及外部中断的控制。

​STM32上有很多 I/ O口以及内置外设(如I2C、ADC、ISP、USART等)。为了节省引出管脚的数量,这些内置外设通常与 I/O 口共用管脚,即 I/O 管脚具有复用功能。例如,一个 GPIO 管脚除了可以作为普通的 I/O端口外,还可以被复用为某个内置外设的功能引脚。

​然而,为了优化64脚或100脚封装的外设数量,有时需要将一些复用功能重新映射到其他引脚上。这时,就可以使用AFIO的复用重映射功能。通过设置复用重映射和调试I/O配置寄存器(AFIO_MAPR),可以实现引脚的重新映射,使得复用功能不再映射到它们的原始分配上。

​此外,AFIO 还用于控制外部中断,用来配置 EXTI 中断线 0~15 对应哪个具体 IO 口。当需要使能外部中断线或进行外部中断线的映射时,通常需要开启AFIO的时钟。

三、中断案例:检测按键按下

外部中断检测按键,当按键按下,翻转LED

PB1拉低,LED点亮

按键按下时,PA0拉高

1. EXTI配置流程

  • 使能GPIO时钟

    __HAL_RCC_GPIOx_CLK_ENABLE();

  • 设置GPIO输入模式

  • 设置AFIO(开启时钟,IO口映射)

  • 设置EXITI(屏蔽 上下沿)

​ 以上三步直接使用HAL_GPIO_Init

  • 设置NVIC(优先级分组 设置优先级 使能中断)

HAL_NVIC_SetPriorityGrouping

HAL_NVIC_SetPriority

HAL_NVIC_EnableIRQ

  • 设计中断服务函数

EXTIx_IRQHandler

2. 寄存器实现

2.1 led.c

#include "led.h"// 初始化
void LED_Init(void)
{	// PB1端口配置通用推挽输出RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;  // 使能APB2GPIOB->CRL &= ~GPIO_CRL_CNF1;		// 00GPIOB->CRL |= GPIO_CRL_MODE1;       // 11LED_Off();}// 控制某个LED的开关
void LED_On()
{GPIOB->ODR &= ~GPIO_ODR_ODR1;  // 0
}
void LED_Off()
{GPIOB->ODR |= GPIO_ODR_ODR1;  // 1
}// 翻转LED状态
void LED_Toggle()
{// 需要先判断当前LED状态,读取IDR对应位if ((GPIOB->IDR & GPIO_ODR_ODR1) == 0){LED_Off();}else{LED_On();}  
}

2.2 delay.c

#include "delay.h"// 延时函数,微秒作为单位,利用系统嘀嗒定时器,72MHz,一次嘀嗒 1/72 us
void Delay_us(uint16_t us)
{// 1. 装载一个计数值,72 * usSysTick->LOAD = 72 * us;// 2. 配置,使用系统时钟(1),计数结束不产生中断(0),使能定时器(1)SysTick->CTRL = 0x05;// 3. 等待计数值变为0,判断CTRL标志位COUNTFLAG是否为1while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG) == 0){}// 4. 关闭定时器SysTick->CTRL &= ~SysTick_CTRL_ENABLE;
}void Delay_ms(uint16_t ms)
{while (ms--){Delay_us(1000);}
}void Delay_s(uint16_t s)
{while (s--){Delay_ms(1000);}
}

2.3 key.c

#include "key.h"
#include "delay.h"
#include "led.h"void key_init(void)
{// 1. 配置时钟 PA0 AFIORCC->APB2ENR |= RCC_APB2ENR_IOPAEN;RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;// 2. GPIO工作模式  CNF-10  MODE-00GPIOA->CRL &= ~GPIO_CRL_MODE0; // 输入模式GPIOA->CRL |= GPIO_CRL_CNF0_1; // 上下拉GPIOA->CRL &= ~GPIO_CRL_CNF0_0;GPIOA->ODR &= ~GPIO_ODR_ODR0; // 默认低电平// 3. AFIO配置AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0_PA; // 选择外部中断输入源// 4. EXTI配置EXTI->RTSR |= EXTI_RTSR_TR0; // 上升沿触发EXTI->IMR |= EXTI_IMR_MR0;   // 开放中断请求(不屏蔽)// 5. NVIC配置 直接调用函数NVIC_SetPriorityGrouping(4);     // 配置优先级组 表示4个二进制位全部用于表示抢占优先级NVIC_SetPriority(EXTI0_IRQn, 1); // 配置优先级NVIC_EnableIRQ(EXTI0_IRQn);      // 使能
}// 中断服务程序
void EXTI0_IRQHandler(void)
{// 清除中断挂起标志位EXTI->PR |= EXTI_PR_PR0;Delay_ms(10);if ((GPIOA->IDR & GPIO_IDR_IDR0) == 1){LED_Toggle();}
}

3. HAL库实现

​ 使用STM32CubeMX配置,注意调整下滴答定时器和外部中断的优先级。否则使用延时消抖会卡死。

在这里插入图片描述

​ 上面就是把滴答定时器抢占优先级调整为高于外部中断抢占优先级

3.1 GPIO初始化

void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pin Output Level */HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);/*Configure GPIO pin : PA0 */GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;GPIO_InitStruct.Pull = GPIO_PULLDOWN;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/*Configure GPIO pin : PtPin */GPIO_InitStruct.Pin = LED1_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);/* EXTI interrupt init*/HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

​ 上面代码为自动生成的部分代码,使用HAL_GPIO_Init配置GPIO口的同时也设置了AFIO和EXTI

​ 值得注意的是设置优先级分组HAL_NVIC_SetPriorityGroupingHAL_Init中,在文件stm32f1xx_hal.c中

3.2 中断处理函数

​ 当有按键按下的时候,检测到上升沿会执行中断服务函数:EXTI0_IRQHandler,内部又会调用HAL库总的外部中断处理函数HAL_GPIO_EXTI_IRQHandler,然后会调用中断回调函数HAL_GPIO_EXTI_Callback,它是一个弱实现函数(用__weak修饰,如果有新的同名函数实现,则执行时会自动调用新的实现函数),我们重新实现这个函数就可以了。

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)
{if (GPIO_Pin == GPIO_PIN_0){HAL_Delay(15);if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET){HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1);}}
}
http://www.xdnf.cn/news/13246.html

相关文章:

  • 机房断电后 etcd 启动失败的排查与快速恢复实录
  • YOLOv11 | 注意力机制篇 | EMAttention与C2PSA机制的协同优化
  • 从0到1:HBase安装与操作指南
  • 3.vue3核心语法
  • 中马泰语言电商系统:打开东南亚电商市场的多语言钥匙
  • 【第二十三章 IAP】
  • Vim 替换命令完整学习笔记
  • 一次消谐器:高效抑制铁磁谐振
  • 对DOM操作 与 jQuery的简单理解(通俗
  • DeepSeek生成流程图
  • 6.10 Mysql 事务 锁 面试题
  • 【Dv3Admin】系统视图角色管理API文件解析
  • 2025蓝奏云软件库合集分享链接汇总:极刻云搜 - 一站式获取海量资源
  • Linux下V2Ray安装配置指南
  • axios访问后台时,返回404
  • chrome插件中如何使用midscene.js
  • Leetcode 3577. Count the Number of Computer Unlocking Permutations
  • LeetCode 240 搜索二维矩阵 II
  • MySQL中的隐式主键和隐藏列
  • Go 语言接口详解
  • 架空线路图像视频监测装置
  • SkyWalking 10.2.0 SWCK 配置过程
  • 『uniapp』url拦截屏蔽 避免webview中打开淘宝店铺自动跳转淘宝
  • 腾讯开源 AniPortrait:音频驱动的逼真肖像动画生成革命
  • 【Java】Arrays.sort:DualPivotQuicksort
  • Spring AI MCP
  • AISHELL-5 全球首套智能驾舱中文语音交互数据集开源
  • 探秘鸿蒙 HarmonyOS NEXT:鸿蒙定时器,简单倒计时的场景应用
  • HAProxy 高可用部署方案详解
  • Blogx项目配置文件读取流程详解