中断

您所在的位置:网站首页 嵌入式中断函数 中断

中断

2023-08-13 14:51| 来源: 网络整理| 查看: 265

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!! 📃个人主页:@rivencode的个人主页 🔥系列专栏:玩转STM32 💬推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站进行注册学习

目录 一.NVIC-嵌套向量中断控制器1.中断向量表2.NVIC内核外设寄存器3.中断编程 二.EXTI—外部中断/事件控制器1.外部中断/事件线路映像2.EXTI功能框图3.选择中断线与EXTI 初始化结构体详解 三.外部中断控制实验实验原理编程要点实验效果 四.总结

一.NVIC-嵌套向量中断控制器

NVIC :嵌套向量中断控制器,属于内核外设,管理着包括内核和片上所有外设的中断相关的功能。

这里解释一下片上外设与内核外设他们都在芯片里面,但内核外设是在内核CPU里面,片上外设就是内核之外咯。 在这里插入图片描述 在这里插入图片描述

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。

几个关于内核外设重要的库文件: Cortex-M3 内核的外设也比较多,但STM32并没有用到这么多内核外设对其进行了裁剪,STM32重要的内核外设用到的库函数放在了misc.c文件之中所以core_cm3.c文件用的较少。

core_cm3.c:内核外设的驱动固件库 core_cm3.h:实现了内核(CPU)里面的外设的寄存器映射,还有很多关于内核外设的库函数。 misc.h:NVIC_InitTypeDef结构体,以及库函数的参数和声明 misc.c:NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)相关函数

1.中断向量表

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。而我们常用的就是这 68 个可屏蔽中断,但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。

而我要讲的是103系列其中系统异常有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60个。除了个别中断的优先级被定死外,其它中断的优先级都是可编程的。

下面灰色的就是系统异常(中断),中断就是异常,异常就是中断这里就不区分了,表中的优先级是硬件编号,数字越小优先级越高这里复位中断的编号最小,所以一按板子上的复位键会立马执行复位程序。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

2.NVIC内核外设寄存器

NVIC管理着中断向量表中的60个中断 在固件库中,NVIC 的结构体定义给每个寄存器都预留了很多位,恐怕为的是日后扩展功能。不过 STM32F103 可用不了这么多,只是用了部分而已

在这里插入图片描述 具体使用了各个寄存器使用了多少个请看图

在这里插入图片描述 在这里插入图片描述

ISER[8]:ISER 全称是:Interrupt Set-Enable Registers:这是一个中断使能寄存器组(有8个这样的寄存器)。上面STM32F103 的可屏蔽中断只有 60 个,一个寄存器有32位一位可以表示一个中断两个寄存器总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),ISER[0]的 bit0-bit31 分别对应中断 0-31。ISER[1]的 bit0-27 对应中中断32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。

在这里插入图片描述

ICER[8]:全称是:Interrupt Clear-Enable Registers:是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。向寄存器写1清除写0无效。

ISPR[8]:全称是:Interrupt Set-Pending Registers:是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1当置位中断挂起寄存器的时候,相应的中断将会被挂起,此时这个中断将不会立即执行,而是等待可执行的时候再执行;比如高低级别的中断同时产生,就先挂起低级别的中断,等高级别的中断执行完毕,解除并执行低级中断这个过程一般是自发进行。

ICPR[8]:全称是:Interrupt Clear-Pending Registers:是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解除挂起。写 0 无效。

IABR[8]:全称是:Interrupt Active Bit Registers:是一个中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。

IP[240]:全称是:Interrupt Priority Registers:是一个中断优先级控制的寄存器组。这个寄存器组相当重要!STM32 的中断分组与这个寄存器组密切相关。240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32 只用到了其中的前 60 个。IP[59]-IP[0]分别对应中断 59~0也就是说:一个中断需要一个IP寄存器来配置优先级。 比如说中断硬件编号为20的中断配置的寄存器就为IP[20]

在这里插入图片描述 看图所知这里只分配了80个IP寄存器但对我们配置60中断绰绰有余,这里建议一下一定要去看看Cortex-M3编程手册有关NIVC的寄存器,这样心底有个底。

IP寄存器 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255(十进制),数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在F103 中,只使用了高 4bit,如下所示:

在这里插入图片描述 用于表达优先级的这 4bit,又被分组成抢占优先级和响应优先级

STM32 将中断分为 5 个组,组 0-4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。 在这里插入图片描述 比如组2来说:2位配置抢占式优先级(00 01 10 11)换成十进制不就是0~3嘛,2位配置响应式优先级0 ~3,数字越小优先级越高其他分组以此类推。 注意:组1抢占式优先级0位,那就没有抢占式优先级,

配置分组

在系统代码执行的过程只进行一次中断优先级分组,设置分组之后一般不会进行变动,不然中断执行会混乱,如:假设你分成组2,抢占式优先级有2位,后面改成组3的话,就会变成3位抢占式优先级,则之前的一位响应优先级变成了抢占式优先级,则之前配置的抢占式和响应优先级的值就不确定了乱套了。

在这里插入图片描述

