基于TI-RTOS的CC2650DK开发(10)---空闲循环

judy的头像

The Idle Loop

dle Loop是 SYS/BIOS中的后台线程,在没有Hwi、Swi或Task时持续运行。任何其它线程在任何时间可抢占Idle Loop。

Idle管理器允许你在Idle Loop中插入函数运行。在configured. Idle_loop中会调用的每个Idle对象所相联的函数。Idle Loop每次调用一个函数,并在一个连续循环中往复调用所有函数。

所有Idle线程都按顺序运行于相同的优先级。函数按照创建的顺序依次运行。一个Idle函数必须完成后才会开始另一个Idle函数。当最后一个Idle函数完成,则又重新运行第一个Idle函数。

Idle Loop函数经常用于轮询那些不会(或不能)产生中断的非时实设备,用于监测系统状态或执行其它后台活动。

在SYS/BIOS应用程序中,Idle Loop的优先级最低,仅在没有Hwis、Swis或Tasks时运行。

CPU和线程的载入是在Idle Loop函数中计算完成的(目标和主机间的数据传送由一个低优先级task完成)。

如果在配置文件中将Task.enableIdleTask设置为false,将无法创建Idle任务并不会执行Idle函数。如果你希望在没有其它线程准备运行时运行一个函数,可以使用Task.allBlockedFunc来指定这样的函数。

如果你希望在无需创建一个专用Idle task的情况下执行Idle Loop,可以禁用Task.enableIdleTask,并按以下方法配置Task.allBlockedFunc。这类声明将导致Idle运行栈中最后一个等待的Task。
Task.enableIdleTask = false;
Task.allBlockedFunc = Idle.run;

使用Hwi、Swi和Task线程的例子

此例描述了程式化版本的SYS/BIOS时钟模块设计。它联合使用了Hwi、Swi和Task线程。

一个周期计时器中断提交一个Swi用于处理时钟对象列表。时钟对象列表中的每个条目都拥有它自己的周期和时钟函数。当一个对象的时间到期,时钟函数被调用,周期重新开始。

由于没有限制列表中可旋转时钟对象的个数,也无法确定每个时钟函数的开销,所以维护所有时钟对象所消耗的时间也无法确定。因此,在计时器的Hwi线程中维护时钟对象是不切实际的。此类问题使用Swi来解决更能成为轻量级解决方案(相对使用Task来说)。

C代码:
C代码:

typedef struct
{
Queue_Elem elem;
UInt32 timeout;
UInt32 period;
Void (*fxn)(UArg);
UArg arg;
} Clock_Object;

Clock_Object clk1, clk2;
Timer_Handle timer;
Semaphore_Handle sem;
Swi_Handle swi;
Task_Handle task;
Queue_Handle clockQueue;
/* Here on Timer interrupt */
Void hwiFxn(UArg arg)
{
Swi_post(swi);
}
/* Swi thread to handle Timer interrupt */
Void swiFxn(UArg arg1, UArg arg2)
{
Queue_Elem *elem;
Clock_Object *obj;
/* point to first clock object in the clockQueue */
elem = Queue_next((Queue_Elem *)clockQueue);
/* service all the Clock Objects in the clockQueue */
while (elem != (Queue_Elem *)clockQueue)
{
obj = (Clock_Object *)elem;
/* decrement the timeout counter */
obj->timeout -= 1;
/* if period has expired, refresh the timeout
* value and invoke the clock func */
if (obj->timeout == 0)
{
obj->timeout = obj->period;
(obj->fxn)(obj->arg);
}
/* advance to next clock object in clockQueue */
elem = Queue_next(elem);
}
}
/* Task thread pends on Semaphore posted by Clock thread */
Void taskFxn(UArg arg1, UArg arg2)
{
System_printf("In taskFxn pending on Sempahore.\n");
Semaphore_pend(sem, BIOS_WAIT_FOREVER);
System_printf("In taskFxn returned from Sempahore.\n");
System_exit(0);
}
/* First Clock function, invoked every 5 timer interrupts */
Void clk1Fxn(UArg arg)
{
System_printf("In clk1Fxn, arg = %d.\n", arg);
clk1.arg++;
}
/* Second Clock function, invoked every 20 timer interrupts */
Void clk2Fxn(UArg sem)
{
System_printf("In clk2Fxn, posting Semaphore.\n");
Semaphore_post((Semaphore_Object *)sem);
}
/* main() */
Int main(Int argc, char *argv[])
{
Timer_Params timerParams;
Task_Params taskParams;
Error_Block eb;
System_printf("Starting HwiSwiTask example.\n");
Error_init(&eb);
Timer_Params_init(&timerParams);
Task_Params_init(&taskParams);
/* Create a Swi with default priority (15).
* Swi handler is 'swiFxn' which runs as a Swi thread. */
swi = Swi_create(swiFxn, NULL, &eb);
if (swi == NULL)
{
System_abort("Swi create failed");
}
/* Create a Task with priority 3.
* Task function is 'taskFxn' which runs as a Task thread. */
taskParams.priority = 3;
task = Task_create(taskFxn, &taskParams, &eb);
if (task == NULL)
{
System_abort("Task create failed");
}
/* Create a binary Semaphore for example task to pend on */
sem = Semaphore_create(0, NULL, &eb);
if (sem == NULL)
{
System_abort("Semaphore create failed");
}
/* Create a Queue to hold the Clock Objects on */
clockQueue = Queue_create(NULL, &eb);
if (clockQueue == NULL)
{
System_abort("Queue create failed");
}
/* setup clk1 to go off every 5 timer interrupts. */
clk1.fxn = clk1Fxn;
clk1.period = 5;
clk1.timeout = 5;
clk1.arg = 1;
/* add the Clock object to the clockQueue */
Queue_put(clockQueue, &clk1.elem);
/* setup clk2 to go off every 20 timer interrupts. */
clk2.fxn = clk2Fxn;
clk2.period = 20;
clk2.timeout = 20;
clk2.arg = (UArg)sem;
/* add the Clock object to the clockQueue */
Queue_put(clockQueue, &clk2.elem);
/* Configure a periodic interrupt using any available Timer
* with a 1000 microsecond (1ms) interrupt period.
*
* The Timer interrupt will be handled by 'hwiFxn' which
* will run as a Hwi thread.
*/
timerParams.period = 1000;
timer = Timer_create(Timer_ANY, hwiFxn, &timerParams, &eb);
if (timer == NULL)
{
System_abort("Timer create failed");
}
BIOS_start();
return(0);
}

