一,中止优先级:
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能够是个其他值了,只需确保这一处的地址不能被其他程序拜访就行了。