91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》
  • 首頁 > 
  • 教程 > 
  • 開發技術 > 
  • freertos實時操作系統臨界段保護開關中斷及進入退出的方法

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

發布時間:2022-04-06 15:10:22 來源:億速云 閱讀:214 作者:iii 欄目:開發技術

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

中斷的基礎知識

嵌套:

嵌套向量中斷控制器 NVIC(Nested Vectored Interrupt Controller與內核是緊耦合的。提供如下的功能:可嵌套中斷支持、向量中斷支持、動態優先級調整支持、中斷延遲大大縮短、 中斷可屏蔽。

所有的外部中斷和絕大多數系統異常均支持可嵌套中斷。異常都可以被賦予不同的優先級,當前優先級被存儲在 xPSR的專用字段中。當一個異常發生時,硬件自動比較該異常的優先級和當前的異常優先級,如果發現該異常的優先級更高,處理器就會中斷當前的中斷服務例程(或者是普通程序),而服務新來的異常(立即搶占)。

如果優先級組設置使得中斷嵌套層次很深,要確認主堆棧空間足夠用。 異常服務程序總是使用MSP,主堆棧的容量應是嵌套最深時需要的量。

優先級:

CM3 支持中斷嵌套,使得高優先級異常會搶占(preempt)低優先級異常。

有3個系統異常:復位,NMI 以及硬 fault,它們有固定的優先級,并且優先級號是負數,高于所有其它異常。所有其它異常的優先級都是可編程的(但不能編程為負數)。

CM3 支持3個固定的高優先級和多達256級的可編程優先級,并且支持128級搶占。但是大多數CM3芯片實際上支持的優先級數會更少如8級、16級、32級等。

裁掉表達優先級的幾個低端有效位,從而讓優先級數減少。如果使用更多的位來表達優先級,優先級數增加,需要的門也更多,帶來更多的成本和功耗。

使用3個位來表達優先級,優先級配置寄存器的結構如下圖所示,能夠使用的8個優先級為:0x00(最高),0x20,0x40,0x60,0x80, 0xA0,0xC0,0xE0。

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

為了使搶占機能變得更可控,CM3 把 256 級優先級按位分成高低兩段,分別是搶占優先級和亞優先級。搶占優先級決定了搶占行為。當搶占優先級相同的異常有不止一個懸起時,就優先響應亞優先級最高的異常(亞優先級處理內務)。

優先級分組規定:亞優先級至少是1個位。所以搶占優先級最多是7個位,最多只有 128 級搶占的現象。

下圖是只使用 3 個位來表達優先級,從bit5處分組,得到4級搶占優先級,每個搶占優先級的內部有2個亞優先級。

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

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

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

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

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

中斷的懸起與解懸:

中斷發生時,正在處理同級或高優先級異常,或者被掩蔽,則中斷不能立即得到響應。此時中斷被懸起。

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

咬尾中斷Tail‐Chaining:

處理器在響應某異常時,如果又發生其優先級高的異常,當前異常被阻塞,轉而執行優先級高的異常。那么異常執行返回后,系統處理懸起的異常時,如果先POP再把POP出的再PUSH回去,這就是浪費CPU時間。

所以CM3不POP這些寄存器,而是繼續使用上一個異常已經PUSH好的成果。如下圖所示。

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

晚到的高優先級異常:

入棧的階段,尚未執行其服務例程時,如果此時收到了高優先級異常的請求,入棧后,將執行高優先級異常的服務例程。如果高優先級異常來得太晚,以至于已經執行了前一個異常的指令,則按普通的搶占處理,這會需要更多的處理器時間和額外32字節的堆棧空間。高優先級異常執行完畢后,以咬尾中斷方式執行之前被搶占的異常。

cortex-m里面開中斷、關中斷指令

臨界段:一段在執行的時候不能被中斷的代碼段(必須完整運行、不能被打斷的代碼段)。一般是對全局變量操作時候,用到臨界段。當一個任務在訪問某個全局變量時,如果被其他中斷打斷,改變了該全局變量,再回到上個任務時,全局變量已經不是當時的它了,這種情況可能會導致不可意料的后果。

臨界段被打斷的情況:系統調度(最終也是產生PendSV中斷);外部中斷。

freertos進入臨界段代碼時需要關閉中斷,處理完臨界段代碼再打開中斷。

首先看下面的代碼。

__asm void prvStartFirstTask( void )
{
PRESERVE8
/* 在Cortex-M中,0xE000ED08是SCB_VTOR這個寄存器的地址,
里面存放的是向量表的起始地址,即MSP的地址 */
ldr r0, =0xE000ED08
ldr r0, [r0]
ldr r0, [r0]
/* 設置主堆棧指針msp的值 */
msr msp, r0
/* 使能全局中斷 */
cpsie i
cpsie f
dsb
isb
/* 調用SVC去啟動第一個任務 */
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的值等于第一個任務堆棧的棧頂 */
ldmia r0!, {r4-r11}		/* 以r0為基地址,將棧里面的內容加載到r4~r11寄存器,同時r0會遞增 */
msr psp, r0				/* 將r0的值,即任務的棧指針更新到psp */
isb
mov r0, #0              /* 設置r0的值為0 */
msr	basepri, r0         /* 設置basepri寄存器的值為0,即所有的中斷都沒有被屏蔽 */
orr r14, #0xd
bx r14
}
__asm void xPortPendSVHandler( void )
{
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8
/* 當進入PendSVC Handler時,上一個任務運行的環境即:
xPSR,PC(任務入口地址),R14,R12,R3,R2,R1,R0(任務的形參)
這些CPU寄存器的值會自動保存到任務的棧中,剩下的r4~r11需要手動保存 */
/* 獲取任務棧指針到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]             /* 將任務棧的新的棧頂指針存儲到當前任務TCB的第一個成員,即棧頂指針 */
stmdb sp!, {r3, r14}        /* 將R3和R14臨時壓入堆棧 */
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY    /* 進入臨界段 */
msr basepri, r0
dsb
isb
bl vTaskSwitchContext       /* 調用函數vTaskSwitchContext,尋找新的任務運行,通過使變量pxCurrentTCB指向新的任務來實現任務切換 */
mov r0, #0                  /* 退出臨界段 */
msr basepri, r0
ldmia sp!, {r3, r14}        /* 恢復r3和r14 */
ldr r1, [r3]
ldr r0, [r1] 				/* 當前激活的任務TCB第一項保存了任務堆棧的棧頂,現在棧頂值存入R0*/
ldmia r0!, {r4-r11}			/* 出棧 */
msr psp, r0
isb
bx r14
nop
}
cpsie i
cpsie f
msr	basepri, r0
cpsie icpsie fmsrbasepri, r0

為了快速地開關中斷,CM3 專門設置了 CPS 指令,有 4 種用法。

CPSID I ;PRIMASK=1, ;關中斷
CPSIE I ;PRIMASK=0, ;開中斷
CPSID F ;FAULTMASK=1, ;關異常
CPSIE F ;FAULTMASK=0 ;開異常

可以看到,上面指令還是控制的PRIMASK和FAULTMASK寄存器。

如下圖所示,可以通過CPS 指令打開全局中斷或者關閉全局中斷。

freertos實時操作系統臨界段保護開關中斷及進入退出的方法

basepri是中斷屏蔽寄存器,下面這個設置,優先級大于等于11的中斷都將被屏蔽。相當于關中斷進入臨界段。

mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY 
msr basepri, r0
/*
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191   /* 高四位有效,即等于0xb0,或者是11 */
191轉成二進制就是11000000,高四位就是1100
*/

下面這個代碼:優先級高于0的中斷被屏蔽,相當于是開中斷退出臨界段。

mov r0, #0                  /* 退出臨界段 */
msr basepri, r0

關中斷和開中斷

下面這個代碼,帶返回值的意思是:往BASEPRI寫入新的值的時候,先將BASEPRI的值保存起來,更新完BASEPRI的值的時候,將之前保存好的BASEPRI的值返回,返回的值作為形參傳入開中斷函數。

不帶返回值的意思是:在往 BASEPRI 寫入新的值的時候,不用先將 BASEPRI 的值保存起來, 不用管當前的中斷狀態是怎么樣的,既然不用管當前的中斷狀態,也就意味著這樣的函數不能在中斷里面調用。

/*portmacro.h*/
/*不帶返回值的關中斷函數,不能嵌套,不能在中斷中使用*/
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
/*不帶中斷保護的開中斷函數*/
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
/*帶返回值的關中斷函數,可以嵌套,可以在中斷里面使用*/
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
/*帶中斷保護的開中斷函數*/
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191   /* 高四位有效,即等于0xb0,或者是11 */
/*不帶返回值的關中斷函數*/
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

/*不帶返回值的關中斷函數*/
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
	}
}
/*帶返回值的關中斷函數*/
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;
}
/*不帶中斷保護的開中斷函數和帶中斷保護的開中斷函數,區別在于參數的值*/
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
	}
}

