文章目录
一、中断的相关概念
1. 中断
单片机执行主程序时,由于某个事件的原因,暂停主程序的执行,调用相应的程序处理该事件,处理完毕后再自动继续执行主程序的过程。
2. 中断优先级
由中断的嵌套可以看出,不同时间的重要程度不同。重要的事件可以打断不重要的事件的处理,用户可以根据自己的需求对不同事件设定重要级别,称为中断的优先级。
3. 中断嵌套
如果在执行一个中断时又被另一个更重要的中断事件打断,暂停该中断处理过程转去处理更重要的中断事件,处理完毕后再继续处理本中断过程,叫做中断的嵌套。中断嵌套有两个基本规则:
(1)低优先级的中断服务可以被高优先级中断服务中断,反之则不能。
(2)任何一种中断,一旦得到响应,不会被它的同等级别的中断源的请求所中断。
4. 中断源
可以引起中断的事件称为中断源。
5. 中断服务程序于中断向量
为了处理中断而编写的程序为中断服务程序,对应中断服务程序的入口地址成为中断向量。
6. 中断请求、中断响应、中断处理及中断返回
中断源对主程序或中断服务程序提出中断请求,叫作中断请求;主程序或中断服务程序接受中断请求,进入中断服务的程序叫作中断响应;执行中断服务程序的过程叫作中断处理;中断服务程序执行完毕后回到主程序或者次一级中断服务程序的过程叫作中断返回。
二、中断系统的组成
1. 中断源
STM32F103中断系统提供10个系统异常和60个可屏蔽中断源,具有16个中断优先级。可屏蔽中断源包括外部中断和定时器中断、串口中断、直接内存访中断、模数转换中断、串行外设接口中断等外设中断。其中,外部中断有嵌套中断向量控制器NVIC和外部中断/事件控制器EXTI控制。外设中断有NVIC和相应外设控制。
2. 中断向量
下图为截取的部分中断向量表的内容,了解即可,如使用到那部分中断可以查找相关的数据手册即可。
3. 中断控制器
STM32F103的中断系统由嵌套中断向量控制器(NVIC)、外部中断/事件控制器(EXTI)和各外设中断控制部分构成。
(1)嵌套中断向量控制器NVIC
NVIC是一个在Cortex-M3中内建的中断控制器,包括众多控制寄存器,支持68个可屏蔽中断。提供16个可编程的优先级,支持中断嵌套,提供向量中断处理机制等功能。中断发生时,自动获得服务历程入口地址并直接调用,无需软件判定中断源,缩短了延时时间。
(2)外部中断/事件控制器EXTI
外部中断/事件控制器由19个产生事件/中断要求的边沿检测器组成。每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的事件触发方式(上升沿或下降沿或者双边沿都触发)。每个输入线都可以被独立地屏蔽,由挂起寄存器保持状态线的中断请求。下图为外部中断线或外部事件线的信号中断结构图:
EXTI控制器其主要特征如下:
① 每个中断/事件都有独立的触发和屏蔽。
② 每个中断线都有专用的状态为。
③ 支持多达19个中断/事件请求。
④ 检测脉冲宽度低于APB2时钟宽度的外部信号。
三、相关寄存器
1. 中断屏蔽寄存器—EXTI_IMR
该寄存器用来设置是否屏蔽中断请求线上的中断请求。
2. 事件屏蔽寄存器—EXTI_EMR
该寄存器用来设置是否屏蔽事件请求线上的中断请求。
3. 上身沿触发选择寄存器—EXTI_RTSR
该寄存器用来设置是否用上升沿来触发中断和事件。
4. 下降沿触发选择寄存器—EXTI_FTSR
该寄存器用来设置是否用下降沿来触发中断和事件,和EXTI_RTSR类似。
5. 软件中断事件寄存器—EXTI_SWIER
该寄存器用来软件触发中断/事件。
6. 中断挂起寄存器—EXTI_PR
该寄存器用来保存中断/事件请求线上是否有请求。
四、中断控制、及执行过程和中断嵌套
4.1 中断控制
1 中断屏蔽控制
中断屏蔽控制包括NVIC控制和外设中断控制。NVIC为中断总开关,由中断设置允许寄存器(NVIC_ISER)、中断清除允许寄存器(NVIC_ICER)、中断设置挂起寄存器(NVIC_ISPR)、中断清除挂起寄存器(NVIC_ICPR)和中断状态寄存器(NVIC_IABR)控制。
除GPIO由EXTI控制中断外,其他外设均有自己的中断屏蔽控制寄存器,如定时器中断由DMA/中断使能寄存器(TIM_DIER)控制等。
2 中断优先级控制
STM32的中断向量有两个属性,即抢占优先级和响应优先级属性,属性编号越小,优先级越高。抢占是指打断其他中断的属性,即中断嵌套。
其中断优先级由中断优先级寄存器组IPR控制。这个寄存器组包含15个32位寄存器,一个可屏蔽中断占用8位,因此一个寄存器可以控制4个可屏蔽中断(32除以8),以共15×4 = 60.在这占用的8位中只使用了高4位,可分为5组,即0,1,,2,3,4五个组,5组分配决定了STM32F103系列控制器中断优先级的分配。分配关系如下表;
组 | 分配结果 |
---|---|
0 | 0位抢占优先级,4位响应优先级 |
1 | 1位抢占优先级,3位响应优先级 |
2 | 2位抢占优先级,2位响应优先级 |
3 | 3位抢占优先级,1位响应优先级 |
4 | 4位抢占优先级,0位响应优先级 |
4.2 中断执行过程和嵌套
中断处理的整个过程包括中断请求、中断响应、中断处理和中断返回。
1. 中断请求优先级
如果系统中存在多个中断源,处理器要先对当前中断的优先级进行判断。当多个中断请求同时到达时,先响应优先级高的中断。有多个中断同时到达,如果 它们的抢占优先级相同,则先处理响应优先级高的中断。
由于具有两个优先级,系统在中断处理中可以实现中断嵌套,即中断系统正在执行一个中断服务时,另一个抢占优先级更高的中断请求,这时会暂停终止当前执行的中断服务区处理抢占优先级更高的中断,处理完毕后返回被中断的中断服务中继续执行。
2. 中断响应
在中断事件产生后,处理器响应中断要满足下列条件:
(1)无同级或高级中断正在服务。
(2)当前指令周期结束,如果查询中断请求的机器周期不是当前指令的最后一个周期,则无法执行当前中断请求。
(3)若处理器正在执行系统指令,则需要执行到当前指令及下一条指令才能响应中断请求。
3. 中断处理
中断处理就是执行中断服务程序,从中断入口地址开始执行直到返回指令为止,具体的执行过程包括3部分内容:
(1)中断现场保护。
(2)处理中断源的请求。
(3)恢复中断现场。
4. 中断返回
中断返回是指中断服务完成后,处理器返回到原来程序断点继续执行原来程序。
五、 外部中断常用库函数
5.1 NVIC相关库函数
- 函数NVIC_PriorityGroupConfig: 设置优先级分组,抢占优先级和响应优先级。
该函数使用方法如下:
// 设置优先级分组为第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- 函数NVIC_Init:根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器。
NVIC_InitStruct结构体:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占
uint8_t NVIC_IRQChannelSubPriority; // 响应
FunctionalState NVIC_IRQChannelCmd;
}NVIC_InitTypeDef;
5.2 EXTI相关库函数
- 函数EXTI_Init:根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器。
EXTI_InitStruct结构体:
typedef struct
{
uint8_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXYIrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}
- 函数EXTI_ClearFlag:清除EXTI线路挂起标志位。
该函数使用方法如下:
// 清除EXTI_Line2挂起标志位
EXTI_ClearFlag(EXTI_Line2);
- 函数EXTI_GetITStatus:检查指定的EXTI线路触发请求发生与否。
该函数的使用方法如下:
// 得到EXTI_Line8的状态
ITStatus EXTIStatus;
EXTIStatus = EXTI_GetITStatus(EXTI_Line8);
- 函数EXTI_ClearITPendingBit:清除EXTI线路挂起位
该函数使用方法如下:
// 清除EXTI_Line2线路挂起位
EXTI_ClearITPendingBit(EXTI_Line2);
六、 中断实例-按键中断控制LED灯闪烁
实例介绍:通过检测按键是否被按下,如果按键被按键则进入中断,LED灯闪烁一次。
(注释:LED相关函数在前一章有)
- KeyDriver.h
#ifndef _KEYDRIVER_H_
#define _KEYDRIVER_H_
#include "stm32f10x.h"
#define GPIO_KEY_PORT GPIOB
#define GPIO_KEY0_PIN GPIO_Pin_6
#define KEY0_DOWN 1
#define KEY0_UP 2
void KeyDriver_Init(void);
uint8_t Key_Scan(void);
#endif
- KeyDriver.c
#include "KeyDriver.h"
void KeyDriver_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
// Key ===> PB6
GPIO_InitStructure.GPIO_Pin = GPIO_KEY0_PIN | GPIO_KEY1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_KEY_PORT, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6); // 选择中断管脚
EXTI_InitStructure.EXTI_Line = EXTI_Line6; // 选择中断线路
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 设置中断请求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 设置中断触发方式:下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 中断使能
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
- 在stm32f103_it.c文件中编辑中断函数
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line6) != RESET)
{
// LED取反
GPIO_WriteBit(GPIOC,GPIO_Pin13,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13))));
}
EXTI_ClearITPendingBit(EXTI_Line6); // 清除中断标志位
}