恭喜,攻克了本课程中最难的课时!接下来我们会增加一点有意思的特性!

如果说前面课时的代码中,任务必须主动进行任务切换放弃CPU才能让其它任务有机会运行。那么这节课时里,我们会让任务的切换自动定期强制发生,这样便避免了这个麻烦的问题。

主要内容

本节课时的内容比较简单。相对上一节课程来说,所做的改进是:配备一个定时器,在定时器溢出中断中定时调用调度函数tTaskSched()进行任务切换。
虽然这在代码上来讲只是一个小的改进;但是意义却非凡,其引入了时间片的概述。

重点难点

本节课程重点在于理解时间片的作用是保证公开性,同时避免某个任务长时间占用CPU不放。

注意事项

常见问题

为什么每次切出去taskFlag都为低电平呢?

Q:看课程视频,发现每次任务退出时间片时,taskFlag的波形都是为0。是不是太巧了?

双任务时间片运行原理 - 图1

A:你可以试试改下各任务的延时大小,或者在IDE中调试工程,然后把波形缩小看得更多,可以看到taskFlag并不总是为0。

为什么每个任务执行20ms的时间片?

Q:在时间片运行时, 为什么每个任务执行20ms?
void tSetSysTickPeriod(uint32_t ms)
{
SysTick->LOAD = ms * SystemCoreClock /1000 - 1;
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
}

void SysTick_Handler()
{
tTaskSched();
}

void delay(uint32_t count)
{
while(—count>0);
}

uint32t TaskFlag1;
uint32_t TaskFlag2;
void Task1Entry (void * param)
{
unsigned long valid =
(unsigned long _) param;
valid++;
tSetSysTickPeriod(10);
for(;;)
{
TaskFlag1 = 0;
delay(100);
TaskFlag1 = 1;
delay(100);
}
}
A:请在工程中的在target选项里把仿真器的时钟设置为25M,指定模拟器以25M运行。或者请配置系统配置文件system_armcm3.c中的XTAL的时钟为12M,以匹配模拟器实际运行的时钟。具体为何这么配置不必深究,这是与硬件和模拟器有关。

systick中断使能放在哪一个第一个任务

Q:systick的中断使能要放在第一个任务中 但是不确定哪一个是第一个任务?
A:可以考虑先初始一个初始任务,然后其它任务在这个任务中初始化。或者放在最高优先级任务那里,假设这个优先级只有一个的话。或者你到最后的课时,可以看到systick初始化已经被放到空闲任务中执行了,不用在用户代码中再初始化