STM32 中断向量,优先级

发布者:王大雷最新更新时间:2016-10-08 来源: eefocus关键字:STM32  中断向量  优先级 手机看文章 扫描二维码
随时随地手机看文章
一,中断优先级:

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。具体的实现可以参考stm32\fwlib\FWLib\examples下的各例子。
 
到这里,我们也只不过看了中断的处理函数,而这些处理函数是如何被硬件中断调用的呢?嗯,说到这里就不得不提一下stm32f10x_vector.c这个文件了。内容如下:
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
 
/**************************************************************
__sfe是IAR的“段操作符”segment operator。表示取某个段的后一个字节的地址。
比如"CSTACK"定义为0x20001000~0x20001fff。那__sfe( "CSTACK" ) 就得到0x20002000这个值,刚好用来初始化msp堆栈指针。
注意使用segment operator前,需要先定义段名如下: #pragma segment="CSTACK"
RSTACK 程序返回用的,保存的是程序调用函数的返回地址  , 你填写的数值 X 2才是占用的字节数
CSTACK 函数局部变量用的区域,所有的功能函数使用的局部变量都是从这个堆栈申请使用的,用完了再还回去。子函数里面用到的局部变量都是在这里面取来用的. 
*****************************************************************/
 
//IAR对所用语言(这里是C)做的一些扩展,也就是说这里可以用扩展的功能
#pragma language=extended#pragma segment="CSTACK"
 
void __iar_program_start( void );
 
/*****************************************************************
把中断向量表放到中断向量表该放的地方。 如果没有次句,中断向量被当作普通常变量处理,被放置的位置由编译器连接后确定。
在.icf文件中有place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
******************************************************************/
#pragma location = ".intvec"
 
/* STM32F10x Vector Table entries */
const intvec_elem __vector_table[] =
{
  { .__ptr = __sfe( "CSTACK" ) },
  &__iar_program_start,
  NMIException,
  HardFaultException,
  MemManageException,
  BusFaultException,
  UsageFaultException,
  0, 0, 0, 0,            /* Reserved */
  vPortSVCHandler,
  DebugMonitor,
  0,                      /* Reserved */
  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)

  /* Check the parameters */
  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】
/* System Control Space memory map */
#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; /* System Control Block Structure */
它们对应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  中断向量  优先级 引用地址:STM32 中断向量,优先级

上一篇:STM32复位/时钟控制
下一篇:STM32 在 MDK环境下 插入汇编

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

