天天看点

STM32F767--->中断EXTI定时器中断外部中断(EXIT)相关概念附录

文章目录

  • 定时器中断
  • 外部中断(EXIT)
    • 概念与介绍
    • 使用的介绍
    • 使能IO时钟&基本配置
    • 中断优先级(NVIC)
    • 中断服务函数
    • 编写中断处理的回调函数HAL_GPIO_EXTI_Callback
  • 相关概念
    • 事件、中断事件、中断
    • 中断服务函数使用的注意事项
  • 附录
    • 程序代码

定时器中断

外部中断(EXIT)

概念与介绍

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

端口 对应中断
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 唤醒事件。
EXTI 线 23: 连接到 LPTIM1 异步事件

 上表可知,中断线 0-15 对应外部 IO 口的输入中断,一共是 16 个外部中断线。

 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 上了。GPIO 和中断线映射关系是在寄存器 SYSCFG_EXTICR1~ SYSCFG_EXTICR4 中配置的。

使用的介绍

因为STM32的中断非常多,所以必须设置中断的优先级,设置完优先级之后编写中断服务函数,进行中断服务函数的挂载。

中断的使用基本流程为

1)使能 IO 口时钟。

2)调用函数 HAL_GPIO_Init 设置 IO 口模式,触发条件,使能 SYSCFG 时钟以及设置 IO

口与中断线的映射关系。

3)配置中断优先级(NVIC),并使能中断。

4)在中断服务函数中调用外部中断共用入口函数 HAL_GPIO_EXTI_IRQHandler。

5)编写外部中断回调函数 HAL_GPIO_EXTI_Callback 实现控制逻辑。

下文为过程详细介绍

使能IO时钟&基本配置

首先,我们要使用 IO 口作为中断输入,所以我们要使能相应的 IO 口时钟,之后

设置 IO 口模式,触发条件,开启 SYSCFG 时钟,设置 IO 口与中断线的映射关系

__HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟	
GPIO_InitTypeDef  GPIO_Initure;
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //外部中断,上升沿触发
GPIO_Initure.Pull=GPIO_PULLDOWN; //默认下拉
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
           

之后设置中断优先级

中断优先级(NVIC)

STM32有两个优先级概念:抢占式优先级和响应式优先级(亚优先级和副优先级)

 抢占式优先级(优先级):高抢占式的优先级的中断会打断当前主程序及中断程序运行,俗称中断嵌套。

 响应优先级(子优先级):在抢占式优先级相同的情况下,高响应级的中断优先被响应;在抢占式优先级不同的情况下,先判断抢占式优先级的级别,再考虑响应优先级。

如果抢占式优先级和响应优先级都相同,则按默认顺序执行。

HAL_NVIC_SetPriority(EXTI0_IRQn,2,1); //抢占优先级为 2,子优先级为 1
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断线 2
           

中断服务函数

们配置完中断优先级之后,接着要做的就是编写中断服务函数。中断服务函数的名字是

在 HAL 库中事先有定义的。有 7 个,分别为:

void EXTI0_IRQHandler(); 
void EXTI1_IRQHandler(); 
void EXTI2_IRQHandler(); 
void EXTI3_IRQHandler(); 
void EXTI4_IRQHandler(); 
void EXTI9_5_IRQHandler(); 
void EXTI15_10_IRQHandler();
           

中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。一般情况下,我们可以把中断控制逻辑直接编写在中断服务函数中,但是 HAL 库把中断处理过程进行了简单封装,也就是中断处理的回调函数。

//使用如下
//中断服务函数
void EXTI0_IRQHandler(void)
{
	 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); //调用中断处理公用函数
}
           

编写中断处理的回调函数HAL_GPIO_EXTI_Callback

在使用 HAL 库的时候,我们也可以跟使用标准库一样,在中断服务函数中编写控制逻辑。但 是 HAL 库 为 了 用 户 使 用 方 便 , 它 提 供 了 一 个 中 断 通 用 入 口 函 数HAL_GPIO_EXTI_IRQHandler,在该函数内部直接调用回调函数HAL_GPIO_EXTI_Callback。

HAL_GPIO_EXTI_IRQHandler 函数定义:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
    if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
    {
   	    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    	HAL_GPIO_EXTI_Callback(GPIO_Pin);
	}
}
           

该函数实现的作用非常简单,通过入口参数 GPIO_Pin 判断中断来自哪个 IO 口,然后清除相应的中断标志位,最后调用回调函数 HAL_GPIO_EXTI_Callback()实现控制逻辑。所以我们编写中断控制逻辑将跟串口实验类似,在所有的外部中断服务函数中直接调用外部中断共用处理函数 HAL_GPIO_EXTI_IRQHandler,然后在回调函数HAL_GPIO_EXTI_Callback 中通过判断中断是来自哪个 IO 口编写相应的中断服务控制逻辑。

相关概念

