前言

上一节中没有使用中断,这小节将使用中断,按键按下,触发中断,中断事情在中断服务函数中处理

一、NVIC 嵌套向量中断控制器

NVIC是嵌套向量中断控制器,控制着整个芯片中中断相关功能,是内核里面的一个外设。
手册中的中断向量:分为系统中断和外设中断。红框框出来的是系统中断,有10个。外设中断比较多有60个,图片仅显示一小部分。
在这里插入图片描述

在这里插入图片描述

优先级定义:

原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。优先级被分组成抢占优先级和子优先级。如果多个中断同时相应,抢占优先级会先执行。如果抢占优先级相同,再比较子优先级,如果子优先级相同在比较硬件的中断号,就是上图中Pritorty编号,编号越小优先级越高。

优先级分组如下:

在这里插入图片描述
固件库中NVIC相关的函数

//设置分组优先级
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
//获取分组优先级
uint32_t NVIC_GetPriorityGrouping(void)

//中断使能
void NVIC_EnableIRQ(IRQn_Type IRQn)
//中断失效
void NVIC_DisableIRQ(IRQn_Type IRQn)
//获取中断
uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
//设置中断
void NVIC_SetPendingIRQ(IRQn_Type IRQn)
//清除中断
void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
//设置中断优先级
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
//获取中断优先级
uint32_t NVIC_GetPriority(IRQn_Type IRQn)

中断编程步骤
1)设置外设中断,比如串口收发中断;
2)初始化NVIC_InitTypeDef结构体;

typedef struct
{
  uint8_t NVIC_IRQChannel;//中断源,参考IRQn_Type 
  uint8_t NVIC_IRQChannelPreemptionPriority; //中断优先级 参考NVIC_Priority_Table 
  uint8_t NVIC_IRQChannelSubPriority;//中断子优先级NVIC_Priority_Table 
  FunctionalState NVIC_IRQChannelCmd; //This parameter can be set either to ENABLE or DISABLE   
} NVIC_InitTypeDef;

3)中断服务函数

二、EXIT 外部中断控制器

EXIT(External interrupt/event controller)外部中断/事件控制器。每个中断/事件线都对应一个边沿检测器,可以实现输入信号的上升沿检测和下降沿检测。输入线一般是存在电平变化的信号。EXIT可分为两类,一类是产生中断,另一类是产生事件。中断直接给NVIC,进一步实现中断。中断事件输送给外设电路使用,比如ADC,定时器TIM。

在这里插入图片描述

EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至EXTI15,还有另外七根用于特定的外设事件。输入源与中断/事件的关系如下
在这里插入图片描述

在这里插入图片描述
通过AFIO_EXTICRx配置GPIO线上的外部中断/事件
在这里插入图片描述

EXIT结构体:

typedef struct
{
  uint32_t EXTI_Line;  /* 中断/事件线,EXTI0-EXTI19 combination of @ref EXTI_Lines */
  EXTIMode_TypeDef EXTI_Mode;/* 模式选择@ref EXTIMode_TypeDef */
  EXTITrigger_TypeDef EXTI_Trigger; /* 触发选择,上升沿,下降沿,上升沿和下降沿 @ref EXTIMode_TypeDef */
  FunctionalState EXTI_LineCmd;  /* 是否使能ENABLE or DISABLE */ 
}EXTI_InitTypeDef;

三 、外部中断实验

使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制点灯的任务。按键KEY1对应的GPIO为使用PA0,EXTI对应于EXTI0中断线;NVIC对应于EXTI0_IRQn中断源。

在这里插入图片描述

1) 初始化用来产生中断的GPIO

2)初始化EXTI

3)配置NVIC

4)编写中断服务函数

头文件

#include "stm32f10x.h"

#define KEY1_INT_GPIO_PORT         GPIOA//组 PA
#define KEY1_INT_GPIO_CLK          (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO)//时钟需要开启AFIO 配置中断 APB2
#define KEY1_INT_GPIO_PIN          GPIO_Pin_0//引脚

#define KEY1_INT_EXTI_PORTSOURCE   GPIO_PortSourceGPIOA//端口
#define KEY1_INT_EXTI_PINSOURCE    GPIO_PinSource0//引脚
#define KEY1_INT_EXTI_LINE         EXTI_Line0//中断线
#define KEY1_INT_EXTI_IRQ          EXTI0_IRQn//中断源中断/事件
#define KEY1_IRQHandler            EXTI0_IRQHandler//中断服务函数

配置EXTI, 产生中断的GPIO

void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; 
	EXTI_InitTypeDef EXTI_InitStructure;
	/*时钟设置*/
	RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK,ENABLE);											
	/* NVIC中断*/
	NVIC_Configuration();	
	/*按键使用的GPIO*/
  GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
  /* 浮空 */	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
	/* 选择EXTI信号源 Selects the GPIO pin used as EXTI Line */
  GPIO_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE, KEY1_INT_EXTI_PINSOURCE); 
  /*中断事件*/
  EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;
	/* 中断模式*/
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	/* 触发模式,上升沿*/
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  /* 使能 */	
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
}

外部中断寄存器的配置 GPIO_EXTILineConfig 函数用来指定中断/事件线的输入源,它实际是设定外部中断配置寄存器的 AFIO_EXTICRx 值,该函数接收两个参数,第一个参数指定 GPIO 端口源,第二个参数为选择对应 GPIO 引脚源编号.

配置NVIC

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  /*优先级选择,组1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  /* 中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 主优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /*子优先级*/
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /*使能中断*/
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  //初始化
  NVIC_Init(&NVIC_InitStructure);
}

中断服务函数

void KEY1_IRQHandler(void)
{
  //获取中断线EXTI line状态
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) 
	{
		//翻转LED灯	
		LED1_TOGGLE;
		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
	}  
}