一、外部中断
在上一节我们介绍了STM32f103的嵌套向量中断控制器,其中包括中断的使能、失能、中断优先级分组以及中断优先级配置等内容。
1.1 外部中断/事件控制器
在STM32f103支持的60个可屏蔽中断中,有一些比较特殊的中断:
-
中断编号
13 EXTI0:EXTI线0中断; -
中断编号
14 EXTI1:EXTI线1中断; -
中断编号
15 EXTI2:EXTI线2中断; -
中断编号
16 EXTI3:EXTI线3中断; -
中断编号
17 EXTI4:EXTI线4中断; -
中断编号
30 EXTI9_5:EXTI线[9:5]中断; -
中断编号
47 EXTI15_10:EXTI线[15:10]中断; -
中断编号
8 PVD:连到EXTI的电源电压检测(PVD)中断; -
中断编号
48 RTCAlarm:连到EXTI线16的RTC闹钟时间; -
中断编号
49 USB唤醒:连到EXTI的线17的USB待机唤醒事件; -
对于
STM32F107来说,还有一个连到EXTI线18的以太网唤醒事件;
对于STM32f103来说,有19个能产生事件/中断请求的边沿检测器。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。
1.1.1 EXTI线与IO映射关系4
EXTI线0~15:对应外部IO口的输入中断。STM32F103供给IO口使用的中断线只有16个,但是 STM32F103的IO口 却远远不止16 个,所以STM32把GPIO管脚GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线0~15。
这样子每个中断线对应了最多9个IO口,以线0为例:它对应了GPIOA0、GPIOB0、 GPIOC0、GPIOD0、GPIOE0、GPIOF0和 GPIOG0。而中断线每次只能连接到1个IO口上, 这样就需要通过配置决定对应的中断线配置到哪个GPIO上了。
GPIO和中断线映射关系是在寄存器AFIO_EXTICR1 ~ AFIO_EXTICR4中配置的。
1.1.2 框图
其中按照控制功能划分,共分为4个部分,有两条主线:
- 一条是由输入线到
NVIC中断控制器; - 一条是由输入线到脉冲发生器。
其中:
- 输入线:是线路的信息输入端,它可以通过配置寄存器设置为任何一个
GPIO口,或者是一些外设的事件。输入线一般都是存在电平变化的信号; - 边沿检测电路(标号1):上升沿触发选择寄存器和下降沿触发选择寄存器。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1,就输出有效信号1到标号2部分电路,否则输入无效信号0。边沿跳变的标准在于对两个触发选择寄存器的设置;
- 或门电路(标号2):它的两个信号输入端分别是软件中断事件寄存器和边沿检测电路的输入信号。或门电路只要输入端有信号1,就会输出1,所以就会输出信号1到标号3电路和标号4电路;
- 与门电路(标号3):两个信号输入端分别是中断屏蔽寄存器和标号2电路信号;-
- 如果中断屏蔽寄存器设置为0时,不管从标号2电路输出的信号特性如何,最终标号3电路输出的信号都是0;
- 如果中断屏蔽寄存器设置为1时,最终标号3电路输出的信号才由标号2电路输出信号决定;
- 与门电路(标号4):输入端来自标号2电路以及来自于事件屏蔽寄存器。可以简单的控制事件屏蔽寄存器来实现是否产生事件的目的。标号4电路输出有效信号1就会使脉冲发生器电路产生一个脉冲,而无效信号就不会使其产生脉冲信号。脉冲信号产生可以给其他外设电路使用,例如定时器,模拟数字转换器等,这样的脉冲信号一般用来触发TIM或者ADC开始转换。
1.2 功能说明
1.2.1 硬件中断选择
要产生中断,必须先配置好并使能中断线;
- 根据需要的边沿检测设置2个触发寄存器(
EXTI_RTSR和EXTI_FTSR),同时在中断屏蔽寄存器(EXTI_IMR)的相应位写1允许中断请求; - 配置对应到外部中断控制器(
EXTI)的NVIC中断通道的使能和屏蔽位,使得20个中断线中的请求可以被正确地响应; - 当外部中断线上发生了期待的边沿时,将产生一个中断请求,对应的挂起位(
EXTI_PR)也随之被置1; - 在挂起寄存器(
EXTI_PR)的对应位写1,将清除该中断请求。
1.2.2 硬件事件选择
如果需要产生事件,必须先配置好并使能事件线;
-
根据需要的边沿检测通过设置2个触发寄存器(
EXTI_RTSR和EXTI_FTSR),同时在事件屏蔽寄存器(EXTI_EMR)的相应位写1允许事件请求; -
当事件线上发生了需要的边沿时,将产生一个事件请求脉冲,对应的挂起位(
EXTI_PR)不被置1。
1.2.3 软件中断/事件的选择
通过在软件中断/事件寄存器写1,也可以通过软件产生中断/事件请求;
- 配置
20个中断/事件线屏蔽位(EXTI_IMR,EXTI_EMR); - 设置软件中断寄存器的请求位(
EXTI_SWIER)。
二、EXTI相关寄存器
在前面的介绍中,所涉及的寄存器共有7个:
- 上升沿触发选择寄存器(
EXTI_RTSR); - 下降沿触发选择寄存器(
EXTI_FTSR); - 挂起寄存器(
EXTI_PR); - 软件中断事件寄存器(
EXTI_SWIER); - 中断屏蔽寄存器(
EXTI_IMR); - 事件屏蔽寄存器(
EXTI_EMR); - 外部中断配置寄存器 (
AFIO_EXTICRx)。
2.1 上升沿触发选择寄存器(EXTI_RTSR)
该寄存器主要用于控制输入线进来的输入信号,上升沿时是否在边沿检测电路被检测出,20位共控制20条EXTI线;
2.2 下降沿触发选择寄存器(EXTI_FTSR)
该寄存器主要用于控制输入线进来的输入信号,下升沿时是否在边沿检测电路被检测出,20位共控制20条EXTI线;
2.3 挂起寄存器(EXTI_PR)
该寄存器的作用主要有两个:
- 检测外部中断线上是否发生了选择的边沿事件,如果发生了该位置1,并将信号传递给与门电路,进而进入
NVIC中; - 在该位手动(软件)写入1,可以清除之前中断信号的1,主要作用是进入中断后,清除中断位,防止多次进入中断。
2.4 软件中断事件寄存器(EXTI_SWIER)
2.5 中断屏蔽寄存器(EXTI_IMR)
该寄存器的主要作用只有一个,就是是否允许来自输入线上的中断信号进入NVIC中断控制器。
2.6 事件屏蔽寄存器(EXTI_EMR)
该寄存器的主要作用只有一个,就是是否允许来自输入线线上的事件进入脉冲发生器。
2.7 外部中断配置寄存器 (AFIO_EXTICRx)
AFIO_EXTICR1寄存器配置EXTI0到EXTI3线,包含的外部中断的引脚包括PAx到PGx, x=0到3。
AFIO_EXTICR2寄存器配置EXTI4到EXTI7线;
AFIO_EXTICR3寄存器配置EXTI8到EXTI11线;
AFIO_EXTICR4寄存器配置EXTI12到EXTI15线;
特别注意:配置AFIO寄存器之前需要使能AFIO的时钟。
2.7.1 AFIO_EXTICR1
2.7.2 AFIO_EXTICR2
2.7.3 AFIO_EXTICR3
2.7.4 AFIO_EXTICR4
三、外部中断源码
3.1 外部中断的初始化步骤
EXTI中断配置流程如下:
(1) GPIO口初始化;
- 外设时钟配置:通过配置
RCC_APB2ENR寄存器使能GPIO时钟; GPIO配置:设置GPIO工作模式为上拉/下拉输入模式、浮空输入模式,即配置GPIOx_CRL、GPIOx_CRH寄存器;
(2) EXTI外部中断配置;
AFIO时钟配置:通过配置RCC_APB2ENR寄存器使能AFIO时钟;- 设置
GPIO与EXTI映射关系:通过AFIO_EXTICRx寄存器配置IO对应的EXTI输入线; - 设置
EXTI屏蔽、上升沿、下降沿:即设置EXTI_EMR、EXTI_RTSR、EXTI_FTSR寄存器;
(3) 设置NVIC;
- 参考《
STM32F103嵌套向量中断控制器》:设置中断优先级分组、设置响应优先级和抢断优先级、使能相应中断位;
(34) 中断处理函数;
- 设置中断服务函数(包括清除中断标志)。
3.2 源码实现
3.2.1 GPIO口初始化
首先调用gpio_init函数进行GPIO初始化,具体参考《STM32F103 GPIO和串口配置》。
比如配置GPIOC5为上拉输入:
gpio_init(PC5,GPI_UP,HIGH); // PC5接按键KEY0 3.2.2 外部中断配置函数
typedef enum //外部中断触发方式
{FALLING = 0x01,RISING = 0x02,LEVEL = 0x03
}TRIGGER_MODE;/********************************************************************************************************* * Description:外部中断配置函数* 决定中断线配置到哪个GPIO口 * AFIO->EXTICR[0~3] 每个寄存器的低16位每4位决定1路中断线 * 0000:PAx 0001:PBx 0010:PCx0011:PDx 0100:PEx 0101:PFx* Parameter:portx_pinx形如PA0,PA1................TRIGGER_Mode: 0x01 下降沿触发 0x02 上升沿触发0x03 任意电平触发* Example : Ex_NVIC_Congig(PA8,0x01); PA8下降沿触发 *********************************************************************************************************/void Ex_NVIC_Congig(PORTx_PINx portx_pinx,TRIGGER_MODE trigger_mode){u8 address; //外部中断触发寄存器编号选择u8 offset; //外部中断触发寄存器偏移位选择u8 portx=portx_pinx/16; //端口号u8 pinx=portx_pinx%16; //引脚号 address = pinx/4;offset = (pinx%4)*4;RCC->APB2ENR |= 1<<0; //使能I/O复用时钟AFIO->EXTICR[address]|=portx<<offset; //EXTI.BITx映射到GPIOx.BITx
// EXTI->PR |= 1<<pinx; //清除线x上的中断标志位 EXTI->IMR |= 1<<pinx; //开放来自线x上的中断请求EXTI->EMR |= 1<<pinx; //开放来自线x上的事件请求if(trigger_mode==0x01)EXTI->FTSR |= 1<<pinx; //设置线x上下降沿触发if(trigger_mode==0x02)EXTI->RTSR |= 1<<pinx; //设置线x上上降沿触发} 比如配置GPIOC5为下降沿触发:
Ex_NVIC_Congig(PC5,FALLING); // 按键KEY0按下触发 高电平->低电平 3.2.3 NVIC中断优先级初始化
调用STM32_NVIC_Init函数初始化PC5中断优先级,具体参考《STM32F103嵌套向量中断控制器》;
GPIOC5对应的EXTI线[9:5],这里设置EXTI9_5_IRQn中断优先级分组2,抢占优先级为2,响应优先级为2;
STM32_NVIC_Init(2,EXTI9_5_IRQn,2,2); 3.2.4 中断处理函数
这里依然以EXTI9_5_IRQn中断为例,对应的中断处理函数为EXTI9_5_IRQHandler;
/*********************************************************************************************************************
* Function Name : EXTI9_5_IRQHandler
* Description : This function handles External lines 9 to 5 interrupt request.
* Input : None
* Output : None
* Return : None
*************************************************************************************************************************/
void EXTI9_5_IRQHandler(void)
{if( EXTI->PR & 1<<5 ) //来自中断线5上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<5; //清中断线5上的中断标志} if( EXTI->PR & 1<<6 ) //来自中断线6上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<6; //清中断线6上的中断标志}if( EXTI->PR & 1<<7 ) //来自中断线7上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<7; //清中断线7上的中断标志 }if( EXTI->PR & 1<<8 ) //来自中断线8上的中断{//**********************自定义用户任务****************************//usart_sendStr(USART_1, "坏人");//*****************************************************************//EXTI->PR = 1<<8; //清中断线8上的中断标志 }if( EXTI->PR & 1<<9 ) //来自中断线9上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<9; //清中断线9上的中断标志 }
} 3.3 实现功能
这里我们就利用手头开发板的独立按键kKEY0来控制LED1灯的亮灭;
其中:
KEY0连接STM32f103的GPIOC5,按键无上拉电阻,需要在GPIOC5上设为上拉状态,提供一个确定电平;- 按键按下时,
GPIOC5输出低电平; - 当按键未按下时,
GPIOC5应为高电平;
- 按键按下时,
LED1连接STM32f103的GPIOA8引脚,低电平点亮;
我们要实现的功能也很简单:即按下KEY0点亮LED1,源码实现包括以下两个步骤;
- 配置
GPIOA8通用推挽输出,最大速度50MHz; - 配置
GPIOC对应的EXTI线[9:5]外部中断,下降沿触发;
3.3.1 main函数实现
int main()
{ u32 i=0;STM32_Clock_Init(9); //系统时钟初始化 while(RTC_Init()); //RTC初始化 STM32_NVIC_Init(2,USART1_IRQn,0,1); //串口中断优先级初始化,其中包括中断使能usart_init(USART_1,115200); //串口1初始化,波特率115200 映射到PA9 PA10// LED1初始化gpio_init(PA8,GPO_SpeedMax_50,HIGH); //PA8接入LED1 // 按键KEY0初始化 gpio_init(PC5,GPI_UP,HIGH); //PC5接按键KEY0Ex_NVIC_Congig(PC5,FALLING); //按键KEY0按下触发 高电平->低电平STM32_NVIC_Init(2,EXTI9_5_IRQn,2,2); //EXTI线[9:5]中断优先级初始化,其中包括中断使能while(1){ delay_ms(100);PAout(8) = 1; // 熄灭LED1}
} 3.3.2 EXTI9_5_IRQHandler函数实现
/*********************************************************************************************************************
* Function Name : EXTI9_5_IRQHandler
* Description : This function handles External lines 9 to 5 interrupt request.
* Input : None
* Output : None
* Return : None
*************************************************************************************************************************/
void EXTI9_5_IRQHandler(void)
{if( EXTI->PR & 1<<5 ) //来自中断线5上的中断{//**********************自定义用户任务****************************//PAout(8) = 0; // 点亮LED1//*****************************************************************//EXTI->PR = 1<<5; //清中断线5上的中断标志} if( EXTI->PR & 1<<6 ) //来自中断线6上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<6; //清中断线6上的中断标志}if( EXTI->PR & 1<<7 ) //来自中断线7上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<7; //清中断线7上的中断标志 }if( EXTI->PR & 1<<8 ) //来自中断线8上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<8; //清中断线8上的中断标志 }if( EXTI->PR & 1<<9 ) //来自中断线9上的中断{//**********************自定义用户任务****************************////*****************************************************************//EXTI->PR = 1<<9; //清中断线9上的中断标志 }} 四、源码下载
源码下载路径:stm32f103。
参考文章
[1] Mini2440裸机开发之中断控制器
[2] STM32--中断使用
[3] 《STM32F10xxx Cortex-M3编程手册》