NVIC与EXTI外设详解

1、NVIC(嵌套向量中断控制器)简介

NVIC控制着整个芯片中断相关的功能,它跟内核紧密耦合。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。

NVIC属于内核外设,MCU组成分为:内核(ARM设计)、片上外设(芯片制造商设计)。如下图
请添加图片描述
NVIC部分标准库函数(由ARM提供,符合CMSIS 标准):
请添加图片描述

2、EXTI(外部中断/事件控制器)简介

EXTI是STM32单片机上的一个外设,控制着单片机上的中断/事件的响应。STM32f1 有19个能产生事件/中断请求的边沿检测器(互联型有20个,比如f105/f107等)。 每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿或者双边沿都触发)。每个输入线都可以独立地被屏蔽。挂起寄存器保持着状态线的中断请求。

F103EXTI中断映射线路框图:
请添加图片描述

3、EXTI 工作流程框图

请添加图片描述EXTI 与NVIC有什么关系?
从上图中可以看出来,当MCU片上外设产生一个中断后,就会输入到NVIC控制器中,去控制中断的响应顺序。
请添加图片描述
请求挂起寄存器发生触发请求后,一定要手动清0,不然会一直进入中断。

4、抢占优先级与响应优先级

请添加图片描述
概括:通过优先级分组,管理中断的响应顺序。高优先级的抢占可以的打断低优先级的中断,如果是同优先级的中断,则会挂起,等现有中断执行完,再根据响应优先级来决定是谁先执行,如果同响应优先级,那么就根据IRQ编号决定先执行。

5、代码流程图
5.1、STM32中断优先级配置流程

请添加图片描述中断优先级配置流程
1、调用HAL设置优先级分组函数HAL_NVIC_SetPriorityGrouping()
2、 配置中断线优先级,HAL_NVIC_SetPriority();
3、 在HAL_NVIC_SetPriority()中;调用NVIC库函数配置中断优先级。NVIC_SetPriority(IRQn,NVIC_EncodePriority());
4、 在NVIC_EncodePriority()里面糅合了中断优先级分组的概念
5、使能中断线 HAL_NVIC_EnableIRQ();

5.2、中断触发到中断服务函数的流程

请添加图片描述
MCU触发了一个中断
1、执行指针就会跳转到中断向量表中
2、 找到对应的中断偏移地址
3、偏移地址:就是中断服务函数的入口 EXTI4_IRQHandler
4、 最终会在HAL_GPIO_EXTI_IRQHandler()里面调用callback函数,并清除PR寄存器

5.3、外设初始化流程图

请添加图片描述
EXTI配置步骤
1、设置中断优先级分组
HAL_NVIC_SetPriorityGrouping();
2、初始化GPIO
1)、使能GPIO时钟
2)、配置GPIO 参数。引脚号,模式,上下拉
3)、初始化 HAL_GPIO_Init(端口号,结构体地址);
3、设置中断线优先级,是能中断线
1)、HAL_NVIC_SetPriority();中断线优先级
2)、HAL_NVIC_EnableIRQ();使能中断线
4、编写中断服务函数
EXTI3_IRQHandler();中断服务函数

6、Cube MX 配置

请添加图片描述

7、示例实验代码

实验目的:利用外部中断方式检测按键输入,并在中断服务函数里面翻转LED电平,每检测到一次按键输入,LED电平翻转一次。

硬件原理图:
请添加图片描述
示例代码:

led.h文件

#ifndef __LED_H
#define __LED_H
#include "stm32f1xx.h"

#define  LED1_PORT  GPIOE
#define  LED1_PIN   GPIO_PIN_5

#define  LED0_PORT  GPIOB
#define  LED0_PIN   GPIO_PIN_5

void LED_Init(void);

#endif

led.c文件

#include "led.h"


/**
  * @brief  初始化GPIO
  * @param  无
  * @retval 无
  */
