任务调度由SysTick_Handler和 PendSV_Handler两个中断接管
系统时钟计数器按给定频率倒计时,至0触发SysTick_Handler中断,此中断函数为宏中对xPortSysTickHandler的重命名。
增加计数,置位可挂起中断。
void xPortSysTickHandler( void )
{
portDISABLE_INTERRUPTS();
{
if( xTaskIncrementTick() != pdFALSE )
{
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
portENABLE_INTERRUPTS();
}
xTaskIncrementTick 以设定频率增加计数,处理延时任务。
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U )
{
taskSWITCH_DELAYED_LISTS(); //计数溢出 交换延时链表与溢出链表
}
if( xConstTickCount >= xNextTaskUnblockTime ) //处理延时链表
{
for( ; ; )
{
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) //空
{
xNextTaskUnblockTime = portMAX_DELAY;
break;
}
else //非空
{
pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
if( xConstTickCount < xItemValue )
{
xNextTaskUnblockTime = xItemValue;
break;
}
listREMOVE_ITEM( &( pxTCB->xStateListItem ) ); //延时已过
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
}
prvAddTaskToReadyList( pxTCB ); //添至就绪链表
#if ( configUSE_PREEMPTION == 1 )
{
if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
{
xSwitchRequired = pdTRUE; //优先级大于现运行任务需切换
}
}
#endif
}
}
}
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
}
#endif
}
else
{
++xPendedTicks; // 增加计数
}
return xSwitchRequired;
}
listLIST_IS_EMPTY宏定义
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
可挂起中断,此中断函数在宏中重命名为PendSV_Handler。系统时钟中断中对其置位,以低优先级运行,无高于其优先级时会调入对任务进行切换。
void xPortPendSVHandler( void )
{
__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" ldr r3, pxCurrentTCBConst \n" //当前运行任务
" ldr r2, [r3] \n"
" stmdb r0!, {r4-r11} \n" //保存r4-r11 psp
" str r0, [r2] \n" //保存当前栈顶pxTopOfStack
" stmdb sp!, {r3, r14} \n" //保存r3 r14至msp
" mov r0, %0 \n" // 优先级
" msr basepri, r0 \n"
" bl vTaskSwitchContext \n" // 在就绪链表寻找最高优先级设为当前切换任务
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n" // 恢复 r3 lr
" ldr r1, [r3] \n" //当前TCB指针 TCB已切换
" ldr r0, [r1] \n" //当前TCB栈顶pxTopOfStack
" ldmia r0!, {r4-r11} \n" //从TCB栈恢复 r4-r11 变动后指向任务参数
" msr psp, r0 \n" //psp栈指向tcb栈
" isb \n" //
" bx r14 \n" // 中断返回从PSP栈恢复寄存器值,任务回调函数指针载入PC,完成任务切换。
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
);
}
参考创建任务时,任务堆栈设置
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters )
{
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; //thumb
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; //task回调函数指针
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}