精华内容
下载资源
问答
  • ARM-异常及中断处理中断中断中断中断/异常优先级异常和中断的区别ARM-7种异常类型ARM-异常中断的响应流程中断 汇编测试代码 中断 中断 1) 硬中断 在处理器中,中断是一过程。即CPU在正常执行程序过程中,...

    中断

    中断

    1) 硬中断
    在处理器中,中断是一个过程。即CPU在正常执行程序的过程中,遇到外部或内部的紧急事件需要处理,暂时中断(中止)当前程序的执行,而转去完成事件服务程序,待事件完毕后,再返回到暂停处(断点)继续执行原来的程序。事件服务程序又称中断处理程序或中断服务程序。严格意义上来说,上面的描述是针对硬件中断而言的。
    2) 软中断
    用软件方法引起的中断,即事先在程序中安排特殊的指令,CPU执行到该类指令的时候,会跳转去执行相应的一段预先安排好的程序,待程序执行完,再返回原来程序处。这种通过软件方法实现的中断叫软中断。

    3) 软、硬中断和信号的区别

    • 硬中断的出现过程是不可预测的,是随机的;而软中断是预先安排好的指令中断方式。
    • “硬中断是外部设备对CPU的中断”,“软中断通常是硬中断服务程序对内核的中断”,“信号则是由内核(或其他进程)对某个进程的中断”。

    中断源

    把引起中断的原因,或者能够发出中断请求信号的来源统称为中断源。

    中断/异常优先级

    ARM处理器五种模式对应的7种异常源优先级顺序(从高到低):

    1 复位异常(reset)
    2 数据异常(Data Abord)
    3 快速中断异常(FIQ)
    4 外部中断异常(IRQ)
    5 预取异常(prefetch Abord)
    6 软中断异常(SWI)
    7 未定义指令异常(Undefined)

    异常和中断的区别

    异常主要是从处理器被动接受异常的角度来考虑;中断带有主动请求处理器处理中断的意味。
    共同点:都是打断当前程序执行,进入特定的程序处理事件的一种机制。

    中断的状态

    • 挂起态:中断已发生,但是中断没有被处理执行
    • 激活态:中断发生,正在执行对应的中断处理函数,但是还没有执行结束
    • 未激活态:中断没有发生。

    ARM-7种异常类型

    1) 复位异常(reset)
    两种情况引起异常:系统上电/系统复位
    复位异常中断处理程序完成的功能:

    • 设置异常向量表
    • 初始化栈和寄存器
    • 初始化MMU,Cache
    • 初始化关键的I/O设备
    • 使能中断
    • 切换到SVC模式
    • 初始化C程序中的变量,跳转到相应程序的执行

    2)数据异常(Data Abord)
    存储器访问指令load/store访问外部数据时,出现地址不存在或不允许访问等时产生:取数据中止异常
    3)快速中断异常(FIQ)
    4)外部中断异常(IRQ)
    5)预取异常(prefetch Abord)
    取一条被标记无效的指令时:取指令中止异常
    6)软中断异常(SWI)
    软中断发生,系统进入SVC模式,跳转到处理程序
    7)未定义指令异常(Undefined)
    :未定义指令的异常,指令取出后通过译码器后未定义指令导致译码出错
    在这里插入图片描述

    ARM-异常中断的响应流程(四大步三小步)

    注意:

    • User和System模式是不可通过异常进入的两种模式,即要进入这两种模式,必须要编程设置CPSR的模式状态。
    • ARM处理器中cortex-A不支持中断嵌套,因为ARM处理器的处理中断处理速度较快;
    • cortex-M系列支持中断嵌套,eg:stm32。

    ARM异常处理:处理器对特定的异常事件进行的处理流程(CPU指导硬件自动完成:四大步三小步)。

    一、保存现场(四大步)
    1)保存CPSR到SPSR_mode
    2)适当设置 CPSR 对应功能位(三小步):

    • 1、切换处理器进入ARM状态:T[5]
    • 2、根据需要,禁止中断位:F[6] / I[7]
    • 3、根据异常切换到对应的异常模式:M[4:0]

    3)保存返回地址:把当前 PC 保存到 lr_mode
    4)设置PC = 存放跳转到对应的异常向量表的固定首地址。
    在这里插入图片描述

    二、恢复现场(必须手动恢复)
    1)恢复 SPSR_mode 到 CPSR
    2)恢复 lr_mode 到 PC
    3)恢复普通寄存器:R0-R7,操作sp将栈中将数据恢复。

    软中断 汇编测试代码

    .text
    .globl _start
    
    _start:
    	b reset
    	b undefined_instruction
    	b software_interrupt
    	b prefetch_abord
    	b data_abord
    	b reserved
    	b irq
    	b fiq
    	
    reset:
    	@初始化SVC下的栈内存空间
    	ldr sp, =0x40000100
    	
    	@切换到User模式下0xD3:SVC;0xD0:User
    	msr CPSR, #0xD0
    	
    	@初始化User下的栈内存空间
    	ldr sp, =0x40000200
    	
    	@执行用户代码
    	mov r0, #0x1
    	mov r1, #0x2
    	
    	@swi软中断:swi <软中断号,取值范围:0-(2^24-1)>
    	swi 2
    	
    	@执行用户代码:r2 = 1+2 = 3
    	add r2, r0,r1
    	
    undefined_instruction:
    	
    software_interrupt:
    	@保存现场
    	stmfd sp!,{r0-r1, lr}
    	
    	@执行用户代码
    	mov r0, #0x5
    	mov r1, #0x6
    	add r3, r0,r1
    	
    	@恢复现场:{r0-r1,pc}将lr恢复给pc,"^"是表示将SPSR恢复到CPSR
    	ldmfd sp!, {r0-r1,pc}^
    	
    prefetch_abord:
    	
    data_abord:
    	
    reserved:
    	
    irq:
    	
    fiq:
    	
    loop:
    	b loop
    	
    .end
    	
    
    展开全文
  • 中断由硬件产生,当中断产生后CPU会中断当前的流程转而去处理中断服务,Cortex-M内核MCU提供了用于中断管理的嵌套向量中断控制器(NVIC)。NVIC最多支持240中断请求(IRQ)、1不可屏蔽中断(NMI)、1滴答...

    记录一下,方便以后翻阅~

    FreeRTOS的中断配置需要根据MCU具体配置,这需要我们了解MCU架构关于中断的知识。

    1. Cortex-M中断

    1.1 简介
    中断由硬件产生,当中断产生后CPU会中断当前的流程转而去处理中断服务,Cortex-M内核MCU提供了用于中断管理的嵌套向量中断控制器(NVIC)。NVIC最多支持240个中断请求(IRQ)、1个不可屏蔽中断(NMI)、1个滴答定时器中断(Systick)和多个系统异常。

    1.2 管理方式
    Cortex-M处理器有多个用于管理中断和异常的可编程寄存器,位于NVIC和系统控制块(SCB)中,CMSIS将这些寄存器定义为结构体。打开core_cm3.h,有如下两个结构体:
    在这里插入图片描述
    在这里插入图片描述
    NVIC和SCB位于系统控制空间(SCS)内,SCS的地址从0XE000E000开始,SCB和NVIC的地址在core_cm3.h中定义如下:

    在这里插入图片描述
    1.3 优先级分组定义
    高优先级的中断可以抢占低优先级的中断,这就是中断嵌套。有些中断是固定优先级的,比如复位、NMI、HardFault,这些中断的优先级是负数,即最高优先级。
    由于大多数芯片会精简设计,实际优先级数会比较少。STM32只有16级优先级。在设计芯片时会裁掉优先级的几个低端有效位,以减少优先级数,如下图所示:
    在这里插入图片描述
    上图中,Bit0~Bit4的返回值总是零。对于3个位的情况,可使用的优先级就是8个:0x00、0x20、0x40、0x60、0x80、0xA0、0xC0和0xE0。注意,STM32选择了4位作为优先级!

    为了使抢占机能变得可控,Cortex-M处理器把256个优先级按位分为高低两段:抢占优先级和亚优先级,NVIC中有一个寄存器叫“应用程序中断及复位控制寄存器AIRCR”,其中有个位段名为优先级组
    在这里插入图片描述
    PRIGROUP是优先级分组,把优先级分为两个位段:MSB所在位段(左)对应抢占优先级,LSB所在位段(右)对应亚优先级:
    在这里插入图片描述
    在msic.h中有定义:
    在这里插入图片描述
    可以看出有5个分组,如果选择分组4,即NVIC_PriorityGroup_4,那4位优先级都是抢占优先级,没有亚优先级。
    由于FreeRTOS的中断配置没有处理亚优先级这种情况,所以必须配置为组4!

    1.4 优先级设置
    每个外部中断有一个对应的优先级寄存器,每个寄存器占8位,因此最大宽度是8位,最小是3位。4个相邻优先级寄存器组成一个32位寄存器。根据优先级分组设置,优先级可分为高、低两个位段,即抢占优先级和亚优先级。优先级寄存器可按字节访问,也可按半字/字来访问,有意义的优先级寄存器数目由厂商确定:
    在这里插入图片描述
    在这里插入图片描述
    根据四个相邻寄存器可拼成一个32位寄存器,因此地址0xE000_ED20~0xE000_ED23这四个寄存器可拼成一个地址为0xE000ED20的32位寄存器。
    FreeRTOS在设置PendSV和SysTick的中断优先级时是直接操作地址0xE000_ED20。

    1.5 用于中断屏蔽的特殊寄存器
    这里重点关注PRIMASK、FAULTMASK和BASEPRI三个寄存器:
    1)PRIMASK和FAULTMASK寄存器
    在应用中,需要暂时屏蔽所有的中断,执行一些对时序要求严格的任务,这时要用PRIMASK寄存器,它用于禁止除NMI和HardFalut外的所有异常和中断,汇编编程时用CPS(修改处理器状态)指令修改PRIMASK寄存器:

    CPSIE     I;    // 清除PRIMASK, 使能中断
    CPSID     I;    // 设置PRIMASK, 禁止中断
    

    PRIMASK寄存器还可以通过MRS和MSR指令访问:

    MOVS    R0,   #1
    MSR     PRIMASK,     R0  ;  // 将1写入PRIMASK禁止所有中断
    MOVS    R0,   #0
    MSR     PRIMASK,     R0  ;  // 将0写入PRIMASK以使能中断
    

    FAULTMASK比PRIMASK更厉害,它可以连HardFault都屏蔽掉,FAULTMASK会在退出时自动清零,汇编编程时用CPS指令:

    CPSIE   F   ; // 清除FAULTMASK
    CPSID   F   ; // 设置FAULTMASK
    

    用MRS和MSR指令:

    MOVS   R0,   #1
    MSR    FAULTMASK,   R0   ; // 将1写入FAULTMASK禁止所有中断
    MOVS   R0,   #0
    MSR    FAULTMASK,   R0   ; // 将0吸入FAULTMASK使能中断
    

    2)BASEPRO寄存器
    当只要屏蔽优先级低于某个阈值的中断时,可用BASEPRI寄存器,向BASEPRI写0会停止屏蔽中断。若屏蔽优先级不高于0x60的中断,可用如下汇编编程:

    MOV   R0,    #0X60
    MSR   BASEPRI,  R0
    

    如要取消BASEPRI对中断的屏蔽:

    MOV   R0,   #0
    MSR   BASEPRI,   R0
    

    FreeRTOS开关中断就是操作BASEPRI寄存器实现的!

    2. FreeRTOS中断配置宏

    1)configPRIO_BITS
    设置MCU使用几位优先级,STM32使用4位,则宏为4!

    2)configLIBRARY_LOWEST_INTERRUPT_PRIORITY
    设置最低优先级。由于STM32优先级使用4位,且STM32配置为分组4,即4位都是抢占优先级。那么最低优先级即为15。所以此宏为15。不同MCU值不同,要看所用的MCU架构。

    3)configKERNEL_INTERRUPT_PRIORITY
    设置内核中断优先级,宏定义如下:

    #define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    

    左移(8 - configPRIO_BITS) 位,即左移4位。因为STM32使用4位作为优先级,且该4位是高4位,因此要左移4位!
    configKERNEL_INTERRUPT_PRIORITY用来设置PendSV和滴答定时器的中断优先级,在port.c中有如下定义:
    在这里插入图片描述
    PendSV优先级设置是configKERNEL_INTERRUPT_PRIORITY左移16位,
    SysTick优先级设置是configKERNEL_INTERRUPT_PRIORITY左移24位。
    因为,PendSV和SysTick的中断优先级操作是0xE000_ED20地址,一次写入的是32位数据,SysTick和PendSV的优先级寄存器对应这个32位数据的最高8位和次高8位,所以是左移16位和左移24位。
    PendSV和SysTick优先级在port.c文件里的xPortStartScheduler()函数里设置:

    BaseType_t xPortStartScheduler( void )
    {
    	#if( configASSERT_DEFINED == 1 )
    	{
    		volatile uint32_t ulOriginalPriority;
    		volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
    		volatile uint8_t ucMaxPriorityValue;
    		/* Determine the maximum priority from which ISR safe FreeRTOS API
    		functions can be called.  ISR safe functions are those that end in
    		"FromISR".  FreeRTOS maintains separate thread and ISR API functions to
    		ensure interrupt entry is as fast and simple as possible.
    		Save the interrupt priority value that is about to be clobbered. */
    		ulOriginalPriority = *pucFirstUserPriorityRegister;
    		/* Determine the number of priority bits available.  First write to all possible bits. */
    		*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
    		/* Read the value back to see how many bits stuck. */
    		ucMaxPriorityValue = *pucFirstUserPriorityRegister;
    		/* Use the same mask on the maximum system call priority. */
    		ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
    		/* Calculate the maximum acceptable priority group value for the number of bits read back. */
    		ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
    		while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
    		{
    			ulMaxPRIGROUPValue--;
    			ucMaxPriorityValue <<= ( uint8_t ) 0x01;
    		}
    		/* Shift the priority group value back to its position within the AIRCR register. */
    		ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
    		ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
    		/* Restore the clobbered interrupt priority register to its original value. */
    		*pucFirstUserPriorityRegister = ulOriginalPriority;
    	}
    	#endif /* conifgASSERT_DEFINED */
    	/* Make PendSV and SysTick the lowest priority interrupts. */
    	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;     // PendSV中断优先级设置
    	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;    // SysTick中断优先级设置
    	/* Start the timer that generates the tick ISR.  Interrupts are disabled here already. */
    	vPortSetupTimerInterrupt();
    	/* Initialise the critical nesting count ready for the first task. */
    	uxCriticalNesting = 0;
    	/* Start the first task. */
    	prvStartFirstTask();
    	/* Should not get here! */
    	return 0;
    }
    

    上述代码中,PendSV和SysTick优先级设置是直接向地址portNVIC_SYSPRI2_REG写入优先级数据,portNVIC_SYSPRI2_REG是个宏,在port.c中定义如下:

    #define portNVIC_SYSPRI2_REG				( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
    

    可知,portNVIC_SYSPRI2_REG地址是0xE000ED20,同时可知在FreeRTOS中PendSV和SysTick的中断优先级是最低的。

    4) configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
    设置FreeRTOS系统可管理的最大优先级,也就是BASEPRI寄存器的那个阈值优先级,这里设为5(不强制),即高于5的优先级不归FreeRTOS管理。

    5)configMAX_SYSCALL_INTERRUPT_PRIORITY
    由configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移4位而来。设置好以后,低于此优先级的中断可安全的调用FreeRTOS的API函数,高于此优先级的中断FreeRTOS是不能禁止的,中断服务函数也不能调用FreeRTOS的API函数。
    以STM32为例,有16个优先级,0为最高优先级,15为最低优先级,配置如下:
    configMAX_SYSCALL_INTERRUPT_PRIORITY == 5;
    configKERNEL_INTERRUPT_PRIORITY == 15。
    结果如下图:
    在这里插入图片描述

    3. FreeRTOS开关中断

    FreeRTOS开关中断函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),位于portmacro.h中:

    #define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
    #define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
    

    可以看出开关中断实际上是通过函数vPortRaiseBASEPRI()和vPortSetBASEPRI(0)来实现的:

    static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
    {
    	__asm
    	{
    		/* Barrier instructions are not used as this function is only used to lower the BASEPRI value. */
    		msr basepri, ulBASEPRI
    	}
    }
    
    static portFORCE_INLINE void vPortRaiseBASEPRI( void )
    {
    	uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    	__asm
    	{
    		/* Set BASEPRI to the max syscall priority to effect a critical section. */
    		msr basepri, ulNewBASEPRI
    		dsb
    		isb
    	}
    }
    

    函数vPortSetBASEPRI()是向寄存器BASEPRI写入一个值,portENABLE_INTERRUPTS()是开中断,它传递一个0值给vPortSetBASEPRI(),即开中断。
    函数vPortRaiseBASEPRI()是向寄存器BASEPRI写入宏configMAX_SYSCALL_INTERRUPT_PRIORITY,那么优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断就会被屏蔽。

    4. 临界段代码

    临界段代码也称临界区,指那些必须完整运行,不能被打断的代码段。FreeRTOS在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS系统本身就有很多临界段代码,这些代码都加了临界段代码保护。
    FreeRTOS与临界段代码保护有关的函数有4个:taskENTER_CRITICAL()、taskEXIT_CRITICAL()、taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR(),这四个函数是宏定义,在task.h文件中。前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护。

    4.1 任务级临界段代码保护
    taskENTER_CRITICAL()和taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个是成对使用的,在task.h文件里如下:

    #define taskENTER_CRITICAL()		portENTER_CRITICAL()
    #define taskEXIT_CRITICAL()			portEXIT_CRITICAL()
    

    portENTER_CRITICAL()和portSET_INTERRUPT_MASK_FROM_ISR()也是宏定义,在portmacro.h文件里:

    #define portENTER_CRITICAL()					vPortEnterCritical()
    #define portEXIT_CRITICAL()						vPortExitCritical()
    

    函数vPortEnterCritical()和vPortExitCritical()位于port.c文件中:

    void vPortEnterCritical( void )
    {
    	portDISABLE_INTERRUPTS();
    	uxCriticalNesting++;
    	/* This is not the interrupt safe version of the enter critical function so assert() if it is being called from an interrupt context.  Only API functions that end in "FromISR" can be used in an interrupt.  Only assert if the critical nesting count is 1 to protect against recursive calls if the assert function also uses a critical section. */
    	if( uxCriticalNesting == 1 )
    	{ configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); }
    }
    
    void vPortExitCritical( void )
    {
    	configASSERT( uxCriticalNesting );
    	uxCriticalNesting--;
    	if( uxCriticalNesting == 0 )
    	{ portENABLE_INTERRUPTS(); }
    }
    

    上述代码可知,在进入函数vPortEnterCritical()以后会首先关闭中断,给变量uxCriticalNesting加1,uxCriticalNesting是全局变量,记录临界段嵌套次数。函数vPortExitCritical()是退出临界段调用的,将uxCriticalNesting减1,只有当uxCriticalNesting为0时才会调用函数portENABLE_INTERRUPTS()使能中断。这样,只有所有临界段代码都退出后才会使能中断!

    任务级临界段代码保护案例:

    void taskcritical_test(void)
    {
    	while(1)
    	{
    		taskENTER_CRITICAL();   // 进入临界区
    		total_num+=0.01f;
    		printf("total_num的值为:%.4f\r\n",total_num);
    		taskEXIT_CRITICAL();    // 退出临界区
    		vTaskDelay(1000);
    	}
    }
    

    注意临界区代码一定要精简,因为进入临界区会关闭中断,导致优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断得不到及时的响应!

    4.2 中断级临界段代码保护
    函数 taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY!
    这两个函数位于task.h文件中:

    #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
    #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
    

    portSET_INTERRUPT_MASK_FROM_ISR()和portCLEAR_INTERRUPT_MASK_FROM_ISR( x )位于portmacro.h文件中:

    #define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
    #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
    

    vPortSetBASEPRI(x)前面已经说明,ulPortRaiseBASEPRI()位于也portmacro.h文件中:

    static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
    {
    	uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
    	__asm
    	{
    		/* Set BASEPRI to the max syscall priority to effect a critical section. */
    		mrs ulReturn, basepri       // 读出BASEPRI的值,保存在ulReturn中
    		msr basepri, ulNewBASEPRI   // 将configMAX_SYSCALL_INTERRUPT_PRIORITY写入到寄存器BASEPRI中。
    		dsb
    		isb
    	}
    	return ulReturn;                // 返回ulReturn,退出临界区代码保护时要用此值!
    }
    

    中断级临界代码保护案例:

    void TIM3_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) // 溢出中断
    	{	
    		status_value=taskENTER_CRITICAL_FROM_ISR();    // 进入临界区
    		total_num+=1;
    		printf("float_num的值为:%d\r\n",total_num);
    		taskEXIT_CRITICAL_FROM_ISR(status_value);      // 退出临界区
    	}
    	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
    }
    
    展开全文
  • 中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务,待中断服务函数执行完后再回来执行之前被中断的任务。Cortex-M 内核的 MCU 提供了一用于中断管理的嵌套向量中断控制器(NVIC)。 ...

    FreeRTOS 的中断配置是一个很重要的内容,我们需要根据所使用的 MCU 来具体配置。因此要先了解 MCU 架构中有关中断的知识。

    Cortex-M 中断

    1.中断简介

    中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务,待中断服务函数执行完后再回来执行之前被中断的任务。Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。

    Cotex-M3 和 M4 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个Systick(滴答定时器)定时器中断和多个系统异常。

    2.中断管理简介

    Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在NVIC 和系统控制块(SCB)中,CMSIS 将这些寄存器定义为结构体。以 STM32F407 为例,打开core_cm4.h,有两个结构体,NVIC_Type 和 SCB_Type,NVIC 和 SCB 都位于系统控制空间(SCS)内,SCS 的地址从 0XE000E000 开始,SCB 和 NVIC的地址也在 core_cm4.h 中有定义。这些中断控制寄存器在我们一直FreeRTOS操作系统时是不必关系的,如果想了解的话可以可以参考 Cortex-M 权威指南,我们重点关心的是三个中断屏蔽寄存器:PRIMASK、FAULTMASK 和 BASEPRI,这三个寄存器后面会讲解。

    3.优先级分组定义

    当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级来决定的,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断,这个就是中断嵌套。Cortex-M 处理器的有些中断是具有固定的优先级的,比如复位、NMI、HardFault,这些中断的优先级都是负数,优先级也是最高的。

    Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。但是,绝大多数的芯片都会精简设计的,以致实际上支持的优先级数会更少,如 8 级、16 级、32 级等,比如 STM32 就只有 16 级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数,所以不管用多少位来表达优先级,都是 MSB 对齐的,如下图就是使用三位来表达优先级。

    Bit0~Bit4 没有实现,所以读它们总是返回零,写如它们的话则会忽略写入的值。因此,对于 3 个位的情况,可是使用的优先级就是 8 个:0X00(最高优先级)、0X20、0X40、0X60、0X80、0XA0、0XC0 和 0XE0。注意,这个是芯片厂商来决定的!不是我们能决定的,比如 STM32 就选择了 4 位作为优先级!

    为了使抢占机能变得更可控,Cortex-M 处理器还把 256 个优先级按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级)。NVIC 中有一个寄存器是“应用程序中断及复位控制寄存器(AIRCR)”,AIRCR 寄存器里面有个位段名为“优先级组” PRIGROUP,它把优先级分为两个位段:MSB 所在的位段(左边的)对应抢占优先级,LSB 所在的位段(右边的)对应亚优先级

    STM32 使用了 4 位,因此最多有 5 组优先级分组设置,这 5 个分组在 msic.h 中有定义。注意,STM32 中定义的分组 0 对应的是分组位置7,因为它对应的值是0x700(=7)。因为FreeRTOS 的中断配置没有处理亚优先级这种情况,所以我们只能配置中断优先级分组为 4,直接就 16 个主优先级,使用起来也简单!

    #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
    4 bits for subpriority */
    #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
    3 bits for subpriority */
    #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
    2 bits for subpriority */
    #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
    1 bits for subpriority */
    #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
    0 bits for subpriority */

    4.用于中断屏蔽的特殊寄存器--PRIMASK,FAULTMASK,BASEPRI

    在许多应用中,需要暂时屏蔽所有的中断一执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器,PRIMASK 用于禁止除 NMI 和 HardFalut 外的所有异常和中断,汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值:

    CPSIE I;  //清除 PRIMASK(使能中断)
    CPSID  I;  //设置 PRIMASK(禁止中断)

    PRIMASK 寄存器还可以通过 MRS 和 MSR 指令访问:

    MOVS R0, #1
    MSR PRIMASK,  R0  ;//将 1 写入 PRIMASK 禁止所有中断
    
    MOVS R0, #0 
    MSR PRIMASK,  R0  ;//将 0 写入 PRIMASK 以使能中断

    FAULTMASK 比 PRIMASK 更狠,它可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似,FAULTMASK 会在退出时自动清零。使用方法如下:

    CPSIE F  ;清除 FAULTMASK
    CPSID F  ;设置 FAULTMASK
    
    MOVS  R0, #1 
    MSR FAULTMASK, R0  ;将 1 写入 FAULTMASK 禁止所有中断
    MOVS R0, #0
    MSR FAULTMASK, R0  ;将 0 写入 FAULTMASK 使能中断
    

    BASEPRI 寄存器用于设置某个阈值,只屏蔽优先级低于这个阈值的中断。比如,我们要屏蔽优先级不高于0x60的中断,则可以使用如下汇编编程:

    MOV  R0, #0X60
    MSR BASEPRI, R0

    如果需要取消 BASEPRI 对中断的屏蔽,可以使用如下代码:

    MOV  R0, #0
    MSR BASEPRI, R0

    注意!FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!


    FreeRTOS  开关中断

    FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS()。利用他们可以打开或屏蔽FreeRTOSConfig.h中指定优先级的中断。


    临界段代码

    临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段,比如有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS 系统本身就有很多的临界段代码,这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。

    FreeRTOS 与 临 界 段 代 码 保 护 有 关 的 函 数 有 4 个 : taskENTER_CRITICAL() 、taskEXIT_CRITICAL() 、 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR()。他们在 task.h 文件中有相应的宏定义。

    1.任务级临界段代码保护

    taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的。任务级临界代码保护使用方法如下:

    void taskcritical_test(void)
    {
        while(1)
        {
            taskENTER_CRITICAL(); 
            total_num+=0.01f;
            printf("total_num 的值为: %.4f\r\n",total_num);
            taskEXIT_CRITICAL();
            vTaskDelay(1000);
        } 
    }

    注意临界区代码一定要精简!因为进入临界区会关闭中断,这样会导致优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及时的响应!

    2.中断级临界段代码保护

    taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY,因为高于这个优先级的中断服务函数不能调用 FreeRTOS 的 API 函数。中断级临界代码保护使用方法如下:

    //定时器 3 中断服务函数
    void TIM3_IRQHandler(void)
    {
        if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
        {
            status_value=taskENTER_CRITICAL_FROM_ISR(); 
            total_num+=1;
            printf("float_num 的值为: %d\r\n",total_num);
            taskEXIT_CRITICAL_FROM_ISR(status_value);
        }
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
    }

     

    展开全文
  • EXIT按键中断函数

    2021-01-26 16:21:05
    EXIT中断函数 一、关于中断函数 EXIT基本是第一接触中断函数,问题来了 什么是中断:中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动...④配置中断服务函数。 三、代码块 void EXTIX_Init(vo

    EXIT中断函数

    一、关于中断函数
    EXIT基本是第一个接触的中断函数,问题来了

    什么是中断:中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

    中断和异常向量表可在stm32中文参考手册中9.1.2查询
    二、编程流程
    要实现STM32外部中断,按照基本流程来讲,可分为四部分:
    ①初始化GPIO;
    ②初始化EXTI;
    ③初始化NVIC;
    ④配置中断服务函数。

    三、代码块

    void EXTIX_Init(void)
    {
    	NVIC_InitTypeDef NVIC_InitStrue;
    	EXTI_InitTypeDef EXTI_InitStrue;
    	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); 
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//注意此处的先后顺序,否则会有bug
    	
    	KEY_Init();
    	//GPIOE.0 中断线以及中断初始化配置   下降沿触发
    	EXTI_InitStrue.EXTI_Line=EXTI_Line0;//设置中断线0
    	EXTI_InitStrue.EXTI_LineCmd=ENABLE;//使能中断线0
    	EXTI_InitStrue.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式
    	EXTI_InitStrue.EXTI_Trigger=EXTI_Trigger_Rising_Falling;//中断捕获的位置
    	EXTI_Init(&EXTI_InitStrue);//根据EXTI_InitStrue中指定的参数初始化外设NVIC寄存器
    	
    	NVIC_InitStrue.NVIC_IRQChannel=EXTI0_IRQn;//使能按键WK_UP所在的外部中断通道
    	NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;//使能外部中断通道
    	NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
    	NVIC_InitStrue.NVIC_IRQChannelSubPriority=2;//子优先级3
    	NVIC_Init(&NVIC_InitStrue);//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    
    //往后可以继续增加EXITX 需要自行配置 与上面两段代码相似
    }
    

    四、中断函数体(中断线路0)

    //外部中断0服务程序 
    void EXTI0_IRQHandler(void)
    {
    	delay_ms(10);//消抖
    	if(WK_UP==1)	 	 //WK_UP按键
    	{				 
    		BEEP=!BEEP;	
    	}
    	EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
    }
    //往后可以继续增加中断函数 内容需要自行编写如KEY0、KEY1等
    
    展开全文
  • MSP430 DM430-A开发板学习笔记(四)按键中断使用 1.按键说明 在MSP430开发板中,有四个按键连接到P1,即 按键 P1口 ...1.可以在中断服务程序中对变量值进行修改,从而达到随时更改变量值
  • 1 驱动程序,操作系统,文件系统和应用程序之间关系 字符设备和块设备映射到...从驱动程序角度来从下到上看层次结构,包含了硬件设备,设备驱动程序(中断处理程序,设备服务程序),文件系统和用户进程四个层次:
  • PHP有着与C一样的流程控制。我将在下面大概介绍。 if, else, elseif, if(): endif if (表达式一) { . . . } elseif (表达式二) { . . . } else { . . . } // 或者像Python一样 if (表达式一) : . . . . . . elseif...
  • 文章在比较了多种生成SPWM波的技术基础上,给出了利用等效面积法来产生SPWM波形的工作原理,详细介绍了由单片机STCl2C5410AD的可编程计数器阵列PCA实现SPWM控制软件的编写过程,并给出了SPWM中断服务程序的流程图。...
  • 15.3 服务客户 15.4 数据报 15.5 一Web应用 15.5.1 服务器应用 15.5.2 NameSender程序片 15.5.3 15.5.3 要注意问题 15.6 Java与CGI沟通 15.6.1 CGI数据编码 15.6.2 程序片 15.6.3 用C++写CGI程序 ...
  • 文章在比较了多种生成SPWM波的技术基础上,给出了利用等效面积法来产生SPWM波形的工作原理,详细介绍了由单片机STCl2C5410AD的可编程计数器阵列PCA实现SPWM控制软件的编写过程,并给出了SPWM中断服务程序的流程图。...
  • 有1,2,3,4四个中断源根据中断服务流程图判断这四个中断源优先级任意写出一种情况各中断源屏蔽字是![图片说明](https://img-ask.csdn.net/upload/201709/16/1505570386_860971.jpg)
  • 叶秉承的四个特性: 1.唯一,绝对不会出现重复ID,并且ID整体趋势递增。 2.高可用,服务完全基于分布式架构,即使MySQL停机机,也能容忍中断的数据库不可用。 3.高并发低延迟,在CentOS 4C8G虚拟机上,远程调用...
  • 博文大纲: 一、何为双机热备? 二、VRRP概念 三、VRRP两种角色 、VRRP选举流程 ...所谓双机热备无非就是以7X24小时不中断的为企业提供服务为目的,各种双机热备技术很多,那么华为使用了这...
  • 本文研究并设计了基于Android+HTML5在线认证检测系统,主要工作包括以下四个方面: (1)针对多窗口类浏览器模式问题,指出并分析了该问题存在原因,利用Activity运行机制,通过Fragment栈对主要模块Webview...
  •  本章总结了在内核模式下的四种等待方法,读者可以利用这些方法灵活地用在自己驱动程序中。最后本章还介绍了如何对IRP超时情况进行处理。  10.1 定时器实现方式一  10.1.1 I/O定时器  10.1.2 示例代码  ...
  • 你可以在QQ找到计算机/考研/南京大学交流群我都加过,基本上没什么卵用(建议大家不要在这上面花费太多时间,当然了,你想没事水水群话另说)。 这群是我迄今为止见到最好,其他群里面有价值资料我都...
  • *部分(第1~2章)讲解计算机程序基本执行流程与元素,以及数据背后二制表示,帮读者掌握编程基本概念。 第二部分(第3~7章)讲解面向对象编程原理与逻辑,涉及类、继承与多态、口与抽象类、异常与常用...
  • 用于个人复习,答案汇总在xmind。 一、概述 ...同步和互斥,同步方式和四个准则 进程通信方法 死锁概念、原因 死锁产生条件、处理策略 三、内存管理 内存管理功能 用户程序变成可执行程序步骤
  • 基于Actions炬芯ATB1103芯片语音遥控器,打造了一AIoT时代高性价比精品。 一、遥控器应用总体架构 遥控器总体架构分层,从上到下依次为应用层、应用框架层、硬件抽象层、底层驱动层 1.1、应用层 • 应用...
  • 包括:网络设备的安全性、操作系统的安全性、协议软件的安全性、系统安全监视乏力、对病毒和黑客侵袭的抵抗不足、应用服务的安全性等。 (3)物理自然环境恶化 物理自然环境恶化是指网上会展信息系统物理基础的支持...
  • 同时要考察软件供应商的综合实力、实施人员素质及其后续服务的能力。 再次,实施ERP项目要依靠整个团队。要想成功实施ERP,必须有一批精通ERP管理思想、掌握软件技术,同时又有中国环境下组织实施ERP项目经验的...
  • 小米和合作单位分别就您在本网站接受服务的过程中享受的权利和承担的义务,与您签订本协议,并独立向您承担责任,互不承担保证、连带或共同责任等。 </p> <p class="fb&#...
  • 继续跟着steven大佬修改 回射程序接着第六章程序那个程序大致流程是 先读输入,读完输入再发给服务器,接着等服务器输入,并输出到屏幕上, 总共四个步骤但是,假如我们一次性输入数据非常多,大概有10000...
  • 操作系统自测题

    2019-02-17 13:34:17
    一、单项选择题,在四个备选答案中选一个合适答案 1.操作系统是扩充( )功能第1层系统软件。 A.软件 B.裸机 C.机器语言 D.中断 2.从资源管理程序观点来看,操作系统是( )管理者。 A.程序和数据 B...
  • TCP/IP

    2015-05-07 13:43:37
    注:熟练掌握TCP/IP 各连接与中断流程,及状态变化;...在TCP/IP协议中,TCP协议提供可靠连接服务,采用三次握手建立一连接。 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SE...
  • TCP/IP 内核参数优化

    2014-07-29 10:45:56
    注:熟练掌握TCP/IP 各连接与中断流程,及状态变化;...建立连接在TCP/IP协议中,TCP协议提供可靠连接服务,采用三次握手建立一连接。第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEN...
  • 第8章Linux设备驱动中的中断 8.1 Linux中断中断处理架构 8.2 Linux中断编程 8.2.1申请和释放中断 8.2.2使能与屏蔽中断 8.2.3底半部机制 8.2.4中断共享 8.3 Linux定时器 8.4 Linux延时处理 8.4.1短延时 8.4.2长延时...

空空如也

空空如也

1 2 3 4
收藏数 72
精华内容 28
关键字:

中断服务的流程四个