vTaskStartSchedular()任务调度函数
void vTaskStartScheduler( void )
{
BaseType_t xReturn;
/*创建空闲任务,并返回xReturn判断是否创建成功*/
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
//如果启用了configUSE_TIMERS宏定义则表明使用定时器,需要创建定时器任务
#if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */
if( xReturn == pdPASS )
{
/* 此处关闭中断,确保不会发生中断,在调用xPortStartScheduler()
之前或调用期间。栈的创建任务中包含打开中断的状态,因此当执行
第一个任务时,中断将自动重新启用,开始运行*/
portDISABLE_INTERRUPTS();
xNextTaskUnblockTime = portMAX_DELAY; //下个任务解锁时间,赋值为最大延迟时间
xSchedulerRunning = pdTRUE; //调度器状态为开始运行
xTickCount = ( TickType_t ) 0U; //时钟节拍计数器清零
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); //初始化定时器
if( xPortStartScheduler() != pdFALSE )
{
/* 如果 xPortStartScheduler()函数启动,则不会运行到这里*/
}
else
{
/* 除非调用xTaskEndScheduler()函数才会运行到这里. */
}
}
else
{
/* 只有在内核无法启动时才会运行至此,因为没有足够的堆内存来创建空闲任务或定时器任务。
此处使用了断言,会输出错误信息,方便错误定位 */
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
}
/* 如果INCLUDE_xTaskGetIdleTaskHandle设置为0,则防止编译器警告
意味着其他地方均不使用xIdleTaskHandle() */
( void ) xIdleTaskHandle;
}
portDISABLE_INTERRUPTS()关中断
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
//configMAX_SYSCALL_INTERRUPT_PRIORITY这个宏默认值为191,高4位有效,即为11,将这个值赋给ulNewBASEPRI
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* 将 ulNewBASEPRI这个值赋给basepri寄存器,意味着优先级高于11的中断全部屏蔽*/
msr basepri, ulNewBASEPRI
dsb
isb
}
}
xPortStartScheduler() 开始调度
BaseType_t xPortStartScheduler( void )
{
/*将PendSV和SysTick的中断优先级配置为最低. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
/* 启动系统节拍定时器,即SysTick定时器,初始化中断周期并使能定时器 */
vPortSetupTimerInterrupt();
/* 初始化临界区嵌套计数器 */
uxCriticalNesting = 0;
/* 开启第一个任务 */
prvStartFirstTask();
/* 不会运行到这里*/
return 0;
}
vPortSetupTimerInterrupt()启动SysTick定时器
void vPortSetupTimerInterrupt( void )
{
#ifndef configSYSTICK_CLOCK_HZ
/* 如果没有定义系统时钟,则系统时钟频率为CPU时钟频率 */
#define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
#else
#define portNVIC_SYSTICK_CLK_BIT ( 0 )
#endif
/* 系统重装载寄存器的值为CPU时钟频率除以系统时钟节拍(25MHz/1000) */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
/*系统控制寄存器配置:使用内核时钟,打开sysitck中断,使能systick*/
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
prvStartFirstTask()开始第一个任务
__asm void prvStartFirstTask( void )
{
PRESERVE8 //考虑到浮点运算,当前栈按照8字节对齐
/*0xE000ED08是SCB_VTOR寄存器的地址,里面存放向量表的起始地址,即Msp地址 */
ldr r0, =0xE000ED08 //将立即数0xE000ED08存入r0
ldr r0, [r0] //将0xE000ED08地址指向的内容赋给r0,r0为SCB_VTOR寄存器的值,为0x00000000,即memory的起始地址
ldr r0, [r0] //将memory起始地址指向的内容赋给r0,也就是取出向量表中的第一项,主堆栈指针MSP的初始值
/* 将r0的值存储到主栈指针 */
msr msp, r0
/* 使能全局中断 */
cpsie i
cpsie f
dsb
isb
/* 产生系统调用,执行SVC中断服务函数开启第一个任务*/
svc 0
nop
nop
}
vPortSVCHandler()SVC中断服务函数
__asm void vPortSVCHandler( void )
{
PRESERVE8
/* pxCurrentTCB指向处于最高优先级的就绪任务TCB */
ldr r3, =pxCurrentTCB /*加载pxCurrentTCB的地址到r3 */
ldr r1, [r3] /*加载pxCurrentTCB到r1*/
ldr r0, [r1] /* 加载pxCurrentTCB指向的任务控制块到r0,即当前堆栈栈顶指针pxTopOfStack */
ldmia r0!, {r4-r11} /*将寄存器r4~r11出栈,栈顶指针先操作在递增*/
msr psp, r0 /* 将最新的栈顶指针赋给线程堆栈指针PSP */
isb
mov r0, #0 /*将立即数0赋给r0*/
msr basepri, r0 /*将0赋给寄存器basepri,打开所有中断*/
orr r14, #0xd /* 这里r14最后四位或上x0d(1101)表示:硬件退出使用进程栈指针psp完成出栈后返回后进入线程模式,
从进程堆栈中做出栈操作,返回Thumb状态,在SVC中断服务中,使用的是msp指针,处于ARM状态*/
bx r14 /*异常返回,bx r14指令后,硬件自动将寄存器xPSR、PC、LR、R12、R3~R0出栈,
这时新任务的任务函数指针会出栈到PC指针中,从而开始执行任务。*/
}