基于TI-RTOS的CC2650DK开发(9)---任务示例

judy的头像

Task Hooks示例

下例使用了单个Task hook集。此例演示了如何读写每个hook集关联的Hook上下文指针。
首先是C代码:

/* ======== TaskHookExample.c ========
* This example demonstrates basic task hook processing
* operation for dynamically created tasks. */
 基于TI-RTOS的CC2650DK开发(9)---任务示例
Task_Handle myTsk0, myTsk1, myTsk2;
Int myHookSetId, myHookSetId2;
/* HookSet functions */
/* ======== myRegister ========
* invoked during Swi module startup before main()
* for each HookSet */
Void myRegister(Int hookSetId)
{
System_printf("myRegister: assigned HookSet Id = %d\n", hookSetId);
myHookSetId = hookSetId;
}
/* ======== myCreate ========
* invoked during Task_create for dynamically
* created Tasks */
Void myCreate(Task_Handle task, Error_Block *eb)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myCreate: task name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(task, myHookSetId, (Ptr)0xdead);
}
/* ======== myReady ========
* invoked when Task is made ready to run */
Void myReady(Task_Handle task)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myReady: task name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(task, myHookSetId, (Ptr)0xc0de);
}
/* ======== mySwitch ========
* invoked whenever a Task switch occurs/is made ready to run */
Void mySwitch(Task_Handle prev, Task_Handle next)
{
String prevName;
String nextName;
Ptr pPrevEnv;
Ptr pNextEnv;
if (prev == NULL)
{
System_printf("mySwitch: ignoring dummy 1st prev Task\n");
}
else
{
prevName = Task_Handle_name(prev);
pPrevEnv = Task_getHookContext(prev, myHookSetId);
System_printf("mySwitch: prev name = '%s', pPrevEnv = 0x%x\n",
prevName, pPrevEnv);
Task_setHookContext(prev, myHookSetId, (Ptr)0xcafec0de);
}
nextName = Task_Handle_name(next);
pNextEnv = Task_getHookContext(next, myHookSetId);
System_printf(" next name = '%s', pNextEnv = 0x%x\n",
nextName, pNextEnv);
Task_setHookContext(next, myHookSetId, (Ptr)0xc001c0de);
}
/* ======== myExit ========
* invoked whenever a Task calls Task_exit() or falls through
* the bottom of its task function. */
Void myExit(Task_Handle task)
{
Task_Handle curTask = task;
String name;
Ptr pEnv;
name = Task_Handle_name(curTask);
pEnv = Task_getHookContext(curTask, myHookSetId);
System_printf("myExit: curTask name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(curTask, myHookSetId, (Ptr)0xdeadbeef);
}
/* ======== myDelete ========
* invoked upon Task deletion */
Void myDelete(Task_Handle task)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myDelete: task name = '%s', pEnv = 0x%x\n", name, pEnv);
}
/* Define 3 identical tasks */
Void myTsk0Func(UArg arg0, UArg arg1)
{
System_printf("myTsk0 Entering\n");
System_printf("myTsk0 Calling Task_yield\n");
Task_yield();
System_printf("myTsk0 Exiting\n");
}
Void myTsk1Func(UArg arg0, UArg arg1)
{
System_printf("myTsk1 Entering\n");
System_printf("myTsk1 Calling Task_yield\n");
Task_yield();
System_printf("myTsk1 Exiting\n");
}
Void myTsk2Func(UArg arg0, UArg arg1)
{
System_printf("myTsk2 Entering\n");
System_printf("myTsk2 Calling Task_yield\n");
Task_yield();
System_printf("myTsk2 Exiting\n");
}
/* ======== main ======== */
Int main(Int argc, Char *argv[])
{
Task_Params params;
Error_Block eb;
Error_init(&eb);
Task_Params_init(?ms);
params.instance->name = "myTsk0";
myTsk0 = Task_create(myTsk0Func, ?ms, &eb);
if (myTsk0 == NULL)
{
System_abort("myTsk0 create failed");
}
params.instance->name = "myTsk1";
myTsk1 = Task_create(myTsk1Func, ?ms, &eb);
if (myTsk1 == NULL)
{
System_abort("myTsk1 create failed");
}
params.instance->name = "myTsk2";
myTsk2 = Task_create(myTsk2Func, ?ms, &eb);
if (myTsk2 == NULL)
{
System_abort("myTsk2 create failed");
}
BIOS_start();
return (0);
}
/* ======== myIdleFunc ======== */
Void myIdleFunc()
{
System_printf("Entering idleFunc().\n");
Task_delete(&myTsk0);
Task_delete(&myTsk1);
Task_delete(&myTsk2);
System_exit(0);
}

接下来是配置cfg代码

