STM32中断向量嵌套NVIC理解

发布者:自由探索最新更新时间:2015-10-21 来源: eefocus关键字:STM32  中断向量嵌套  NVIC 手机看文章 扫描二维码
随时随地手机看文章
一,中断优先级:

STM32(Cortex-M3)中的优先级概念
STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。

--------------------------------------------------------------------------------
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
 中断设置:时能中断->优先级分组方式(对应的每个中断都有)->设定抢占式优先级别->设定响应优先级别->调用NVIC_Init(&xx)
// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
 
// 使能EXTI9_5中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

要注意的几点是:

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

二,开关总中断:

在STM32/Cortex-M3中是通过改变CPU的当前优先级来允许或禁止中断。
PRIMASK位:只允许NMI和hard fault异常,其他中断/异常都被屏蔽(当前CPU优先级=0)。
FAULTMASK位:只允许NMI,其他所有中断/异常都被屏蔽(当前CPU优先级=-1)。

在STM32固件库中(stm32f10x_nvic.c和stm32f10x_nvic.h) 定义了四个函数操作PRIMASK位和FAULTMASK位,改变CPU的当前优先级,从而达到控制所有中断的目的。

下面两个函数等效于关闭总中断:
void NVIC_SETPRIMASK(void);
void NVIC_SETFAULTMASK(void);

下面两个函数等效于开放总中断:
void NVIC_RESETPRIMASK(void);
void NVIC_RESETFAULTMASK(void);

上面两组函数要成对使用,不能交叉使用。

例如:

第一种方法:
NVIC_SETPRIMASK();   //关闭总中断
NVIC_RESETPRIMASK();//开放总中断

第二种方法:
NVIC_SETFAULTMASK();   //关闭总中断
NVIC_RESETFAULTMASK();//开放总中断

常常使用

NVIC_SETPRIMASK();                    // Disable Interrupts
NVIC_RESETPRIMASK();                  // Enable Interrupts

 STM32中断流程处理

作为我的一个习惯,学习某一个平台的东西,总是先要摸清楚中断的处理流程,当然是从文件代码级的流程分析了。
 
下面就说下stm32的中断流程。我们知道,stm32的库中写好了很多的驱动程序,可以说包括了所有的。同时也提供很多数据处理方式,例如串口的读写,用户可以选择轮询、中断、DMA等3中方式来处理。
 
关于中断,stm32的库中做好了框架,用户只要填写好几个函数的实现就ok了,就像网上说的,这就是傻瓜式开发。
 
了解中断,首先要知道stm32f10x_it.c这个文件,一般情况下是和main文件在同一个目录下的。打开这个文件,我们可以看到xyz_IRQHandler函数的实现,虽然说是实现,但是几乎都是空的。对了,这些函数就是要用户填写的中断处理函数,如果你用到了哪个中断来做相应的处理,你就要填写相应的中断处理函数,需要根据各外设的实际情况来填写,但是一般都会有关闭和开启中断。在这个文件中还有很多系统相关的中断处理函数,例如系统时钟SysTickHandler。具体的实现可以参考stm32fwlibFWLibexamples下的各例子。
 
到这里,我们也只不过看了中断的处理函数,而这些处理函数是如何被硬件中断调用的呢?嗯,说到这里就不得不提一下stm32f10x_vector.c这个文件了。内容如下:
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
 

 
//IAR对所用语言(这里是C)做的一些扩展,也就是说这里可以用扩展的功能
#pragma language=extended#pragma segment="CSTACK"
 
void __iar_program_start( void );
 

#pragma location = ".intvec"
 

const intvec_elem __vector_table[] =
{
  { .__ptr = __sfe( "CSTACK" ) },
  &__iar_program_start,
  NMIException,
  HardFaultException,
  MemManageException,
  BusFaultException,
  UsageFaultException,
  0, 0, 0, 0,           
  vPortSVCHandler,
  DebugMonitor,
  0,                     
  xPortPendSVHandler,
  xPortSysTickHandler,
  WWDG_IRQHandler,
  PVD_IRQHandler,
  TAMPER_IRQHandler,
  RTC_IRQHandler,
  FLASH_IRQHandler,
  RCC_IRQHandler,
  EXTI0_IRQHandler,
  EXTI1_IRQHandler,
  EXTI2_IRQHandler,
  EXTI3_IRQHandler,
  EXTI4_IRQHandler,
  DMAChannel1_IRQHandler,
  DMAChannel2_IRQHandler,
  DMAChannel3_IRQHandler,
  DMAChannel4_IRQHandler,
  DMAChannel5_IRQHandler,
  DMAChannel6_IRQHandler,
  DMAChannel7_IRQHandler,
  ADC_IRQHandler,
  USB_HP_CAN_TX_IRQHandler,
  USB_LP_CAN_RX0_IRQHandler,
  CAN_RX1_IRQHandler,
  CAN_SCE_IRQHandler,
  EXTI9_5_IRQHandler,
  TIM1_BRK_IRQHandler,
  TIM1_UP_IRQHandler,
  TIM1_TRG_COM_IRQHandler,
  TIM1_CC_IRQHandler,
  vTimer2IntHandler,
  TIM3_IRQHandler,
  TIM4_IRQHandler,
  I2C1_EV_IRQHandler,
  I2C1_ER_IRQHandler,
  I2C2_EV_IRQHandler,
  I2C2_ER_IRQHandler,
  SPI1_IRQHandler,
  SPI2_IRQHandler,
  vUARTInterruptHandler,
  USART2_IRQHandler,
  USART3_IRQHandler,
  EXTI15_10_IRQHandler,
  RTCAlarm_IRQHandler,
  USBWakeUp_IRQHandler,
};
现在我们清楚了,这儿就是中断向量表,每一个item对应一个中断或异常处理,这里item的填写要和stm32spec中的Interrupt and exception vectors一节中的列表中的顺序一致。
 
