文章目录
- 一、前言
- 二、NVIC中断控制器
- 2.1、NVIC结构体成员
- 2.2、抢占优先级和响应优先级
- 2.3、NVIC的优先级组
- 三、EXTI外部中断
- 四、中断实战
- 4.1、确定连线
- 4.2、配置中断控制端口
- 4.3、配置中断端口
- 4.4、配置中断服务函数
- 4.5、主函数调用
一、前言
单片机无系统执行逻辑:main函数–>while死循环
EXTI(External Interrupt):外部中断,通过GPIO检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后再返回到中断之前的代码中执行。
二、NVIC中断控制器
Cortex内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常和中断,并把他们用一个表管理起来,编号为0 ~ 15的称为内核异常,而16以上的则称为外部中断,这个表称为中断向量表。
这个表可以从《STM32参考手册》找到,但是已办结建议从启动文件startup_stm32f10x_hd.s中查找,因为不同型号的STM32芯片,中断向量表稍微有点区别,在启动文件中,已经有相应芯片可用的全部中断向量。而且在编写中断服务函数时,需要从启动文件中定义的中断向量表查找中断服务函数名。
STM32的中断如此之多,配置起来并不容易,因此我们需要一个强大二方便的中断控制器:NVIC(Nested Vectored Interrupt Controller)
NVIC属于Cortex内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理。
2.1、NVIC结构体成员
对NVIC进行初始化,首先要定义一个NVIC_InitTypeDef
类型的结构体:
结构体成员名称 | 描述 |
---|---|
NVIC_IRQChannel | 需要配置的中断向量 |
NVIC_IRQChannelCmd | 使能或关闭相应中断向量的中断响应,ENABLE or DISABLE |
NVIC_IRQChannelPreemptionPriority | 配置相应中断向量抢占优先级 |
NVIC_IRQChannelSubPriority | 配置相应中断向量的响应优先级 |
2.2、抢占优先级和响应优先级
STM32的中断向量具有两个属性:一个为抢占属性,一个为响应属性
其属性编号越小,表明它的优先级别越高
抢占:打断其它中断的属性
响应:当抢占属性相同的情况下,如果中断同时到达,则优先处理响应优先级高的中断
例如:现在有3个中断向量,如下图所示:
- 如果内核正在执行C的中断服务函数,则它能被抢占优先级更高的中断A打断;
- 由于B和C的抢占优先级相同,所以C不能被B打断;
- 但如果B和C中断是同时到达的,内核就会优先执行响应优先级更高的B中断;
2.3、NVIC的优先级组
在配置优先级的时候,还要注意一个很重要的问题,即中断向量的数量。
NVIC只可以配置16种中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4位的数字来决定;
把这个4位数字的位数分配成抢占优先级部分和响应优先级部分,有5组分配方式:
分配 | 说明 |
---|---|
第0组 | 0位抢占优先级,4位响应优先级,NVIC_PriorityGroup_0 |
第1组 | 1位抢占优先级,3位响应优先级,NVIC_PriorityGroup_1 |
第2组 | 2位抢占优先级,2位响应优先级,NVIC_PriorityGroup_2 |
第3组 | 3位抢占优先级,1位响应优先级,NVIC_PriorityGroup_3 |
第4组 | 4位抢占优先级,0位响应优先级,NVIC_PriorityGroup_4 |
要配置这些优先级组,可以采用库函数:NVIC_PriorityGroupConfig()
,可输入参数为NVIC_PriorityGroup_0 ~ NVIC_PriorityGroup_4
STM32单片机的所有I/O端口都可以配置为EXTI中断模式,用来捕捉外部信号,可以配置为:
- 下降沿中断(
EXTI_Trigger_Falling
) - 上升沿中断(
EXTI_Trigger_Rising
) - 上升下降沿中断(
EXTI_Trigger_Rising_Falling
)
三、EXTI外部中断
STM32的所有GPIO都引入到EXTI外部中断线上,使得所有的GPIO都能作为外部中断的输入源,它们以下图的方式连接到16个外部中断/事件线上:
PAx ~ PGx端口的中断事件都连接到了EXTIx外部中断线上,同一时刻只能响应一个端口的事件触发,不能同时响应所有GPIO端口的事件,但可以分时复用;
EXTI最普通的应用就是:接上一个按键,设置为下降沿触发,用中断来检测按键;
四、中断实战
4.1、确定连线
如上图所示:我将PA1配置为中断端口,将PB0配置为中断控制端口
期望效果:单片机上电,PB0小灯默认亮,按下PA1按钮,进入中断函数,翻转PB0电位,实现开-关小灯
4.2、配置中断控制端口
#include "led.h"
void LED_GPIO_Config(void)
{
//定义一个GPIO_InitTypeDef类型的结构体
GPIO_InitTypeDef GPIO_InitStructure;
//开启GPIOB和GPIOC的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC,ENABLE);
//选择要控制的GPIOC引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
//设置引脚模式:通用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//设置引脚速率:50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//调用库函数,初始化GPIOC13
GPIO_Init(GPIOC,&GPIO_InitStructure);
//选择要控制的GPIOC引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
//设置引脚模式:通用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//设置引脚速率:50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//调用库函数,初始化GPIOC13
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
4.3、配置中断端口
#include "exit.h"
//NVIC初始化
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//设置优先级组为高1位为抢占优先级,低3位为响应优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//配置中断源
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //因为我们使用的是PA1,所示是EXTI1_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC控制器
}
void EXIT_PA1_Config(void)
{
//定义结构体
//-------------------------------------
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
//开启外设时钟
//-------------------------------------
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
//配置NVIC
//-------------------------------------
NVIC_Configuration();
//中断端口GPIO配置(PA1)
//-------------------------------------
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//中断线模式配置
//-------------------------------------
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
4.4、配置中断服务函数
中断服务函数必须定义在stmf10x_it.c
文件中,中断服务函数的名字必须要与启动文件startup_stm32f10x_hd.s
中的中断向量表定义一致
一般来说:
EXTI0
对应的中断函数为EXTI0_IRQChannel
;EXTI1
对应的中断函数为EXTI1_IRQChannel
;EXTI2
对应的中断函数为EXTI2_IRQChannel
;EXTI3
对应的中断函数为EXTI3_IRQChannel
;EXTI4
对应的中断函数为EXTI4_IRQChannel
;EXTI5 -> EXTI9
对应的中断函数为EXTI9_5_IRQHandler
;EXTI10 ->EXTI15
对应的中断函数为EXTI15_10_IRQHandler
;
//定义中断服务函数
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_0,
(BitAction)((1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0))));
EXTI_ClearITPendingBit(EXTI_Line1);
}
}