配置文件cfg代码:
/* ======== HwiSwiTaskExample.cfg ======== */
var Defaults = xdc.useModule('xdc.runtime.Defaults');
var Diags = xdc.useModule('xdc.runtime.Diags');
var Error = xdc.useModule('xdc.runtime.Error');
var Log = xdc.useModule('xdc.runtime.Log');
var LoggerBuf = xdc.useModule('xdc.runtime.LoggerBuf');
var Main = xdc.useModule('xdc.runtime.Main');
var Memory = xdc.useModule('xdc.runtime.Memory')
var SysMin = xdc.useModule('xdc.runtime.SysMin');
var System = xdc.useModule('xdc.runtime.System');
var Text = xdc.useModule('xdc.runtime.Text');
var BIOS = xdc.useModule('ti.sysbios.BIOS');
var Task = xdc.useModule('ti.sysbios.knl.Task');
var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
var Queue = xdc.useModule('ti.sysbios.knl.Queue');
var Hwi = xdc.useModule('ti.sysbios.hal.Hwi');
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var Timer = xdc.useModule('ti.sysbios.hal.Timer');
Program.argSize = 0x0;
System.maxAtexitHandlers = 4;
BIOS.heapSize = 0x2000;
/* System stack size (used by ISRs and Swis) */
Program.stack = 0x1000;
/* Circular buffer size for System_printf() */
SysMin.bufSize = 0x400;
/* Create and install logger for the whole system */
var loggerBufParams = new LoggerBuf.Params();
loggerBufParams.numEntries = 32;
var logger0 = LoggerBuf.create(loggerBufParams);
Defaults.common$.logger = logger0;
Main.common$.diags_INFO = Diags.ALWAYS_ON;
System.SupportProxy = SysMin;
BIOS.libType = BIOS.LibType_Custom;

运行结果:
[Cortex_M3_0] Starting HwiSwiTask example.
In taskFxn pending on Sempahore.
In clk1Fxn, arg = 1.
In clk1Fxn, arg = 2.
In clk1Fxn, arg = 3.
In clk1Fxn, arg = 4.
In clk2Fxn, posting Semaphore.
In taskFxn returned from Sempahore.

这个程序演示了如何用Hwi和Swi模拟一个时钟,注意,不是系统自带的那个时钟'ti.sysbios.knl.Clock'。我估计实现原理我系统自带时钟会有似之处吧。

计时这块使用的是系统的timer,我们知道它是硬中断,但硬中断用来处理复杂操作肯定不合适,所以在硬中断中只是简单调用Swi_post,把真正的处理交由Swi进行。Swi实际就是计算每个时钟是否到期,如果到期就触发相应的函数。模拟出来的时钟1是一个周期时钟,重复运行;时钟2则是一个单周期(one shot)时钟,用于关闭程序。当然,它也不是直接关闭,而是通过一个信号量发信号给task来关闭。

通过这个例子,我们也可以了解到timer(计时器)和clock(时钟)的区别。timer属硬中断,执行那些操作少,对时间要求极为精准的操作。clock属软中断,执行那些操作复杂,对时间要求不太精准的操作。我们已经看到在Swi函数中,需要遍历时钟列表,并一一进行操作,这样是无法保证时间非常精准的。

有了这个思路再去看代码,相信会简单很多,我就不哆嗦了。这回总算是把TI-RTOS中的所有线程给学完了,当然,这远远不够,估计我得把这本书翻译完。好在翻译这类文章比翻译RFC文档容易得多了。

转自: abatei的博客