0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

单片机的NVIC与EXTI中断详解

CHANBAEK 来源:无限琢磨 作者: Cai 2023-11-01 12:35 次阅读

01嵌套向量中断控制器——NVIC

NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。控制着整个芯片中断相关的功能,通过对NVIC寄存器进行配置可以实现对内核和片上外设的中断的控制。但是各个芯片厂商在设计芯片的时候会对 Cortex-M4内核里面的 NVIC进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是 Cortex-M4的 NVIC 的一个子集,只是用到了NVIC的一部分功能,其余的保留以后备用。

抢占式优先级(占先式优先级)和响应优先级(子优先级)

抢占优先级(占先式优先级)

抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A)。

通俗的讲,完成某一件事正常是有顺序,先完成事件 A ,再完成事件 B,假如,张三现在在做事件 A ,突然李四叫张三去做事件 B ,那么现在就有一个问题,张三是先完成事件 A ?还是去做事件 B ?这里就需要看看那个事件的优先级了,倘若事件 A ,比事件 B 比较重要,那么先完成事件 A 后再完成事件 B ,假如是事件 B, 比事件 A 比较重要,则张三需要先完成事件 B ,再回来继续完成事件 A ,不管事件 A,有没有完成。

在片内中设置好事件执行的优先级之后,总是高优先级先执行完然后再执行低优先级的。编号越低优先级越高。即 “0”的优先级最高。

响应优先级(子优先级)

响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断。

通俗的讲,同时有事件 A 、事件 B 、事件 C ,其中事件 A 的抢占优先级要高于事件 B 、事件 C ,其次事件 B 、事件 C 的抢占优先级一样,在完成事件 A 之后,假如事件 B 、事件 C 这两件事同时“到达”,那这个时候响应属性的作用就开始发挥了,假如事件 B 的响应属性高于事件 C ,则优先完成事件B。

响应和抢占优先级,有种抢占优先级里面包含着响应优先级的感觉,只不过抢占优先级强调的事,我正在做某一件事,有另外一件事来打断我现在在做的这件。响应优先级则强调的是,同时“到达”,我先处理哪一件事的问题。

NVIC 的定义

在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在 F103 中,只使用了高 4bit,如下所示:

图片

在仅剩的4位中,又包含抢占优先级和响应优先级。理论是会有16个中断源,但是STM32又进行了分组,共分为5组,如下:

图片

在库文件misc.c和misc.h中分别用宏定义定义了 "NVIC_PtiorityGroup"这五组分组源。

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /* 0 bits for pre-emption priority
                                                          4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /* 1 bits for pre-emption priority
                                                          3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /* 2 bits for pre-emption priority
                                                          2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /* 3 bits for pre-emption priority
                                                          1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /* 4 bits for pre-emption priority
                                                          0 bits for subpriority */

通过misc.c文件中定义的 “ **NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) ** ” 函数:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));

  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB- >AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;

图片

配置响应优先级及应用举例

例如配置以下一位抢占优先级,三位子优先级

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//中断优先级分组:2位的抢占,2位的子优先级

在选择完组源之后,就需要对该组的中断源、抢占优先级和响应优先级、配置通道的开启进行配置

通过在 misc.h 中的结构体,进行配置

typedef struct
{
uint8_t NVIC_IRQChannel;//中断源
uint8_t NVIC_IRQChannelPreemptionPriority;//抢占优先级
uint8_t NVIC_IRQChannelSubPriority;//响应优先级
FunctionalState NVIC_IRQChannelCmd;//是否使能
} NVIC_InitTypeDef;

针对每个中断,设置对应的抢占优先级和响应优先级,下面以中断源为 "USART2_IRQn",抢占优先级为1,响应优先级为0,的例子,优先级分组为一。

static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  /* 嵌套向量中断控制器组选择 */

NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;  /* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; /* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  /* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  /* 使能中断 */
 
NVIC_Init(&NVIC_InitStructure); /* 初始化配置NVIC */
}

其中中断源通过在 "stm32f10x.h"中的结构体

typedef enum IRQn
{
  .
  .
  .
  .
  .
}IRQn_Type;

