閔行區(qū)做網(wǎng)站公司寧德市教育局
目錄
FreeRTOS的中斷管理
1、STM32中斷優(yōu)先級管理
2、FreeRTOS任務(wù)優(yōu)先級管理
3、寄存器和內(nèi)存映射寄存器
4、BASEPRI寄存器
5、FreeRTOS與STM32中斷管理結(jié)合使用
?vPortRaiseBASEPRI
?vPortSetBASEPRI
6、FromISR后綴
7、在中斷服務(wù)函數(shù)中調(diào)用FreeRTOS的API函數(shù)需注意
FreeRTOS的臨界段代碼
1、通過中斷管理臨界區(qū)
1)taskENTER_CRITICAL():進(jìn)入臨界區(qū)
2)taskEXIT_CRITICAL():退出臨界區(qū)
3)taskENTER_CRITICAL_FROM_ISR():進(jìn)入臨界區(qū)(中斷級)
4)taskEXIT_CRITICAL_FROM_ISR():退出臨界區(qū)(中斷級)
2、通過掛起和恢復(fù)任務(wù)調(diào)度器管理臨界區(qū)
1)uxSchedulerSuspended
2)vTaskSuspendAll():掛起任務(wù)調(diào)度器
3)xTaskResumeAll():恢復(fù)任務(wù)調(diào)度器
FreeRTOS的中斷管理
1、STM32中斷優(yōu)先級管理
在STM32中,中斷優(yōu)先級是通過中斷優(yōu)先級配置寄存器的高4位 [7:4] 來配置的。因此STM32支持最多16級中斷優(yōu)先級,其中數(shù)值越小表示優(yōu)先級越高,即更緊急的中斷。
2、FreeRTOS任務(wù)優(yōu)先級管理
FreeRTOS任務(wù)調(diào)度的優(yōu)先級相反,數(shù)值越大越優(yōu)先。任務(wù)優(yōu)先級的最大值由FreeRTOSConfig.h中的配置項configMAX_PRIORITIES決定,默認(rèn)為5,如下。
#define configMAX_PRIORITIES ? ? ? ?( 5 )
?該配置項在當(dāng)前環(huán)境下不能大于32,否則編譯報錯,如下。
#if ( configMAX_PRIORITIES > 32 )#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. ?It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
#endif
因此,可以得出結(jié)論,在當(dāng)前配置下,FreeRTOS的任務(wù)最多可以有32個優(yōu)先級。
優(yōu)先級范圍[0, configMAX_PRIORITIES),即最大優(yōu)先級為configMAX_PRIORITIES-1。
3、寄存器和內(nèi)存映射寄存器
這里的寄存器不同于STM32的外設(shè)寄存器,后者是內(nèi)存映射寄存器,實際上是在內(nèi)存中劃分特定的地址空間,用于訪問和控制外設(shè)的功能。而此處的寄存器是Cortex-M3內(nèi)核中“真正的”寄存器,基于SR鎖存器構(gòu)建。
4、BASEPRI寄存器
BASEPRI寄存器即Base Priority Mask Register(基本優(yōu)先級屏蔽寄存器)。顧名思義,該寄存器是用來屏蔽中斷響應(yīng)的,位定義如下。
BASEPRI只有bit7-bit4起作用,當(dāng)這四位不為0時,會屏蔽優(yōu)先級數(shù)值上大于等于該值的中斷響應(yīng)。如:BASEPRI設(shè)置為0x50(只看bit7-bit4,也就是5),代表中斷優(yōu)先級在5~15內(nèi)的均被屏蔽,0~4的中斷優(yōu)先級正常執(zhí)行。
當(dāng)bit7-bit4為0時無效,即不屏蔽任何中斷。
5、FreeRTOS與STM32中斷管理結(jié)合使用
FreeRTOS可以與STM32原生的中斷機(jī)制結(jié)合使用。
FreeRTOS初始化時,PendSV和SysTick被設(shè)置最低中斷優(yōu)先級(數(shù)值最大,15),保證系統(tǒng)任務(wù)切換不會阻塞系統(tǒng)其他中斷的響應(yīng)。
FreeRTOS提供了一組宏定義用于禁用和啟用中斷,如下。
#define portDISABLE_INTERRUPTS() ? ? ? ? ? ? ? ? ?vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() ? ? ? ? ? ? ? ? ? vPortSetBASEPRI( 0 )
?vPortRaiseBASEPRI
vPortRaiseBASEPRI是一個內(nèi)連匯編函數(shù),如下。
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. */
/* *INDENT-OFF* */msr basepri, ulNewBASEPRIdsbisb
/* *INDENT-ON* */}
}
該函數(shù)先把configMAX_SYSCALL_INTERRUPT_PRIORITY賦值給ulNewBASEPRI,然后通過msr指令將ulNewBASEPRI的值賦給basepri寄存器。上述過程實際上是將configMAX_SYSCALL_INTERRUPT_PRIORITY配置項的值賦給了basepri寄存器。該配置項在FreeRTOSConfig.h中定義,默認(rèn)值為191。因為是賦給basepri的,所以只有bit7-bit4有效,因而191等價于0xB0。?
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ? ?191
該配置項默認(rèn)值為191,那么,默認(rèn)情況下,FreeRTOS進(jìn)入臨界區(qū)時會屏蔽中斷優(yōu)先級數(shù)值上大于等于11的中斷。我們可以說FreeRTOS管理的最大優(yōu)先級為11。
在STM32F103ZET6單片機(jī)上使用FreeRTOS時,建議將上述配置項設(shè)置為較低的優(yōu)先級值(邏輯上優(yōu)先級較高),通常是5。需要注意的是,vPortRaiseBASEPRI函數(shù)中,會將configMAX_SYSCALL_INTERRUPT_PRIORITY直接賦值給BASEPRI寄存器,而不會進(jìn)行移位操作,因此,當(dāng)我們要讓FreeRTOS可以管理的最大優(yōu)先級設(shè)置為5時,要確保configMAX_SYSCALL_INTERRUPT_PRIORITY的bit7-bit4為5,通常賦值為0x50。
?vPortSetBASEPRI
vPortSetBASEPRI也是一個內(nèi)聯(lián)匯編函數(shù),如下。
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. */
/* *INDENT-OFF* */msr basepri, ulBASEPRI
/* *INDENT-ON* */}
}
?該函數(shù)將ulBASEPRI賦值給BASEPRI寄存器。因此,portENABLE_INTERRUPTS()宏的邏輯是將0賦給BASERPI,不再屏蔽任何中斷。
6、FromISR后綴
FreeRTOS提供了一組帶FromISR后綴的函數(shù),這類函數(shù)是對應(yīng)無后綴API的另一個版本,專門用于在ISR(Interrupt Service Routine,中斷服務(wù)函數(shù)或例程)中調(diào)用,相較于無后綴版本,增加了一些額外操作。
7、在中斷服務(wù)函數(shù)中調(diào)用FreeRTOS的API函數(shù)需注意
1、中斷服務(wù)函數(shù)的優(yōu)先級需在FreeRTOS所管理的范圍內(nèi),閾值由configMAX_SYSCALL_INTERRUPT_PRIORITY指定。
2、建議將所有優(yōu)先級位指定為搶占優(yōu)先級位,方便FreeRTOS管理。
3、在中斷服務(wù)函數(shù)里邊需調(diào)用FreeRTOS的API函數(shù),必須使用帶“FromISR”后綴的函數(shù)。
FreeRTOS的臨界段代碼
臨界段代碼,又稱為臨界區(qū),指的是那些必須在不被打斷的情況下完整運(yùn)行的代碼段。例如,某些外設(shè)的初始化可能要求嚴(yán)格的時序,因此在初始化過程中不允許被中斷打斷。
1、通過中斷管理臨界區(qū)
在FreeRTOS中,臨界段代碼需要被“臨界區(qū)進(jìn)入”和“臨界區(qū)退出”函數(shù)保護(hù)起來。臨界區(qū)的進(jìn)入和退出可以通過中斷來實現(xiàn),相關(guān)函數(shù)有 4 個:
1)taskENTER_CRITICAL():進(jìn)入臨界區(qū)
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 );}
}
是通過portDISABLE_INTERRUPTS()宏禁用中斷實現(xiàn)的。?
2)taskEXIT_CRITICAL():退出臨界區(qū)
void vPortExitCritical( void )
{configASSERT( uxCriticalNesting );uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}
}
是通過調(diào)用portENABLE_INTERRUPTS()宏啟用中斷實現(xiàn)的。
3)taskENTER_CRITICAL_FROM_ISR():進(jìn)入臨界區(qū)(中斷級)
4)taskEXIT_CRITICAL_FROM_ISR():退出臨界區(qū)(中斷級)
進(jìn)入和退出臨界區(qū)是成對使用的。每進(jìn)入一次臨界區(qū),全局變量uxCriticalNesting都會加一,每調(diào)用一次退出臨界區(qū),uxCriticalNesting減一,只有當(dāng) uxCriticalNesting 為 0 時才會調(diào)用函數(shù) portENABLE_INTERRUPTS()使能中斷。這確保了在存在多個臨界區(qū)代碼的情況下,不會因為某個臨界區(qū)代碼的退出而破壞其他臨界區(qū)的保護(hù)。只有當(dāng)所有的臨界區(qū)代碼都退出時,中斷才會被重新使能。
上文提到,PendSV和SysTick兩個中斷的優(yōu)先級在FreeRTOS初始化時都被置為最低優(yōu)先級15,而這兩個中斷時FreeRTOS任務(wù)切換的核心。因此,在進(jìn)入臨界區(qū)時,FreeRTOS無法執(zhí)行任務(wù)切換,保證了臨界區(qū)操作的原子性。但要注意,優(yōu)先級高于(數(shù)值上低于)配置項configMAX_SYSCALL_INTERRUPT_PRIORITY的中斷未被FreeRTOS管理,仍然可以打斷任務(wù)執(zhí)行。
2、通過掛起和恢復(fù)任務(wù)調(diào)度器管理臨界區(qū)
掛起和恢復(fù)任務(wù)調(diào)度器, 調(diào)用此函數(shù)不需要關(guān)閉中斷:
1)uxSchedulerSuspended
這是一個全局變量,聲明及初始化如下。
PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE;
pdFALSE數(shù)值上為0,如下。?
#define pdFALSE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?( ( BaseType_t ) 0 )
在FreeRTOS中,任務(wù)調(diào)度器可以被多次掛起,uxSchedulerSuspended用于記錄調(diào)度器被掛起的次數(shù),只要它不為0,則無法進(jìn)行任務(wù)切換。?
2)vTaskSuspendAll():掛起任務(wù)調(diào)度器
該函數(shù)的核心邏輯只有一行,如下。
++uxSchedulerSuspended;
調(diào)用該函數(shù)后,任務(wù)切換被禁止,當(dāng)前任務(wù)的執(zhí)行不會被其它任務(wù)打斷,從而保護(hù)了臨界區(qū)代碼。
3)xTaskResumeAll():恢復(fù)任務(wù)調(diào)度器
該函數(shù)核心邏輯如下。
--uxSchedulerSuspended;if( uxSchedulerSuspended?==?( UBaseType_t?) pdFALSE?){……}
首先將uxSchedulerSuspended減1,而后判斷是否為pdFALSE,是則說明所有臨界區(qū)已退出,可以恢復(fù)任務(wù)調(diào)度,執(zhí)行相關(guān)操作。
vTaskSuspendAll和xTaskResumeAll是成對出現(xiàn)的,臨界區(qū)可以嵌套,僅在所有臨界區(qū)全部退出時,才能恢復(fù)任務(wù)切換。
與中斷管理臨界區(qū)不同的是,掛起任務(wù)調(diào)度器時未關(guān)閉中斷;這種方式僅僅防止了任務(wù)之間的資源爭奪,中斷仍然可以直接響應(yīng);掛起調(diào)度器的方法適用于臨界區(qū)位于任務(wù)與任務(wù)之間的情況;這樣既不需要延遲中斷,同時又能確保臨界區(qū)的安全性。