抢占式优先级与响应式优先级的区别 在这里插入图片描述 加深理解: 1.抢占式优先级高的可以打断正在执行的(抢占式优先级低)中断(挂起),转而执行抢占式优先级高的中断执行完毕后在返回原来(抢占式优先级低)中断继续执行,这就是所谓的中断嵌套。而响应优先级并不能嵌套

2.若抢占式优先级与响应式优先级都相同则硬件编号(在中断向量表的排序顺序)小的先执行

3.中断编程

一般中断使能一般有两个门,外设使能相应的中断然后送入NVIC再使能,外设使能中断是小门,NVIC使能中断是大门,只有都使能才能响应中断。 请添加图片描述

1.使能外设某个中断,这个具体由每个外设的相关中断使能位控制。

2.配置中断优先级分组,然后初始化 NVIC_InitTypeDef 结构体,设置抢占优先级和子优先级,使能中断请求。NVIC_InitTypeDef 结构体在固件库头文件 misc.h 中定义。

配置中断优先级分组 在这里插入图片描述 初始化 NVIC_InitTypeDef 结构体

在这里插入图片描述

NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。

在这里插入图片描述

NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定。

NVIC_IRQChannelSubPriority:子优先级(响应优先级),具体的值要根据优先级分组来确定 。

NVIC_IRQChannelCmd:中断使能(ENABLE)或者(DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

配置好 NVIC_InitTypeDef 结构体然后就调用NVIC_Init()函数,由函数将参数写入寄存器

现在来具体来分析一下这个函数加深我们对NVIC寄存器的理解

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) { uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F; /* Check the parameters */ assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd)); assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority)); assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority)); if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE) { /* Compute the Corresponding IRQ Priority --------------------------------*/ tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08; tmppre = (0x4 - tmppriority); tmpsub = tmpsub >> tmppriority; tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] = (uint32_t)0x01 NVIC_InitTypeDef NVIC_InitStruct; //中断优先级分组这里是组1 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //选择中断源 NVIC_InitStruct.NVIC_IRQChannel= EXTI15_10_IRQn; //设置抢占式优先级 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority =1; //设置响应式优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority =1; //使能中断源 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //调用NVIC初始化函数 NVIC_Init(&NVIC_InitStruct); } void EXTI_Key_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(EXTI_Key1_GPIO_CLK,ENABLE); //上拉输入 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU; GPIO_InitStruct.GPIO_Pin= EXTI_Key1_GPIO_PIN ; GPIO_Init(EXTI_Key1_GPIO_POTR,&GPIO_InitStruct); //配置NVIC中断 NVIC_EXTI_Config(); //一定要使能外设AFIO外设的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //选择信号源 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15); //选择中断线 EXTI_InitStruct.EXTI_Line = EXTI_Line15; //选择模式这里选择中断模式 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //下降沿模式 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; //使能中断 EXTI_InitStruct.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStruct); }

exti.h

#ifndef __EXTI_H #define __EXTI_H #include "stm32f10x.h" #define EXTI_Key1_GPIO_PIN GPIO_Pin_15 #define EXTI_Key1_GPIO_POTR GPIOA #define EXTI_Key1_GPIO_CLK RCC_APB2Periph_GPIOA void EXTI_Key_Config(void); #endif /*__EXTI_H */

main.c

#include "stm32f10x.h" #include "led.h" #include "exti.h" #define SOFT_DELAY Delay(0x0FFFFF); void Delay(__IO u32 nCount); int main(void) { /* LED 端口初始化 */ LED_GPIO_Config(); EXTI_Key_Config(); LED_G(OFF); LED_R(OFF); while(1) { } } void Delay(__IO uint32_t nCount) //简单的延时函数 { for(; nCount != 0; nCount--); }

中断服务函数

void EXTI15_10_IRQHandler(void) { //防抖 Delay(0x0FFFF); //判断按键是否按下 if( GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)== 0) { if( EXTI_GetITStatus(EXTI_Line15) != RESET ) { //电平翻转 GPIOA->ODR ^= GPIO_Pin_8; } //清除中断挂起位 EXTI_ClearITPendingBit(EXTI_Line15); } }

注意:执行完函数时一定要清除中断挂起为,不然系统会一直进入中断函数

在这里插入图片描述

实验效果

请添加图片描述

四.总结

由于本文涉及的知识点太多,参考了很多资料然后结合自己的理解写出这篇文章快接近万字了,到这终于写完啦,如果有错误还请指正,如果涉及到初始化函数那些寄存器具体怎么运算写入的不懂的可以评论区问我,建议虽然是库函数数编程,但尽量去看看函数是如何将参数写入寄存器的以及各个寄存器的作用,这样可以极大加深我们对原理的理解。觉得文章对你有所帮助就快快点赞收藏叭!!!

结束语: 最近发现一款刷题神器,如果大家想提升编程水平,玩转C语言指针,还有常见的数据结构(最重要的是链表和队列)后面嵌入式学习操作系统的时如freerots、RT-Thread等操作系统,链表与队列知识大量使用。 大家可以点击下面连接进入牛客网刷题 点击跳转进入网站(C语言方向) 点击跳转进入网站(数据结构算法方向) 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3