说道这里,又有一个问题,这个向量表是放在何处的呢?上面对.intvec的解释可以看出是被链接器放到了一个地址上(这里是0x08000000,NVIC_VectTab_FLASH)。但是stm32是怎么知道这个地址的呢,也许有个默认值,或者是就这一个固定值?)。我们在stm32f10x_nvic.c文件中发现下面这样的一个函数
void NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{
 
  assert(IS_NVIC_VECTTAB(NVIC_VectTab));
  assert(IS_NVIC_OFFSET(Offset)); 
  
  SCB->ExceptionTableOffset = (((u32)Offset << 0x07) & (u32)0x1FFFFF80);
  SCB->ExceptionTableOffset |= NVIC_VectTab;
}
同时在example目录下有vectortable_relocation这样的一个例子:This example describes how to use the NVIC firmware library to set the CortexM3 vector table in a specific address other than default.
在这个例子里面就是直接调用了上面的那个函数,似乎意思很明显了。但是SCB->ExceptionTableOffset是如何起作用的呢?
 
着重解释这个问题,先看一组定义:【stm32f10x_map.b】

#define SCS_BASE              ((u32)0xE000E000)
#define SysTick_BASE          (SCS_BASE + 0x0010)
#define NVIC_BASE             (SCS_BASE + 0x0100)
#define SCB_BASE              (SCS_BASE + 0x0D00)
#ifdef _SCB
#define SCB                   ((SCB_TypeDef *) SCB_BASE)
#endif
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;
 
其实这里主要就是要弄清楚这个SCB是什么意思,因为这个结构是映射到一个物理地址上的。像别的控制寄存器都是这么个玩法,莫非这也是个某类控制器。google一下,果然对于系统控制寄存器组【上篇文章有提到】STM32的固件库中有如下定义:
typedef struct
{
  vuc32 CPUID;
  vu32 ICSR;
  vu32 VTOR;
  vu32 AIRCR;
  vu32 SCR;
  vu32 CCR;
  vu32 SHPR[3];
  vu32 SHCSR;
  vu32 CFSR;
  vu32 HFSR;
  vu32 DFSR;
  vu32 MMFAR;
  vu32 BFAR;
  vu32 AFSR;
} SCB_TypeDef;
它们对应ARM手册中的名称为
CPUID = CPUID Base Register
ICSR = Interrupt Control State Register
VTOR = Vector Table Offset Register
AIRCR = Application Interrupt/Reset Control Register
SCR = System Control Register
CCR = Configuration Control Register
SHPR = System Handlers Priority Register
SHCSR = System Handler Control and State Register
CFSR = Configurable Fault Status Registers
HFSR = Hard Fault Status Register
DFSR = Debug Fault Status Register
MMFAR = Mem Manage Address Register
BFAR = Bus Fault Address Register
AFSR = Auxiliary Fault Status Register
 
至此,我们终于清楚了,这个中断向量表的地址,最终是要写到某个控制器中去。那这么说来,上述的0x08000000可以是个别的值了,只要保证这一处的地址不能被别的程序访问就行了。


关键字:STM32  中断向量嵌套  NVIC 引用地址:STM32中断向量嵌套NVIC理解

上一篇:关于stm32单片机,用id加密,明码安全问题分析
下一篇:在Keil4中建立基于V3.4.0固件库的STM32工程

推荐阅读最新更新时间:2024-03-16 14:36