/* Lots of System_printf() output requires a bigger bufSize */
SysMin = xdc.useModule('xdc.runtime.SysMin');
SysMin.bufSize = 4096;
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
Idle.addFunc('&myIdleFunc');
var Task = xdc.useModule('ti.sysbios.knl.Task');
Task.defaultStackSize = 512;
Task.idleTaskStackSize = 512;
/* Enable instance names */
Task.common$.namedInstance = true;
/* Define and add one Task Hook Set */
Task.addHookSet(
{
registerFxn: '&myRegister',
createFxn: '&myCreate',
readyFxn: '&myReady',
switchFxn: '&mySwitch',
exitFxn: '&myExit',
deleteFxn: '&myDelete',
});

最后是运行结果:

[Cortex_M3_0] myRegister: assigned HookSet Id = 0
myCreate: task name = 'ti.sysbios.knl.Task.IdleTask', pEnv = 0x0
myReady: task name = 'ti.sysbios.knl.Task.IdleTask', pEnv = 0xdead
myCreate: task name = 'myTsk0', pEnv = 0x0
myReady: task name = 'myTsk0', pEnv = 0xdead
myCreate: task name = 'myTsk1', pEnv = 0x0
myReady: task name = 'myTsk1', pEnv = 0xdead
myCreate: task name = 'myTsk2', pEnv = 0x0
myReady: task name = 'myTsk2', pEnv = 0xdead
mySwitch: ignoring dummy 1st prev Task
next name = 'myTsk0', pNextEnv = 0xc0de
myTsk0 Entering
myTsk0 Calling Task_yield
mySwitch: prev name = 'myTsk0', pPrevEnv = 0xc001c0de
next name = 'myTsk1', pNextEnv = 0xc0de
myTsk1 Entering
myTsk1 Calling Task_yield
mySwitch: prev name = 'myTsk1', pPrevEnv = 0xc001c0de
next name = 'myTsk2', pNextEnv = 0xc0de
myTsk2 Entering
myTsk2 Calling Task_yield
mySwitch: prev name = 'myTsk2', pPrevEnv = 0xc001c0de
next name = 'myTsk0', pNextEnv = 0xcafec0de
myTsk0 Exiting
myExit: curTask name = 'myTsk0', pEnv = 0xc001c0de
mySwitch: prev name = 'myTsk0', pPrevEnv = 0xdeadbeef
next name = 'myTsk1', pNextEnv = 0xcafec0de
myTsk1 Exiting
myExit: curTask name = 'myTsk1', pEnv = 0xc001c0de
mySwitch: prev name = 'myTsk1', pPrevEnv = 0xdeadbeef
next name = 'myTsk2', pNextEnv = 0xcafec0de
myTsk2 Exiting
myExit: curTask name = 'myTsk2', pEnv = 0xc001c0de
mySwitch: prev name = 'myTsk2', pPrevEnv = 0xdeadbeef
next name = 'ti.sysbios.knl.Task.IdleTask', pNextEnv = 0xc0de
Entering idleFunc().
myDelete: task name = 'myTsk0', pEnv = 0xcafec0de
myDelete: task name = 'myTsk1', pEnv = 0xcafec0de
myDelete: task name = 'myTsk2', pEnv = 0xcafec0de

不多说,上图理解,主要观察Hook何时触发,以及Task如何轮流执行。

 基于TI-RTOS的CC2650DK开发(9)---任务示例

时间片调度Task Yielding

上例演示了可由用户管理的时间片调度。这种模式的抢占无需任何tasks间的协作(使用代码)。tasks代码好象它们是唯一运行的线程。虽然不同优先级的SYS/BIOS tasks可以在任意给定应用中存在,但时间片模式仅在相同优先级的tasks中可以使用。

下例中,一个周期时钟对象配置为运行一个简单函数,每4个时钟周期调用一次Task_yield()。另一个周期时钟对象运行一个简单函数,每16毫秒调用Semaphore_post()函数。

C代码:

/*
* ======== slice.c ========
* This example utilizes time-slice scheduling among three
* tasks of equal priority. A fourth task of higher
* priority periodically preempts execution.
*
* A periodic Clock object drives the time-slice scheduling.
* Every 4 milliseconds, the Clock object calls Task_yield()
* which forces the current task to relinquish access to
* to the CPU.
*
* Because a task is always ready to run, this program
* does not spend time in the idle loop. Calls to Idle_run()
* are added to give time to the Idle loop functions
* occasionally. The call to Idle_run() is within a
* Task_disable(), Task_restore() block because the call
* to Idle_run() is not reentrant.
*/
 基于TI-RTOS的CC2650DK开发(9)---任务示例