至此,关于NCIV的简单配置就完成了

02外部中断/事件控制器——EXTI

EXTI简介

EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

中断/事件线

EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另外七根用于特定的外设事件。这里得并不是以PA的整个系列作为一个中断/事件线,而是以PA0,PB0,PC0 …… PG0作为一个中断源,如下图所示。

图片

图片

16个中断线的不是每个中断都有独立的中断服务函数,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。

图片

从表可以看出外部中断EXTI5_9共用一个服务函数,外部中断EXTI15_10共用一个服务函数,对应的中断函数在启动文件里面

EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler

因为这里是通过STM32 的引脚,通过映射的方法,让GPIO具有了中断的功能,所以在使用某一个GPIO作为中断的引脚的时候,就需要调动映射服务函数。( “ stm32f10x_gpio.c” 中的 “ GPIO_EXTILineConfig”)

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
  uint32_t tmp = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));


  tmp = ((uint32_t)0x0F) < < (0x04 * (GPIO_PinSource & (uint8_t)0x03));
  AFIO- >EXTICR[GPIO_PinSource > > 0x02] &= ~tmp;
  AFIO- >EXTICR[GPIO_PinSource > > 0x02] |= (((uint32_t)GPIO_PortSource) < < (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}

其中,GPIO_PortSource,通过在 stm32f10x_gpio.h 中的查找响应的指令,如下所示

#define GPIO_PortSourceGPIOA       ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB       ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC       ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD       ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE       ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF       ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG       ((uint8_t)0x06)

同理,GPIO_PinSource ,通过在 stm32f10x_gpio.h 中的查找响应的指令,如下所示

#define GPIO_PinSource0            ((uint8_t)0x00)
#define GPIO_PinSource1            ((uint8_t)0x01)
#define GPIO_PinSource2            ((uint8_t)0x02)
#define GPIO_PinSource3            ((uint8_t)0x03)
#define GPIO_PinSource4            ((uint8_t)0x04)
#define GPIO_PinSource5            ((uint8_t)0x05)
#define GPIO_PinSource6            ((uint8_t)0x06)
#define GPIO_PinSource7            ((uint8_t)0x07)
#define GPIO_PinSource8            ((uint8_t)0x08)
#define GPIO_PinSource9            ((uint8_t)0x09)
#define GPIO_PinSource10           ((uint8_t)0x0A)
#define GPIO_PinSource11           ((uint8_t)0x0B)
#define GPIO_PinSource12           ((uint8_t)0x0C)
#define GPIO_PinSource13           ((uint8_t)0x0D)
#define GPIO_PinSource14           ((uint8_t)0x0E)
#define GPIO_PinSource15           ((uint8_t)0x0F)

EXTI功能框图

图片

输入线通过边沿检测电路,检测是上升沿还是下降沿,至于哪一种触发方式,通过上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)进行相应位置置 “1”。

图片

图片

通过在 “stm32f10x_exti.h”中的结构体进行选择

typedef enum
{
  EXTI_Trigger_Rising = 0x08,//上升沿
  EXTI_Trigger_Falling = 0x0C,  //下降沿
  EXTI_Trigger_Rising_Falling = 0x10//双触发
}EXTITrigger_TypeDef;

在通过或门,分别有两个信号来源,一个是软件中断事件寄存器,允许我们通过程序控制就可以启动中断/事件线,另一个是边沿检测电路,假如我设置的是是上升沿触发,当外部输入输入线是上升沿的时,则边沿检测电路则会输出“1”至或门,再通过或门,有 “1”则输出是 “1”,或门的输出端分别有两个信号源去向,一个是请求挂起寄存器,一个事件屏蔽寄存器。

中断屏蔽寄存器和请求挂起寄存器的与门逻辑运算至NVIC中断寄存器,再去判断优先级等等。

事件屏蔽寄存器和事件屏蔽寄存器的与门逻辑运算至脉冲发生器,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。

事件:是表示检测到某一动作(电平边沿)触发事件发生了。

中断:有某个事件发生并产生中断,并跳转到对应的中断处理程序中。

选择中断线与EXTI 初始化结构体详解

