本文针对CW32L083系列MCU,同系列产品亦可参考。
ARM® Cortex®-M0+ 内核的嵌套向量中断控制器 (NVIC),用于管理中断和异常。NVIC和处理器内核紧密相连,可以实现低延迟的异常和中断处理。处理器支持最多32个中断请求 (IRQ)输入,支持多个内部异常。
**主要特性 **
• 16个内部异常
• 32个可屏蔽外部中断
• 4个可编程的优先级
• 低延时的异常和中断处理
• 支持中断嵌套
• 中断向量表重映射
本文介绍了处理器的 32 个外部中断请求(IRQ0 ~ IRQ31),处理器内部异常的具体情况请参考“ARM® Cortex®-M0+Technical Reference Manual”与“ARM® v6-M Architecture Reference Manual”。
**中断优先级 **
外部中断可设置 4 级优先级,最高优先级为“0”,最低优先级为“3”,默认值为“0”。当处理器正在执行一个中断处理程序时,如果出现一个更高优先级的中断,那么这个中断就被抢占。如果出现的中断的优先级和正在处理的中断的优先级相同或更低,这个中断就不会被抢占,但是新中断的状态就变为挂起。如果多个挂起的中断具有相同的优先级,中断编号越小的挂起中断优先处理。例如,如果IRQ[0]和IRQ[1]均挂起时,并且两者的优先级相同,那么先处理 IRQ[0]。
中断向量表
ARM® Cortex®-M0+ 响应中断时,处理器自动从存储器的中断向量表中取出中断服务程序 ( ISR )的起始地址。中断向量表包括主栈指针(MSP)的初始值,内部异常和外部中断的服务程序入口地址。每个中断向量占用1个字(4 字节),中断向量的存储地址为向量编号乘以4,下面的是CW32L083的中断向量表。
CW32L083由于部分外设的中断复用一个IRQ中断源,在中断服务程序中应先检查中断标志位,以确定产生中断的外设。NMI在CW32L083中未使用。HSE 、LSE 时钟信号起振失败和 LSI、LSE、HSIOSC、HSE、PLL 时钟信号稳定对应 RCC 全局中断。HSE 或 LSE 时钟信号在运行中失效对应 FAULT 中断。
中断寄存器的相关配置
1.中断的使能、挂起、清除挂起
ARM® Cortex-M0+处理器支持最多32个外部中断源,分别对应中断使能设置寄存器NIVC_ISER的32个使能位,和中断使能清除寄存器NVIC_ICER的32个禁止位。将使能位置1,允许中断;将禁止位置1,禁止中断。上文中NVIC中断使能仅针对处理器 NVIC而言,外设的中断是否使能,还受相应外设的中断控制寄存器控制。
而在中断发生的时候,如果系统正在处理相同优先级的或者更高优先级的中断,系统将不会立马的处理这个中断,而是将这个中断的状态设置为挂起,保存在中断挂起状态寄存器中,在处理器未进去此中断处理之前,如果没有手动清除挂起状态,这个状态会一直有效,等处理器进入中断处理的时候,硬件会自动清除相应的中断挂起状态。也可以通过设置中断挂起设置寄存器NVIC_ISPR的对应位,将此中断的状态设置为挂起状态,如果系统没有正在处理与之相同优先级或更高优先级的中断,此中断将被立即响应并处理。可以通过设置中断挂起清除寄存器NVIC_ICPR的对应位,将此中断的状态设置为挂起清除状态。
2.中断的优先级、中断屏蔽
中断优先级控制寄存器NVIC_IPR0 ~ NVIC_IPR7,用于设置IRQ0~IRQ31 的中断优先级,每个中断源使用8位,在CW32L083中仅使用了高两位,最多可设置4个中断优先级。
在某些特殊场合,需要禁止所有中断,可以使用中断屏蔽寄存器PRIMASK实现。PRIMASK只有最低1位有效,将此位置1,除了NMI和硬件错误异常之外的所有外部中断和异常都被禁止;清0后,允许响应中断和异常。该位复位后默认为0。
按键中断检测实验分析
以CW32L083评估板为例,按键连接至CW32L083的PA4、PA5端口,LED连接至PC2、PC3接口。按键按下将产生中断,在中断服务程序中进行LED翻转。
int32_t main(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA的配置时钟
__RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC的配置时钟
//按键GPIO初始化
GPIO_InitStruct.IT = GPIO_IT_RISING | GPIO_IT_FALLING;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pins =GPIO_PIN_4 | GPIO_PIN_5;
GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
//LED的GPIO初始化
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pins =GPIO_PIN_2 | GPIO_PIN_3;
GPIO_Init(CW_GPIOC, &GPIO_InitStruct);
//配置中断滤波
GPIO_ConfigFilter(CW_GPIOA, bv4, GPIO_FLTCLK_RC150K);
//清除PA4、PA5中断标志并使能NVIC
GPIOA_INTFLAG_CLR(bv4| bv5);
NVIC_EnableIRQ(GPIOA_IRQn);
__enable_irq();
//----------------------------------------------------------------------
//相关程序在中断服务GPIOA_IRQHandler中进行处理
while (1)
{
}
}
//GPIOA的中断服务函数
void GPIOA_IRQHandlerCallback(void)
{
if (CW_GPIOA->ISR_f.PIN4)
{
GPIOA_INTFLAG_CLR(bv4);清除PA04的中断标志位
PC03_TOG();
}
if (CW_GPIOA->ISR_f.PIN5)
{
GPIOA_INTFLAG_CLR(bv5);
PC02_TOG();
}
}
由上面代码可以看到,如果按下KEY1(PA04),LED1(PC03)的状态会翻转,如果松开按键,LED1又会翻转一次。在每次执行中断函数的时候,需要清除中断标志位即GPIOA_INTFLAG_CLR(bv4)。