事件、中断事件、中断

 事件、中断事件 中断三个概念或术语。这三个概念彼此关联,有时会让人有点混淆或犯迷糊。

 拿一件生活中的事情打比方对上述三个概念做个基本的粗略理解,比如一老师在教室里给学生们上课。课堂上的学生可能做出各种行为动作,比方做笔记、打哈气、翻书包、讲小话等,我们把这些行为统称为事件,其中有些行为老师往往只是视而不见,继续他的上课;而有些行为可能导致老师的上课中止,比方讲小话,并对学生的相关行为予以警告、批评或纠正等,然后继续上课。我们把老师因为学生的某些行为而中止授课,并产生后续动作,之后接着上课的这个过程理解为中断或中断响应。我们把可能导致老师上课中断的学生行为理解为中断事件。

 结合上面的比方,不难理解中断事件是一种可以导致中断发生的事件,中断则是因为中断事件的发生而导致的后续行为过程。事件与中断事件是包含关系,即事件可分为中断事件或非中断事件。而中断事件与中断之间属于前后关联的因果关系,虽有关联,但二者在时序上、行为上并不一样。

中断服务函数使用的注意事项

中断服务函数名是固定的,在启动代码里面已经定下来了。

书写中断服务函数的时候注意的问题:

  1. 中断服务函数名尽量用复制,不要自己写,因为只要你写错一个字母,这个函数就变成普通函数了。
  2. (如果中断服务函数是公共入口)进入到中断服务函数后先要查询是哪种中断
  3. 先清中断标志,然后再做中断处理,不要把清中断标志放在函数的最后。(如果把清除中断标志放在中断服务函数的最后,会出现当发出清中断标志指令后,硬件还没有把相关标志清除掉,程序就已经跳出了中断服务函数,这个时候NVIC又会识别到标志是1,出现重复中断)。—可以清除中断标志命令发出后,等待清除成功再往下执行。
  4. 中断服务函数应该尽量简短,一般是做一些标识,不要在中断中做延时之类的占用CPU很长时间的工作。----快进快出
  5. 中断服务函数不会被任何一个函数调用,当中断条件满足后(在启动文件中有中断服务函数的调用),NVIC控制把CPU拉到中断服务函数中执行。

附录

程序代码

//本程序是通过外部中断实现,四个按键按下从而控制LED灯呈现不同效果的程序
#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"

//中断初始化
void EXTI_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    //几个按键相关的时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();               //开启时钟GPIOA
    __HAL_RCC_GPIOC_CLK_ENABLE();               //开启时钟GPIOC
    __HAL_RCC_GPIOH_CLK_ENABLE();               //开启时钟GPIOH
    
    GPIO_Initure.Pin=GPIO_PIN_0;                //PA0
    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
    GPIO_Initure.Pull=GPIO_PULLDOWN;			//下拉
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);
    
    GPIO_Initure.Pin=GPIO_PIN_13;               //PC13
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发
    GPIO_Initure.Pull=GPIO_PULLUP;				//上拉
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);
    
    GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3 下降沿触发,上拉
    HAL_GPIO_Init(GPIOH,&GPIO_Initure);
    
    //中断线0优先级设置
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //优先级为2,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0
    
    //中断线2优先级设置
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);             
    
    //中断线3优先级设置
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);        
    
    //中断线13优先级设置
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);       
}


//中断服务函数
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);   //调用中断公用处理函数
}

void EXTI2_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);    //调用中断公用处理函数
}

void EXTI3_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);    //调用中断公用处理函数
}

void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);   //调用中断公用处理函数
}

//中断处理公用函数的回调函数
//中断处理公用函数在stm32f7xx_hal_gpio.c中定义,并且调用HAL_GPIO_EXTI_Callback(GPIO_Pin);
//在hal库中所有外部中断服务函数都会调用此函数
//Pram:GPIO_Pin-中断引脚
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    static u8 led0sta=1,led1sta=1;
    delay_ms(50);      //消抖
    switch(GPIO_Pin)
    {
        case GPIO_PIN_0:
            if(WK_UP==1)	//控制LED0 LED1 互斥点亮
            {
                led1sta=!led1sta;
                led0sta=!led1sta;
                LED1(led1sta);
                LED0(led0sta);
            }
            break;
        case GPIO_PIN_2:
            if(KEY1==0) 	//同时控制 LED1
            {
                led1sta=!led1sta;
                LED1(led1sta);	
            };
            break;
        case GPIO_PIN_3:
            if(KEY0==0)  	//同时控制LED0 LED1
            {
                led1sta=!led1sta;
                led0sta=!led0sta;
                LED1(led1sta);
                LED0(led0sta); 
            }
            break;

        case GPIO_PIN_13:
            if(KEY2==0)  	//LED0翻转
            {
                led0sta=!led0sta;
                LED0(led0sta);
            }
            break;
    }
}

           

继续阅读