首先选择某一个GPIO作为中断的输入引脚,第一步使能起对应的时钟线,其次是配置起响应的GPIO的功能,如下

GPIO_InitTypeDef GPIO_InitStructure;
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//配置相应的Pin脚
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* 配置为浮空输入 */ 
 GPIO_Init(GPIOA, &GPIO_InitStructure);//配置相应的GPIO

"激活"该引脚之后,就需要将中断的功能赋于这个引脚,即把响应的映射功能使能,同时对该中断的详细内容进行配置.如下

首先找到以下函数进行映射使能

"RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) "

图片

图片

找到"RCC_APB2Periph_AFIO"

图片

码源:

EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO ,ENABLE);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;

/* EXTI为中断模式 */
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
/* 上升沿中断 */
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
/* 使能中断 */  
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);

下面是对应查找的位置

通过查找EXTI相应的设置结构体

图片

EXTI_Line :——>stm32f10x_exti.h

图片

"EXTI为中断模式":

图片

EXTI_Trigger :

图片

最后一步则是选择EXTI的信号源,通过下面该函数,进行配置

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)

以上中断服务函数的初始化就完成了,接下来则是编写中断服务函数

void  EXTI0_IRQHandler (void)
{
    //要执行的内容
    EXTI_ClearITPendingBit( 相应的中断线);//查询相应中断线和EXTI_Line 一样
}

" EXTI0_IRQHandler "在启动文件中查找

图片

void EXTI_ClearITPendingBit( 相应的中断线):

其作用就是清除进入中断时对寄存器某个位置 "1",进行清 "0"

图片

理论完成,进入喜闻乐见的实验环节

以按键触发中断为例子,触发引脚为:PA1

简单的逻辑过程

图片

EXIT.c

#include "exti.h"


static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}


void EXIT_config(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

  GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IN_FLOATING ;
  GPIO_InitStruct.GPIO_Pin= GPIO_Pin_1 ;
  GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz ;
  GPIO_Init(GPIOA,&GPIO_InitStruct);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

  EXTI_InitTypeDef EXTI_InitStruct;

  EXTI_InitStruct.EXTI_Line=EXTI_Line1  ;
  EXTI_InitStruct.EXTI_LineCmd=ENABLE ;
  EXTI_InitStruct.EXTI_Mode= EXTI_Mode_Interrupt;
  EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling;

  NVIC_Configuration();
  EXTI_Init(&EXTI_InitStruct);

}


void  EXTI1_IRQHandler (void)
{
    SysTick_Config_delay_ms(10);
    GPIO_ResetBits(GPIOC, GPIO_Pin_13 );
    EXTI_ClearITPendingBit( EXTI_Line1 );//查询相应中断线和EXTI_Line 一样
}

EXIT.h

#ifndef _EXIT_H
#define _EXIT_H


#include "misc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x.h"
#include "led.h"
#include "systick.h"
#include "stm32f10x_exti.h"




void EXIT_config(void);
static void NVIC_Configuration(void);
#endif

main.c

#include "led.h"
#include "stm32f10x.h"
#include "exti.h"
#include "systick.h"


