本帖最后由 @乔木 于 2017-8-31 20:47 编辑
中断简介中断概念
用文字进行描述就是: 是指CPU在正常运行程序时,由于内部或外部事件引起暂时中止现行程序,转去执行请求CPU为其服务的那个外设或事件的服务程序,等待该服务程序执行完成后又返回到被中止的地方程序,这样一个过程。可以使用以下面一张流程图来描述。 中断执行流程图
中断是指CPU在正常运行程序时,由于内部/外部事件或由程序预先安排的事件,引起CPU中断正在运行的程序,而转到为内部/外部事件或由程序预先安排的事件服务的程序中去,服务完毕,再返回去执行暂时中断的程序。
中断意义用中断的意义-------及时处理紧急任务 中断,程序设计中占着非常重要的地位。如果没有中断,则CPU的工作效率会大折扣。就像上一篇帖子UART模块中,接收电脑发送来的数据,使用了while(查询标志){}查询状态的方式,如果电脑没有发送数据,则程序会一直阻塞,使CPU做不了其他事情。如果有一种机制,不用CPU循环查询是否有数据到来,而是硬件自动接收数据,当收到数据时候的自动通知CPU,这时候CPU再去把数据读取出来。这样,在没有数据接收到前,CPU可以去做其他事情,工作效率自然就提高了。在CPU硬件中,通过中断这种机制来实现这个功能,每个片上外设硬件都提供一个中断信号,当模块处理特定工作状态时,会发出中断信号通信CPU。
中断优先级,中断嵌套中断优先级解决CPU控制权的竞争问题。 现在CPU都集成了许多片上外设模块,而每个模块都有自己的中断信号,CPU设计者给这些中断源分配好一个固定的地址,当CPU收到某一中断信号时,中断源就会强制中止当前的程序,跳转到和中断信号源对应的中断入口地址,而这个地址上,通常在编程时候是放置一个函数地址,这样,CPU实际就是去执行这个函数的代码,这个函数称作中断服务函数。中断源之间有优先级之分,高优先级可以中断低优先级程序。比如,先是发生发一个优先级比较低的中断,CPU转到其中断服务函数去执行,在执行过程中,发生更高优先级的中断,那么,CPU同样中止当前的代码,转到高优先级的中断源对应的中断入口去执行中断服务函数,当高优先级中断服务函数执行完成后再返回原来被中断的低优先级的中断服务函数断点处继续运行,运行完成后,返回到主程序的断点片继续运行。 中断嵌套示例图
STM32中断STM32中断特性 Cortex-M4 内部包含有嵌套向量中断控制器 与内核紧密联系的中断控制器,可支持低中断延时 可对系统异常和外设中断进行控制 16个可编程的优先等级(使用了4位中断优先级) 82个可屏蔽中断通道(不包含16个Cortex™-M4F的中断线);可重定位的向量表 不可屏蔽中断 软件中断功能 嵌套向量中断控制器(NVIC)是 Cortex-M4 的一个内部器件。与CPU紧密结合,降低中断延时,让新进中断可以得到高效处理。 Cortes-M3核心的中断实现是通过嵌入一个NVIC控制器,该控制器功能非常强大。在中断处理上效率很高,优先级配置也很灵活。
嵌套矢量中断控制器(NVIC)介绍最大可以支持256个中断入口,最大支持128级嵌套(不是256) NVIC是Cortex-M处理器不可或缺的部分,它为处理器提供了卓越的中断处理能力。 Cortex-M处理器使用一个矢量表,其中包含要为特定中断处理程序执行的函数的地址。接受中断时,处理器会从该矢量表中提取地址。 为了减少门数并增强系统灵活性,Cortex-M处理器使用一个基于堆栈的异常模型。出现异常时,系统会将关键通用寄存器推送到堆栈上。完成入栈和指令提取后,将执行中断服务例程或故障处理程序,然后自动还原寄存器以使中断的程序恢复正常执行。使用此方法,便无需编写汇编器包装器了(而这是对基于C语言的传统中断服务例程执行堆栈操作所必需的),从而使得应用程序的开发变得非常容易。NVIC支持中断嵌套(入栈),从而允许通过运用较高的优先级来较早地为某个中断提供服务。
NVIC控制器的优先级表示NVIC控制器的优先级使用8位二进制位表示,分成两部分表示,分别是抢占优先级和响应优先级。 抢占优先级:含义是不同等级间的中断可以嵌套,高优先级可以中断低优先级,数字小的优先级高。 响应优先级:含义是不同响应优先级的中断不能嵌套,但是当抢占优先级相同,响应优先级不同,多个中断源同时发生中断时候,响应优先级高的中断事件会优先响应,数字小的优先级高。响应优先级只解决中断条件同时满足的情况 自然优先级:就是NVIC控制器的中断源编号,数字小的,优先级高。作用:当抢占优先级和响应优先级都相同的中断源同时发生中断时候,自动优先级高的优先响应。不存在嵌套执行。 1. 当两个中断条件同时满足 先比较抢占优先级;如果抢占优先级一样,则比较响应优先级;如果响应优先级一样,则比较自然优先级 2. 当中断条件先后满足 当一个中断服务函数正在执行的过程中另外一个中断条件满足,只有后一个中断的抢占优先级大于前一个中断的抢占优先级才会打断前一个中断。
优先级等级小结:抢占优先级>响应优先级>自然优先级,抢占优先级决定是否可以嵌套,响应优先级和自然优先级决定同时发生时,先响应谁(如果抢占相同,才依据响应,如果抢占和响应都相同,自然优先级才会起作用)
STM32F4把指定中断优先级的寄存器位用了4位(高四位),这4个寄存器位的分组方式如下: 第0组:所有4位用于指定响应优先级 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级 第4组:所有4位用于指定抢占式优先级中断分组的组优先级和子优先级的配置 组编号 | PRIGROUP区域(3位) AIRCR[10:8]0xE000_ED0C | | | | | 0 | | | | | | 1 | | | | | | 2 | | | | | | 3 | | | | | | 4 | | | | | |
要配置分组,按照上面这个表格,把PRIGROUP列的值写入AIRCR[10:8]中。一个程序只能有一种分组,分组确定,那其抢占优先级和响应优先级就确定了。
整个系统中只有一种分组
中断向量表系统中断(内部中断) 外设中断
STM32中断编程由于NVIC属于内核级的外设,所有厂家的芯片在寄存器上是完全相同的。配置方式,作用也是完全一样的,所以ARM公司提供一份通用的NVIC配置函数,我们只需要弄清楚它们的作用以及使用方法就可以了。 中断相关的配置函数定义在core_cm4.h这个头文件中。
NVIC分组设置voidNVIC_SetPriorityGrouping(uint32_tPriorityGroup) PriorityGroup:分组方式对应的值,0~7前面表格中说过。比如说选择第2组,则是2位抢占优级,那对应的值是7-2=5。 设置分组2方式,NVIC_SetPriorityGrouping(7-2); AIRCR[10:8]
NVIC优先级编码uint32_t NVIC_EncodePriority(uint32_tPriorityGroup,uint32_tPreemptPriority,uint32_tSubPriority); 这个函数负责把分组方式,抢占优先级,子优先级数值编码成一个32位的数字,做为返回值返回。 PriorityGroup:分组方式对应的值;也就是AIRCR[10:8]中的值。或者说是NVIC_SetPriorityGrouping函数的参数。 PreemptPriority:正确的抢占优先级值,比如分组2,范围是0~3取值, SubPriority:正确的子优先级值,比如分组2,范围是0~3取值, 说明:这个函数并没有把优先级设置到寄存器中,只是简单的合成一个表示优先级数字。后面再使用其他函数设置这个值到寄存器中。 可以使用移位方式配置优先级。但是配置上比较麻烦。 Pri=NVIC_EncodePriority(7-2,1,3);à分组2,抢占优先级是1,响应优先级是3。返回编码后的值,这个值就是写入对应中断源中断寄存器中的值。这个函数并没有写入寄存器中。
NVIC优先级设置Void NVIC_SetPriority(IRQn_TypeIRQn,uint32_tpriority) 该函数用于设置指定中断源的中断优先级。 IRQn_Type是在stm32F10x.h文件中有定义,一个枚举类型,定义了STM32F10x芯片对应所有的中断源编号。 IRQn:中断源编号; priority:中断优先级,就是使用NVIC_EncodePriority()函数编码的返回值。
Pri=NVIC_EncodePriority(7-2,1,3);
//USART1_IRQn=37, NVIC_SetPriority(USART1_IRQn,Pri)//设置串口1的中断优先级。
NVIC中断使能中断使能也是由NVIC控制器控制的。 0xE000_E100–0xE000_E11C这个地址范围的寄存器是用来配置中断使能。 一个中断源只要一个位。 一个寄存器是32位。总共有8个寄存器,位数是32*8=256。
这图最后一个寄存器SETENA7管224~239号中断。共15+1=16个中断。前面的都是管32个。 原因是SETENA0~SETENA7只能用于使能厂家的外扩的中断源。不用于管理Cortex-M3核心占用的前16个中断。 比如:STM32F103的USART1中断使能,就要配置SETENA1[3]位为1,其他中断类似。 Void NVIC_EnableIRQ(IRQn_TypeIRQn) 该函数用于使能指定中断源。 //使能串口中断 NVIC_EnableIRQ(USART1_IRQn)
NVIC中断禁止和使能中断一样,也由NVIC控制。 中断对应的位计算方法和使能计算方法相同。 Void NVIC_DisableIRQ(IRQn_TypeIRQn) 该函数用于禁能指定中断源。 禁止串口中断 NVIC_DisableIRQ(USART1_IRQn) 初始化NVIC控制器一般步骤: 1. 设置分组方式 2. 确定中断源的抢占优先级和响应优先级,使用NVIC_EncodePriority函数进行编码 3. 把上下编码得到优先级值写入优先级配置寄存器,使用NVIC_SetPriority函数。 4. 使能对应中断源,使用NVIC_EnableIRQ函数。 5. 具体的模块中断:还要去配置外设中断相关的寄存器(下小节会以外部中断以例子讲解) 6. 编写中断服务函数: 1) 却启动代码中找到对应的中断入口,以确定中断函数名字 2) 在C文件中编写一个无返回值,无类型的函数。函数名就是上一步得到的名字 例子:USART1中断函数编写: 则以C程序中编写一个中断服务函数: voidUSART1_IRQHandler(void) { 发生串口1中断时候你要做什么,写在这里 }
其他NVIC相关函数void__set_BASEPRI(uint32_tbasePri) NVIC_GetPriorityGrouping(void) uint32_tNVIC_GetPendingIRQ(IRQn_TypeIRQn) voidNVIC_SetPendingIRQ(IRQn_TypeIRQn) voidNVIC_ClearPendingIRQ(IRQn_TypeIRQn) NVIC_GetActive(IRQn_TypeIRQn) uint32_tNVIC_GetPriority(IRQn_TypeIRQn) NVIC_DecodePriority(uint32_tPriority,uint32_tPriorityGroup,uint32_t*pPreemptPriority,uint32_t*pSubPriority) 这些用得比较少,不详细说明了。
中断服务函数中断服务函数编写内容上和普通代码没有什么区别,用户根据需要编写功能代码,只是里面一定要写有清除中断标志的代码。中断服务函数名要和启动代码对应中断源的中断入口标志相同。且函数原型是voidfun(void)。
中断编程通用步骤经过前面的讲解,可以总结出M4芯片中断编程的一般步骤。步骤如下: 1. 配置中断源所属模块中断控制相关寄存器 2. 配置整个应用程序的中断分组方式,STM32F407只有0~4组。 3. 设置中断优先级 4. 使能中断 查找启动代码,得到中断服务函数名,编写中断服务程序。
外部中断外部中断/概述对于互联型产品,外部中断/事件控制器由20个产生事件/中断请求的边沿检测器组成,对于其它产品,则有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
事件:不需要CPU干预(当条件满足,直接触发硬件工作) 中断:需要CPU干预(执行中断服务函数) 外部中断/事件控制器框图
外部中断/事件线路映像STM32F4的外部中断对应的IO口和大部分CPU一样,是需要配置才能使用的。STM32F4的外部和IO相关的中断最多是16个,且一个中断线可以分布在每一组IO口上,但是,也是有限制的,一组IO口最多16个引脚,外部中断线只能出现在和中断线编号相同的任何一组相同IO引脚编号的IO口上。如下图所示: 这几个寄存器属于第8章中中系统配置寄存器小节中的寄存器。 外部中断配置寄存器1(SYSCFG_EXTICR1) 地址偏移:0x08 复位值:0x0000
位31:16 | | 位15:0 | EXTIx[3:0]:EXTIx配置(x=0…3)(EXTIxconfiguration) 这些位可由软件读写,用于选择EXTIx外部中断的输入源。 0000:PA[x]引脚0100:PE[x]引脚 0001:PB[x]引脚0101:PF[x]引脚 0010:PC[x]引脚0110:PG[x]引脚 0011:PD[x]引脚 |
外部中断配置寄存器2(SYSCFG_EXTICR2) 地址偏移:0x0C 复位值:0x0000
位31:16 | | 位15:0 | EXTIx[3:0]:EXTIx配置(x=4…7)(EXTIxconfiguration) 这些位可由软件读写,用于选择EXTIx外部中断的输入源。 0000:PA[x]引脚0100:PE[x]引脚 0001:PB[x]引脚0101:PF[x]引脚 0010:PC[x]引脚0110:PG[x]引脚 0011:PD[x]引脚 |
外部中断配置寄存器3(SYSCFG_EXTICR3) 地址偏移:0x10 复位值:0x0000 位31:16 | | 位15:0 | EXTIx[3:0]:EXTIx配置(x=8…11)(EXTIxconfiguration) 这些位可由软件读写,用于选择EXTIx外部中断的输入源。 0000:PA[x]引脚0100:PE[x]引脚 0001:PB[x]引脚0101:PF[x]引脚 0010:PC[x]引脚0110:PG[x]引脚 0011:PD[x]引脚 |
外部中断配置寄存器4(SYSCFG_EXTICR4) 地址偏移:0x14 复位值:0x0000
位31:16 | | 位15:0 | EXTIx[3:0]:EXTIx配置(x=12…15)(EXTIxconfiguration) 这些位可由软件读写,用于选择EXTIx外部中断的输入源。 0000:PA[x]引脚0100:PE[x]引脚 0001:PB[x]引脚0101:PF[x]引脚 0010:PC[x]引脚0110:PG[x]引脚 0011:PD[x]引脚 |
EXTI寄存器描述中断屏蔽寄存器(EXTI_IMR)偏移地址:0x00 复位值:0x00000000 位31:20 | | 位22:0 | MRx:线x上的中断屏蔽(InterruptMaskonlinex) 0:屏蔽来自线x上的中断请求; 1:开放来自线x上的中断请求。 |
事件屏蔽寄存器(EXTI_EMR)偏移地址:0x04 复位值:0x00000000
位31:23 | | 位22:0 | MRx:线x上的事件屏蔽(EventMaskonlinex) 0:屏蔽来自线x上的事件请求; 1:开放来自线x上的事件请求。 |
上升沿触发选择寄存器(EXTI_RTSR)偏移地址:0x08 复位值:0x00000000
位31:23 | | 位22:0 | TRx:线x上的上升沿触发事件配置位(Risingtriggereventconfigurationbitoflinex) 0:禁止输入线x上的上升沿触发(中断和事件) 1:允许输入线x上的上升沿触发(中断和事件) 注:位19只适用于互联型产品,对于其它产品为保留位。 |
下降沿触发选择寄存器(EXTI_FTSR)偏移地址:0x0C 复位值:0x00000000
位31:23232319 | | 位22:0 | TRx:线x上的下降沿触发事件配置位(Fallingtriggereventconfigurationbitoflinex) 0:禁止输入线x上的下降沿触发(中断和事件) 1:允许输入线x上的下降沿触发(中断和事件) 注:位19只适用于互联型产品,对于其它产品为保留位。 |
软件中断事件寄存器(EXTI_SWIER)偏移地址:0x10 复位值:0x00000000 位31:23 | | 位22:0 | SWIERx:线x上的软件中断(Softwareinterruptonlinex)当该位为’0’时,写’1’将设置EXTI_PR中相应的挂起位。如果在EXTI_IMR和EXTI_EMR中允许产生该中断,则此时将产生一个中断。注:通过清除EXTI_PR的对应位(写入’1’),可以清除该位为’0’。 注:位19只适用于互联型产品,对于其它产品为保留位。 |
挂起寄存器(EXTI_PR)偏移地址:0x14 复位值:0xXXXXXXXX
位31:23 | | 位22:0 | PRx:挂起位(Pendingbit) 0:没有发生触发请求 1:发生了选择的触发请求当在外部中断线上发生了选择的边沿事件,该位被置’1’。 在该位中写入’1’可以清除它,也可以通过改变边沿检测的极性清除。 注:位19只适用于互联型产品,对于其它产品为保留位。 |
外部中断使用步骤: 一:初始化按键对应的GPIO控制器的功能 二:使能SYSCFG模块,使用SYSCFR(外部中断配置寄存器),选择你要使用哪个端口,A/B/C/D--心里知道你要使用EXITx,x是哪一个根线,因为还没有配置的 三:上升沿下降沿选择(触发条件选择) 四:开放哪一根中断线,0/1/2/3,EXITx,x就是代表哪一根中断线 五:配置NVIC中断控制器(中断分组,抢占优先级,响应优先级,中断源)
中断服务函数不会被任何一个函数调用,当条件满足的时候,直接由NVIC中断控制器把CPU拉到中断服务函数中执行,执行完后返回断点处。 CORTEX M3/4中断服务函数名字是固定的 中断服务函数书写原则:快进快出,在中断不要执行占用CPU较长时间的代码。一般在中断服务函数中 做标志。
如果中断服务函数公共入口,先查询是哪种中断,然后再清除相关标志,再做处理。
不要在中断服务函数的最后清除中断标志。
最后或许还有些同学会说NVIC控制器的优先级使用了8位二进制位表示,那么中断优先级等级就应该有256级,而不是16级,这个问题我在上文中稍微提及了一下:STM32中只使用了该寄存器的高四位,这就是为什么只有16级的原因。《Cortex-M4权威指南》160页:优先级定义,也对这个问题进行了很详细的讲述:可编程优先级的实际数量由芯片设计商决定,在STM32中采用了四位,在其他芯片中有可能则采用了三位,五位,等等;为什么只采用四位,这其中则有NVIC的复杂度及功耗方面的考量。它们还有个共同点,便是采用寄存器高位,这是为了程序移植方便,不至于在采用不同优先级位的芯片之间进行程序移植时出现优先级翻转的情况。
|