文章归档友情连接照片地图

stm32 NVIC中断管理实现

分类:电子设计  作者:rming  时间:2012-07-18

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

/* IRQ Channels --------------------------------------------------------------*/
#define WWDG_IRQChannel ((u8)0x00) /* Window WatchDog Interrupt */
#define PVD_IRQChannel ((u8)0x01) /* PVD through EXTI Line detection Interrupt */
#define TAMPER_IRQChannel ((u8)0x02) /* Tamper Interrupt */
#define RTC_IRQChannel ((u8)0x03) /* RTC global Interrupt */
#define FLASH_IRQChannel ((u8)0x04) /* FLASH global Interrupt */
#define RCC_IRQChannel ((u8)0x05) /* RCC global Interrupt */
#define EXTI0_IRQChannel ((u8)0x06) /* EXTI Line0 Interrupt */
#define EXTI1_IRQChannel ((u8)0x07) /* EXTI Line1 Interrupt */
#define EXTI2_IRQChannel ((u8)0x08) /* EXTI Line2 Interrupt */
#define EXTI3_IRQChannel ((u8)0x09) /* EXTI Line3 Interrupt */
#define EXTI4_IRQChannel ((u8)0x0A) /* EXTI Line4 Interrupt */
#define DMA1_Channel1_IRQChannel ((u8)0x0B) /* DMA1 Channel 1 global Interrupt */
#define DMA1_Channel2_IRQChannel ((u8)0x0C) /* DMA1 Channel 2 global Interrupt */
#define DMA1_Channel3_IRQChannel ((u8)0x0D) /* DMA1 Channel 3 global Interrupt */
#define DMA1_Channel4_IRQChannel ((u8)0x0E) /* DMA1 Channel 4 global Interrupt */
#define DMA1_Channel5_IRQChannel ((u8)0x0F) /* DMA1 Channel 5 global Interrupt */
#define DMA1_Channel6_IRQChannel ((u8)0x10) /* DMA1 Channel 6 global Interrupt */
#define DMA1_Channel7_IRQChannel ((u8)0x11) /* DMA1 Channel 7 global Interrupt */
#define ADC1_2_IRQChannel ((u8)0x12) /* ADC1 et ADC2 global Interrupt */
#define USB_HP_CAN_TX_IRQChannel ((u8)0x13) /* USB High Priority or CAN TX Interrupts */
#define USB_LP_CAN_RX0_IRQChannel ((u8)0x14) /* USB Low Priority or CAN RX0 Interrupts */
#define CAN_RX1_IRQChannel ((u8)0x15) /* CAN RX1 Interrupt */
#define CAN_SCE_IRQChannel ((u8)0x16) /* CAN SCE Interrupt */
#define EXTI9_5_IRQChannel ((u8)0x17) /* External Line[9:5] Interrupts */
#define TIM1_BRK_IRQChannel ((u8)0x18) /* TIM1 Break Interrupt */
#define TIM1_UP_IRQChannel ((u8)0x19) /* TIM1 Update Interrupt */
#define TIM1_TRG_COM_IRQChannel ((u8)0x1A) /* TIM1 Trigger and Commutation Interrupt */
#define TIM1_CC_IRQChannel ((u8)0x1B) /* TIM1 Capture Compare Interrupt */
#define TIM2_IRQChannel ((u8)0x1C) /* TIM2 global Interrupt */
#define TIM3_IRQChannel ((u8)0x1D) /* TIM3 global Interrupt */
#define TIM4_IRQChannel ((u8)0x1E) /* TIM4 global Interrupt */
#define I2C1_EV_IRQChannel ((u8)0x1F) /* I2C1 Event Interrupt */
#define I2C1_ER_IRQChannel ((u8)0x20) /* I2C1 Error Interrupt */
#define I2C2_EV_IRQChannel ((u8)0x21) /* I2C2 Event Interrupt */
#define I2C2_ER_IRQChannel ((u8)0x22) /* I2C2 Error Interrupt */
#define SPI1_IRQChannel ((u8)0x23) /* SPI1 global Interrupt */
#define SPI2_IRQChannel ((u8)0x24) /* SPI2 global Interrupt */
#define USART1_IRQChannel ((u8)0x25) /* USART1 global Interrupt */
#define USART2_IRQChannel ((u8)0x26) /* USART2 global Interrupt */
#define USART3_IRQChannel ((u8)0x27) /* USART3 global Interrupt */
#define EXTI15_10_IRQChannel ((u8)0x28) /* External Line[15:10] Interrupts */
#define RTCAlarm_IRQChannel ((u8)0x29) /* RTC Alarm through EXTI Line Interrupt */
#define USBWakeUp_IRQChannel ((u8)0x2A) /* USB WakeUp from suspend through EXTI Line Interrupt */
#define TIM8_BRK_IRQChannel ((u8)0x2B) /* TIM8 Break Interrupt */
#define TIM8_UP_IRQChannel ((u8)0x2C) /* TIM8 Update Interrupt */
#define TIM8_TRG_COM_IRQChannel ((u8)0x2D) /* TIM8 Trigger and Commutation Interrupt */
#define TIM8_CC_IRQChannel ((u8)0x2E) /* TIM8 Capture Compare Interrupt */
#define ADC3_IRQChannel ((u8)0x2F) /* ADC3 global Interrupt */
#define FSMC_IRQChannel ((u8)0x30) /* FSMC global Interrupt */
#define SDIO_IRQChannel ((u8)0x31) /* SDIO global Interrupt */
#define TIM5_IRQChannel ((u8)0x32) /* TIM5 global Interrupt */
#define SPI3_IRQChannel ((u8)0x33) /* SPI3 global Interrupt */
#define UART4_IRQChannel ((u8)0x34) /* UART4 global Interrupt */
#define UART5_IRQChannel ((u8)0x35) /* UART5 global Interrupt */
#define TIM6_IRQChannel ((u8)0x36) /* TIM6 global Interrupt */
#define TIM7_IRQChannel ((u8)0x37) /* TIM7 global Interrupt */
#define DMA2_Channel1_IRQChannel ((u8)0x38) /* DMA2 Channel 1 global Interrupt */
#define DMA2_Channel2_IRQChannel ((u8)0x39) /* DMA2 Channel 2 global Interrupt */
#define DMA2_Channel3_IRQChannel ((u8)0x3A) /* DMA2 Channel 3 global Interrupt */
#define DMA2_Channel4_5_IRQChannel ((u8)0x3B) /* DMA2 Channel 4 and DMA2 Channel 5 global Interrupt */

