3.7 The Idle Loop
Idle 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; |
3.8 使用Hwi、Swi和Task线程的例子
此例描述了程式化版本的SYS/BIOS时钟模块设计。它联合使用了Hwi、Swi和Task线程。
一个周期计时器中断提交一个Swi用于处理时钟对象列表。时钟对象列表中的每个条目都拥有它自己的周期和时钟函数。当一个对象的时间到期,时钟函数被调用,周期重新开始。
由于没有限制列表中可旋转时钟对象的个数,也无法确定每个时钟函数的开销,所以维护所有时钟对象所消耗的时间也无法确定。因此,在计时器的Hwi线程中维护时钟对象是不切实际的。此类问题使用Swi来解决更能成为轻量级解决方案(相对使用Task来说)。
(书上的代码还是无法直接运行,还得改,但发现改着改着也改习惯了,呵呵)
C代码:
#include <xdc/std.h>
#include <xdc/runtime/System.h> #include <xdc/runtime/Error.h> #include <ti/sysbios/BIOS.h> #include <ti/sysbios/hal/Timer.h> #include <ti/sysbios/knl/Semaphore.h> #include <ti/sysbios/knl/Swi.h> #include <ti/sysbios/knl/Task.h> #include <ti/sysbios/knl/Queue.h> #include <xdc/cfg/global.h> 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文档容易得多了