1 多优先级任务
1.1 设计目标
问题引入:
- 任务数量无限,而资源有限。当多个任务在等待同一资源/事件时,如果资源/事件不能在任务间共享,应该有哪个任务来处理?
1.2 优先级原理
在此我们只讨论CPU占用的优先级:
- RTOS维护一个就绪表,每个表项对应一个任务,对应一种优先级。就绪表指明哪些优先级的任务等待占用CPU运行。
- 为便于快速找到优先级更高的任务运行,使用了就绪位置标记就绪,快速查找。
1.3 设计实现
添加优先级字段:
添加优先级位图表:
添加获取当前最高优先级任务的函数:
/**********************************************************************************************************
** Function name : tTaskHighestReady
** Descriptions : 获取当前最高优先级且可运行的任务
** parameters : 无
** Returned value : 优先级最高的且可运行的任务
***********************************************************************************************************/
tTask * tTaskHighestReady (void)
{
uint32_t highestPrio = tBitmapGetFirstSet(&taskPrioBitmap);
return taskTable[highestPrio];
}
修改任务初始化函数:
/**********************************************************************************************************
** Function name : tTaskInit
** Descriptions : 初始化任务结构
** parameters : task 要初始化的任务结构
** parameters : entry 任务的入口函数
** parameters : param 传递给任务的运行参数
** Returned value : 无
***********************************************************************************************************/
void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack)
{
// 为了简化代码,tinyOS无论是在启动时切换至第一个任务,还是在运行过程中在不同间任务切换
// 所执行的操作都是先保存当前任务的运行环境参数(CPU寄存器值)的堆栈中(如果已经运行运行起来的话),然后再
// 取出从下一个任务的堆栈中取出之前的运行环境参数,然后恢复到CPU寄存器
// 对于切换至之前从没有运行过的任务,我们为它配置一个“虚假的”保存现场,然后使用该现场恢复。
// 注意以下两点:
// 1、不需要用到的寄存器,直接填了寄存器号,方便在IDE调试时查看效果;
// 2、顺序不能变,要结合PendSV_Handler以及CPU对异常的处理流程来理解
*(--stack) = (unsigned long)(1<<24); // XPSR, 设置了Thumb模式,恢复到Thumb状态而非ARM状态运行
*(--stack) = (unsigned long)entry; // 程序的入口地址
*(--stack) = (unsigned long)0x14; // R14(LR), 任务不会通过return xxx结束自己,所以未用
*(--stack) = (unsigned long)0x12; // R12, 未用
*(--stack) = (unsigned long)0x3; // R3, 未用
*(--stack) = (unsigned long)0x2; // R2, 未用
*(--stack) = (unsigned long)0x1; // R1, 未用
*(--stack) = (unsigned long)param; // R0 = param, 传给任务的入口函数
*(--stack) = (unsigned long)0x11; // R11, 未用
*(--stack) = (unsigned long)0x10; // R10, 未用
*(--stack) = (unsigned long)0x9; // R9, 未用
*(--stack) = (unsigned long)0x8; // R8, 未用
*(--stack) = (unsigned long)0x7; // R7, 未用
*(--stack) = (unsigned long)0x6; // R6, 未用
*(--stack) = (unsigned long)0x5; // R5, 未用
*(--stack) = (unsigned long)0x4; // R4, 未用
task->stack = stack; // 保存最终的值
task->delayTicks = 0;
task->prio = prio; // 设置任务的优先级
taskTable[prio] = task; // 填入任务优先级表
tBitmapSet(&taskPrioBitmap, prio); // 标记优先级位置中的相应位
}
修改tTaskDelay函数:
/**********************************************************************************************************
** Function name : tTaskDelay
** Descriptions : 使当前任务进入延时状态。
** parameters : delay 延时多少个ticks
** Returned value : 无
***********************************************************************************************************/
void tTaskDelay (uint32_t delay) {
// 配置好当前要延时的ticks数
uint32_t status = tTaskEnterCritical();
currentTask->delayTicks = delay;
tBitmapClear(&taskPrioBitmap, currentTask->prio);
tTaskExitCritical(status);
// 然后进行任务切换,切换至另一个任务,或者空闲任务
// delayTikcs会在时钟中断中自动减1.当减至0时,会切换回来继续运行。
tTaskSched();
}
修改tTaskSystemTickHandler函数:
/**********************************************************************************************************
** Function name : tTaskSystemTickHandler
** Descriptions : 系统时钟节拍处理。
** parameters : 无
** Returned value : 无
***********************************************************************************************************/
void tTaskSystemTickHandler ()
{
// 检查所有任务的delayTicks数,如果不0的话,减1。
int i;
uint32_t status = tTaskEnterCritical();
for (i = 0; i < TINYOS_PRO_COUNT; i++)
{
if (taskTable[i]->delayTicks > 0)
{
taskTable[i]->delayTicks--;
}
else
{
tBitmapSet(&taskPrioBitmap, i);
}
}
tTaskExitCritical(status);
// 这个过程中可能有任务延时完毕(delayTicks = 0),进行一次调度。
tTaskSched();
}
修改调度算法:
/**********************************************************************************************************
** Function name : tTaskSched
** Descriptions : 任务调度接口。tinyOS通过它来选择下一个具体的任务,然后切换至该任务运行。
** parameters : 无
** Returned value : 无
***********************************************************************************************************/
void tTaskSched (void)
{
tTask * tempTask;
// 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
uint32_t status = tTaskEnterCritical();
// 如何调度器已经被上锁,则不进行调度,直接退bm
if (schedLockCount > 0)
{
tTaskExitCritical(status);
return;
}
// 找到优先级最高的任务,如果其优先级比当前任务的还高,那么就切换到这个任务
tempTask = tTaskHighestReady();
if (tempTask != currentTask)
{
nextTask = tempTask;
tTaskSwitch();
}
// 退出临界区
tTaskExitCritical(status);
}
参考资料: