溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

發(fā)布時間:2022-04-06 15:10:22 來源:億速云 閱讀:212 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法的相關(guān)知識點,內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

中斷的基礎(chǔ)知識

嵌套:

嵌套向量中斷控制器 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)是嵌套最深時需要的量。

優(yōu)先級:

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。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

為了使搶占機(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)先級。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

下圖是3 位優(yōu)先級,從比特1處分組,(雖然[4:0]未使用,卻允許從它們中分組)。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

應(yīng)用程序中斷及復(fù)位控制寄存器(AIRCR)(地址:0xE000_ED00)如下。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

中斷的懸起與解懸:

中斷發(fā)生時,正在處理同級或高優(yōu)先級異常,或者被掩蔽,則中斷不能立即得到響應(yīng)。此時中斷被懸起。

可以通過中斷設(shè)置懸起寄存器(SETPEND)、中斷懸起清除寄存器(CLRPEND)讀取中斷的懸起狀態(tài),還可以寫它們來手工懸起中斷。

咬尾中斷Tail‐Chaining:

處理器在響應(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好的成果。如下圖所示。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jìn)入退出的方法

晚到的高優(yōu)先級異常:

入棧的階段,尚未執(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)閉全局中斷。

freertos實時操作系統(tǒng)臨界段保護(hù)開關(guān)中斷及進(jì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
	}
}

進(jìn)入臨界段和退出臨界段

對于不帶中斷保護(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è)資訊頻道。

向AI問一下細(xì)節(jié)

免責(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)容。

AI