您好,登錄后才能下訂單哦!
今天小編給大家分享一下freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法的相關(guān)知識點,內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
嵌套向量中斷控制器 NVIC(Nested Vectored Interrupt Controller與內(nèi)核是緊耦合的。提供如下的功能:可嵌套中斷支持、向量中斷支持、動態(tài)優(yōu)先級調(diào)整支持、中斷延遲大大縮短、 中斷可屏蔽。
所有的外部中斷和絕大多數(shù)系統(tǒng)異常均支持可嵌套中斷。異常都可以被賦予不同的優(yōu)先級,當(dāng)前優(yōu)先級被存儲在 xPSR的專用字段中。當(dāng)一個異常發(fā)生時,硬件自動比較該異常的優(yōu)先級和當(dāng)前的異常優(yōu)先級,如果發(fā)現(xiàn)該異常的優(yōu)先級更高,處理器就會中斷當(dāng)前的中斷服務(wù)例程(或者是普通程序),而服務(wù)新來的異常(立即搶占)。
如果優(yōu)先級組設(shè)置使得中斷嵌套層次很深,要確認(rèn)主堆??臻g足夠用。 異常服務(wù)程序總是使用MSP,主堆棧的容量應(yīng)是嵌套最深時需要的量。
CM3 支持中斷嵌套,使得高優(yōu)先級異常會搶占(preempt)低優(yōu)先級異常。
有3個系統(tǒng)異常:復(fù)位,NMI 以及硬 fault,它們有固定的優(yōu)先級,并且優(yōu)先級號是負(fù)數(shù),高于所有其它異常。所有其它異常的優(yōu)先級都是可編程的(但不能編程為負(fù)數(shù))。
CM3 支持3個固定的高優(yōu)先級和多達(dá)256級的可編程優(yōu)先級,并且支持128級搶占。但是大多數(shù)CM3芯片實際上支持的優(yōu)先級數(shù)會更少如8級、16級、32級等。
裁掉表達(dá)優(yōu)先級的幾個低端有效位,從而讓優(yōu)先級數(shù)減少。如果使用更多的位來表達(dá)優(yōu)先級,優(yōu)先級數(shù)增加,需要的門也更多,帶來更多的成本和功耗。
使用3個位來表達(dá)優(yōu)先級,優(yōu)先級配置寄存器的結(jié)構(gòu)如下圖所示,能夠使用的8個優(yōu)先級為:0x00(最高),0x20,0x40,0x60,0x80, 0xA0,0xC0,0xE0。
為了使搶占機(jī)能變得更可控,CM3 把 256 級優(yōu)先級按位分成高低兩段,分別是搶占優(yōu)先級和亞優(yōu)先級。搶占優(yōu)先級決定了搶占行為。當(dāng)搶占優(yōu)先級相同的異常有不止一個懸起時,就優(yōu)先響應(yīng)亞優(yōu)先級最高的異常(亞優(yōu)先級處理內(nèi)務(wù))。
優(yōu)先級分組規(guī)定:亞優(yōu)先級至少是1個位。所以搶占優(yōu)先級最多是7個位,最多只有 128 級搶占的現(xiàn)象。
下圖是只使用 3 個位來表達(dá)優(yōu)先級,從bit5處分組,得到4級搶占優(yōu)先級,每個搶占優(yōu)先級的內(nèi)部有2個亞優(yōu)先級。
下圖是3 位優(yōu)先級,從比特1處分組,(雖然[4:0]未使用,卻允許從它們中分組)。
應(yīng)用程序中斷及復(fù)位控制寄存器(AIRCR)(地址:0xE000_ED00)如下。
中斷發(fā)生時,正在處理同級或高優(yōu)先級異常,或者被掩蔽,則中斷不能立即得到響應(yīng)。此時中斷被懸起。
可以通過中斷設(shè)置懸起寄存器(SETPEND)、中斷懸起清除寄存器(CLRPEND)讀取中斷的懸起狀態(tài),還可以寫它們來手工懸起中斷。
處理器在響應(yīng)某異常時,如果又發(fā)生其優(yōu)先級高的異常,當(dāng)前異常被阻塞,轉(zhuǎn)而執(zhí)行優(yōu)先級高的異常。那么異常執(zhí)行返回后,系統(tǒng)處理懸起的異常時,如果先POP再把POP出的再PUSH回去,這就是浪費CPU時間。
所以CM3不POP這些寄存器,而是繼續(xù)使用上一個異常已經(jīng)PUSH好的成果。如下圖所示。
入棧的階段,尚未執(zhí)行其服務(wù)例程時,如果此時收到了高優(yōu)先級異常的請求,入棧后,將執(zhí)行高優(yōu)先級異常的服務(wù)例程。如果高優(yōu)先級異常來得太晚,以至于已經(jīng)執(zhí)行了前一個異常的指令,則按普通的搶占處理,這會需要更多的處理器時間和額外32字節(jié)的堆??臻g。高優(yōu)先級異常執(zhí)行完畢后,以咬尾中斷方式執(zhí)行之前被搶占的異常。
cortex-m里面開中斷、關(guān)中斷指令
臨界段:一段在執(zhí)行的時候不能被中斷的代碼段(必須完整運行、不能被打斷的代碼段)。一般是對全局變量操作時候,用到臨界段。當(dāng)一個任務(wù)在訪問某個全局變量時,如果被其他中斷打斷,改變了該全局變量,再回到上個任務(wù)時,全局變量已經(jīng)不是當(dāng)時的它了,這種情況可能會導(dǎo)致不可意料的后果。
臨界段被打斷的情況:系統(tǒng)調(diào)度(最終也是產(chǎn)生PendSV中斷);外部中斷。
freertos進(jìn)入臨界段代碼時需要關(guān)閉中斷,處理完臨界段代碼再打開中斷。
首先看下面的代碼。
__asm void prvStartFirstTask( void ) { PRESERVE8 /* 在Cortex-M中,0xE000ED08是SCB_VTOR這個寄存器的地址, 里面存放的是向量表的起始地址,即MSP的地址 */ ldr r0, =0xE000ED08 ldr r0, [r0] ldr r0, [r0] /* 設(shè)置主堆棧指針msp的值 */ msr msp, r0 /* 使能全局中斷 */ cpsie i cpsie f dsb isb /* 調(diào)用SVC去啟動第一個任務(wù) */ svc 0 nop nop } __asm void vPortSVCHandler( void ) { extern pxCurrentTCB; PRESERVE8 ldr r3, =pxCurrentTCB /* 加載pxCurrentTCB的地址到r3 */ ldr r1, [r3] /* 加載pxCurrentTCB到r1 */ ldr r0, [r1] /* 加載pxCurrentTCB指向的值到r0,目前r0的值等于第一個任務(wù)堆棧的棧頂 */ ldmia r0!, {r4-r11} /* 以r0為基地址,將棧里面的內(nèi)容加載到r4~r11寄存器,同時r0會遞增 */ msr psp, r0 /* 將r0的值,即任務(wù)的棧指針更新到psp */ isb mov r0, #0 /* 設(shè)置r0的值為0 */ msr basepri, r0 /* 設(shè)置basepri寄存器的值為0,即所有的中斷都沒有被屏蔽 */ orr r14, #0xd bx r14 } __asm void xPortPendSVHandler( void ) { extern pxCurrentTCB; extern vTaskSwitchContext; PRESERVE8 /* 當(dāng)進(jìn)入PendSVC Handler時,上一個任務(wù)運行的環(huán)境即: xPSR,PC(任務(wù)入口地址),R14,R12,R3,R2,R1,R0(任務(wù)的形參) 這些CPU寄存器的值會自動保存到任務(wù)的棧中,剩下的r4~r11需要手動保存 */ /* 獲取任務(wù)棧指針到r0 */ mrs r0, psp isb ldr r3, =pxCurrentTCB /* 加載pxCurrentTCB的地址到r3 */ ldr r2, [r3] /* 加載pxCurrentTCB到r2 */ stmdb r0!, {r4-r11} /* 將CPU寄存器r4~r11的值存儲到r0指向的地址 */ str r0, [r2] /* 將任務(wù)棧的新的棧頂指針存儲到當(dāng)前任務(wù)TCB的第一個成員,即棧頂指針 */ stmdb sp!, {r3, r14} /* 將R3和R14臨時壓入堆棧 */ mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY /* 進(jìn)入臨界段 */ msr basepri, r0 dsb isb bl vTaskSwitchContext /* 調(diào)用函數(shù)vTaskSwitchContext,尋找新的任務(wù)運行,通過使變量pxCurrentTCB指向新的任務(wù)來實現(xiàn)任務(wù)切換 */ mov r0, #0 /* 退出臨界段 */ msr basepri, r0 ldmia sp!, {r3, r14} /* 恢復(fù)r3和r14 */ ldr r1, [r3] ldr r0, [r1] /* 當(dāng)前激活的任務(wù)TCB第一項保存了任務(wù)堆棧的棧頂,現(xiàn)在棧頂值存入R0*/ ldmia r0!, {r4-r11} /* 出棧 */ msr psp, r0 isb bx r14 nop }
cpsie i cpsie f msr basepri, r0
cpsie icpsie fmsrbasepri, r0
為了快速地開關(guān)中斷,CM3 專門設(shè)置了 CPS 指令,有 4 種用法。
CPSID I ;PRIMASK=1, ;關(guān)中斷 CPSIE I ;PRIMASK=0, ;開中斷 CPSID F ;FAULTMASK=1, ;關(guān)異常 CPSIE F ;FAULTMASK=0 ;開異常
可以看到,上面指令還是控制的PRIMASK和FAULTMASK寄存器。
如下圖所示,可以通過CPS 指令打開全局中斷或者關(guān)閉全局中斷。
basepri是中斷屏蔽寄存器,下面這個設(shè)置,優(yōu)先級大于等于11的中斷都將被屏蔽。相當(dāng)于關(guān)中斷進(jìn)入臨界段。
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri, r0 /* #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* 高四位有效,即等于0xb0,或者是11 */ 191轉(zhuǎn)成二進(jìn)制就是11000000,高四位就是1100 */
下面這個代碼:優(yōu)先級高于0的中斷被屏蔽,相當(dāng)于是開中斷退出臨界段。
mov r0, #0 /* 退出臨界段 */ msr basepri, r0
關(guān)中斷和開中斷
下面這個代碼,帶返回值的意思是:往BASEPRI寫入新的值的時候,先將BASEPRI的值保存起來,更新完BASEPRI的值的時候,將之前保存好的BASEPRI的值返回,返回的值作為形參傳入開中斷函數(shù)。
不帶返回值的意思是:在往 BASEPRI 寫入新的值的時候,不用先將 BASEPRI 的值保存起來, 不用管當(dāng)前的中斷狀態(tài)是怎么樣的,既然不用管當(dāng)前的中斷狀態(tài),也就意味著這樣的函數(shù)不能在中斷里面調(diào)用。
/*portmacro.h*/ /*不帶返回值的關(guān)中斷函數(shù),不能嵌套,不能在中斷中使用*/ #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() /*不帶中斷保護(hù)的開中斷函數(shù)*/ #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) /*帶返回值的關(guān)中斷函數(shù),可以嵌套,可以在中斷里面使用*/ #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() /*帶中斷保護(hù)的開中斷函數(shù)*/ #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* 高四位有效,即等于0xb0,或者是11 */ /*不帶返回值的關(guān)中斷函數(shù)*/ static portFORCE_INLINE void vPortRaiseBASEPRI( void ) { uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; /*不帶返回值的關(guān)中斷函數(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. */ msr basepri, ulNewBASEPRI dsb isb } } /*帶返回值的關(guān)中斷函數(shù)*/ 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 msr basepri, ulNewBASEPRI dsb isb } return ulReturn; } /*不帶中斷保護(hù)的開中斷函數(shù)和帶中斷保護(hù)的開中斷函數(shù),區(qū)別在于參數(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. */ msr basepri, ulBASEPRI } }
對于不帶中斷保護(hù)情況,vPortEnterCritical函數(shù)里面的uxCriticalNesting是一個全局變量,記錄臨界段嵌套次數(shù),vPortExitCritical函數(shù)每次將uxCriticalNesting減一,只有當(dāng)uxCriticalNesting = 0才會調(diào)用portENABLE_INTERRUPTS函數(shù)使能中斷。這樣的話,在有多個臨界段代碼的時候,不會因為某一個臨界段代碼的退出而打斷其他臨界段的保護(hù),只有所有的臨界段代碼都退出后,才會使能中斷。
帶中斷保護(hù)的,主要就是往BASEPRI寫入新的值的時候,先將BASEPRI的值保存起來,更新完BASEPRI的值的時候,將之前保存好的BASEPRI的值返回,返回的值作為形參傳入開中斷函數(shù)。
/*進(jìn)入臨界段,不帶中斷保護(hù)*/ #define taskENTER_CRITICAL() portENTER_CRITICAL() /*退出臨界段,不帶中斷保護(hù)*/ #define taskEXIT_CRITICAL() portEXIT_CRITICAL() /*進(jìn)入臨界段,帶中斷保護(hù),可以嵌套*/ #define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() /*退出臨界段,帶中斷保護(hù),可以嵌套*/ #define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) /*進(jìn)入臨界段,不帶中斷保護(hù)*/ #define portENTER_CRITICAL() vPortEnterCritical() /*退出臨界段,不帶中斷保護(hù)*/ #define portEXIT_CRITICAL() vPortExitCritical() /*進(jìn)入臨界段,帶中斷保護(hù),可以嵌套*/ #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() /*退出臨界段,帶中斷保護(hù),可以嵌套*/ #define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x) /*進(jìn)入臨界段,不帶中斷保護(hù)*/ void vPortEnterCritical( void ) { /*不帶返回值的關(guān)中斷函數(shù),不能嵌套,不能在中斷中使用*/ portDISABLE_INTERRUPTS(); uxCriticalNesting++; if( uxCriticalNesting == 1 ) { configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); } } /*退出臨界段,不帶中斷保護(hù)*/ void vPortExitCritical( void ) { configASSERT( uxCriticalNesting ); uxCriticalNesting--; if( uxCriticalNesting == 0 ) { /*不帶中斷保護(hù)的開中斷函數(shù)*/ portENABLE_INTERRUPTS(); } } /*進(jìn)入臨界段,帶中斷保護(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 msr basepri, ulNewBASEPRI dsb isb } return ulReturn; } /*退出臨界段,帶中斷保護(hù),可以嵌套*/ 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 } }
/*臨界段代碼的應(yīng)用場合*/ /* 在中斷場合,臨界段可以嵌套 */ { uint32_t ulReturn; /* 進(jìn)入臨界段,臨界段可以嵌套 */ ulReturn = taskENTER_CRITICAL_FROM_ISR(); /* 臨界段代碼 */ /* 退出臨界段 */ taskEXIT_CRITICAL_FROM_ISR( ulReturn ); } /* 在非中斷場合,臨界段不能嵌套 */ { /* 進(jìn)入臨界段 */ taskENTER_CRITICAL(); /* 臨界段代碼 */ /* 退出臨界段*/ taskEXIT_CRITICAL(); }
以上就是“freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。