目录
一、任务调度
二、任务切换
三、关于PendSV
一、任务调度
在创建好任务函数后,需要调用函数vTaskStartScheduler()开启任务调度器,创建的任务在调度器的调度下执行。
开启任务调度器函数为:
vTaskStartScheduler(); //开启任务调度
该任务会创建一个函数名为prvIdleTask()的空闲任务,以及完成相关硬件初始化,如SysTick定时器、FPU(浮点运算)单元和PendSV中断。所谓空闲任务就是在处理器不执行其余任务时执行的任务(功能可自定)。
硬件初始化函数:
BaseType_t xPortStartScheduler( void )
{
/*设置SysTick定时器中断和PendSV中断优先级为最低优先级*/
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
vPortSetupTimerInterrupt(); /*SysTick定时器相关配置*/
uxCriticalNesting = 0; /*初始化临界嵌套计数器*/
prvEnableVFP(); /*使能FPU单元*/
*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; /*设置FPU相关寄存器*/
prvStartFirstTask(); /*启动第一个任务*/
return 0;
}
二、任务切换
任务的切换是在PendSV中断函数里实现的,触发该中断的方法是向中断控制器和壮志寄存器ICSR的bit28写入1。
PendSV中断函数是用汇编写的:
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
/* *INDENT-OFF* */
PRESERVE8
mrs r0, psp
isb
/* Get the location of the current TCB. */
ldr r3, =pxCurrentTCB
ldr r2, [ r3 ]
/* Is the task using the FPU context? If so, push high vfp registers. */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
/* Save the core registers. */
stmdb r0!, {r4-r11, r14}
/* Save the new top of stack into the first member of the TCB. */
str r0, [ r2 ]
stmdb sp!, {r0, r3}
mov r0, # configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0 //关中断,进入临界段
dsb
isb
bl vTaskSwitchContext //获取下一个执行的任务
mov r0, # 0
msr basepri, r0 //开中断,退出临界段
ldmia sp!, {r0, r3}
/* The first item in pxCurrentTCB is the task top of stack. */
ldr r1, [ r3 ]
ldr r0, [ r1 ]
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14}
/* Is the task using the FPU context? If so, pop the high vfp registers* too. */
tst r14, # 0x10
it eq
vldmiaeq r0!, {s16-s31}
msr psp, r0
isb
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */
#if WORKAROUND_PMU_CM001 == 1
push { r14 }
pop { pc }
nop
#endif
#endif
bx r14
/* *INDENT-ON* */
}
任务切换函数taskYIELD()和SysTick中断函数就是向中断控制器和壮志寄存器ICSR的bit28写入1触发PendSV函数完成任务的切换。
taskYIELD()函数实现:
#define portYIELD() \
{ \
/* Set a PendSV to request a context switch. */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ //在ICSR的bit28写入1,触发PendSV中断
\
__dsb( portSY_FULL_READ_WRITE ); \
__isb( portSY_FULL_READ_WRITE ); \
}
SysTick中断函数实现:
void xPortSysTickHandler( void )
{
vPortRaiseBASEPRI(); //关中断,进入临界段
{
if( xTaskIncrementTick() != pdFALSE ) //增加时钟计数器xTickCount的值
{
/*在ICSR的bit28写入1,触发PendSV中断*/
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; //( 1UL << 28UL )
}
}
vPortClearBASEPRIFromISR(); //开中断,退出临界段
}
三、关于PendSV
以下是在《Cortex-M3权威指南》中的部分截图:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)