欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32F407学习笔记(1)-EXTI中断事件与NVIC寄存器

程序员文章站 2022-06-09 11:18:02
...

EXTI中断实验

1、实验目的:控制按键KEY1、KEY2触发中断,进入中断服务函数使 LED1和LED二分别状态反转。

2、实验步骤:

1、使能中断:即就是配置EXTI(将按键GPIOA_0和GPIOC_13分别接到EXTI的 EXTI_Line0和 EXTI_Line13上)
2、配置中断优先级分组(NVIC_PriorityGroupConfig函数实现)
3、配置NVIC寄存器(主要是配置中断源,本实验中断源是EXTI0_IRQn和 EXTI15_10_IRQn)
4、编写中断服务函数(本实验通过EXTI_GetITStatus获取终端是否产生从而控制LED状态反转)
5、在main函数里调用各个初始化函数

3、EXTI相关知识以及库函数操作

1、STM32F4 的每个 IO 都可以作为外部中断的中断输入口,STM32F407 的中断控制器支持 22个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。
STM32F407 的 22 个外部中断为:

     // EXTI 线 0~15:对应外部 IO 口的输入中断。
     //EXTI 线 16:连接到 PVD 输出。
     //EXTI 线 17:连接到 RTC 闹钟事件。
     // EXTI 线 18:连接到 USB OTG FS 唤醒事件。
     //EXTI 线 19:连接到以太网唤醒事件。
     //EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。
     // EXTI 线 21:连接到 RTC 入侵和时间戳事件。
     //EXTI 线 22:连接到 RTC 唤醒事件

从上面可以看出, STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32就这样设计, GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E, F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、GPIOD.0、 GPIOE.0、 GPIOF.0、 GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。具体的EXTI与GPIO映射关系如下图
STM32F407学习笔记(1)-EXTI中断事件与NVIC寄存器2、初始化EXTI,设置 IO 口与中断线的映射关系
第一步:开启EXTI SYSCFG 时钟,使用库函数RCC_APB2PeriphClockCmd具体参考STM32F4XX中文参考手册

第二步:配置 GPIO 与中断线的映射关系:使用库函数 SYSCFG_EXTILineConfig实现;例如将中断线 0 与 GPIOA 映射起来SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
第三步:初始化EXTI线上中断,设置触发条件等
本实验设置 EXTI_Line0和EXTI_Line13的触发模式(通过EXTI_InitTypeDef定义一个结构体变量设置中断源,中断触发模式,使能中断事件然后赋值然后再由初始化函数EXTI_Init(&EXTI_InitStructure)写入相关寄存器)

4、NVIC分组以及NVIC初始化

CM4内核支持256个中断,其中包含16个内核中断和240个外设中断,并且具有256级的可屏蔽中断设置。STM32F4只使用了内核中断的一部分,例如STM32F4XX/STM32F41XX总共有92个中断
STM32F42XX/STM32F43XX总共有96个中断。STM32F40XX/STM32F41XX的92个中断中,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。

中断管理办法:

1、首先对中断进行分组,组0~4.同时对每个中断设置一个抢占优先级和一个响应优先级值。
2、分组的配置在寄存器SCB->AIRCR[10:8]中设置(参考内核手册),确定分组的时候一般只设置一次(否则会引起混乱),数值越低,优先级越高。具体实现方式是通过分组库函数 NVIC_PriorityGroupConfig实现分组:misc.c文件 里定义的。
3、高抢占优先级可以打断正在进行的低的抢占优先级。在抢占优先级相同的情况下,高响应优先级不能打断低响应优先级。响应优先级同时发生时由高低决定。
4、对于每一个中断,都对应一个中断优先级控制的寄存器,CM4内核中断优先级控制的寄存器组:IP[240](240个8位的中断优先级控制寄存器组,对应每个中断),STM32F4只用到了IP[81]~IP[0],每个IP寄存器的高四位用来设置抢占优先级和响应优先级,低四位没有用到。
5、开始初始化NVIC,即就是定义一个NVIC_InitTypeDef类型的结构体变量,根据上一步使能的中断源,选择中断源以及中断源优先级设置,使能中断通道,将相关的值给结构体变量赋值,再通过NVIC_Init写入相关寄存器。
6、编写中断服务函数,切记中断服务函数名要与向量表里定义的函数名一致,一般把终端服务函数写在stm32f4xx_it.c文件里。然后再由main函数调用,整个流程就完了。

