天天看點

FreeRTOS(三)任務切換

taskYIELD()任務切換

#define taskYIELD()                        portYIELD()	
#define portYIELD()															\
{																				\
	/* 向中斷控制寄存器bit28位寫入1,将PendSV中斷設定為挂起狀态,
	等到優先級高于PendSV的中斷執行完成後,PendSV中斷服務函數将被執行,進行任務切換工作 */								\
	portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;								\
																				
	__dsb( portSY_FULL_READ_WRITE );											\
	__isb( portSY_FULL_READ_WRITE );											\
}
           

xPortPendSVHandler( )中斷服務函數

__asm void xPortPendSVHandler( void )
{
	extern uxCriticalNesting;       /*臨界段嵌套計數器,初始化為0xaaaaaaaa,排程器啟動後初始化為0,每進入一次臨界段+1*/
	extern pxCurrentTCB;            /* 指向目前激活的任務 */
	extern vTaskSwitchContext;      
 
 
	PRESERVE8
 
 
	mrs r0, psp                   /* PSP内容存入R0 */    
	isb                           /* 指令同步隔離,清流水線 */
 
	ldr	r3, =pxCurrentTCB     /* 将目前指向的TCB的位址賦給R3 */
	ldr	r2, [r3]           	  /* 将TCB賦給R2 */
 
	stmdb r0!, {r4-r11}          /* 以r0為基址,指針先遞減再操作,儲存r4-r11寄存器入棧,并更新r0的值,r0最終指向r4*/
	str r0, [r2]		     /* 将棧頂指針賦給r0 */
 
	stmdb sp!, {r3, r14}         /* 将R3和R14臨時壓入堆棧,因為即将調用函數vTaskSwitchContext,調用函數時,傳回位址自動儲存到R14中,是以一旦調用發生,
	                            R14的值會被覆寫,是以需要入棧保護; R3儲存的目前激活的任務TCB指針(pxCurrentTCB)位址,函數調用後會用到,是以也要入棧保護*/
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY   
	msr basepri, r0              /* 屏蔽中斷,進入臨界區 */
	dsb                         /* 資料和指令同步隔離 */
	isb
	bl vTaskSwitchContext        /* 調用函數,尋找新的任務運作,通過使變量pxCurrentTCB指向新的任務來實作任務切換 */
	mov r0, #0                   /* 開中斷,退出臨界區*/
	msr basepri, r0
	ldmia sp!, {r3, r14}         /* 恢複R3和R14*/ 
 
	ldr r1, [r3]             /* 将TCB賦給r1*/
	ldr r0, [r1]		     /* 将目前激活的任務TCB的棧頂指針存入R0*/
	ldmia r0!, {r4-r11}	     /* r4-r11出棧*/
	msr psp, r0               /*更新psp指針的值,将最新的棧頂指針賦給psp*/
	isb
	bx r14                      /* 異常發生時,R14中儲存異常傳回标志,包括傳回後進入線程模式還是處理器模式、使用PSP堆棧指針還是MSP堆棧指針,
	                             當調用 bx r14指令後,硬體會知道要從異常傳回,然後出棧,這個時候堆棧指針PSP已經指向了新任務堆棧的正确位置,
	                             當新任務的運作位址被出棧到PC寄存器後,新的任務也會被執行。*/
	nop
}
           

taskSELECT_HIGHEST_PRIORITY_TASK()尋找最高優先級任務

vTaskSwitchContext()函數實際上是調用

taskSELECT_HIGHEST_PRIORITY_TASK()這個函數

#define taskSELECT_HIGHEST_PRIORITY_TASK()														\
	{																								\
	UBaseType_t uxTopPriority;																		\
																									\
		/* 尋找最高優先級并加入就緒清單中 */								\
		portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );								\
		/* 就緒清單中找到最高優先級任務的TCB,然後更新到 pxCurrentTCB */
		listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );		\
	}