STM32驱动LCD12864显示屏
我们做一个电子产品,往往需要实现人机交互的功能。那么人机交互的方式除了输出到上位机通过电脑去显示,显示器也是一个很不错的方式,可用于一些不能使用电脑的场合。LCD12864显示器中的一种,具有价格低廉,操作简单的优点。今天就为大家带来一个STM32驱动12864的例程,使用SPI串行通信,仅仅需要三根数据线就可以完成通信。废话不多说,进入正题。 接线: RS----PB15 RW----PB14 EN----PB13 PSB---GND 1.初始化IO口以及显示屏 void Lcd_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2P
[单片机]
《如何制作STM32开发板》之串口
一说到串口,大家应该在脑海中出现下面这个画面就对了: 看到没有,这就是正儿八经的串口。在现在的工控机上,和以前的家用电脑上,都有串口,现在的家用电脑上,已经没有串口了。(千万不要把VGA口看成串口,VGA口是15针,串口是9针) 我们要搞单片机,就必须会用串口。所以,开发板上,就必须要有学习串口的功能。 串口的硬件应用,现在最多的有3种: USB转TTL串口 232串口 485通信 在我们的开发板上,把这3种功能都实现。 一、USB转TTL串口 这个功能,在讲程序下载方式的那一篇文章已经讲过了。STM32VET6有5个串口,但是只有串口1可以下载程序,所以我们默认把USART1与CH340G转成的RXD和TXD放到一起
[单片机]
《如何制作<font color='red'>STM32</font>开发板》之串口
STM32 ADC采样时间、采样周期、采样频率计算方法
ADC转换就是输入模拟的信号量,单片机转换成数字量。读取数字量必须等转换完成后,完成一个通道的读取叫做采样周期。采样周期一般来说=转换时间+读取时间 。而转换时间=采样时间+12.5个时钟周期。采样时间是你通过寄存器告诉 STM32 采样模拟量的时间,设置越长越精确 一 STM32 ADC采样频率的确定 1. :先看一些资料,确定一下ADC的时钟: (1),由时钟控制器提供的ADCCLK时钟和PCLK2(APB2时钟)同步。CLK控制器为ADC时钟提供一个专用的可编程预分频器。 (2) 一般情况下在程序 中将 PCLK2 时钟设为 与系统时钟 相同 RCC_HCLKConfig(RCC_SYSCLK_Div1); RC
[单片机]
STM32的八种GPIO工作方式详解
STM32的GPIO介绍 STM32引脚说明 GPIO是通用输入/输出端口的简称,是STM32可控制的引脚。GPIO的引脚与外部硬件设备连接,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 STM32F103ZET6芯片为144脚芯片,包括7个通用目的的输入/输出口(GPIO)组,分别为GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF、GPIOG,同时每组GPIO口组有16个GPIO口。通常简略称为PAx、PBx、PCx、PDx、PEx、PFx、PGx,其中x为0-15。 STM32的大部分引脚除了当GPIO使用之外,还可以复用位外设功能引脚(比如串口),这部分在【STM32】STM32端
[单片机]
<font color='red'>STM32</font>的八种GPIO工作方式详解
stm32 l0相关的eeprom
内部FLASH和EEPROM这种掉电后还能保存内容的内存统称为non-volatile memory(NVM),STM32L053内部有2K的EEPROM. 内部EEPROM的页大小为一个Word为单位,擦除是必须以页为单位,所以库函数里的擦除函数也是每次擦除4个字节: view plaincopy to clipboardprint? /** * @brief Erase a word in data memory. * @param Address: specifies the address to be erased. * @note To correctly run this function,
[单片机]
stm32在manin()前做了什么?
最近要在Cortex-M3上写一个简单的操作系统,打算使用IAR,为了写好启动代码,花了一些时间了解了IAR在main()以前做了些什么事。 首先系统复位时,Cortex-M3从代码区偏移0x0000'0000处获取栈顶地址,用来初始化MSP寄存器的值。 接下来从代码区偏移0x0000'0004获取第一个指令的跳转地址。这些地址,是CM3要求放置中断向量表的地方。 这里是一个程序的启动区的反汇编: __vector_table: 08004000 2600 08004002 2000 08004004 7E1D 08004006 0800 这个程序是由IAP程序来启动的,IAP程序获
[单片机]
关于STM32浮点运算单元FPU的应用示例
有人利用STM32芯片做些DSP处理,在启用FPU单元进行调试、验证过程中可能会遇到些小问题、小困惑,这里通过STM32F4芯片一个具体的应用示例简单分享下,希望顺便能给同仁提供些帮助或提醒。 我这里通过调用DSP库里的FFT相关函数实现1024点的FFT运算,样点数据及运算结果均为浮点数。 上图中A区代码是做样点数据准备,B区代码完成FFT运算。我们来一起看看基本的配置以及不启用硬件浮点单元和启用硬件浮点单元执行B区代码的时间上的差别。 程序里要调用一些数学函数,而这些数学函数往往集成在相应的数学函数库里。我们选用ARM公司的DSP数学库,该库系专门针对AMR核芯片及指令系统而组织的代码,相比IDE自带的通用数学函数库会
[单片机]
关于<font color='red'>STM32</font>浮点运算单元FPU的应用示例
STM32存储器 — <2>STM32存储器知识的相关应用
在我的另一篇笔记《stm32的存储器》中讲述了STM32的存储器结构,及个人理解。 本篇文章将重点描述在对存储器有了比较深入了解之后的一些相关的操作案例;重点在于STM32启动设置和IAP使用,以及bit banding的理解,加上一个简单的IAP程序设计。 1 STM32的启动 根据参考手册RM0008中的图表: 得知STM32的启动有三种模式,三种模式的选择在于芯片上的两个Boot引脚,如RM0008种描述: 在系统复位之后的四个上升沿后索存BOOT引脚,从而决定启动方式;用户对BOOT引脚的设置决定了系统复位之后的启动模式。 三个不同的启动区域有着不同的起始地址,STM32这样规定:
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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