天天看点

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 ] ) );		\
	}