前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NVIC和EXTI

NVIC和EXTI

作者头像
全栈程序员站长
发布2022-09-01 14:16:44
6420
发布2022-09-01 14:16:44
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

NVIC:Nest Vector Interrupt Controller,嵌套中断向量控制器,是用来管理中断嵌套的,核心任务在于其优先级的管理。NVIC给每个中断赋予先占优先级(抢占优先级)和次占优先级(响应优先级)。

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

STM32将CM3内核的中断向量表进行了重新编排,将编号-3至6的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,从编号7开始为外部中断,这些中断优先级都是可以自行设置的。

EXTI:External Interrupt,外部中断,通过GPIO检查输入脉冲,引起中断时间,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后再返回中断之前的代码中执行。

STM32 的EXTI控制器支持19个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32的19 个外部中断为 : 线 0~15:对应外部IO口的输入中断。 线 16:连接到PVD输出。 线 17:连接到RTC闹钟事件。 线 18:连接到USB唤醒事件。

EXTI中断步骤:

① 配置端口为输入模式

② 开启与该IO口相对应的线上中断/事件,并设置触发条件

③ 配置中断分组(NVIC)并使能中断

④ 中断函数

一、配置端口为输入模式

void GPIOA_Init(void)

{

GPIO_DeInit(GPIOA);

RCC->APB2ENR|=1<<2; //使能PORTA时钟

GPIOA->CRL&=0XFFFFFFF0;//PA0设置成上拉输入

GPIOA->CRL|=0X00000008;

}

在对端口进行任何操作之前,必须打开对应的时钟信号,其设置才能生效。这里使

用了GPIOA.00 端口作为中断0输入,作为输入时一般我们设置为上拉输入,如果要

设置成浮空输入的话,外部一定要加上拉电阻,这样对于过滤输入波动很有益处(假

设在电压在3.3-2.0之间进行波动,时间上没尝试按键操作,因为一旦有按键,就应

该为0,那么接了上拉的话,除非产生了低电平,否则小波动都会被拉高过滤掉)。

二、开启与该IO口相对应的线上中断/事件,并设置触发条件

这一步封装在函数void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)中,可以直接调用,例如:Ex_NVIC_Config(GPIO_A,0,RTIR); //设置PA(0)上升沿触发

Ex_NVIC_Config(GPIO_A,13,FTIR);//设置PA(13)下降沿触发

该函数为 Ex_NVIC_Config,该函数有3个参数:GPIOx为GPIOA~G(0~6),在sys.h里面有定义。代表要配置的IO口。BITx则代表这个IO口的第几位。TRIM为触发方式,低2位有效(0x01代表下降触发;0x02代表上升沿触发;0x03代表任意电平触发)。其代码如下: /**************************

该函数只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个,参数:GPIOA~G(0~6),代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,上升沿;2,下降沿;3,任意电平触发;该函数一次只能配置1个IO 口,多个IO 口,需多次调用,该函数会自动开启对应中断,以及屏蔽线

***************************/

void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) {

u8 EXTADDR; u8 EXTOFFSET; EXTADDR=BITx/4;//得到中断寄存器组的编号 EXTOFFSET=(BITx%4)*4; RCC->APB2ENR|=0x01;//使能IO复用时钟 AFIO->EXTICR[EXTADDR]|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到GPIOx.BITx //自动设置 EXTI->IMR|=1<<BITx;//开启line BITx上的中断 EXTI->EMR|=1<<BITx;//不屏蔽lineBITx上的事件 if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//lineBITx 上事件下降沿触发 if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//lineBITx 上事件上升降沿触发 }

EXTICR(External interrupt configuration register)即外部中断配置寄存器。

因为STM32 任何一个IO口都可以配置成中断输入口,但是IO口的数目远大于中断线数(16个),所以我们需要选择哪一个中断是经由那个IO口输入的。于是STM32就这样设计,GPIOA~GPIOG的[15:0]分别对应中断线15~0。这样每个中断线对应了最多7个IO 口,以线0为例:它对应了GPIOA.0、PIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到1个IO 口上,这样就需要EXTICR来决定对应的中断线配置到哪个GPIO上了。寄存器EXTIR有四组,在书写时要注意。具体对应关系如下:0000: PA[x] pin,0001: PB[x] pin,0010: PC[x] pin,0011: PD[x] pin0100: PE[x] pin,0101: PF[x] pin,0110: PG[x] pin。假设我们需要设定外部中断0由GPIOA_0来控制,那么我就可以写成AFIO->EXTIR[0] |=0X00;

NVIC和EXTI
NVIC和EXTI
NVIC和EXTI
NVIC和EXTI

Ex_NVIC_Config,首先根据GPIOx的位得到中断寄存器组的编号,即EXTICR的编号,在EXTICR里面配置中断线应该配置到GPIOx的哪个位。然后使能该位的中断及事件,最后配置触发方式。这样就完成了外部中断的的配置了。从代码中可以看到该函数默认是开启中断和事件的。其次还要注意的一点就是该函数一次只能配置一个IO口,如果你有多个IO口需要配置,则多次调用这个函数就可以了。

三、配置中断分组(NVIC)并使能中断

这一步封装在函数void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)里面可以直接调用,例如