与NVIC相关的寄存器

typedef struct
{
vu32 ISER[2];
u32 RESERVED0[30];
vu32 ICER[2];
u32 RSERVED1[30];
vu32 ISPR[2];
u32 RESERVED2[30];
vu32 ICPR[2];
u32 RESERVED3[30];
vu32 IABR[2];
u32 RESERVED4[62];
vu32 IPR[15];
} NVIC_TypeDef;

STM32就是在这些寄存器的控制下有序的执行的,下面介绍一下这些寄存器

ISER[2]   中断使能寄存器组 (Interrupt Set-En able Registers)    ,STM32的可屏蔽中断只有60 个,这里用了2 个32 位的寄存器,总共可以表示64 个中断。(ISER[0]的bit0~bit31分别对应中断 0~31。ISER[1] 的bit0~27对应中断32~59 。)

ICER[2]  中断除能寄存器组(Interrupt Clear-Enable Registers) 该寄存器组与ISER 的作用恰好相反,是用来清除某个中断的使能的。对应位的功能和ICER 一样,这里要专门设置一个ICER 来清除中断位,而不是向 ISER 写0 来清除,是因为NVIC的这些寄存器都是写1 有效写 0 是无效的

ISPR[2]  中断挂起控制寄存器组(Interrupt Set-Pending Registers )对应的中断和ISER 是一样的。通过置 1可以将正在进行的中断挂起,而执行同级或更高级别的中断,写0 无效。

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

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

IPR[15]  中断优先级控制的寄存器组(Interrupt Priority Registers) 重要!STM32的中断分组与这个寄存器组密切相关。IPR 寄存器组由15 个32bit的寄存器组成,每个可屏蔽中断占用 8bit ,这样总共可以表示 15*4=60 个可屏蔽中断。刚好和 STM32的可屏蔽中断数相等。IPR[0] 的[31~24],[23~16],[15~8] ,[7~0]分别对应中中断 3~0 ,依次类推,总共对应 60 个外部中断。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是 只用了高4 位。这4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据SCB->AIRCR 中中断分组的设置来决定。

 

