NVIC
- 在NVIC的中断控制及状态寄存器中,有一个VECTACTIVE位段;特殊功能寄存器ISR,都记录了当前正服务异常的编号。
- 如果一个中断不能立即响应,就称它被“悬起”(pending)。少数fault不允许被悬起,可能是系统当前正在执行一个更高优先级异常的服务例程,或者相关掩蔽位被设置导致异常被除能
- 每个异常源,在被悬起的情况下,都会有一个对应的“悬起状态寄存器”保存其异常请求,直到该异常能执行
-
NVIC寄存器
- NVIC访问地址是0xE000 E000,
- 所有NVIC的中断控制/状态寄存器都只能在特权级下访问
- 但软件触发中断寄存器可以在用户级下访问以产生软件中断
- 所有中断控制/状态寄存器均可以按 半字/字节的方式访问
- 有些中断屏蔽寄存器(特殊功能寄存器)只能通过 MRS/MSR及CPS来访问
-
中断的使能与除能(SETENA/CLRENA)
共240对使能/除能位,每个中断有一对,分布在8对32位寄存器中(0~8 + 0~8 = 16)
欲使能一个中断,需要写1到对应SETENA的位中,写0无效
欲除能一个中断,需要写1到对应CLRENA的位中,写0无效
中断 0 的异常号是 16
-
中断的悬起与解悬(SETPEND/CLRPEND)
当中断正发生时,正在处理同级或高优先级异常时,或被掩蔽,则中断不能立即得到响应,此时中断被悬起,中断的悬起状态可以通过"中断设置悬起寄存器(SETPEND)"和"中断悬起清除寄存器(CLRPEND)"来读取,还可以写来手工悬起中断.
悬起寄存器和解悬寄存器也有8对(8 + 8 = 16)
中断优先级
每个外部中断都有一个对应的优先级寄存器,每个寄存器占8位,最少只使用最高3位
4个相邻的寄存器拼成一个32位寄存器
活动状态(中断激活标志位)
每个外部中断都有一个活动状态位,在处理完ISR的第一条指令后,活动状态被置1,直到ISR返回时才硬件清0, 该寄存器只读
哪怕一个中断被抢占,其活动状态依然为1
STM32的NVIC软件配置程序
ST官方 NVIC控制结构体如下:
ISER[8]:ISER ( Interrupt Set-Enable Registers): 中断使能寄存器组
8个32位寄存器来控制,每个位控制一个中断,使能设置相应ISER位为1
由于STM32F1系列最多68个中断,所以只用到了 ISER[0~2]
ICER[8]:ICER( Interrupt Clear-Enable Registers): 中断除能寄存器组
8个32位寄存器来控制,每个位控制一个中断,除能设置相应ICER位为1
ISPR[8]:ISPR(Interrupt Set-Pending Registers): 中断挂起控制寄存器组
8个32位寄存器来控制,每个位控制一个中断,中断挂起设置相应ISPR位为1
ICPR[8]:ICPR(Interrupt Clear-Pending Registers):中断解挂控制寄存器组
8个32位寄存器来控制,每个位控制一个中断,中断解挂设置相应ICPR位为1
IABR[8]:IABR(Interrupt Active Bit Registers): 中断激活标志位
8个32位寄存器来控制,每个位控制一个中断,只读,当前正在执行的中断被设置成1,执行完后自动清0
IP[240]:IP(Interrupt Priority Registers): 中断优先级控制寄存器组
240个8位的寄存器来控制,STM32F1可屏蔽中断只用到了前68个(IP[67]~IP[0])
每个8位寄存器只用到了高4位( bit[7:4] ), 抢占优先级在前,子优先级在后
优先级各占几个位根据 SCB->AIRCR [10:8] 来决定
第一, 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
第二, 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
第三, 而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断
NVIC分组函数 MY_NVIC_PriorityGroupConfig
//STM32只分了5个组(组0~4)
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; //设置分组
}
NVIC设置函数 MY_NVIC_Init
//设置 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,u8 NVIC_Channel,u8 NVIC_Group)
{
u32 temp;
MY_NVIC_PriorityGroupConfig(NVIC_Group); //设置分组
temp = NVIC_PreemptionPriority << (4-NVIC_Group);
temp |= NVIC_SubPriority & (0x0f>>NVIC_Group);
temp &= 0xf; //取低四位
NVIC->ISER[NVIC_Channel/32] |= (1<<NVIC_Channel%32); //使能中断位
NVIC->IP[NVIC_Channel] |= temp<<4; //设置响应优先级和抢断优先级
}
STM32的外部中断软件程序
IMR: 中断屏蔽寄存器,32位, 只有前19位有效( bit[18:0] )
设置为1开启这个线上的中断,否则关闭该线上的中断
EMR: 事件屏蔽寄存器, 32位, 只有前19位有效( bit[18:0] )
设置为1开启这个线上的事件,否则关闭该线上的事件
RTSR:上升沿触发选择寄存器, 32位, 前19位有效( bit[18:0] )
设置为1允许上升沿触发中断/事件, 否则不允许
FTSR:下降沿触发选择寄存器, 32位, 前19位有效( bit[18:0] )
设置为1允许下降沿触发中断/事件, 否则不允许, 上升沿和下降沿可以同时设置,任意电平触发
SWIER: 软件中断事件寄存器, 32位
未设置IMR和EMR时写1,将会挂起PR的相应位,设置了IMR和EMR时将产生一次中断
SWIER被设置的位将会在PR中对应位清除后清除
PR: 挂起寄存器, 32位
当外部中断线上发生了选择的边沿事件,该寄存器的对应位会被置1
通过对该寄存器写1可以清除该位,以清除中断请求
EXTICR:外部中断复用寄存器, 32位,只用低16位
EXTICR[0] ~ EXTICR[3] 对应 AFIO_EXTICR1~4
外部中断配置 Ex_NVIC_Config
/**
* @brief 外部中断配置函数,只针对GPIOA~G;
* 不包括PVD,RTC和USB唤醒这三个
* 该函数一次只能配置1个IO口,多个IO口,需多次调用
* @param GPIOx:0~6,代表GPIOA~G
* BITx:需要使能的位
* TRIM:触发模式,1,下升沿;2,上降沿;3,任意电平触发
* @retval None
*/
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] &= ~(0x000F<<EXTOFFSET); //清除原来设置!!!
AFIO->EXTICR[EXTADDR] |= GPIOx<<EXTOFFSET; //EXTI.BITx 映射到 GPIOx.BITx
//自动设置
EXTI->IMR |= 1<<BITx; //开启 line BITx 上的中断
if(TRIM&0x01)EXTI->FTSR |=1<< BITx; //line BITx 上事件下降沿触发
if(TRIM&0x02)EXTI->RTSR |=1<< BITx; //line BITx 上事件上升降沿触发}
}