Void hiPriTask(UArg arg0, UArg arg1);
Void task(UArg arg0, UArg arg1);
Void clockHandler1(UArg arg);
Void clockHandler2(UArg arg);
Semaphore_Handle sem;
/* ======== main ======== */
Void main()
{
Task_Params taskParams;
Task_Handle myTsk0, myTski;
Clock_Params clockParams;
Clock_Handle myClk0, myClk1;
Error_Block eb;
UInt i;
System_printf("Slice example started!\n");
Error_init(&eb);
/* Create 1 task with priority 15 */
Task_Params_init(&taskParams);
taskParams.stackSize = 512;
// Note: Larger stack needed for some targets, including ’C6748
taskParams.priority = 15;
myTsk0 = Task_create((Task_FuncPtr)hiPriTask, &taskParams, &eb);
if (myTsk0 == NULL)
{
System_abort("hiPriTask create failed");
}
/* Create 3 tasks with priority 1 */
/* re-uses taskParams */
taskParams.priority = 1;
for (i = 0; i < 3; i++)
{
taskParams.arg0 = i;
myTski = Task_create((Task_FuncPtr)task, &taskParams, &eb);
if (myTski == NULL)
{
System_abort("LoPri Task create failed");
}
}
/*
* Create clock that calls Task_yield() every 4 Clock ticks
*/
Clock_Params_init(&clockParams);
clockParams.period = 4;/* every 4 Clock ticks */
clockParams.startFlag = TRUE;/* start immediately */
myClk0 = Clock_create((Clock_FuncPtr)clockHandler1, 4, &clockParams, &eb);
if (myClk0 == NULL)
{
System_abort("Clock0 create failed");
}
/*
* Create clock that calls Semaphore_post() every
* 16 Clock ticks
*/
clockParams.period = 16;/* every 16 Clock ticks */
clockParams.startFlag = TRUE;/* start immediately */
myClk1 = Clock_create((Clock_FuncPtr)clockHandler2, 16, &clockParams, &eb);
if (myClk1 == NULL)
{
System_abort("Clock1 create failed");
}
/*
* Create semaphore with initial count = 0 and default params
*/
sem = Semaphore_create(0, NULL, &eb);
if (sem == NULL)
{
System_abort("Semaphore create failed");
}
/* Start SYS/BIOS */
BIOS_start();
}
/* ======== clockHandler1 ======== */
Void clockHandler1(UArg arg)
{
/* Call Task_yield every 4 ms */
Task_yield();
}
/* ======== clockHandler2 ======== */
Void clockHandler2(UArg arg)
{
/* Call Semaphore_post every 16 ms */
Semaphore_post(sem);
}
/* ======== task ======== */
Void task(UArg arg0, UArg arg1)
{
Int time;
Int prevtime = -1;
UInt taskKey;
/* While loop simulates work load of time-sharing tasks */
while (1)
{
time = Clock_getTicks();
/* print time once per clock tick */
if (time >= prevtime + 1)
{
prevtime = time;
System_printf("Task %d: time is %d\n", (Int)arg0, time);
}
/* check for rollover */
if (prevtime > time)
{
prevtime = time;
}
/* Process the Idle Loop functions */
taskKey = Task_disable();
Idle_run();
Task_restore(taskKey);
}
}
/* ======== hiPriTask ======== */
Void hiPriTask(UArg arg0, UArg arg1)
{
static Int numTimes = 0;
while (1)
{
System_printf("hiPriTask here\n");
if (++numTimes < 3)
{
Semaphore_pend(sem, BIOS_WAIT_FOREVER);
}
else
{
System_printf("Slice example ending.\n");
System_exit(0);
}
}
}

配置文件cfg代码

var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
var Clock = xdc.useModule('ti.sysbios.knl.Clock');
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var Task = xdc.useModule('ti.sysbios.knl.Task');

最后是输出结果:

[Cortex_M3_0] Slice example started!
hiPriTask here
Task 0: time is 0
Task 0: time is 1
Task 0: time is 2
Task 0: time is 3
Task 1: time is 4
Task 1: time is 5
Task 1: time is 6
Task 1: time is 7
Task 2: time is 8
Task 2: time is 9
Task 2: time is 10
Task 2: time is 11
Task 0: time is 12
Task 0: time is 13
Task 0: time is 14
Task 0: time is 15
hiPriTask here
Task 1: time is 16
Task 1: time is 17
Task 1: time is 18
Task 1: time is 19
Task 2: time is 20
Task 2: time is 21
Task 2: time is 22
Task 2: time is 23
Task 0: time is 24
Task 0: time is 25
Task 0: time is 26
Task 0: time is 27
Task 1: time is 28
Task 1: time is 29
Task 1: time is 30
Task 1: time is 31
hiPriTask here
Slice example ending.

上个例子演示了如何使用yield来划分时间片,这个例子则演示了如何通过时钟来划分时间片。上个例子是一个task完成自动把执行权交给另一个task,这个例子则是用时钟强制每个task执行指定时间。这里感叹一下,TI公司产品代码规范,注释、文档详细,每个例子直指要害,的确是学习的不二选择。

这里使用到了信号量(Semaphore),这个东西到后面才会详细学习。可以这样理解,hiPriTask通过调用Semaphore_pend蛰伏起来,让出控制权,一旦信号来了,立马一跃而起,奔赴战场。当然,此例中,这个信号量由时钟2每隔16ms通过调用Semaphore_post发送一次。

整个程序有两个时钟,一个高优先级task,三个低优先级task。
第一个时钟周期为4ms,控制每个低先级task的执行时间。
第二个时钟周期为16ms,控制高优先级task每隔16ms执行一次。执行到第三次退出程序。

转自: abatei的博客