NVIC与EXTI外设详解

这篇具有很好参考价值的文章主要介绍了NVIC与EXTI外设详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

NVIC属于内核外设,MCU组成分为:内核(ARM设计)、片上外设(芯片制造商设计)。如下图
nvic,STM32学习笔记,单片机,stm32,c语言
NVIC部分标准库函数(由ARM提供,符合CMSIS 标准):
nvic,STM32学习笔记,单片机,stm32,c语言

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

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

F103EXTI中断映射线路框图:
nvic,STM32学习笔记,单片机,stm32,c语言

3、EXTI 工作流程框图

nvic,STM32学习笔记,单片机,stm32,c语言EXTI 与NVIC有什么关系?
从上图中可以看出来,当MCU片上外设产生一个中断后,就会输入到NVIC控制器中,去控制中断的响应顺序。
nvic,STM32学习笔记,单片机,stm32,c语言
请求挂起寄存器发生触发请求后,一定要手动清0,不然会一直进入中断。

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

nvic,STM32学习笔记,单片机,stm32,c语言
概括:通过优先级分组,管理中断的响应顺序。高优先级的抢占可以的打断低优先级的中断,如果是同优先级的中断,则会挂起,等现有中断执行完,再根据响应优先级来决定是谁先执行,如果同响应优先级,那么就根据IRQ编号决定先执行。

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

nvic,STM32学习笔记,单片机,stm32,c语言中断优先级配置流程
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、中断触发到中断服务函数的流程

nvic,STM32学习笔记,单片机,stm32,c语言
MCU触发了一个中断
1、执行指针就会跳转到中断向量表中
2、 找到对应的中断偏移地址
3、偏移地址:就是中断服务函数的入口 EXTI4_IRQHandler
4、 最终会在HAL_GPIO_EXTI_IRQHandler()里面调用callback函数,并清除PR寄存器

5.3、外设初始化流程图

nvic,STM32学习笔记,单片机,stm32,c语言
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 配置

nvic,STM32学习笔记,单片机,stm32,c语言

7、示例实验代码

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

硬件原理图:
nvic,STM32学习笔记,单片机,stm32,c语言
示例代码:

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);
          
  }
}
作者公众号

为了让各位读者更方便地阅读文章,作者开始把博客的文章同步到个人的微信公众号,欢迎您的关注。
文章来源地址https://www.toymoban.com/news/detail-536330.html

到了这里,关于NVIC与EXTI外设详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://blog.csdn.net/weixin_41241810/article/details/128873979

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包