進入臨界段和退出臨界段

對于不帶中斷保護情況,vPortEnterCritical函數里面的uxCriticalNesting是一個全局變量,記錄臨界段嵌套次數,vPortExitCritical函數每次將uxCriticalNesting減一,只有當uxCriticalNesting = 0才會調用portENABLE_INTERRUPTS函數使能中斷。這樣的話,在有多個臨界段代碼的時候,不會因為某一個臨界段代碼的退出而打斷其他臨界段的保護,只有所有的臨界段代碼都退出后,才會使能中斷。

帶中斷保護的,主要就是往BASEPRI寫入新的值的時候,先將BASEPRI的值保存起來,更新完BASEPRI的值的時候,將之前保存好的BASEPRI的值返回,返回的值作為形參傳入開中斷函數。

/*進入臨界段,不帶中斷保護*/
#define taskENTER_CRITICAL()		       portENTER_CRITICAL()
/*退出臨界段,不帶中斷保護*/
#define taskEXIT_CRITICAL()			       portEXIT_CRITICAL()
/*進入臨界段,帶中斷保護,可以嵌套*/
#define taskENTER_CRITICAL_FROM_ISR()      portSET_INTERRUPT_MASK_FROM_ISR()
/*退出臨界段,帶中斷保護,可以嵌套*/
#define taskEXIT_CRITICAL_FROM_ISR( x )    portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
/*進入臨界段,不帶中斷保護*/
#define portENTER_CRITICAL()					vPortEnterCritical()
/*退出臨界段,不帶中斷保護*/
#define portEXIT_CRITICAL()						vPortExitCritical()
/*進入臨界段,帶中斷保護,可以嵌套*/
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
/*退出臨界段,帶中斷保護,可以嵌套*/
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
/*進入臨界段,不帶中斷保護*/
void vPortEnterCritical( void )
{
    /*不帶返回值的關中斷函數,不能嵌套,不能在中斷中使用*/
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}
/*退出臨界段,不帶中斷保護*/
void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
        /*不帶中斷保護的開中斷函數*/
		portENABLE_INTERRUPTS();
	}
}
/*進入臨界段,帶中斷保護,可以嵌套*/
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;
}
/*退出臨界段,帶中斷保護,可以嵌套*/
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
	}
}
/*臨界段代碼的應用場合*/
/* 在中斷場合,臨界段可以嵌套 */
{
    uint32_t ulReturn;
    /* 進入臨界段,臨界段可以嵌套 */
    ulReturn = taskENTER_CRITICAL_FROM_ISR();
    /* 臨界段代碼 */
    /* 退出臨界段 */
    taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
/* 在非中斷場合,臨界段不能嵌套 */
{
    /* 進入臨界段 */
    taskENTER_CRITICAL();
    /* 臨界段代碼 */
    /* 退出臨界段*/
    taskEXIT_CRITICAL();
}

以上就是“freertos實時操作系統臨界段保護開關中斷及進入退出的方法”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

昭苏县| 遂宁市| 阳朔县| 灵台县| 乌拉特后旗| 合肥市| 托克托县| 台东市| 北碚区| 柘荣县| 维西| 新沂市| 宽城| 舞钢市| 长白| 蚌埠市| 湾仔区| 杨浦区| 鹤山市| 绥德县| 额尔古纳市| 长垣县| 浠水县| 科技| 方山县| 沁源县| 长子县| 全椒县| 故城县| 正定县| 岢岚县| 碌曲县| 华宁县| 吉安县| 调兵山市| 尼勒克县| 宜章县| 锡林浩特市| 措勤县| 武陟县| 泽库县|