芯嵌STM32:对STM32中断理解的补充
初学者看到中断,头就大了。很多朋友学习51单片机,都学在了表面,不敢深入的其中一个原因就是惧怕、抵触中断、串口之类。只是,这是走技术必经之路。
所有CPU的中断,一般都要处理两个方面的内容。
首先,要从CPU芯片层面来看,中断需要对应的向量表管理和中断优先级的选择,大家可以思考这样的场景,在某一个时刻,内部定时器中断正在运行,突然,来了一个更加紧急的外部IO引脚中断,告知一个紧急情况。那就必须暂停定时器中断,先执行IO引脚中断。这就是外部IO引脚中断优先级比定时器中断优先级高导致的。这在stm32中是通过NVIC(中断控制器)来实现。
其次,对于某个片内外设,或者内核的中断,为了让它工作,必须先让它先进入准备状态,然后在特定条件下触发它。这就涉及两个问题:1)让它随时处于准备状态——这就告知我们需要先初始化它;2)在特定条件下触发中断——这个就看外部给不给力。
以上,我们只是让大家稍微理解下中断的配置。下面,我们开始讲解stm32的中断。
针对STM32F103XX,总共有60个可屏蔽的中断通道,16个可编程的优先等级。注意,后者就是刚才讲的第一个方面的内容,即NVIC(中断控制器)实现的部分。前者是刚才讲的第二个方面内容,也就是平时所说的外部中断(通道)。
先看NVIC。这部分是有专门的寄存器管理的。最重要的一个寄存器是IPR[15],它的功能是,将众多的中断通道进行优先等级划分。每个中断使用4bit位来控制自己的优先级别。这种优先级别又分为抢占优先级和子优先级两种。通俗点说,每个正在使用的中断,都要设置其抢占优先级和子优先级,前者优先于后者。并且其数字越小,优先级别越高。举例:
假设一个外部中断,其抢占优先级为2,子优先级为2;定时器中断,其抢占优先级为1,子优先级为2。那么先比较抢占优先级,外部中断的抢占优先级2大于定时器的抢占优先级1,因此定时器中断会优先执行。如果抢占优先级别一样,就看子优先级。
以上这种机制怎么实现呢?STM32内部有一个SCB->AIRCR寄存器,将抢占优先级和子优先级进行了分组,分5组,如下所示:
现在,我们设置优先级的时候,可以先设置组别。比如设置第2组,那么,对应的,一共有2个抢占优先级别可选,有2个子优先级可选。而设置第几个分组,由函数来实现:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
知道分组后,再进行具体的优先级设定,比如抢占优先级设为2,子优先级设为2,可按如下方法:
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
以上就是NVIC的设置。当然,具体程序实现时,还需要确定是哪个通道要中断,并且使能它。
下面来看某一个中断通道(比如外部中断)的中断设置。
在STM32中,每个IO口都可以作为一个外部中断入口。每个GPIO有16个,就有16个外部中断入口。。。而一个STM32有GPIOA,GPIOB, GPIOC,。。。好多。。。那要设置无数个。。。??不是的。STM32只设置了16个,即只有0-15个中断线,所有的GPIO共用,如下表所示:
原来,只要是第0个IO引脚的中断线,GPIOA/GPIOB/GPIOC….都共用它。同理,第1个IO引脚,第2个IO引脚。。。第15个IO引脚,都共用。。。这样,stm32就只需要19个外部中断线:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到PVD输出。
线 17:连接到RTC闹钟事件。
线 18:连接到USB唤醒事件。
那如果我想设置GPIOA.3这个引脚作为外部中断入口,怎么设置呢?很简单,我们先选中这个引脚,然后配置这个引脚的中断属性。选中引脚,使用函数:
GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_tGPIO_PinSource);
比如要选中GPIOA.3,只要:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);
中断属性设置呢?如下:
EXTI_InitStructure.EXTI_Line = EXTI_Line03; //GPIOA第3引脚通道
EXTI_InitStructure.EXTI_Mode =EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling; //下降沿触发
讲到这里,大家应该对中断有所了解了。具体理解还是要实践操作编程才可以。编程时候需要注意的是,如果同时想设置两个以上的外部IO引脚作为中断入口,那么程序初始化时,一定要分开写。不能这样写:
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
而应该分开写:
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
……
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
……
如果你对中断的理解还有不明白的地方,请直接到我们的网站:www.51stm32.com ,进行讨论。我们将会尽快给您做出答复。