目录
一、NVIC基本介绍
1.1、NVIC基本结构
NVIC被称为做嵌套中断向量控制器,其作用是统一分配中断优先级和管理中断的。
- NVIC可以有多个输入,但是只有一个输出,输入可以是 EXTI、TIM、ADC等外设,其中红色框选部分代表着一个外设可能占有多个中断通道。
- NVIC根据优先级排序,指引CPU处理不同任务。
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 。抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队 。
抢占优先级:抢占优先级高的中断,可以在CPU暂停处理当前中断,转而处理抢占优先级较高的中断事件,处理完抢占优先级较高的中断事件后,继续处理之前暂停的中断。
优先级和响应优先级均相同:按以下表格中的中断顺序排队。
1.2、NVIC介绍
- EXTI(Extern Interrupt)外部中断。
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿/软件触发
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
- 触发响应方式:中断响应/事件响应
第四点中, 但相同的Pin不能同时触发中断,即PA0 与PB0等不可同时使用。
1.3、整体结构
AFIO是选择开启需要GPIOx引脚。对应1.2、中的第四点,相同的Pin不能同时触发中断,即PA0 与PB0等不可同时使用。因为选择GPIOA后无法再使其他GPIOx口的引脚信号输入。
引脚5-9、引脚10-15共用一个中断函数。
1.4、电路结构
1.4.1、AFIO
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。
1.4.2、NVIC
二、对射红外传感计次
上图为硬件线路接线。
我们要配置中断函数就需要知道我们需要配置那些外设。下图可以清晰的了解我们需要配置的外设
此时,我选择的红外连接在GPIOB--14引脚,所以我需要配置外设GPIOB的时钟信号,而AFIO在图一中也是必不可少的,所以我们还需要配置AFIO外设。由于EXTI由MVIC模块直接控制,是默认开启时钟的,所以并不需要单独设置。以下就是我们的配置代码
2.1、CountSensor.c文件
2.1.1、开启GPIO时钟
uint8_t CountSensor_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//开启外设AFIO时钟 EXTI由MVIC模块直接控制,并不需要单独外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
//上拉输入,高电平输入;不知道配置什么模式,可以查看芯片手册
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
}
2.1.2、选择用作EXTI线的GPIO引脚
即选择哪个GPIO口作为EXTI输入
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
2.1.3、EXTI边沿检测及控制
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);
【1】EXTI_Line: 指定要启用或禁用的EXTI行。
【2】EXTI_LineCmd:指定选定EXTI线的新状态。
【3】EXTI_Mode:指定EXTI线的模式。即选择事件模式还是中断模式。事件模式一般是外设与外设的联系。
【4】EXTI_Trigger:指定EXTI线的触发信号激活边缘模式。即是继续上升沿、下降沿还是其他边沿触发。
2.1.4、配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
【1】 NVIC_PriorityGroupConfig()://配置优先级分组:抢占优先级和次优先级。
【2】NVIC_IRQChannel:指定要启用或禁用的IRQ通道。即选择图中黄色框选进入NVIC的通道。为节省资源在该系列中引脚10-15、引脚5-9共用一个通道。本例中使用的是14引脚,因此使用EXTI15_10_IRQn。
【3】NVIC_IRQChannelCmd:指定在NVIC_IRQChannel中定义的IRQ通道将被启用或禁用。即选择禁用或启用【2】中选中的通道。本例中是启用 。
【4】NVIC_IRQChannelPreemptionPriority:指定NVIC_IRQChannel中指定的IRQ信道的抢占优先级。
【5】NVIC_IRQChannelSubPriority:指定NVIC_IRQChannel中指定的IRQ通道的响应优先级级别。由于本例子中并无太多中断,因此可以【4】【5】自行选择。
2.1.5、调用中断
在STM32里,中断函数的名字都是固定的,需要在启动文件 startup_stm32f10x_md.s 中找到对应的函数名字,由于本例程中使用的NVIC_IRQChannel 通道为 EXTI15_10_IRQn,因此使用的中断函数名称应为 EXTI15_10_IRQHandler。
uint16_t CountSensor_Get(void) //返回计次值
{
return CountSensor_Count;
}
void EXTI15_10_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
CountSensor_Count++; //计次
Delay_ms(200); //消除抖动
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
2.2、CountSensor.c & main.c文件源码
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint16_t CountSensor_Count;
void CountSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启外设AFIO时钟 EXTI由MVIC模块直接控制,并不需要单独外设
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入,高电平输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising; // EXTI_Trigger_Falling 下降沿触发 /EXTI_Trigger_Rising 上升沿触发
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t CountSensor_Get(void) //返回计次值
{
return CountSensor_Count;
}
void EXTI15_10_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
CountSensor_Count++; //计次
Delay_ms(200); //消除抖动
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
#include "stm32f10x.h" // Device header
#include "Delay.h" // Device header
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
CountSensor_Init();
OLED_Init(); //OLED初始化
CountSensor_Get();
OLED_ShowString(1,1,"Count:");
while(1)
{
}
}
三、与 AFIO & EXTI 有关的函数简介
3.1 与 AFIO 有关的函数
3.1.1、GPIO_AFIODeInit(void)
//清除 AFIO 外设配置
GPIO_AFIODeInit(void)
3.1.2、GPIO_PinLockConfig()
//锁定GPIO配置,参数指定某个引脚,该引脚配置被锁定,无法更改
GPIO_PinLockConfig((GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin))
3.1.3、GPIO_EXTILineConfig()
//配置 AFIO的数据选择器,选择想要的中断引脚;参数1、选择某个GPIO外设作为外部中断源
参数2、指定要配置的外部中断线
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
3.1.4、GPIO_PinRemapConfig()
//引脚重映射 参数1、选择重映射的方式; 参数2、新的状态
GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
3.1.5、GPIO_EventOutputConfig()&GPIO_EventOutputCmd()
//这两个函数用来配置AFIO的时间输出功能(使用频率不多)
GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
GPIO_EventOutputCmd(FunctionalState NewState);
3.1.6、GPIO_ETH_MediaInterfaceConfig()
//与以太网有关
GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
3.2 AFIO 的配置
//参数1、选择某个GPIO外设作为外部中断源 参数2、指定要配置的外部中断线
本实验中我选择的红外连接在GPIOB--14引脚,所以参数配置如下
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
3.2、关于 EXTI 的函数与配置
3.2 .1、 GPIO_EXTILineConfig()
//清除EXTI的配置 并回复成上电默认的转态.
GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
3.2.2、EXTI_DeInit()
//清除EXTI配置
EXTI_DeInit(void)
3.2.3、EXTI_Init()
//根据结构体参数配置EXTI外设,与GPIO_Init 用法类似
EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
3.2.4、EXTI_StructInit()
//把参数传递的结构体变量赋一个默认值
EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct)
3.2.5、EXTI_GenerateSWInterrupt()
//软件触发外部中断
EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
3.2.6、EXTI_GetFlagStatus()
//获取指定的标志位是否被置1
EXTI_GetFlagStatus(uint32_t EXTI_Line);
3.2.7、EXTI_ClearFlag()
//对被置1的标志位进行清除
EXTI_ClearFlag(uint32_t EXTI_Line);
3.2.8、EXTI_GetITStatus()
//获取中断位是否被置1
EXTI_GetITStatus(uint32_t EXTI_Line);
3.2.9、EXTI_ClearITPendingBit()
//清除中断挂起的标志位
EXTI_ClearITPendingBit(uint32_t EXTI_Line);
若想在主程序中查看和清除标志位,使用5/6这两个函数,
若想在中断函数中查看和清除标志位,使用7/8这两个函数
四、关于 NVIC 的函数与配置(位于msci.h文件)
4.1关于 NVIC 的函数
4.1.1、NVIC_PriorityGroupConfig()
//中断分组,参数为中断分组的方式
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
4.1.2、NVIC_Init
//初始化NVIC
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
4.1.3、NVIC_SetVectorTable()
//设置中断向量表(本实验中暂未用到)
NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
4.1.4、NVIC_SystemLPConfig
//系统低功耗配置(本实验中暂未用到)
NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState)
以上为更新后的EXTI以及中断函数使用,主要优化了文章代码思路以及简介。本文将不定期优化更新。