NVIC探究与学习
嵌套向量中断控制器(
NVIC
)
NVIC 驱动有多种用途:例如使能或者失能 IRQ 中断,使能或者失能单独的 IRQ 通道,改变 IRQ 通道的优先级等等
NVIC
Enable 中断设置使能寄存器
Disable 中断清除使能寄存器
Set 中断设置待处理寄存器
Clear 中断清除待处理寄存器
Active 中断活动位寄存器
Priority 中断优先级寄存器
//NVIC 寄存器结构,NVIC_TypeDeff,在文件“stm32f10x_map.h”中定义如下:
typedef struct
{
vu32 Enable[2];
u32 RESERVED0[30];
vu32 Disable[2];
u32 RSERVED1[30];
vu32 Set[2];
u32 RESERVED2[30];
vu32 Clear[2];
u32 RESERVED3[30];
vu32 Active[2];
u32 RESERVED4[62];
vu32 Priority[11];
} NVIC_TypeDef;/* NVIC Structure */
typedef struct
SCB
CPUID CPU ID 基寄存器
IRQControlStatus 中断控制状态寄存器
ExceptionTableOffset 向量表移位寄存器
AIRC 应用控制/
重置寄存器
SysCtrl 系统控制寄存器
ConfigCtrl 设置控制寄存器
SystemPriority 系统处理优先级寄存器
SysHandlerCtrl 系统处理控制和状态寄存器
ConfigFaultStatus 设置错误状态寄存器
HardFaultStatus 硬件错误状态寄存器
DebugFaultStatus 除错错误寄存器
MemorymanageFaultAddr 存储器管理错误地址寄存器
BusFaultAddr 总线错误地址寄存器
typedef struct
{
vu32 CPUID;
vu32 IRQControlState;
vu32 ExceptionTableOffset;
vu32 AIRC;
vu32 SysCtrl;
vu32 ConfigCtrl;
vu32 SystemPriority[3];
vu32 SysHandlerCtrl;
vu32 ConfigFaultStatus;
vu32 HardFaultStatus;
vu32 DebugFaultStatus;
vu32 MemoryManageFaultAddr;
vu32 BusFaultAddr;
} SCB_TypeDef; /* System Control Block Structure */
函数
NVIC函数较多,配置一个中断似乎看上去并不容易。我们需要先理解NVIC工作原理
1.什么是优先级分组
寄存器高四位用于配置优先级
- 抢占优先级越小,优先级越高;相同抢占优先级的中断不能嵌套;
- 相同抢占优先级N个中断发生时,响应优先级越小的中断首先执行(不能嵌套),如果响应优先级也均相同,则根据各中断对应向量表的位置来确定,向量表中越靠前的中断先响应。
- 参考STM32中的NVIC详解_Adamsky-CSDN博客
2.如何配置NVIC
Step.1 分组函数NVIC_PriorityGroupConfig
参数列表
![](https://img-blog.csdnimg.cn/2021042421410235.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzU1NzgwNzkz,size_16,color_FFFFFF,t_70)
Step.2 定义初始化结构体NVIC_InitTypeDef structure
//NVIC_InitTypeDef structure
//NVIC_InitTypeDef 定义于文件“stm32f10x_nvic.h”:
typedef struct
{
u8 NVIC_IRQChannel; //该参数用以使能或者失能指定的 IRQ 通道
u8 NVIC_IRQChannelPreemptionPriority; //该参数设置了成员 NVIC_IRQChannel 中的先占优先级
u8 NVIC_IRQChannelSubPriority; //该参数设置了成员 NVIC_IRQChannel 中的从优先级
FunctionalState NVIC_IRQChannelCmd; //设置IRQ通道使能/失能
} NVIC_InitTypeDef;
Step.3 配置初始化结构体
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the Priority Grouping with 1 bit */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Enable TIM3 global interrupt with Preemption Priority 0 and Sub
Priority as 2 */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQChannel; //IRQ通道参考Table.272
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0(值越低优先级越高)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
参数列表
![](https://img-blog.csdnimg.cn/20210425103111954.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzU1NzgwNzkz,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210425103755366.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzU1NzgwNzkz,size_16,color_FFFFFF,t_70)
设置优先级的时候要考虑周全,不要在高优先级中调用低优先级的中断
例如在USART1中断中调用Delay函数时要将USART优先级调低不能高于Delay
Step.4 调用NVIC_InitStructure(&NVIC_InitStructure)函数将值写入寄存器
NVIC_InitStructure(&NVIC_InitStructure); //初始化函数将参数写入寄存器
至此就完成了NVIC的分组配置和优先级配置,在对应的模块使能中断后就可以使用中断了
例如
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
NVIC_InitTypeDef NVIC_InitStructure;
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); //使用串口中断方式接收数据
Step.5 编写中断函数
void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)
u8 Res;
//以下是字符串接收到USART_RX_BUF[]的程序,(USART_RX_STA&0x3FFF)是数据的长度(不包括回车)
//当(USART_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
//在主函数里写判断if(USART_RX_STA&0xC000),然后读USART_RX_BUF[]数组,读到0x0d 0x0a即是结束。
//注意在主函数处理完串口数据后,要将USART_RX_STA清0
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){ //接收中断(接收到的数据必须是0x0d 0x0a结尾)
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
printf("%c\n",Res); //把收到的数据以 a符号变量 发送回电脑
a=Res;
if((USART_RX_STA&0x8000)==0){//接收未完成
if(USART_RX_STA&0x4000){//接收到了0x0d
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}else{ //还没收到0X0D
if(Res==0x0d)USART_RX_STA|=0x4000;
else{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组
USART_RX_STA++; //数据长度计数加1
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
外设初始化时不要忘记启用中断,如USART_ITConfig(USART1, USART_IT_RXNE, Enable);//开启ENABLE
中断函数在参考手册向量表或者启动文件中可找到,函数名是固定的(如果没修改启动文件)