Tutorial 2
Tutorial 2
Example:
In BlinkC example visited in Tutuorial 1, we will replace the three timers by only one timer that would be used for the three LEDs. If you compare the ROM and RAM sizes with the unmodified Blink application, you should see that they are a bit smaller: TinyOS is only allocating state for a single timer, and there is event code for only one timer. These are the steps to modify the application code: 1. Make a copy of the Blink application, BlinkSingle, and go into its directory.
$ cd tinyos-2.x/apps $ cp -R Blink BlinkSingle $ cd BlinkSingle
module BlinkC @safe(){ uses interface Timer<TMilli> as Timer0; uses interface Leds; uses interface Boot; } implementation { uint8_t counter = 0; event void Boot.booted() { call Timer0.startPeriodic( 250 ); } event void Timer0.fired() { counter++; call Leds.set(counter); } }
Using this counter, we can replace the three timers by only one timer and the leds are managed through Leds.set(counter) command. 3. Open BlinkAppC and remove the two Timers (Timer 1 and Timer 2) and their wirings. Compile the application
III. Tasks
All of the code we've looked at so far is synchronous. It runs in a single execution context and does not have any kind of pre-emption. It does not relinquish the CPU to other sync code until it completes. It means that if one piece of sync code runs for a long time, it prevents other sync code from running, which can adversely affect system responsiveness.
For large computations. A component needs to be able to split a large computation into smaller parts, which can be executed one at a time. Giving TinyOS the ability to defer the computation until later can let it deal with everything else that's waiting first. Tasks enable components to perform general-purpose "background" processing in an application. A task is a function which a component tells TinyOS to run later, rather than now. A component can post a task in a command, an event, or a task. Because they are the root of a call graph, a tasks can safely both call commands and signal events. Commands do not signal events to avoid creating recursive loops across component boundaries (e.g., if command X in component 1 signals event Y in component 2, which itself calls command X in component 1). Tasks do not preempt each other, but a task can be preempted by a hardware interrupts (which we haven't seen yet). If you need to run a series of long operations, you should dispatch a separate task for each operation, rather than using one big task. A task is declared in your implementation module using the syntax: task void taskname() { ... } where taskname() is whatever symbolic name you want to assign to the task. Tasks must return void and may not take any arguments.
To dispatch a task for (later) execution, use the syntax: post taskname(); A task can be posted from within a command, an event, or even another task. The post operation places the task on an internal task queue which is processed in FIFO order. When a task is executed, it runs to completion before the next task is run; therefore, a task should not spin or block for long periods of time. Tasks do not preempt each other, but a task can be preempted by a hardware event handler. If you need to run a series of long operations, you should dispatch a separate task for each operation, rather than using one big task.
A component can define standard C functions, which other components cannot name and therefore cannot invoke directly. While these functions do not have the command or event modifier, they can freely call commands or signal events. Internal functions act just like C functions: they don't need the call or signal keywords.
Example:
void startTimers() { call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } event void Boot.booted() { startTimers(); }