int main(void)
{
   SysTick_Init();
   LED_GPIO_Config()  ;
   EXIT_config( );
  while(1)
  {  


   }
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6001

    文章

    43973

    浏览量

    620854
  • 寄存器
    +关注

    关注

    30

    文章

    5030

    浏览量

    117734
  • 中断
    +关注

    关注

    5

    文章

    884

    浏览量

    41025
  • NVIC
    +关注

    关注

    0

    文章

    35

    浏览量

    11518
  • EXTI
    +关注

    关注

    0

    文章

    25

    浏览量

    3614
收藏 人收藏

    评论

    相关推荐

    STM32-NVIC中断向量表设置以及EXTI中断寄存器设置

    STM32-NVIC中断向量表设置以及EXTI中断寄存器设置
    发表于 08-22 10:44

    STM32学习之EXTI(外部中断篇)

    _InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);//选择中断优先级分组NVIC_InitStructure.NV
    发表于 07-06 03:38

    STM32CUBEMX的BUG:NVIC里找不到EXTI中断

    建立STM32F373CCT6,把PA1设置为外部中断NVIC里找不到EXTI中断,stm32cubemx版本v4.21.0,f3库版本为v1.8.0
    发表于 12-07 08:51

    STM32F4中断NVIC简介

    目录目录前言STM32F4中断NVIC简介优先级定义NVIC_InitTypeDef结构体EXTI外部中断/事件控制器
    发表于 08-13 06:16

    STM32中断NVIC/EXTI外部中断是什么?

    STM32中断NVIC/EXTI外部中断是什么?
    发表于 11-17 06:22

    51单片机中断详解

    单片机_中断理解51单片机中断详解(上)51单片机中断
    发表于 11-22 06:08

    《振南电子STM32视频教程》第六讲:EXTINVIC的配置和使用

    [第6讲] EXTINVIC的配置和使用(34分钟),由何强主讲.本课主要讲解:(1).STM32中的NVIC的理解;(2).NVIC的寄存器和库函数的理解;(3).STM32中的
    发表于 10-09 15:50 897次阅读

    51单片机中断编程实例详解

    51单片机中断编程实例详解.pdf
    发表于 12-16 15:42 17次下载

    STM32单片机中断NVIC的详细资料概述

    对51单片机有一定了解的都知道51单片机有5个中断源,2个优先级,通过对IP这个寄存器赋值来进行中断优先级的处理而STM32的中断非常强大,
    发表于 07-17 17:39 3次下载
    STM32<b class='flag-5'>单片机</b><b class='flag-5'>中断</b>及<b class='flag-5'>NVIC</b>的详细资料概述

    MCU_关于STM32Fxxx中断EXTI产生时多次(两次)进入中断的原因

    调试新的芯片Stm32F407时,发现和以前的不一样。相同的代码,EXTI中断总是会进入两次,为了验证,我手动在中断中进行了清除,void EXTI0_IRQHandler(void
    发表于 11-18 17:21 11次下载
    MCU_关于STM32Fxxx<b class='flag-5'>中断</b><b class='flag-5'>EXTI</b>产生时多次(两次)进入<b class='flag-5'>中断</b>的原因

    STM32学习笔记(4)——NVIC中断优先级管理和外部中断EXTI

    STM32学习笔记(4)——NVIC中断优先级管理和外部中断EXTI一、NVIC中断优先级管理1
    发表于 11-26 11:36 0次下载
    STM32学习笔记(4)——<b class='flag-5'>NVIC</b><b class='flag-5'>中断</b>优先级管理和外部<b class='flag-5'>中断</b><b class='flag-5'>EXTI</b>

    基于STM32L4的NVIC中断系统

    (如NMI、SYSTICK等),编号16~255被归为外部异常(也称外部中断,如UART、EXTI等),各个异常源与NVIC和Core的关系如下图,对于STM32L4系列MCU,除了16个系统异常外,还支持82个
    发表于 12-04 14:06 12次下载
    基于STM32L4的<b class='flag-5'>NVIC</b><b class='flag-5'>中断</b>系统

    NVIC与外部中断

    步骤RCC,NVIC,GPIO,USART,EXIT配置GPIOA.0 为按键GPIO用作EXTI外部中断时,需要打开AFIO#ifdef
    发表于 12-27 19:30 6次下载
    <b class='flag-5'>NVIC</b>与外部<b class='flag-5'>中断</b>

    STM32F10X的外部中断EXTI)前篇

    STM32F10X的外部中断EXTI)首先,在学习外部中断之前,我们要有一个前要知识,关于NVIC:嵌套向量中断控制器
    发表于 01-14 15:22 3次下载
    STM32F10X的外部<b class='flag-5'>中断</b>(<b class='flag-5'>EXTI</b>)前篇

    【STM32F4教程】第三节:外部中断EXTI)的实现

    中断控制器 NVIC 包含以下特性:中断管理抢占优先级 & 响应优先级区别:NVIC中断优先级分组按键
    发表于 01-14 15:42 4次下载
    【STM32F4教程】第三节:外部<b class='flag-5'>中断</b>(<b class='flag-5'>EXTI</b>)的实现