STM32调试支持
STM32F10xxx使用Cortex™-M3内核,该内核内含硬件调试模块,支持复杂的调试操作。硬件调试模块允许内核在取指(指令断点)或访问数据(数据断点)时停止。内核停止时,内核的内部状态和系统的外部状态都是可以查询的。完成查询后,内核和外设可以被复原,程序将继续执行。 SWJ调试端口(serial wire and JTAG) STM32F10xxx内核集成了串行/JTAG调试接口(SWJ-DP)。这是标准的ARM CoreSight调试接口,包括JTAG-DP接口(5个引脚)和SW-DP接口(2个引脚)。 ● JTAG调试接口(JTAG-DP)为AHP-AP模块提供5针标准JTAG接口。 ● 串行调试接口(SW-DP)
[单片机]
<font color='red'>STM32</font>调试支持
学习STM32(1)-IO-GPIO
GPIO: STM32的(64引脚的)IO口一共有3个,分别是PA、PB、PC. STM32的IO端口可以由软件配置成8种模式: 1,输入浮空 2,输入上拉 3,输入下拉 4,模拟输入 5,开漏输出 6,推挽输出 7,推挽复用功能 8,开漏复用功能 STM32的每个IO端口都有7个寄存器来控制。他们分别是:配置模式的2个32位的端口配置寄存器CRL和CRH;2个32位的数据寄存器IDR和ODR;1个32位的置位/复位寄存器BSRR;一个16位的复位寄存器BRR;1个32位的锁存寄存器LCKR;我们常用的IO端口寄存器只有4个:CRL、CRH、IDR、ODR。 注意(在配置STM32外设的时候,任何时候都要先使能该外设的时钟!APB
[单片机]
STM32定时器做外部脉冲信号计数器用
简介:今天,尝试使用STM32的计数器的外部时钟功能,来对外部脉冲信号进行计数。效果还不错。 具体设置如下: /* TIM3_CH2 为脉冲输入口 1. 配置GPIO_GPIOA_PIN7 输入 2. 配置TIM3 计数器在TI2 端的上升沿计数: 1). TIMx_CCMR1: CC2S =01; 配置通道2检测TI2输入的上升沿 2). TIMx_CCMR1:IC2F =000; 选择输入滤波器带宽 3). TIMx_CCER: CC2P =0; 配置上升沿极性 √ 4). TIMx_SMCR: SMS =111; 选择定时器外部时钟模式1 5). TIMx_SMCR: TS =110; 选择TI2作为触发输入源 √
[单片机]
STM32 SPI SLAVE
一般使用SPI都用MASTER,但是用SLAVE没有用过.参考了ST的例子,发现不能满足自己的使用.于是,自己修改了一下. 初始化配置SPI /** ****************************************************************************** * @file app.c * @author MCD Application Team * @version V1.1.0 * @date 19-March-2012 * @brief This file provides all the Application firmware functions.
[单片机]
STM32时钟配置,不同晶振时需要配置的程序
在“stm32f10x.h”这个头文件里: #define HSE_Value ((uint32_t)8000000) /! Value of the External oscillator in Hz/ 斜体内容默认是8000000,在你修改了外部晶振时,要和你的外部晶振数值相同,不如我的外部晶振时24M,我就把它改成24M,这样就可以了, 还有一处要更改是在“system_stm32f10x.c”里: static void SetSysClockTo72(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /! SYSCLK, HCLK, PCLK2 and PC
[单片机]
stm32专题三十六:MDK编译过程和文件类型(四)
sct 分散加载文件的格式与应用 在使用 MDK 新建工程时,MDK 软件会要求选择对应的芯片型号: 然后,MDK会根据选择的芯片型号,自动获取(写入)芯片内部的 FLASH 和 SRAM 存储器概况如下: 然后,MDK 会生成一个后缀名为 sct 的分散加载文件,如下所示: 链接器根据该文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置。 下面,来分析一下这个 sct 文件: 在默认的 sct 文件配置中仅分配了 Code、 RO-data、 RW-data 及 ZI-data 这些大区域的地址,链接时各个节区(函数、变量等)直接根据属性排列到具体的地址空间。
[单片机]
<font color='red'>stm32</font>专题三十六:MDK编译过程和文件类型(四)
第四节:定时器中断及定时器产生PWM(用CubeMX学习STM32)
前言: STM32定时器概述 我演示用的STM32F407ZGt6的核心板有多达14个定时器;  其中包含两个高级定时器(TIM1和TIM8); 十个通用定时器(TIM2~TIM5, TIM9~TIM14); 两个基础定时器(TIM6、TIM7)。 Tips: 在十个通用定时器里面包含两个看门狗定时器(two watchdog timers) 下面是datasheet里面官方给出的介绍 Couter resolution— 计数器位数, 位数越高精度越高; Counter type — 计数类型 即向上计数还是向下计数 Prescaler factor — 分频因子, 对时钟的分频, 比如单片机的时钟为84MHz, 预
[单片机]
第四节:定时器中断及定时器产生PWM(用CubeMX学习<font color='red'>STM32</font>)
深度解析STM32驱动LCD原理
TFTLCD即薄膜晶体管液晶显示器。它与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。 01驱动流程 使用FSMC驱动LCD 关于FSMC,把数据写入相应的地址,FSMC就会把地址从FSMC_A出去,写入的数据会会从FSMC_D发出去。至于片选等信号线都是自动的。读的话,直接读相应的地址,就会拿到改地址上的数据。 FSMC驱动外部SRAM时,外部SRAM的控制一般有: 地址线(如A0~A25) 数据线(如D0~D15) 写信号(WE,即WR) 读信号(OE,即RD) 片选信号
[单片机]
深度解析<font color='red'>STM32</font>驱动LCD原理
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved