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文章来源:https://www.toymoban.com/news/detail-536330.html
#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模板网!