任务调度器的实现
任务调度器调度步骤
跳入任务后,永远不会回调度器函数。
vTaskStartScheduler函数
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/*先创建一个空闲任务 优先级最低 使用最小的堆栈内存*/
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
/*配置 是否启用软件定时器 如果启用软件定时器会进入一个Tmrsvc的任务*、
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
/*当空闲任务且者tmrsvc任务创建成功*/
if( xReturn == pdPASS )
{
/*关闭中断 要配置pendsv和systick相关中断*/
portDISABLE_INTERRUPTS();
/*配置外部的库*/
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */
/*由于第一个是空闲任务 下一个任务阻塞时间是portMAXDELAY 如果有其余任务创建 则会取最小的任务延时替换portMAX_DELAY*/
xNextTaskUnblockTime = portMAX_DELAY;
/*开启任务调度器*/
xSchedulerRunning = pdTRUE;
/*系统时间戳为0*/
xTickCount = ( TickType_t ) 0U;
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
if( xPortStartScheduler() != pdFALSE )
{
/*调用xPortStartScheduler后永远不会到这里*/
}
else
{
}
}
else
{
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
( void ) xIdleTaskHandle;
}
**
xPortStartScheduler函数
**
BaseType_t xPortStartScheduler( void )
{
/*配置断言*/
#if( configASSERT_DEFINED == 1 )
{
volatile uint32_t ulOriginalPriority;
volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );
volatile uint8_t ucMaxPriorityValue;
ulOriginalPriority = *pucFirstUserPriorityRegister;
*pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;
ucMaxPriorityValue = *pucFirstUserPriorityRegister;
ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )
{
ulMaxPRIGROUPValue--;
ucMaxPriorityValue <<= ( uint8_t ) 0x01;
}
ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;
*pucFirstUserPriorityRegister = ulOriginalPriority;
}
#endif /* conifgASSERT_DEFINED */
/*设置PendSV和SYSTICK为最低优先级*/
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
vPortSetupTimerInterrupt();
/*任务调度嵌套值为0*/
uxCriticalNesting = 0;
prvStartFirstTask();
return 0;
}
**
prvStartFirstTask函数 启动第一个任务 汇编实现
**
__asm void prvStartFirstTask( void )
{
PRESERVE8/*8字节对齐*/
ldr r0, =0xE000ED08/*取NVIC的地址到r0*/
ldr r0, [r0]
ldr r0, [r0]
msr msp, r0 /*r0的值给主堆栈指针 要求读中断向量表*/
/*开启全局中断*/
cpsie i
cpsie f
dsb/*要求以上指令强制执行完*/
isb
/*调用SVC中断 去进入第一个函数 由于SVC中断优先级高 先进SVC中断 再进入systick pendsv只有启用任务调度时才会使用*/
svc 0
nop
nop
}
**
SVC函数
由汇编启动
__asm void vPortSVCHandler( void )
{
PRESERVE8/*8字节对齐*/
ldr r3, =pxCurrentTCB/*保存pxCurrentTCB的值到r3 保存上文*/
ldr r1, [r3] /* r3再存到r1 */
ldr r0, [r1] /* r1再存到r0 */
ldmia r0!, {r4-r11} /* r0的地址由低到高依次压入r4-r11 */
msr psp, r0 /* 将r0存入任务栈指针psp */
isb /*要求上述指令强制执行完成*/
mov r0, #0 /*寄存器r0清0*/
msr basepri, r0 /*设置 basepri 寄存器的值为 0,即打开所有中断。basepri 是一个中
断屏蔽寄存器,大于等于此寄存器值的中断都将被屏蔽*/
orr r14, #0xd /*当从 SVC 中断服务退出前,通过向 r14 寄存器最后 4 位按位或上
0x0D,使得硬件在退出时使用进程堆栈指针 PSP 完成出栈操作并返回后进入任务模式、返
回 Thumb 状态。在 SVC 中断服务里面,使用的是 MSP 堆栈指针,是处在 ARM 状态
当从 SVC 中断服务退出前,通过向 r14 寄存器最后 4 位按位或上
0x0D,使得硬件在退出时使用进程堆栈指针 PSP 完成出栈操作并返回后进入任务模式、返
回 Thumb 状态。在 SVC 中断服务里面,使用的是 MSP 堆栈指针,是处在 ARM 状态
*/
bx r14 /*异常返回,这个时候出栈使用的是 PSP 指针,自动将栈中的剩下
内容加载到 CPU 寄存器: xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0
(任务的形参)同时 PSP 的值也将更新,即指向任务栈的栈顶*/
}
SVC会直接转到第一个任务 通过任务入口地址PC 和任务的形参 且正在使用PSP会切入第一个任务