IPR中优先级的设置前需要在SCB->AIRCR中进行中断分组,在了解中断分组和优先级设置前,我们先看一下先占优先级、次占优先级解释

 先占优先级的概念等同于51单片机中的中断。假设有两中断先后触发,已经在执行的中断先占优先级如果没有后触发的中断 先占优先级更高,就会先处理先占优先级高的中断。也就是说又有较高的先占优先级的中断可以打断先占优先级较低的中断。这是实现中断嵌套的基础。

    次占优先级只在同一先占优先级的中断同时触发时起作用,先占优先级相同,则优先执行次占优先级较高的中断。次占优先级不会造成中断嵌套。 如果中断的两个优先级都一致,则优先执行位于中断向量表中位置较高的中断。
还需要注意的一点是 这里的中断优先级 高是指  是指是否更接近0级,0级优先级是最高的。

STM32的中断分组

STM32将中断分为5 个组,组0~4 。该分组的设置是由SCB->AIRCR寄存器的 bit10~8来定义的。具体的分配关系如下表所示:

通过这个表,我们就可以清楚的看到组0~4 对应的配置关系,例如组设置为 3 ,那么此时所有的60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低1 位是响应优先级。每个中断,你可以设置抢占优先级为0~7 ,响应优先级为 1 或0 。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。

结合实例说明一下:假定设置中断优先级组为 2,然后设置中断 3(RTC 中断)的抢占优先级为2 ,响应优先级为 1 。中断 6(外部中断 0 )的抢占优先级为 3 ,响应优先级为 0 。中断 7(外部中断1 )的抢占优先级为 2 ,响应优先级为 0 。那么这 3 个中断的优先级顺序为:中断 7>中断3>中断6。

这里需要注意2 点:

1 ,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。

2高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。上面例子中的中断3 和中断7 都可以打断中断6 的中断。而中断 7 和中断3 却不可以相互打断!

下面看一下程序部分

设置NVIC分组

/ 设置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; //设置分组
}

STM32优先级分组的5 个分组是通过设置 SCB->AIRCR 的BIT[10:8]来实现的,SCB->AIRCR 的修改需要通过在高 16 位写入0X05FA 这个密钥才能修改的,故在设置 AIRCR之前,应该把密钥加入到要写入的内容的高 16 位,以保证能正常的写入AIRCR。在修改IRCR的时候,我们一般采用读-> 改-> 写的步骤,来实现不改变AIRCR原来的其他设置。以上就是MY_NVIC_PriorityGroupConfig函数设置中断优先级分组的思路。

NVIC初始化(中断分组、抢占优先级、响应优先级、中断编号)的实现

// 设置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;
u8 IPRADDR=NVIC_Channel/4; //每组只能存 4 个,得到组地址
u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移
IPROFFSET=IPROFFSET*8+4; // 得到偏移的确切位置
MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组
temp=NVIC_PreemptionPriority<>NVIC_Group);
temp&=0xf;//取低四位
if(NVIC_ChannelISER[0]|=1<<NVIC_Channel;//使能中断位( 要清除的话,相反 操作就OK) else NVIC->ISER[1]|=1<IPR[IPRADDR]|=temp<<IPROFFSET;//设置响应优先级和抢断优先级
}

 

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

注意:该函数里面还引用了MY_NVIC_PriorityGroupConfig这个函数来设置分组。其实这个分组函数在每个系统里面只要设置一次就够了,设置多次,则是以最后的那一次为准。但是只要多次设置的组号都是一样,就没事。否则前面设置的中断会因为后面组的变化优先级会发生改变,这点在使用的时候要特别注意!一个系统代码里面,所有的中断分组都要统一!!

通过以上两个函数就实现了对NVIC 的管理和配置。但是外部中断的设置,还需要配置相关寄存器才可以。下面就介绍外部中断的配置和使用。



  1. 志同道合啊 你也学过网页? 有什么问题欢迎到博客留言探讨一下

    1. Rming Rming

      额 不过看上去我更业余些 呵呵 我是机械专业的 不过对机械不太感兴趣 业余做做嵌入式和web

提交评论