Embedded_Systems
嵌入式系统教学与研究实验室
posts - 2,comments - 3,trackbacks - 0
 
Lesson 2: Event-Driven Sensor Acquisition

Last updated 9 September  2003

This lesson demonstrates a simple sensor application that takes light intensity readings (from the sensor board's photo sensor) and displays those readings on the LEDs. This is based on the Sense application, found in apps/Sense. This is a very simple application that reads a value from the light sensor and displays the lower 3 bits of the value on the LEDs. The application configuration is found in Sense.nc and the implementation module in SenseM.nc.
The SenseM.nc component

Let's first look at the top portion of SenseM.nc:

SenseM.nc
module SenseM {
  provides {
    interface StdControl;
  }
  uses {
    interface Timer;
    interface ADC;
    interface StdControl as ADCControl;
    interface Leds;
  }
}
// Implementation not shown ...

Like BlinkM, this component provides the StdControl interface and uses Timer and Leds. It also uses two more interfaces: ADC, which is used to access data from the analogue-to-digital converter, and StdControl, which is used to initialize the ADC component.

This application uses a new component, TimerC, in place of SingleTimer. The reason is that TimerC allows multiple instance of timers, while SingleTimer only provides a single one that only one component can use. We'll discuss more on Timer a bit later.

Note the line

  interface StdControl as ADCControl;
This is saying that this component uses the StdControl interface but gives the interface instance the name ADCControl. In this way a component can require multiple instances of the same interface, but give them different names. For example, a component might need to use the StdControl interface to control both the ADC and the sounder components. In this case we might say:
  interface StdControl as ADCControl;
  interface StdControl as SounderControl;
The configuration that uses this module will be responsible for wiring each interface instance to some actual implementation.

One related note: if you don't provide an interface instance name then the instance is named the same as the interface. That is, the line

  interface ADC;
is just shorthand for
  interface ADC as ADC;
Take a look at the StdControl and ADC interfaces (both in tos/interfaces). You'll see that StdControl is used to initialize and power a component (usually a piece of physical hardware) and ADC is used to get data from an ADC channel. ADC signals an event, dataReady(), when data is ready on the channel. Notice the async keyword is used in the ADC interface. This is declaring the commands and events as asynchronous code, i.e., code which can run in response to a hardware interrupt.

Open the file apps/Sense/SenseM.nc.  You'll see the code simply calls ADC.getData() each time Timer.fired() is signaled. Likewise, when ADC.dataReady() is signaled, the internal function display() is invoked, which sets the LEDs to the low-order bits of the ADC value.

Notice the use of the function rcombine() in the implementation of StdControl.init().

  return rcombine(call ADCControl.init(), call Leds.init());
The function rcombine() is a special nesC combining function which returns the logical-and of two commands who result type is result_t.
 
The Sense.nc configuration

At this point you should be wondering, "how does the Sense application know that the ADC channel should access the light sensor?" This is exactly what the Sense.nc configuration wires up for us.

Sense.nc
configuration Sense {
  // this module does not provide any interface
}
implementation
{
  components Main, SenseM, LedsC, TimerC, Photo;

  Main.StdControl -> SenseM;
  Main.StdControl -> TimerC;

  SenseM.ADC -> Photo;
  SenseM.ADCControl -> Photo;
  SenseM.Leds -> LedsC;
  SenseM.Timer -> TimerC.Timer[unique("Timer")];
}

Most of this should be familiar from Blink. We're wiring up the Main.StdControl to SenseM.StdControl, as before. The Leds wirings is also similar to Blink, and we'll discuss Timer in a minute. The wiring for the ADC is:

  SenseM.ADC -> Photo;
  SenseM.ADCControl -> Photo;
All we're doing is wiring up the ADC interface (used by SenseM) to a new component called Photo. Same goes for the ADCControl interface, which you'll recall is an instance of the StdControl interface used by SenseM.

Remember that

  SenseM.ADC -> Photo;
is just shorthand for
  SenseM.ADC -> Photo.ADC;
On the other hand,
  SenseM.ADControl -> Photo;
is not shorthand for
  SenseM.ADC -> Photo.ADCControl;
What's going on here? If you look at the Photo.nc component (found in tos/sensorboards/micasb), you'll see that it provides both the ADC and StdControl interfaces -- it doesn't have an interface called ADCControl. (ADCControl was just the name that we gave to the instance of StdControl in the SenseM component.) Rather, the nesC compiler is smart enough to figure out that because SenseM.ADCControl is an instance of the StdControl interface, that it needs to be wired up to an instance of the StdControl interface provided by Photo. (If Photo were to provide two instances of StdControl, this would be an error because the wiring would be ambiguous.) In other words,
  SenseM.ADControl -> Photo;
is shorthand for
  SenseM.ADControl -> Photo.StdControl;
Whew!
 
Timer and parameterized interfaces

Let's look at the line

  SenseM.Timer -> TimerC.Timer[unique("Timer")];
This is introducing a new bit of syntax, called a parameterized interface.

A parameterized interface allows a component to provide multiple instances of an interface that are parameterized by a runtime or compile-time value. Recall that it's possible for a component to provide multiple instances of an interface and to give them different names, e.g.,

  provides {
    interface StdControl as fooControl;
    interface StdControl as barControl;
  }
This is just a generalization of the same idea. The TimerC component declares:
  provides interface Timer[uint8_t id];
In other words, it provides 256 different instances of the Timer interface, one for each uint8_t value!

In this case, we want TinyOS applications to create and use multiple timers, each of which can be managed independently. For example, an application component might need one timer to fire at a certain rate (say, once per second) to gather sensor readings, while another component might need the a timer to fire at a different rate to manage radio transmission. By wiring the Timer interface in each of these components to a separate instance of the Timer interface provided by TimerC, each component can effectively get its own "private" timer.

When we say TimerC.Timer[someval], we are specifying that BlinkM.Timer should be wired to the instance of the Timer interface specified by the value (someval) in the square brackets. This can be any 8-bit positive number. If we were to specify a particular value here, such as 38 or 42, we might accidentally conflict with the timer being used by another component (if that component used the same value in the brackets). So, we use the compile-time constant function unique(), which generates a unique 8-bit identifier from the string given as an argument. Here,

  unique("Timer")
generates a unique 8-bit number from a set corresponding to the string "Timer". Therefore, Every component that uses unique("Timer") is guaranteed to get a different 8-bit value as long as the string used in the argument is the same. Note that if one component uses unique("Timer") and another uses unique("MyTimer"), they might get the same 8-bit value. Therefore it's good practice to use the name of the parameterized interface as an argument to the unique() function. The compile-time constant function

  uniqueCount("Timer")

will count the number of uses of unique("Timer").
 
Running the Sense application

As before, just do a make mica install in the Sense directory to compile and install the application on your mote. You'll need a sensor board attached to the mote with a photo sensor. The Mica sensor board, for example, snaps into place via the 51 pin connector. The type of sensorboard is selected by the -board command line option to ncc. On the Mica mote, the default sensor board type is micasb. However, if you have an older ("basic") sensor board, you might need to pass in the -board basicsb option to ncc. To do this, edit the Makefile in the Sense directory and add the line SENSORBOARD=basicsb before the line that includes Makerules. The sensor boards supported by TinyOS are represented by the directory names in tos/sensorboards .

The operation of the photo sensor is a bit unusual. The ADC yields a 10-bit digitized sample of the photo sensor. What we would like is for the LEDs to be off when the node is in the light, and on when the node is in the dark. Here, we are looking at the upper three bits of this data coming from the ADC, and inverting the value. Note the line:

  display(7 - ((data >> 7) & 0x7));
in the ADC.dataReady() function of SenseM.

Note that when running the application you may really have to cover up the photo sensor pretty well to get it to respond - we'll admit that this is not the best application to really see the photo sensor at work!
 
Exercises

Extend the capability of the application by chirping the sensor board's sounder when it is dark! The TinyOS component for the Sounder is located in tos/sensorboards/micasb/Sounder.nc and provides the StdControl interface. Modify SenseM.nc to add a StdControl as SounderControl, wiring it as appropriate in Sense.nc.  Remember to call init() to initialize the Sounder component. To make the sounder turn on from SenseM.nc, call SounderControl.start(). To turn off the sounder, call SounderControl.stop(). Be sure to test this new application many times to annoy everyone sitting around you.


posted on 2005-11-28 01:06 井冈山 阅读(480) 评论(0)  编辑 收藏 引用 所属分类: tinyOS
只有注册用户登录后才能发表评论。