MY_NVIC_Init(2,2,EXTI0_IRQChannel,2); //抢占2,子优先级2,组2 NVIC 设置函数MY_NVIC_Init,该函数有4个参数,分别为:NVIC_PreemptionPriority、NVIC_SubPriority、NVIC_Channel、NVIC_Group。第一个参数NVIC_PreemptionPriority为中断抢占优先级数值,第二个参数NVIC_SubPriority为中断子优先级数值,前两个参数的值必须在规定范围内,否则也可能产生意想不到的错误。第三个参数NVIC_Channel为中断的编号(范围为0~59),最后一个参数NVIC_Group为中断分组设置(范围为0~4)。

/********************设置NVIC**************** *NVIC_PreemptionPriority:抢占优先级 *//NVIC_SubPriority :响应优先级 *NVIC_Channel :中断编号 *NVIC_Group :中断分组 0~4 *注意优先级不能超过设定的组的范围!否则会有意想不到的错误 *组划分: *组0:0 位抢占优先级,4 位响应优先级 *组1:1 位抢占优先级,3 位响应优先级 *组2:2 位抢占优先级,2 位响应优先级 *组3:3 位抢占优先级,1 位响应优先级 *组4:4 位抢占优先级,0 位响应优先级 *NVIC_SubPriority 和NVIC_PreemptionPriority的原则是,数值越小,越优先

************************************/ void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8NVIC_Channel,u8 NVIC_Group) { u32 temp; u8 IPRADDR=NVIC_Channel/4; //每组只能存4个,得到组地址 u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移 IPROFFSET=IPROFFSET*8+4; //得到偏移的确切位置 MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组 temp=NVIC_PreemptionPriority<<(4-NVIC_Group); temp|=NVIC_SubPriority&(0x0f>>NVIC_Group); temp&=0xf;//取低四位

if(NVIC_Channel<32)NVIC->ISER[0]|=1<<NVIC_Channel;//使能中断位(要清除的话,相反操作就OK) else

NVIC->ISER[1]|=1<<(NVIC_Channel-32); NVIC->IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级 }

MY_NVIC_PriorityGroupConfig为NVIC的分组函数,该函数的参数NVIC_Group为要设置的分组号,可选范围为0~4,总共5组:EXTI0、EXTI1、EXTI2、EXTI3、EXTI4为Line0~Line4、EXTI15_10为Line15~Line10、EXTI9_5为Line9~Line5。如果参数非法,将可能导致不可预料的结果。

/***************设置NVIC分组***************** *NVIC_Group:NVIC 分组 0~4总共5 组

************************************/

void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group) { u32 temp,temp1; temp1=(~NVIC_Group)&0x07;//取后三位

temp1<<=8;

temp=SCB->AIRCR; //读取先前的设置

temp&=0X0000F8FF; //清空先前分组

temp|=0X05FA0000; //写入钥匙

temp|=temp1;

SCB->AIRCR=temp; //设置分组 }

MY_NVIC_PriorityGroupConfig 函数设置中断优先级分组的思路:STM32的5 个分组是通过设置SCB->AIRCR的BIT[10:8]来实现的,SCB->AIRCR的修改需要通过在高16位写入0X05FA这个密钥才能修改的,故在设置AIRCR之前,应该把密钥加入到要写入的内容的高16位,以保证能正常的写入AIRCR。在修改AIRCR的时候,我们一般采用读->改->写的步骤,来实现不改变AIRCR原来的其他设置。MY_NVIC_PriorityGroupConfig分组函数在每个系统里面只要设置一次就够了,设置多次,则是以最后的那一次为准。但是只要多次设置的组号都是一样,就没事。否则前面设置的中断会因为后面组的变化优先级会发生改变,这点在使用的时候要特别注意!一个系统代码里面,所有的中断分组都要统一,以上代码对要配置的中断号默认是开启中断的。也就是ISER中的值设置为1了。

四、中断函数

A. 中断函数名的书写有要求,否则会找不到中断入口。

stm32f10x_it.c是专门用来存放中断服务函数的。

在 3.5 库函数之前,在stm32f10x_it.c中就预先写好了个外部中断的函数名称,我们只要将对应的执行过程填充进去就可以。

在3.5库函数中,文件中默认只有几个福安与系统异常的中断服务函数,而且都是空函数,在需要的时候自行编写。但是中断服务函数名是不可以自己定义,中断服务函数的名字必须要与启动文件startup_stm32f10x_hd.s中的中断向量表中定义一致。

B. 在进入中断后我们需要做的动作有 2 部,一次执行中断过程,2 是清除中断挂起标识,因为执行完毕了。如果不清除中断挂起标识,则无法再次进入中断。

例程:

void EXTI0_IRQHandler(void)

{

delayMs(10);

if(((GPIOA->IDR)&0x01)==0)

{

while(((GPIOA->IDR)&0x01)==1);

GPIOA->ODR = ~(GPIOA->ODR&0X02);

EXTI->PR=1;

}

}

这里 EXTI->PR=1,即想中断挂起寄存器中写1,清除中断

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/140280.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档