相关代码以及代码分析

//NVIC初始化,以及中断向量优先级分组相关函数
static void NVIC_Configuration(void)
{
	//定义一个NVIC初始化结构体变量
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 配置NVIC为优先级组1 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* 配置中断源:按键1 */
  NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;
  /* 配置抢占优先级:1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 配置子优先级:1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断通道 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
  /* 配置中断源:按键2,其他使用上面相关配置 */  
  NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;
  NVIC_Init(&NVIC_InitStructure);
}

//EXTI中断相关函数:
void EXTI_KEY_Config(void)
{
	//定义两个结构体变量
		GPIO_InitTypeDef GPIO_InitStructure; 
	  EXTI_InitTypeDef EXTI_InitStructure;
	
	/*开启按键GPIO口的时钟*/
	RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);
	
	/* 选择按键1的引脚 */ 
  GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;
  /* 设置引脚为输入模式 */ 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;	    		
  /* 设置引脚不上拉也不下拉 */
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  /* 使用上面的结构体初始化按键 */
  GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure); 
	
	  /* 选择按键2的引脚 ,其他参数和按键1相同*/ 
  GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;  
  /* 其他配置与上面相同 */
  GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);      
	
	/*第一步:初始化EXTI,中断使能*/
	   /*首先要开启系统配置控制寄存器*/
		 /* 使能 SYSCFG 时钟 ,使用GPIO外部中断时必须使能SYSCFG时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	/* 连接 EXTI 中断源 到key1引脚,GPIOA端口第0引脚与EXTI相连 */
  SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,KEY1_INT_EXTI_PINSOURCE);
	
	/* 选择 EXTI 中断源 */
  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);
	
		/* 连接 EXTI 中断源 到key2 引脚 */
  SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,KEY2_INT_EXTI_PINSOURCE);
	 /* 选择 EXTI 中断源 */
  EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  /* 下降沿沿触发 */
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
	
	
	/*第二、三步调用配置中断优先级分组和初始化NVIC*/
	NVIC_Configuration();
	/*编写中断服务函数*/
	
}
//EXTI初始化函数里面的宏定义
#ifndef __EXTI_H
#define	__EXTI_H

#include "stm32f4xx.h"

//引脚定义
/*******************************************************/
#define KEY1_INT_GPIO_PORT                GPIOA
#define KEY1_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define KEY1_INT_GPIO_PIN                 GPIO_Pin_0
#define KEY1_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOA
#define KEY1_INT_EXTI_PINSOURCE           EXTI_PinSource0
#define KEY1_INT_EXTI_LINE                EXTI_Line0
#define KEY1_INT_EXTI_IRQ                 EXTI0_IRQn

#define KEY1_IRQHandler                   EXTI0_IRQHandler

#define KEY2_INT_GPIO_PORT                GPIOC
#define KEY2_INT_GPIO_CLK                 RCC_AHB1Periph_GPIOC
#define KEY2_INT_GPIO_PIN                 GPIO_Pin_13
#define KEY2_INT_EXTI_PORTSOURCE          EXTI_PortSourceGPIOC
#define KEY2_INT_EXTI_PINSOURCE           EXTI_PinSource13
#define KEY2_INT_EXTI_LINE                EXTI_Line13
#define KEY2_INT_EXTI_IRQ                 EXTI15_10_IRQn

#define KEY2_IRQHandler                   EXTI15_10_IRQHandler



void EXTI_Key_Config(void);

#endif /* __EXTI_H */