1. 程式人生 > 實用技巧 >2.FreeRTOS中斷優先順序和任務優先順序

2.FreeRTOS中斷優先順序和任務優先順序

FreeRTOS中斷優先順序和任務優先順序

  • 前言:

    最開始,我並沒有搞清楚什麼是中斷優先順序和任務優先順序,但看了部分資料後發現這兩個並沒有半毛錢關係,於是便有了這篇筆記,本篇文章以Cortex-M3(STM32F103)為例子。

1.Cortex-M3的中斷優先順序

​ 根據Cortex-M3權威指南,每個外部中斷都有一個可程式設計的中斷優先順序暫存器。並且記住優先順序越大在這個暫存器的值是越小的。在STM32F103中,該暫存器的高四位才是代表優先順序值,低四位沒用。

​ 優先順序分組暫存器PRIGROUP,把優先順序分為搶佔優先順序和子優先順序,一般為了方便,我們把它設為NVIC_PriorityGroup_4

,意思就是4個bit都為搶佔優先順序,所以一共是0~15個級別的優先順序,0的最優先順序大,15的最優先順序小。

1.1 PenSV和SysTick的中斷優先順序

​ 在FreeRTOS中SysTickPenSV的優先順序都是最低的,具體在開啟排程器函式BaseType_t xPortStartScheduler( void )裡面賦值:

	/* Make PendSV and SysTick the lowest priority interrupts. */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

portNVIC_SYSPRI2_REG實際上是0xE000_ED20-0xE000_ED23(一共四位元組),22-23分別是設定PenSV和Systick優先順序的暫存器。portNVIC_PENDSV_PRIportNVIC_SYSTICK_PRI**的值具體展開:


1. #define __NVIC_PRIO_BITS          4 /*!< STM32 uses 4 Bits for the Priority Levels    */
2. #define configPRIO_BITS       		__NVIC_PRIO_BITS

3. #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			15         //中斷最低優先順序

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

5.#define portNVIC_PENDSV_PRI					( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
6.#define portNVIC_SYSTICK_PRI				( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
    
    

前面說了,STM32使用了4個bit來表示優先順序,並且15是為最低優先順序,很明白,把(0xFFFF0000)這個優先順序放到了0xE000_ED22和0xE000_ED23中,即把Systick和PenSV的優先順序設為最低。

1.1.2 在FreeRTOS中關閉中斷

Cortex-M3中有一個暫存器BASEPRI,和配置優先順序一樣,當低於這個暫存器的優先順序數,會被遮蔽,比如設定了優先順序為5,那麼優先順序0~4會被關閉,FreeRTOS用函式portDISABLE_INTERRUPTS() portENABLE_INTERRUPTS() 控制中斷開關,根據配置的巨集configMAX_SYSCALL_INTERRUPT_PRIORITY確認遮蔽的優先順序數。

1.1.3 FreeRTOS利用關閉中斷實現臨界程式碼區的保護

1.1.3.1普通臨界區

所謂臨界區就是不希望有中斷來打擾某段程式的執行,用函式taskENTER_CRITICAL()taskEXIT_CRITICAL() ,具體看如何實現:

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 );
	}
}

沒錯,就是利用BASEPRI暫存器來關閉中斷,當進入呼叫這個函式時全域性變數uxCriticalNesting會加1,那麼我們基本可以得出,退出臨界區一定是當uxCriticalNesting為1時,會開啟中斷,退出臨界區函式如下:

void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
1.1.3.2中斷臨界區

除此之外還有在保護中斷臨界區的函式,就是不希望某個中斷裡的程式碼不被另一箇中斷打斷,用函式taskENTER_CRITICAL_FROM_ISR() taskEXIT_CRITICAL_FROM_ISR( x ),函式實現上普通臨界區是一模一樣的

2. FreeRTOS 的任務優先順序

FreeRTOS的任務優先順序和中斷優先順序最大的不同就是,任務優先順序數的值越大,任務的優先順序越大。