void LED_Init(void)
{
      //使能端口时钟,在hal_rcc里面找
      __HAL_RCC_GPIOB_CLK_ENABLE();
      __HAL_RCC_GPIOE_CLK_ENABLE();
    
      //创建一个 GPIO_InitTypeDef类型的结构体
        GPIO_InitTypeDef GPIO_InitStruct;  
      
    
       //配置结构体成员参数:
       //模式:输出推挽
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
       //Pin
      GPIO_InitStruct.Pin  = LED1_PIN;
       //上下拉
      GPIO_InitStruct.Pull = GPIO_NOPULL;
       //输出速度 2M ---------/10M/50M
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
      //调用库函数,使用上面配置的结构体成员参数,初始化端口IO
        HAL_GPIO_Init(LED1_PORT,&GPIO_InitStruct);
    
    
        //LED0端口的其他配置相同,也是用的同一个结构体,\
        //不用改的参数在上一步还有保持在 GPIO_InitStruct的堆栈中。(不确定是不是堆栈?)
        GPIO_InitStruct.Pin  = LED0_PIN;
        //初始化化端口IO
        HAL_GPIO_Init(LED0_PORT,&GPIO_InitStruct);
        
        //不可以这样写,是同一个寄存器才可以这样写,现在GPIO_TypeDef *GPIOx 是一个指针,\
        //原本是去地址40011800跟40010c00的基地址配置参数,结果变成了去40011c00的基地址配置参数
        //  40011800 | 40010c00 = 40011c00
//    HAL_GPIO_WritePin(((uint32_t)LED1_PORT | (uint32_t)LED0_PORT),GPIO_PIN_5,GPIO_PIN_SET);


        //配置端口的初始电平状态
      HAL_GPIO_WritePin(LED1_PORT ,GPIO_PIN_5,GPIO_PIN_SET);
        HAL_GPIO_WritePin(LED1_PORT ,GPIO_PIN_5,GPIO_PIN_SET);
    
}

key.h文件

#ifndef __KEY_H
#define __KEY_H
#include "stm32f1xx.h"

#define  KEY1_PORT  GPIOE
#define  KEY1_PIN   GPIO_PIN_3

#define  KEY0_PORT  GPIOE
#define  KEY0_PIN   GPIO_PIN_4

void EXTI_KEY_Init(void);

#endif

key.c文件

#include "key.h"
/**
  * @brief  初始化外部中断输入
  * @param  无
  * @retval 无
  */
void EXTI_KEY_Init(void )
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    
    //使能端口时钟,在hal_rcc里面找
    __HAL_RCC_GPIOE_CLK_ENABLE();
    
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pin  = KEY1_PIN;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(KEY1_PORT,&GPIO_InitStruct);
    
    //设置中断线优先级,在hal_cortex里面找
    HAL_NVIC_SetPriority(EXTI3_IRQn,2,1);
    //使能对应的时钟线
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);
     
    GPIO_InitStruct.Pin  = KEY0_PIN;
    HAL_GPIO_Init(KEY0_PORT,&GPIO_InitStruct);
    
    HAL_NVIC_SetPriority(EXTI4_IRQn,2,1);
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
    
}

exti.h文件

#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f1xx_hal.h"


#endif

exti.c文件 :调库如果不知道用哪个函数,可以先去相关外设库找,比如按键去hal_gpio.h库,中断去hal_exti库,如果外设库中不到,就去与内核相关的库找,比如hal_cortex.h

#include "exti.h"
#include "led.h"

/**
*  @brief  EXTI3中断服务函数
  * @param  无
  * @retval 无
  */
    
//在startup_stm32f103xe.s文件里面找中断服务函数名称
void EXTI3_IRQHandler(void)
{
      //硬件按键可能有问题,抖动100ms还可能有
      HAL_Delay(100);  
      //处理中断请求的函数,在hal_gpio.h里面找
        HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}
//中断线检测回调函数,在hal_gpio.h里面找
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
      HAL_GPIO_TogglePin(LED1_PORT,LED1_PIN);
}


/**
*  @brief  EXTI4中断服务函数
  * @param  无
  * @retval 无
  */
void EXTI4_IRQHandler(void)
{
        HAL_Delay(100);
      if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != 0x00u) //判断中断线有没有挂起,在hal_gpio_h中找
  {
          //这里是要先清PR寄存器,因为确定了有中断进来,接下来执行功能代码,
          //假如接下来的程序执行时间很长,中间又有一次同一个中断线触发,
          //可能会漏掉一次。
          __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);  //在hal_gpio_h中找这个函数。
          HAL_GPIO_TogglePin(LED0_PORT,LED0_PIN);
          
  }
}
作者公众号

为了让各位读者更方便地阅读文章,作者开始把博客的文章同步到个人的微信公众号,欢迎您的关注。
在这里插入图片描述

  • 13
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林时小卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值