[Tinyos Core WG] Meeting: 10/4

Philip Levis pal at cs.stanford.edu
Tue Oct 3 21:42:45 PDT 2006


Wednesday, October 4, 2006 9:30 AM US Pacific Time
Bridge: 2, Passcode: 1373995

Numbers:
US: 1-916-356-2663 or 1-888-875-9370 (non-Intel)
UK: +44 1793 402663
Denmark: +45 4527 5090

Agenda:

Status
TEP 111: message_t (attached)
TEP 101 + 109 (attached), 109 needs proper Implementation section
Release and SenSys plans

Phil

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.millennium.berkeley.edu/pipermail/tinyos-2.0wg/attachments/20061003/aec61ff8/tep111-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.millennium.berkeley.edu/pipermail/tinyos-2.0wg/attachments/20061003/aec61ff8/tep101-0001.html
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.millennium.berkeley.edu/pipermail/tinyos-2.0wg/attachments/20061003/aec61ff8/tep109-0001.html
-------------- next part --------------

-------------- next part --------------
============================
message_t
============================

:TEP: 111
:Group: Core Working Group 
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: Philip Levis

:Draft-Created: 11-Jul-2005
:Draft-Version: $Revision: 1.1.2.12 $
:Draft-Modified: $Date: 2006/06/13 16:44:17 $
:Draft-Discuss: TinyOS Developer List <tinyos-devel at mail.millennium.berkeley.edu>

.. Note::

   This memo documents a part of TinyOS for the TinyOS Community, and
   requests discussion and suggestions for improvements.  Distribution
   of this memo is unlimited. This memo is in full compliance with
   TEP 1.

Abstract
====================================================================

This memo covers the TinyOS 2.x message buffer abstraction, ``message_t``.
It describes the message buffer design considerations, how and where 
``message_t`` is specified, and how data link layers should access it. 

1. Introduction
====================================================================

In TinyOS 1.x, a message buffer is a ``TOS_Msg``. A buffer contains an
active message (AM) packet as well as packet metadata, such as timestamps,
acknowledgement bits, and signal strength if the packet was received.
``TOS_Msg`` is a fixed size structure whose size is defined by the maximum
AM payload length (the default is 29 bytes). Fixed sized buffers allows
TinyOS 1.x to have zero-copy semantics: when a component receives a
buffer, rather than copy out the contents it can return a pointer
to a new buffer for the underlying layer to use for the next received 
packet.

One issue that arises is what defines the ``TOS_Msg`` structure, as different
link layers may require different layouts. For example, 802.15.4 radio 
hardware (such as the CC2420, used in the Telos and micaZ platforms) 
may require 802.15.4 headers, while a software stack built on top of
byte radios (such as the CC1000, used in the mica2 platform) can specify 
its own packet format. This means that ``TOS_Msg`` may be different on
different platforms.

The solution to this problem in TinyOS 1.x is for there to be a standard
definition of ``TOS_Msg``, which a platform (e.g., the micaZ) can
redefine to match its radio. For example, a mica2 mote uses the standard 
definition, which is::

  typedef struct TOS_Msg {
    // The following fields are transmitted/received on the radio.
    uint16_t addr;
    uint8_t type;
    uint8_t group;
    uint8_t length;
    int8_t data[TOSH_DATA_LENGTH];
    uint16_t crc;

    // The following fields are not actually transmitted or received
    // on the radio! They are used for internal accounting only.
    // The reason they are in this structure is that the AM interface
    // requires them to be part of the TOS_Msg that is passed to
    // send/receive operations.
    uint16_t strength;
    uint8_t ack;
    uint16_t time;
    uint8_t sendSecurityMode;
    uint8_t receiveSecurityMode;
  } TOS_Msg;

while on a mote with a CC420 radio (e.g., micaZ), ``TOS_Msg`` is defined as::

  typedef struct TOS_Msg {
    // The following fields are transmitted/received on the radio.
    uint8_t length;
    uint8_t fcfhi;
    uint8_t fcflo;
    uint8_t dsn;
    uint16_t destpan;
    uint16_t addr;
    uint8_t type;
    uint8_t group;
    int8_t data[TOSH_DATA_LENGTH];
    
    // The following fields are not actually transmitted or received
    // on the radio! They are used for internal accounting only.
    // The reason they are in this structure is that the AM interface
    // requires them to be part of the TOS_Msg that is passed to
    // send/receive operations.
    
    uint8_t strength;
    uint8_t lqi;
    bool crc;
    uint8_t ack;
    uint16_t time;
   } TOS_Msg;

There are two basic problems with this approach. First, exposing all of
the link layer fields leads components to directly access the packet
structure. This introduces dependencies between higher level components
and the structure layout. For example, many network services built on
top of data link layers care whether sent packets are acknowledged. They
therefore check the ``ack`` field of ``TOS_Msg``. If a link layer does not 
provide acknowledgements, it must still include the ``ack`` field
and always set it to 0, wasting a byte of RAM per buffer.

Second, this model does not easily support multiple data link layers.
Radio chip implementations assume that the fields they require are 
defined in the structure and directly access them. If a platform
has two different link layers (e.g., a CC1000 *and* a CC2420 radio),
then a ``TOS_Msg`` needs to allocate the right amount of space for both
of their headers while allowing implementations to directly access
header fields. This is very difficult to do in C.

The ``data`` payload is especially problematic. Many
components refer to this field, so it must be at a fixed offset.
Depending on the underlying link layer, the header fields 
preceding it might have different lengths, and packet-level radios
often require packets to be contiguous memory regions. Overall, these 
complexities make specifying the format of ``TOS_Msg`` very difficult.

2. message_t
====================================================================

In TinyOS 2.x, the standard message buffer is ``message_t``. The
message_t structure is defined in ``tos/types/message.h``::

  typedef nx_struct message_t {
    nx_uint8_t header[sizeof(message_header_t)];
    nx_uint8_t data[TOSH_DATA_LENGTH];
    nx_uint8_t footer[sizeof(message_footer_t)];
    nx_uint8_t metadata[sizeof(message_metadata_t)];
  } message_t;

This format keeps data at a fixed offset, which is important when
passing a message buffer between two different link layers. If the
data payload were at different offsets for different link layers, then
passing a packet between two link layers would require a ``memmove(3)``
operation (essentially, a copy).

The header, footer, and metadata formats are all opaque. Source code
cannot access fields directly. Instead, data-link layers provide access
to fields through nesC interfaces.  Section 3 discusses this in 
greater depth.

Every link layer defines its header, footer, and metadata
structures. These structures MUST be external structs (``nx_struct``), 
and all of their fields MUST be external types (``nx_*``), for two 
reasons. First, external types ensure cross-platform compatibility. 
Second, it forces structures to be aligned on byte boundaries, 
circumventing issues with the 
alignment of packet buffers and field offsets within them.
For example, the CC1000 radio implementation defines
its structures in ``CC1000Msg.h``::

  typedef nx_struct cc1000_header {
    nx_am_addr_t addr;
    nx_uint8_t length;
    nx_am_group_t group;
    nx_am_id_t type;
  } cc1000_header_t;

  typedef nx_struct cc1000_footer {
    nxle_uint16_t crc;
  } cc1000_footer_t;

  typedef nx_struct cc1000_metadata {
    nx_uint16_t strength;
    nx_uint8_t ack;
    nx_uint16_t time;
    nx_uint8_t sendSecurityMode;
    nx_uint8_t receiveSecurityMode;
  } cc1000_metadata_t;

Each link layer defines its structures, but a **platform** is
responsible for defining ``message_header_t``, ``message_footer_t``,
and ``message_metadata_t``. This is because a platform may have
multiple link layers, and so only it can resolve which structures are
needed. These definitions MUST be in a file in a platform directory
named ``platform_message.h``. The mica2 platform, for example, has
two data link layers: the CC1000 radio and the TinyOS serial
stack [1]_. The serial packet format does not have a footer
or metadata section. The ``platform_message.h`` of the mica2
looks like this::

  typedef union message_header {
    cc1000_header_t cc1k; 
    serial_header_t serial;
  } message_header_t;

  typedef union message_footer {
    cc1000_footer_t cc1k;
  } message_footer_t;
  
  typedef union message_metadata {
    cc1000_metadata cc1k;
  } message_metadata_t;

For a more complex example, consider a fictional platform named
'megamica' that has both a CC1000 and a CC2420 radio. Its
``platform_message.h`` would look like this::

  typedef union mega_mica_header {
    cc1000_header_t cc1k;
    cc2420_header_t cc2420;
    serial_header_t serial;
  } message_header_t;

  typedef union mega_mica_footer {
    cc1000_footer_t cc1k;
    cc2420_footer_t cc2420;
  } message_footer_t;

  typedef union mega_mica_metadata {
    cc1000_metadata_t cc1k;
    cc2420_metadata_t cc2420;
  } message__metadata_t;
 
If a platform has more than one link layer, it SHOULD define each of the
message_t fields to be a union of the underlying link layer structures.
This ensures that enough space is allocated for all underlying link layers.

3. The message_t fields
====================================================================

TinyOS 2.x components treat packets as abstract data types (ADTs),
rather than C structures, obtaining all of the traditional benefits
of this approach. First and foremost, clients of a packet layer
do not depend on particular field names or locations, allowing the
implementations to choose packet formats and make a variety of
optimizations.

Components above the basic data-link layer MUST always access 
packet fields through interfaces.  A component that introduces 
new packet fields SHOULD provide an interface to those that 
are of interest to other components.  
For example, active messages have an interface named ``AMPacket`` 
which provides access commands to AM fields. In TinyOS 1.x, a 
component would directly access ``TOS_Msg.addr``; in TinyOS 2.x, 
a component calls ``AMPacket.getAddress(msg)``.
The most basic of these interfaces is Packet, which provides
access to a packet payload. TEP 116 describes common TinyOS 
packet ADT interfaces [2]_. 

Link layer components MAY access packet fields differently than other 
components, as they are aware of the actual packet format. They can
therefore implement the interfaces that provide access to the fields
for other components. 

3.1 Headers
----------------------------------------------------------------

The message_t header field is an array of bytes whose size is 
the size of a platform's union of data-link headers.
Because packets are stored contiguously, the layout of a packet
in memory is not the same as the layout of its nesC structure.

A packet header does not necessarily start at the beginning of
the message_t. For example, consider the Telos platform::

  typedef union message_header {
    cc2420_header_t cc2420;
    serial_header_t serial;
  } message_header_t;

The CC2420 header is 11 bytes long, while the serial header is
5 bytes long. The serial header ends at the beginning of the
data payload, and so six padding bytes precede it. This figure
shows the layout of message_t, a 12-byte CC2420 packet, and
a 12-byte serial packet on the Telos platform::

              11 bytes         TOSH_DATA_LENGTH        7 bytes
            +-----------+-----------------------------+-------+
  message_t |  header   |          data               | meta  |
            +-----------+-----------------------------+-------+
              
            +-----------+------------+                +-------+
  CC2420    |  header   |   data     |                | meta  |
            +-----------+------------+                +-------+
  
                  +-----+------------+                   
  Serial          | hdr |   data     |   
                  +-----+------------+                 

Neither the CC2420 nor the serial stack has packet footers, and
the serial stack does not have any metadata. 

The packet for a link layer does not necessarily start at the beginning
of the message_t. Instead, it starts at a negative offset from the
data field.  When a link layer component needs to read or write protocol 
header fields, it MUST compute the location of the header as a negative 
offset from the data field.  For example, the serial stack header has
active message fields, such as the AM type. The command that returns
the AM type, ``AMPacket.type()``, looks like this::

  serial_header_t* getHeader(message_t* msg) {
    return (serial_header_t*)(msg->data - sizeof(serial_header_t));
  } 
  command am_id_t AMPacket.type(message_t* msg) {
    serial_header_t* hdr = getheader(msg); 
    return hdr->type;
  }


Because calculating the negative offset is a little bit unwieldy, the
serial stack uses the internal helper function getHeader(). Many
single-hop stacks follow this approach, as it is very likely
that nesC will inline the function, eliminating the possible overhead.
In most cases, the C compiler also compiles the call into a simple
memory offset load.

The following code is incorrect, as it directly casts the header field.
It is an example of what components MUST NOT do::

  serial_header_t* getHeader(message_t* msg) {
    return (serial_header_t*)(msg->header);
  } 


In the case of Telos, for example, this would result in a packet 
layout that looks like this::

              11 bytes         TOSH_DATA_LENGTH           7 bytes
            +-----------+-----------------------------+-------+
  message_t |  header   |          data               | meta  |
            +-----------+-----------------------------+-------+
  
            +-----+     +------------+                   
  Serial    | hdr |     |   data     |   
            +-----+     +------------+                 

3.2 Data
----------------------------------------------------------------

The data field of message_t stores the single-hop packet payload.
It is TOSH_DATA_LENGTH bytes long. The default size is 28 bytes.
A TinyOS application can redefine TOSH_DATA_LENGTH at compile time
with a command-line option to ncc: ``-DTOSH_DATA_LENGTH=x``.
Because this value can be reconfigured, it is possible that two
different versions of an application can have different MTU sizes.
If a packet layer receives a packet whose payload size is 
longer than TOSH_DATA_LENGTH, it MUST discard the packet. 

3.3 Footer
----------------------------------------------------------------

The message_footer_t field ensures that message_t has enough space to
store the footers for all underlying link layers when there are
MTU-sized packets. Like headers, footers are not necessarily stored
where the C structs indicate they are: instead, their placement is
implementation dependent. A single-hop layer MAY store the footer
contiguously with the data region. For short packets, this can mean
that the footer will actually be stored in the data field.

3.4 Metadata
----------------------------------------------------------------

The metadata field of message_t stores data that 
a single-hop stack uses or collects does not transmit. 
This mechanism allows packet layers to store per-packet 
information such as RSSI or timestamps. For example, this is
the CC2420 metadata structure::

  typedef nx_struct cc2420_metadata_t {
    nx_uint8_t tx_power;
    nx_uint8_t rssi;
    nx_uint8_t lqi;
    nx_bool crc;
    nx_bool ack;
    nx_uint16_t time;
  } cc2420_metadata_t;

5. Implementation
====================================================================

The definition of message_t can be found in 
``tinyos-2.x/tos/types/message.h``. The definition of the CC2420
message format can be found in ``tinyos-2.x/tos/chips/cc2420/CC2420.h``.
The defintion of the CC1000 message format can be found in 
``tinyos-2.x/tos/chips/cc1000/CC1000Msg.h``. The definition
of the standard serial stack packet format can be found in
``tinyos-2.x/tos/lib/serial/Serial.h''. The definition of
the telosb packet format can be found in 
``tinyos-2.x/tos/platform/telosa/platform_message.h`` and the micaz format can be found in
``tinyos-2.x/tos/platforms/micaz/platform_message.h``.

6. Author's Address
====================================================================

| Philip Levis
| 358 Gates Hall
| Computer Science Laboratory
| Stanford University
| Stanford, CA 94305
|
| phone - +1 650 725 9046
| email - pal at cs.stanford.edu

6. Citations
====================================================================

.. [1] TEP 113: Serial Communication.

.. [2] TEP 116: Packet Protocols.


-------------- next part --------------
===================================
Analog-to-Digital Converters (ADCs)
===================================

:TEP: 101
:Group: Core Working Group 
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: Jan-Hinrich Hauer, Philip Levis, Vlado Handziski, David Gay

:Draft-Created: 20-Dec-2004
:Draft-Version: $Revision: 1.1.2.7 $
:Draft-Modified: $Date: 2006/07/14 17:01:14 $
:Draft-Discuss: TinyOS Developer List <tinyos-devel at mail.millennium.berkeley.edu>

.. Note::

   This memo documents a part of TinyOS for the TinyOS Community, and
   requests discussion and suggestions for improvements.  Distribution
   of this memo is unlimited. This memo is in full compliance with
   [TEP1]_.


Abstract
====================================================================

This TEP proposes a hardware abstraction for TinyOS 2.x analog-to-digital
converters (ADCs). It focuses on aligning the ADC abstraction with the
three-layer Hardware Abstraction Architecture (HAA) described in [TEP2]_, but
addresses only the HPL and HAL, because the highest level abstraction of an
ADC is platform-dependent.

1. Introduction
====================================================================

Analog-to-digital converters (ADCs) are devices that convert analog input
signals to discrete digital output signals, typically voltage to a binary
number.  The interested reader can refer to Appendix A for a brief overview of
the ADC hardware on some current TinyOS platforms.  In earlier versions of
TinyOS, the distinction between a sensor and an ADC were blurred: this led
components that had nothing to do with an ADC to still resemble one
programatically, even though the semantics and forms of operation were
completely different.  To compensate for the difference non-ADC sensors
introduced additional interfaces, such as ADCError, that were tightly bound to
sensor acquisition but separate in wiring. The separation between the ADC and
ADCError interface is bug prone and problematic, as is the equation of a
sensor and an ADC. TinyOS 2.x separates the structure and interfaces of an ADC
from those of sensors (which may be on top of an ADC, but this fact is hidden
from higher level components). This TEP presents how TinyOS 2.x decomposes and
structures ADC software. TEP 109 (Sensor Boards) shows how a platform can
present actual named sensors [TEP109]_.

As can be seen in Appendix A the ADC hardware used on TinyOS platforms differ
in many respects, which makes it difficult to find a chip independent
representation for an ADC. Even if there were such a representation, the
configuration details of an ADC would still depend on the actual device
producing the input signal (sensor).  Neither a platform independent
application nor the ADC hardware stack itself has access to this information,
as it can only be determined on a platform or sensorboard level. For example,
determining which ADC port a sensor is attached to and how a conversion result
is to be interpreted is a platform-specific determination. 

In spite of their hardware differences, one aspect represents a common
denominator of all ADCs: they produce conversion results. In order to
facilitate sensor software development this capability can be made available
via chip-independent interfaces for every ADC. However, conversion results
depend on and have to be interpreted with respect to the platform-specific
configuration settings (the ADC channel, the applied reference voltage, etc.).
Therefore the highest level of ADC abstraction consists of
platform-independent interfaces for ADC data collection and chip-specific
interfaces for ADC hardware configuration.  The top layer of the ADC stack
thus remains platform-dependent and consequently the ADC abstraction does not
include an HIL, but ends with the HAL. Following the principles of the
HAA [TEP2]_ the HAL of an ADC should also expose the chip-specific capabilities
for ADC data collection. For example, the ADC12 on the MSP430 MCU supports a
complex repeat conversion mode for a set of different input channels, which is
too specific to be represented by a platform-independent data collection
interface.  Therefore the HAL of an ADC abstraction is broken into two
sublayers: The bottom HAL layer, called HAL1, exposes the full capabilities of
the respective ADC in a chip-specific way. It realizes the standard HAL in the
HAA [TEP2]_ and the HPL lies below it.  On top of the HAL1 sits the HAL2 which
maps the interfaces it uses from HAL1 to a set of platform-independent
interfaces for data collection and chip-specific configuration interfaces.

The rest of this TEP specifies:

* the set of platform-independent interfaces for the collection of ADC
  conversion results (`2. Interfaces`_)
* guidelines on how an ADC's HAL should be split into HAL1 and HAL2 and
  how the HAL1 should expose chip-specific interfaces (`3. HAL1 guidelines`_)
* what components an ADC's HAL2 MUST implement (`4. HAL2 requirements`_)
* guidelines on how the HAL2 may be structured (`5. HAL2 implementation guidelines`_)
* a section pointing to the current implementation (`6. Implementation`_)


This TEP ends with appendices documenting, as an example, the ADC
implementation for the TI MSP430 MCU.


2. Interfaces
====================================================================

This TEP proposes to adopt the following three generic, source-independent
data collection interfaces from [TEP114]_ for the collection of ADC conversion
results::

  interface Read< size_type >
  interface ReadNow< size_type >
  interface ReadStream< size_type >

Every data collection interface is associated with certain chip-specific
configuration data (e.g. input channel, sample-hold-time, etc.).  How this
association can be realized is explained in Section `4.  HAL2 requirements`_.
As the resolution of conversion results is chip-specific, the 'size_type'
parameter reflects an upper bound for the chip-specific resolution of the
conversion results - the actual resolution may be smaller, depending on the
ADC and/or data source (e.g.  uint16_t for a 12-bit ADC). The above interfaces
are specified in [TEP114]_, in the following their usage is explained with
respect to ADCs.

Read
--------------------------------------------------------------------

The Read interface can be used to sample an ADC channel and return a single
conversion result. It provides no guarantees about when exactly the sampling
occurs (the request may be buffered). 

ReadNow
--------------------------------------------------------------------

The ReadNow interface provides more precise control over the time of the
sampling: If a call to ReadNow.read() succeeds, the ADC starts to sample the
channel immediately (the request is not buffered). Due to its timing
constraints the ReadNow interface is always provided in conjunction with an
instance of the Resource interface. Refer to [TEP108]_ on how the 'Resource'
interface should be used by a client component.


ReadStream
--------------------------------------------------------------------

The ReadStream interface can be used to sample an ADC channel multiple times
with a specified sampling period. It provides no guarantees about when exactly
the first sampling occurs, but all subsequent samplings occur with the
specified sampling period.


3. HAL1 guidelines
====================================================================

As explained in `1. Introduction`_ the HAL of an ADC abstraction consists of
two sublayers, HAL1 and HAL2. In the ADC component stack the HAL1 resides
below HAL2 and above the HPL. It exposes the full capabilities of the ADC in a
chip-specific way and has the same function as the 'traditional' HAL in the
HAA [TEP2]_. Therefore only chip- and platform-dependent clients MAY wire to
the HAL1. Although the HAL1 is chip-specific, both, in terms of implementation
and representation, its design should follow the guidelines described below to
facilitate the mapping to platform-independent interfaces on the level of
HAL2. Appendix B shows the HAL1 specification for the TI MSP430 MCU.

Resource reservation
--------------------------------------------------------------------

As the ADC hardware is a shared resource that is multiplexed between several
clients, it requires access arbitration. Therefore the HAL1 configuration
component should provide a parameterized 'Resource' interface, instantiate a
generic arbiter component and connect the 'Resource' interface to the arbiter
as described in [TEP108]_. To ensure fair and uniform arbitration on all
platforms the standard round robin arbiter is recommended. Refer to [TEP108]_
on how the 'Resource' interface is to be used by a client wiring to HAL1.

Configuration and sampling
--------------------------------------------------------------------

As the ADC hardware is a shared resource the HAL1 should support hardware
configuration and sampling on a per-client basis (although per-port
configuration is possible, it is not recommended, because it forces all
clients to use the same settings for a given port). Therefore an HAL1 should
provide sampling interfaces parameterized by a client identifier. An HAL1
client can use its instance of the sampling interface to configure the ADC
hardware, start the sampling process and get conversion results. It wires to a
sampling interface using a unique client identifier. All commands and events
in the sampling interface should be 'async' to reflect the potential timing
requirements of clients. An HAL1 may provide multiple different parameterized
sampling interfaces, depending on the hardware capabilities.  This allows to
differentiate/group ADC functionality, for example single vs.  repeated
sampling, single channel vs. multiple channels or low-frequency vs.
high-frequency sampling.  Every sampling interface should allow the client to
individually configure the ADC hardware, for example by including the
configuration data as parameters in the sampling commands.  However, if
configuration data is passed as a pointer, the HAL1 component MUST NOT
reference it after the return of the respective command. Appendix B shows the
HAL1 interfaces for the TI MSP430 MCU.

HAL1 virtualization
--------------------------------------------------------------------

In order to hide wiring complexities and/or export only a subset of all ADC
functions generic ADC wrapper components may be provided on the level of HAL1
to be instantiated by chip- and platform-dependent clients.


4. HAL2 requirements
====================================================================

The following components MUST be provided on all platforms that have an ADC::

  AdcReadClient 
  AdcReadNowClient 
  AdcReadStreamClient 

These generic components are instantiated and provide access to the ADC  on a
per-client basis via a platform-independent interface for data collection and
a chip-specific ADC configuration interface. This section describes the
representation of the HAL2. Guidelines on how the HAL2 can be implemented are
discussed in Section `5. HAL2 implementation guidelines`_.  Appendix C shows
the AdcReadClient for the TI MSP430 MCU.  

The fact that the components use chip-specific ADC configuration interfaces
(see below) and the fact that the provided interfaces for data-collection must
be interpreted with respect to the configuration data - for example the
reference voltage - makes the HAL2 representation chip dependent. Therefore
the ADC abstraction does not include an HIL.

AdcReadClient
--------------------------------------------------------------------
::

  generic configuration AdcReadClient() {
    provides {
      interface Read< size_type >;
    }
    uses {
      // chip-dependent configuration interface
    }
  }

The AdcReadClient provides platform-independent access for data collection via
the 'Read' interface. The actual ADC channel (port) and further configuration
details are determined by a chip-dependent configuration interface. It is the
task of the client to wire this interface to a component that provides its ADC
configuration.  The HAL2 implementation will use this interface to "pull" the
client's ADC settings when it translates the 'Read.read()' command to a
chip-specific sampling command. The resolution of the conversion result is
chip-specific, the 'size_type' parameter represents an upper bound for the
resolution of the conversion results.

AdcReadNowClient
--------------------------------------------------------------------
::

  generic configuration AdcReadNowClient() {
    provides {
      interface Resource;
      interface ReadNow< size_type >;
    }
    uses {
       // chip-dependent configuration interface
    }
  }

The AdcReadNowClient provides platform-independent access for data collection
via the 'ReadNow' and 'Resource' interface. The actual ADC channel (port) and
further configuration details are determined by a chip-dependent configuration
interface. It is the task of the client to wire this interface to a component
that provides its ADC configuration.  The HAL2 implementation will use this
interface to "pull" the client's ADC settings when it translates the
'ReadNow.read()' command to a chip-specific sampling command. A client MUST
use the 'Resource' interface to request access to the ADC as described in
[TEP108]_ (the HAL2 implementation SHOULD return the error code 'ERESERVE' if
the client has not reserved access). The resolution of the conversion result
is chip-specific, the 'size_type' parameter represents an upper bound for the
resolution of the conversion result.

AdcReadStreamClient
--------------------------------------------------------------------
::

  generic configuration AdcReadStreamClient() {
    provides {
      interface ReadStream< size_type >;
    }
    uses {
       // chip-dependent configuration interface
    }
  }

The AdcReadStreamClient provides platform-independent access for data
collection via the 'ReadStream' interface. The actual ADC channel (port) and
further configuration details are determined by a chip-dependent configuration
interface. It is the task of the client to wire this interface to a component
that provides its ADC configuration.  The HAL2 implementation will use this
interface to "pull" the client's ADC settings when it translates the
'ReadStream.read()' command to a chip-specific sampling command. The
resolution of the conversion results is chip-specific, the 'size_type'
parameter represents an upper bound for the resolution of the conversion
results.

5. HAL2 implementation guidelines
====================================================================

The HAL2 implementation of an ADC stack has two main tasks: It translates a
platform-independent HAL2 request (from the 'Read', 'ReadNow' or 'ReadStream'
interface) to a chip-specific HAL1 sampling command and it abstracts from the
'Resource' interface. The first task cannot be solved in a chip-independent
way, because it involves chip-specific configuration data. The second task MAY
be performed by the following library components: ArbitratedReadC, and
ArbitratedReadStreamC (in tinyos-2.x/tos/system) - refer to the Atmel Atmega
128 HAL2 implementation (in tinyos-2.x/tos/chips/atm128/adc), for an example.
Note that since the 'ReadNow' interface is always provided in conjunction with
a 'Resource' interface the HAL2 implementation does not have to perform the
ADC resource reservation in this case, but can simply forward an instance of
the 'Resource' interface from the HAL1 (to AdcReadNowClient).

To support multiple ADC clients the HAL2 implementation should provide
parameterized 'Read', 'ReadNow' and 'ReadStream' interfaces as well as a
parameterized chip-specific configuration interface. It should also use an
instance of the 'Resource' interface (provided by the HAL1) per provided
'Read' and 'ReadStream' interface to perform automatic resource reservation.
The HAL2 representation ('AdcReadClient', 'AdcReadNowClient' and
'AdcReadStreamClient') should ensure the correct wiring between the HAL1 and
HAL2.

From the perspective of the HAL2 the typical sequence of events is as follows:
After a client has requested data via the 'Read' or 'ReadStream' interface the
HAL2 will request access to the HAL1 via the 'Resource' interface, e.g. using
the library components mentioned above.  When it is signalled the 'granted'
event, the HAL2 will 'pull' the client's ADC settings and translate the
client's command to a chip-specific HAL1 sampling command. Once it is
signalled the conversion result(s) it releases the ADC via the 'Resource'
interface and forwards the conversion result(s) to the client. When a client
has requested data via the 'ReadNow' interface the HAL2 translates the
client's command to the chip-specific HAL1 sampling command without using the
'Resource' interface (it may check ownership of the client via the
'ArbiterInfo' interface). In order to reduce state in the HAL2 and facilitate
the mapping between used and provided interfaces the 'AdcReadClient',
'AdcReadNowClient' and 'AdcReadStreamClient' should use the same interface
identifier when it connects the HAL2 to HAL1 (see, for example, the MSP430
ADC12 implementation in Appendix C).

6. Implementation
====================================================================

The implementation of the ADC12 stack on the MSP430 can be found in
``tinyos-2.x/tos/chips/msp430/adc12``:

  * ``HplAdc12P.nc`` is the HPL implementation
  * ``Msp430Adc12P.nc`` is the HAL1 implementation
  * ``AdcC.nc`` is the HAL2 implementation
  * ``AdcReadClientC.nc``, ``AdcReadNowClientC.nc`` and
    ``AdcReadStreamClientC.nc`` provide access to the ADC on a per-client
    basis via the interfaces 'Read', 'ReadNow' and 'ReadStream', 
    respectively, and the msp430-specific ADC configuration 
    interface ``Msp430Adc12Config.nc``

The Atmel Atmega 128 ADC implementation can be found in
``tinyos-2.x/tos/chips/atm128/adc``:

  * ``HplAtm128AdcC.nc`` is the HPL implementation
  * ``Atm128AdcP.nc`` is the HAL1 implementation
  * ``WireAdcP.nc`` and the library components for arbitrating 'Read', 
    'ReadNow' and 'ReadStream', ``ArbitratedReadC`` and
    ``ArbitratedReadStreamC`` (in ``tinyos-2.x/tos/system``), realize
    the HAL2
  * ``AdcReadClientC.nc``, ``AdcReadNowClientC.nc`` and
    ``AdcReadStreamClientC.nc`` provide access to the ADC on a per-client
    basis via the platform-independent interfaces 'Read', 'ReadNow' and
    'ReadStream', respectively, and the atmega-specific ADC configuration 
    interface ``Atm128AdcConfig.nc``


Appendix A: Hardware differences between platforms
====================================================================

The following table compares the characteristics of two microcontrollers
commonly used in TinyOS platforms:

+----------------------+----------------------+---------------------+
|                      | Atmel Atmega 128     | TI MSP430 ADC12     |
+======================+======================+=====================+
|Resolution            | 10-bit               | 12-bit              |
+----------------------+----------------------+---------------------+
|channels              |- 8 multiplexed       |- 8 individually     |
|                      |  external channels   |  configurable       |
|                      |- 16 differential     |  external channels  |
|                      |  voltage input       |- internal channels  |
|                      |  combinations        |  (AVcc, temperature,|
|                      |- 2 differential      |  reference voltages)|
|                      |  inputs with gain    |                     |
|                      |  amplification       |                     |
+----------------------+----------------------+---------------------+
|internal reference    | 2.56V                | 1.5V or 2.5V        |
|voltage               |                      |                     |
+----------------------+----------------------+---------------------+
|conversion reference  |- positive terminal:  | individually        |
|                      |  AVcc or 2.56V  or   | selectable per      |
|                      |  AREF (external)     | channel:            |
|                      |- negative terminal:  |                     |
|                      |  GND                 |- AVcc and AVss      |
|                      |                      |- Vref+ and AVss     |
|                      |                      |- Veref+ and AVss    |
|                      |                      |- AVcc and (Vref- or |
|                      |                      |  Veref-)            |
|                      |                      |- AVref+ and (Vref-  |
|                      |                      |  or Veref-)         |
|                      |                      |- Veref+ and (Vref-  |
|                      |                      |  or Veref-)         |
+----------------------+----------------------+---------------------+
|conversion modes      |- single channel      |- single conversion  |
|                      |  conversion mode     |  mode               |
|                      |- free running mode   |- repeat single      |
|                      |  (channels and       |  conversion mode    |
|                      |  reference voltages  |- sequence mode      |
|                      |  can be switched     |  (sequence <= 16    |
|                      |  between samples)    |  channels)          |
|                      |                      |- repeat sequence    |
|                      |                      |  mode               |
+----------------------+----------------------+---------------------+
|conversion clock      |clkADC with prescaler |ACLK, MCLK, SMCLK or |
|source                |                      |ADC-oscillator (5MHz)|
|                      |                      |with prescaler       |
|                      |                      |respectively         |
+----------------------+----------------------+---------------------+
|sample-hold-time      |1.5 clock cycles      |selectable values    |
|                      |(fixed)               |from 4 to 1024 clock |
|                      |                      |cycles               |
+----------------------+----------------------+---------------------+
|conversion triggering |by software           |by software or timers|
+----------------------+----------------------+---------------------+
|conversion during     |yes                   |yes                  |
|sleep mode possible   |                      |                     |
+----------------------+----------------------+---------------------+
|interrupts            |after each conversion |after single or      |
|                      |                      |sequence conversion  |
+----------------------+----------------------+---------------------+


Appendix B: an HAL1 representation: MSP430 ADC12
====================================================================

The following shows the HAL1 representation for the ADC12 of the TI MSP430
MCU. It reflects the four MSP430 ADC12 conversion modes as it lets a client
sample an ADC channel once ("Single-channel-single-conversion") or repeatedly
("Repeat-single-channel"), multiple times ("Sequence-of-channels") or multiple
times repeatedly ("Repeat-sequence-of-channels"). In contrast to the single
channel conversion modes the sequence conversion modes trigger a single
interrupt after multiple samples and thus enable high-frequency sampling (a
sequence conversion mode for multiple different channels is not (yet)
implemented).::

  configuration Msp430Adc12C 
  { 
    provides interface Resource[uint8_t id]; 
    provides interface Msp430Adc12SingleChannel as SingleChannel[uint8_t id]; 
  }  

  interface Msp430Adc12SingleChannel 
  {   
    async command error_t getSingleData(const msp430adc12_channel_config_t *config);
    async command error_t getSingleDataRepeat(const msp430adc12_channel_config_t *config, 
      uint16_t jiffies);
    async command error_t getMultipleData( const msp430adc12_channel_config_t *config,
      uint16_t *buffer, uint16_t numSamples, uint16_t jiffies);
    async command error_t getMultipleDataRepeat(const msp430adc12_channel_config_t *config, 
      uint16_t *buffer, uint8_t numSamples, uint16_t jiffies);
    async event error_t singleDataReady(uint16_t data);
    async event uint16_t* multipleDataReady(uint16_t *buffer, uint16_t
      numSamples); 
  }

There exist two wrapper components, Msp430Adc12ClientC and
Msp430Adc12RefVoltAutoClientC, which SHOULD be used to eliminate wiring
errors.

Appendix C: an HAL2 representation: MSP430 ADC12
====================================================================

The AdcReadClientC component for the MSP430 ADC12 is implemented as follows:
::

  generic configuration AdcReadClientC() {
    provides interface Read<uint16_t>;
    uses interface Msp430Adc12Config;
  } implementation {
    components AdcC;
  #ifdef REF_VOLT_AUTO_CONFIGURE     
    components new Msp430Adc12RefVoltAutoClientC() as Msp430AdcClient;
  #else
    components new Msp430Adc12ClientC() as Msp430AdcClient;
  #endif

    enum {
      CLIENT = unique(ADCC_SERVICE),
    };

    Read = AdcC.Read[CLIENT];
    Msp430Adc12Config = AdcC.Config[CLIENT];
    AdcC.SingleChannel[CLIENT] -> Msp430AdcClient.Msp430Adc12SingleChannel;
    AdcC.Resource[CLIENT] -> Msp430AdcClient.Resource;
  #ifdef REF_VOLT_AUTO_CONFIGURE
    Msp430Adc12Config = Msp430AdcClient.Msp430Adc12Config;
  #endif 
  }

Note that the same CLIENT identifier is used for all involved interfaces to
facilitate the mapping between the HAL2 and HAL1 interfaces. The conditional
compile directive REF_VOLT_AUTO_CONFIGURE can be used to automatically enable
the internal reference voltage generator during the sampling process.

.. [TEP1] TEP 1: TEP Structure and Keywords. 
.. [TEP2] TEP 2: Hardware Abstraction Architecture. 
.. [TEP108] TEP 108: Resource Arbitration. 
.. [TEP109] TEP 109: Sensor Boards. 
.. [TEP114] TEP 114: SIDs: Source and Sink Independent Drivers. 
-------------- next part --------------
=========================
Sensors and Sensor Boards
=========================

:TEP: 109
:Group: Core Working Group 
:Type: Documentary
:Status: Draft
:TinyOS-Version: 2.x
:Author: David Gay, Phil Levis, Wei Hong, Joe Polastre, and Gilman Tolle

:Draft-Created: 10-Jun-2006
:Draft-Discuss: TinyOS Developer List <tinyos-devel at mail.millennium.berkeley.edu>

.. Note::

   This memo documents a part of TinyOS for the TinyOS Community, and
   requests discussion and suggestions for improvements.  Distribution
   of this memo is unlimited. This memo is in full compliance with
   TEP 1.

Abstract
====================================================================

This memo documents how sensor drivers are organized in TinyOS and how
sets of sensor drivers are combined into sensor boards and sensor
platforms, along with general principles followed by the components
that provide access to sensors.

1. Principles
====================================================================

This section describes the basic organization principles for sensor
drivers in TinyOS.

For background, a sensor may be attached to the microcontroller on a
TinyOS platform through a few different types of connections:

 * Included within the microcontroller itself
 * Connected to general-purpose IO pins for level/edge detection
 * Connected to an ADC in the microcontroller for voltage sampling
 * Connected to general-purpose IO pins for digital communication
 * Connected through a standard digital bus protocol (1-Wire, I2C, SPI)

Physically, these connections may also be decoupled by attaching the
sensors to a `sensor board`, which can be removed from the TinyOS
platform, and may fit multiple different TinyOS platforms.

The capabilities of a physical sensor are made available to a TinyOS
application through a `sensor driver`. 

According to the HAA [TEP2]_, TinyOS devices should provide both
simple hardware-independent interfaces for common-case use (HIL) and
rich hardware-dependent interfaces for special-case use (HAL). Sensor
drivers should follow this spirit as well.

TinyOS 2.x represents each sensor as an individual component. This
allows the compilation process to minimize the amount of code
included. A sensor board containing multiple sensors should be
represented as a collection of components, one for each sensor,
contained within a sensor board directory.

Sensors, being physical devices that may be shared, can benefit from
virtualization and arbitration. This document describes a design
pattern for sensor virtualization that may be followed by sensor
drivers.

The same physical sensor may be attached to multiple different TinyOS
platforms, through platform-dependent interconnections. The common
logic of sensor driver should be factored into chip-dependent,
platform-independent components, and those components should be bound
to the hardware resources on a platform by platform-dependent
components, and to the hardware resources on a sensor board by
sensorboard-dependent components.

A physical sensor has a general class and a specific set of
performance characteristics, captured by the make and model of the
sensor itself. The naming of the sensor driver components should
reflect the specifc name of the sensor, and optionally provide a
component with a generic name for application authors who only care
about the general class of the sensor.

This document takes no position on the meaning of the values returned
by sensor drivers. They may be raw uninterpreted values or they may
have some physical meaning. If a driver returns uninterpreted values,
the driver may provide additional interfaces that would allow
higher-level clients to interpret the value properly.

2. Sensor HIL Components
====================================================================

A sensor HIL component MUST provide:

- One or more SID interfaces [TEP114]_, for reading data.

A sensor HIL component MAY provide:

- One or more SID interfaces [TEP114]_, for reading or
  writing calibration coefficients or control registers.

A sensor device driver SHOULD be a generic component that virtualizes
access to the sensor. A sensor device driver can provide such
virtualization for itself by defining a nesC generic client
component. When a client component is being used, a call to a
top-level SID interface should be delayed when the device is busy,
rather than failing. This virtualization may be easier to accomplish
by using one of the arbiters provided by the system.

For example::

  generic configuration SensirionSht11C() {
    provides interface Read<uint16_t> as Temperature;
    provides interface ReadStream<uint16_t> as TemperatureStream;
    provides interface Read<uint16_t> as Humidity;
    provides interface ReadStream<uint16_t> as HumidityStream;
  }
  implementation {
    // connect to the ADC HIL, GPIO HAL, or sensor's HAL
  }

When a HIL component is being used, the sensor MUST initialize itself,
either by including the `MainC` component and wiring to the
`SoftwareInit` interface, or by allowing a lower-level component (like
an ADC) to initialize itself.

In addition, the HIL sensor driver MUST start the physical sensor
automatically. For sensors without a constant power draw, the sensor
MAY be started once at boot time by wiring to the `MainC.Boot`
interface. Sensors that draw appreciable power MUST be started in
response to a call to one of the top-level SID interfaces, and stopped
some time after that call completes. One of the power-management
components described in [TEP115]_ may be useful for this purpose.

Generally, simple types are made up of octets. However, sensor values
often have levels of precision besides a multiple of 8. A device MAY
specify the precision of one of its interfaces with the DeviceMetadata
interface::

  interface DeviceMetadata {
    command uint8_t getSignificantBits();
  }

The name of the instance of DeviceMetadata SHOULD clearly indicate
which interface it corresponds to.

A value contained returned from the device through a SID interface
MAY be left shifted so that it covers as much of the type's range as
possible. For example, if a 12-bit ADC reading is presented as a
16-bit Read interface::

  component DemoSensorC {
    provides interface Read<uint16_t>;
  }

then the driver MAY shift the 12-bit value left so that its range is
0x0000 - 0xfff0, rather than 0x0000 - 0x0fff. 

Sensor driver components SHOULD be named according to the make and
model of the sensing device being presented. Using specific names
gives the developer the option to bind to a particular sensor, which
provides compile-time detection of missing sensors. However, wrapper
components using "common" names MAY also be provided by the driver
author, to support application developers who are only concerned with
the particular type of the sensor and not its make, model, or detailed
performance characteristics.

A "common" naming layer atop a HIL may look like this::

  generic configuration TemperatureC() {
    provides interface Read<uint16_t>;
    provides interface ReadStream<uint16_t>;
  }
  implementation {
    components new SensirionSht11C();
    Read = SensirionSht11C.Temperature;
    ReadStream = SensirionSht11C.TemperatureStream;
  }

  generic configuration HumidityC() {
    provides interface Read<uint16_t>;
    provides interface ReadStream<uint16_t>;
  }
  implementation {
    components new SensirionSht11C();
    Read = SensirionSht11C.Humidity;
    ReadStream = SensirionSht11C.HumidityStream;
  }

3. Sensor HAL Components
====================================================================

Sensors with a richer interface than would be supported by the SID
interfaces MAY provide a HAL component in addition to a HIL
component.

A sensor HAL component MUST provide:

- A SID-based interface or a specific hardware-dependent interface
  with commands for sampling and controlling the sensor device.

A sensor HAL component MAY need to provide:

- A `StdControl` or `SplitControl` interface for manual power
  management by the user, following the conventions described in
  [TEP115]_.

- A Resource[] interface for requesting access to the device and
  possibly performing automated power management.

- Any other interfaces needed to control the device.

For example::

  configuration SensirionSht11DeviceC {
    provides interface Resource[ uint8_t client ];
    provides interface SensirionSht11[ uint8_t client ];
  }
  implementation {
    // connect to the sensor's platform-dependent HPL here
  }

4. Directory Organization Guidelines
====================================================================

Because the same physical sensor may be attached to TinyOS platforms
in many different ways, the organization of sensor drivers should
reflect the distinction between sensor and sensor interconnect.

Sensor components commonly exist at three levels:
platform-independent, sensorboard-dependent, and
platform-dependent. Factoring a sensor driver into these three pieces
allows for greater code reuse when the same sensor is attached to
different sensorboards or platforms.

Platform-independent sensor driver components for a particular sensor,
like protocol logic, when in the core TinyOS 2.x source tree, SHOULD
be placed into "tos/chips/<sensor>", where <sensor> reflects the make
and model of the sensor device being supported. When not a part of the
core source tree, this directory can be placed anywhere as long as the
nesC compiler recieves a `-I` directive pointing to the sensor's
directory. However, not all sensors have a sufficiently large amount
of platform-independent logic to justify a separate "chips"
directory. Sensor chips are more likely to be digital sensors than
analog sensors, for example.

A sensor board is a collection of sensor components with a fixed name,
intended for attachment to multiple platforms. Each sensor board MUST
have its own directory named <sensorboard>. Default TinyOS 2.x sensor
boards are placed in "tos/sensorboards/<sensorboard>", but sensor
board directories can be placed anywhere as long as the nesC compiler
receives a `-I` directive pointing to the sensor board's directory.

Both sensors and sensor boards MUST have unique names. Case is
significant, but two sensor boards MUST differ in more than case. This
is necessary to support platforms where filename case differences are
not significant.

Each sensor board directory MUST contain a `.sensor` file.  This file
is a perl script which gets executed as part of the `ncc` nesC
compiler frontend. It can add or modify any compile-time options
necessary for a particular sensor board. It MAY modify the following
perl variables, and MUST NOT modify any others:

- @new_args: This is the array of arguments which will be passed to
  nescc. For instance, you might add an include directive to @new_args
  with push @new_args, `-Isomedir`. This could be used to include
  subdirectories.

- @commonboards: This can be set to a list of sensor board names which
  should be added to the include path list. These sensor boards must be
  in tinyos-2.x/tos/sensorboards.

If the sensor board wishes to define any C types or constants, it
SHOULD place these in a file named <sensorboard>.h in the sensor
board's directory.

A sensor board directory MAY contain a "chips" directory, with
subdirectories for each of the sensors connected to the sensor board.
If a "chips" subdirectory is used, sensorboard-dependent driver
components needed to connect platform-independent logic to a
particular attachment for that sensor should be placed in
"<sensorboard>/chips/<sensor>".

Components needed to connect the platform-independent sensor driver
components or sensorboard-dependent components to the hardware
resources available on a particular platform SHOULD be placed in
"tos/<platform>/chips/<sensor>". In addition, components for a sensor
that only exists on a particular platform should be placed in a such a
directory.

Sensors that exist as part of a larger chip, like a MCU internal
voltage sensor, SHOULD be placed in a subdirectory of the chip's
directory. "tos/<chip>/sensors/<sensor>".

The `.platform` and `.sensor` files need to include enough `-I`
directives to locate all of the necessary components needed to support
the sensors on a platform and/or sensorboard.

All of these directory organization guidelines are only intended for
code that will enter the core source tree. In general, sensor
components may be placed anywhere as long as the nesC compiler
receives enough `-I` directives to locate all of the necessary pieces.

5. Authors' Addresses
====================================================================

| David Gay
| 2150 Shattuck Ave, Suite 1300
| Intel Research
| Berkeley, CA 94704
|
| phone - +1 510 495 3055
|
| email - david.e.gay at intel.com
|
| Wei Hong
| Arch Rock
| 657 Mission St. Suite 600
| San Francisco, CA 94105
|
| email - wei.hong at gmail.com
|
| Philip Levis
| 358 Gates Hall
| Computer Science Department
| 353 Serra Mall
| Stanford, CA 94305
|
| phone - +1 650 725 9046
|
| email - pal at cs.stanford.edu
| 
| Joe Polastre
| 467 Soda Hall
| UC Berkeley
| Berkeley, CA 94720
|
| email - polastre at cs.berkeley.edu
|
| Gilman Tolle
| Arch Rock
| 657 Mission St. Suite 600
| San Francisco, CA 94105
|
| email - gtolle at archrock.com

6. Citations
====================================================================

.. [TEP2] TEP 2: Hardware Abstraction Architecture
.. [TEP114] TEP 114: SIDs: Source and Sink Indepedent Drivers
.. [TEP115] TEP 115: Power Management of Non-Virtualized Devices

Appendix A: Sensor Driver Examples
====================================================================

1. Analog ADC-Connected Sensor
------------------------------

The Analog sensor requires two components

* a component to present the sensor itself (HamamatsuS1087ParC)

* a component to select the appropriate hardware resources, such as
  ADC port 4, reference voltage 1.5V, and a slow sample and hold time
  (HamamatsuS1087ParP).

The AdcReadClientC component and underlying machinery handles all of
the arbitration and access to the ADC.

::

  tos/platforms/telosa/chips/s1087/HamamatsuS1087ParC.nc

  generic configuration HamamatsuS1087ParC() {
    provides interface Read<uint16_t>;
    provides interface ReadStream<uint16_t>;
  }
  implementation {
    components new AdcReadClientC();
    Read = AdcReadClientC;

    components new AdcReadStreamClientC();
    ReadStream = AdcReadStreamClientC;

    components HamamatsuS1087ParP;
    AdcReadClientC.Msp430Adc12Config -> HamamatsuS1087ParP;
    AdcReadStreamClientC.Msp430Adc12Config -> HamamatsuS1087ParP;
  }
  
::

  tos/platforms/telosa/chips/s1087/HamamatsuS1087ParP.nc

  module HamamatsuS1087ParP {
    provides interface Msp430Adc12Config;
  }
  implementation {

    async command msp430adc12_channel_config_t 
      Msp430Adc12Config.getChannelSettings() {
 
      msp430adc12_channel_config_t config = {
        inch: INPUT_CHANNEL_A4,
        sref: REFERENCE_VREFplus_AVss,
        ref2_5v: REFVOLT_LEVEL_1_5,
        adc12ssel: SHT_SOURCE_ACLK,
        adc12div: SHT_CLOCK_DIV_1,
        sht: SAMPLE_HOLD_4_CYCLES,
        sampcon_ssel: SAMPCON_SOURCE_SMCLK,
        sampcon_id: SAMPCON_CLOCK_DIV_1
      };
      
      return config;
    }
  }

2. Binary Pin-Connected Sensor
------------------------------

The Binary sensor gets a bit more complex, because it has three
components: 

* one to present the sensor (UserButtonC)

* one to execute the driver logic (UserButtonLogicP)

* one to select the appropriate hardware resources, such as MSP430
  Port 27 (HplUserButtonC).

Note that the presentation of this sensor is not arbitrated because
none of the operations are split-phase. 

::

  tos/platforms/telosa/UserButtonC.nc

  configuration UserButtonC {
    provides interface Get<bool>;
    provides interface Notify<bool>;
  }
  implementation {

    components UserButtonLogicP;

    components HplUserButtonC;
    UserButtonLogicP.GpioInterrupt -> HplUserButtonC.GpioInterrupt;
    UserButtonLogicP.GeneralIO -> HplUserButtonC.GeneralIO;

    Get = UserButtonLogicP;
    Notify = UserButtonLogicP;
  }

::

  tos/platforms/telosa/UserButtonLogicP.nc
 
  module UserButtonLogicP {
    provides interface Get<bool>;
    provides interface Notify<bool>;

    uses interface GeneralIO;
    uses interface GpioInterrupt; 
  }
  implementation {
    norace bool m_pinHigh;

    task void sendEvent();
 
    command bool Get.get() { return call GeneralIO.get(); }

    command error_t Notify.enable() {
      call GeneralIO.makeInput();

      if ( call GeneralIO.get() ) {
        m_pinHigh = TRUE;
        return call GpioInterrupt.enableFallingEdge();
      } else {
        m_pinHigh = FALSE;
        return call GpioInterrupt.enableRisingEdge();
      }
    }

    command error_t Notify.disable() {
      return call GpioInterrupt.disable();
    }

    async event void GpioInterrupt.fired() {
      call GpioInterrupt.disable();

      m_pinHigh = !m_pinHigh;

      post sendEvent();
    }

    task void sendEvent() {
      bool pinHigh;
      pinHigh = m_pinHigh;
    
      signal Notify.notify( pinHigh );
    
      if ( pinHigh ) {
        call GpioInterrupt.enableFallingEdge();
      } else {
        call GpioInterrupt.enableRisingEdge();
      }
    }
  }

::

  tos/platforms/telosa/HplUserButtonC.nc

  configuration HplUserButtonC {
    provides interface GeneralIO;
    provides interface GpioInterrupt;
  }
  implementation {

    components HplMsp430GeneralIOC as GeneralIOC;

    components new Msp430GpioC() as UserButtonC;
    UserButtonC -> GeneralIOC.Port27;
    GeneralIO = UserButtonC;

    components HplMsp430InterruptC as InterruptC;

    components new Msp430InterruptC() as InterruptUserButtonC;
    InterruptUserButtonC.HplInterrupt -> InterruptC.Port27;
    GpioInterrupt = InterruptUserButtonC.Interrupt;
  }

3. Digital Bus-Connected Sensor
-------------------------------

The Digital sensor is the most complex out of the set, and includes
six components:

* one to present the sensor (SensirionSht11C)

* one to request arbitrated access and to transform the sensor HAL
  into the sensor HIL (SensirionSht11P)

* one to present the sensor HAL (HalSensirionSht11C)

* one to perform the driver logic needed to support the HAL, which
  twiddles pins according to a sensor-specific protocol
  (SensirionSht11LogicP).

* one to select the appropriate hardware resources, such as the clock,
  data, and power pins, and to provide an arbiter for the sensor
  (HplSensirionSht11C).

* one to perform the power control logic needed to support the power
  manager associated with the arbiter (HplSensirionSht11P).

This bus-connected sensor is overly complex because it does not rely
on a shared framework of bus manipulation components. A sensor built
on top of the I2C or SPI bus would likely require fewer components.

::

  tos/platforms/telosa/chips/sht11/SensirionSht11C.nc
  
  generic configuration SensirionSht11C() {  
    provides interface Read<uint16_t> as Temperature;
    provides interface Read<uint16_t> as Humidity;
  }
  implementation {
    components new SensirionSht11ReaderP();
  
    Temperature = SensirionSht11ReaderP.Temperature;
    Humidity = SensirionSht11ReaderP.Humidity;
  
    components HalSensirionSht11C;
  
    enum { TEMP_KEY = unique("Sht11.Resource") };
    enum { HUM_KEY = unique("Sht11.Resource") };
  
    SensirionSht11ReaderP.TempResource -> HalSensirionSht11C.Resource[ TEMP_KEY ];
    SensirionSht11ReaderP.Sht11Temp -> HalSensirionSht11C.SensirionSht11[ TEMP_KEY ];
    SensirionSht11ReaderP.HumResource -> HalSensirionSht11C.Resource[ HUM_KEY ];
    SensirionSht11ReaderP.Sht11Hum -> HalSensirionSht11C.SensirionSht11[ HUM_KEY ];
  }
  
::
  
  tos/chips/sht11/SensirionSht11ReaderP.nc
  
  generic module SensirionSht11ReaderP() {
    provides interface Read<uint16_t> as Temperature;
    provides interface Read<uint16_t> as Humidity;
    
    uses interface Resource as TempResource;
    uses interface Resource as HumResource;
    uses interface SensirionSht11 as Sht11Temp;
    uses interface SensirionSht11 as Sht11Hum;
  }
  implementation {
    command error_t Temperature.read() {
      call TempResource.request();
      return SUCCESS;
    }
  
    event void TempResource.granted() {
      error_t result;
      if ((result = call Sht11Temp.measureTemperature()) != SUCCESS) {
        call TempResource.release();
        signal Temperature.readDone( result, 0 );
      }
    }
  
    event void Sht11Temp.measureTemperatureDone( error_t result, uint16_t val ) {
      call TempResource.release();
      signal Temperature.readDone( result, val );
    }
  
    command error_t Humidity.read() {
      call HumResource.request();
      return SUCCESS;
    }
  
    event void HumResource.granted() {
      error_t result;
      if ((result = call Sht11Hum.measureHumidity()) != SUCCESS) {
        call HumResource.release();
        signal Humidity.readDone( result, 0 );
      }
    }
  
    event void Sht11Hum.measureHumidityDone( error_t result, uint16_t val ) {
      call HumResource.release();
      signal Humidity.readDone( result, val );
    }
  
    event void Sht11Temp.resetDone( error_t result ) { }
    event void Sht11Temp.measureHumidityDone( error_t result, uint16_t val ) { }
    event void Sht11Temp.readStatusRegDone( error_t result, uint8_t val ) { }
    event void Sht11Temp.writeStatusRegDone( error_t result ) { }
  
    event void Sht11Hum.resetDone( error_t result ) { }
    event void Sht11Hum.measureTemperatureDone( error_t result, uint16_t val ) { }
    event void Sht11Hum.readStatusRegDone( error_t result, uint8_t val ) { }
    event void Sht11Hum.writeStatusRegDone( error_t result ) { }
  
    default event void Temperature.readDone( error_t result, uint16_t val ) { }
    default event void Humidity.readDone( error_t result, uint16_t val ) { }
  }
  
::
  
  tos/platforms/telosa/chips/sht11/HalSensirionSht11C.nc
  
  configuration HalSensirionSht11C {
    provides interface Resource[ uint8_t client ];
    provides interface SensirionSht11[ uint8_t client ];
  }
  implementation {
    components new SensirionSht11LogicP();
    SensirionSht11 = SensirionSht11LogicP;
  
    components HplSensirionSht11C;
    Resource = HplSensirionSht11C.Resource;
    SensirionSht11LogicP.DATA -> HplSensirionSht11C.DATA;
    SensirionSht11LogicP.CLOCK -> HplSensirionSht11C.SCK;
    SensirionSht11LogicP.InterruptDATA -> HplSensirionSht11C.InterruptDATA;
    
    components new TimerMilliC();
    SensirionSht11LogicP.Timer -> TimerMilliC;
  
    components LedsC;
    SensirionSht11LogicP.Leds -> LedsC;
  }
  
::
  
  tos/chips/sht11/SensirionSht11LogicP.nc
  
  generic module SensirionSht11LogicP() {
    provides interface SensirionSht11[ uint8_t client ];
  
    uses interface GeneralIO as DATA;
    uses interface GeneralIO as CLOCK;
    uses interface GpioInterrupt as InterruptDATA;
  
    uses interface Timer<TMilli>;
  
    uses interface Leds;
  }
  implementation {
  
    ... bus protocol details omitted for brevity ...
  
  }
  
::
  
  tos/platforms/telosa/chips/sht11/HplSensirionSht11C.nc
  
  configuration HplSensirionSht11C {
    provides interface Resource[ uint8_t id ];
    provides interface GeneralIO as DATA;
    provides interface GeneralIO as SCK;
    provides interface GpioInterrupt as InterruptDATA;
  }
  implementation {
    components HplMsp430GeneralIOC;
    
    components new Msp430GpioC() as DATAM;
    DATAM -> HplMsp430GeneralIOC.Port15;
    DATA = DATAM;
  
    components new Msp430GpioC() as SCKM;
    SCKM -> HplMsp430GeneralIOC.Port16;
    SCK = SCKM;
  
    components new Msp430GpioC() as PWRM;
    PWRM -> HplMsp430GeneralIOC.Port17;
  
    components HplSensirionSht11P;
    HplSensirionSht11P.PWR -> PWRM;
    HplSensirionSht11P.DATA -> DATAM;
    HplSensirionSht11P.SCK -> SCKM;
  
    components new TimerMilliC();
    HplSensirionSht11P.Timer -> TimerMilliC;
  
    components HplMsp430InterruptC;
    components new Msp430InterruptC() as InterruptDATAC;
    InterruptDATAC.HplInterrupt -> HplMsp430InterruptC.Port15;
    InterruptDATA = InterruptDATAC.Interrupt;
  
    components new FcfsArbiterC( "Sht11.Resource" ) as Arbiter;
    Resource = Arbiter;
    
    components new SplitControlPowerManagerC();
    SplitControlPowerManagerC.SplitControl -> HplSensirionSht11P;
    SplitControlPowerManagerC.ArbiterInit -> Arbiter.Init;
    SplitControlPowerManagerC.ArbiterInfo -> Arbiter.ArbiterInfo;
    SplitControlPowerManagerC.ResourceController -> Arbiter.ResourceController;
  }
  
::
  
  tos/platforms/telosa/chips/sht11/HplSensirionSht11P.nc
  
  module HplSensirionSht11P {
    provides interface SplitControl;
    uses interface Timer<TMilli>;
    uses interface GeneralIO as PWR;
    uses interface GeneralIO as DATA;
    uses interface GeneralIO as SCK;
  }
  implementation {
    task void stopTask();
  
    command error_t SplitControl.start() {
      call PWR.makeOutput();
      call PWR.set();
      call Timer.startOneShot( 11 );
      return SUCCESS;
    }
    
    event void Timer.fired() {
      signal SplitControl.startDone( SUCCESS );
    }
  
    command error_t SplitControl.stop() {
      call SCK.makeInput();
      call SCK.clr();
      call DATA.makeInput();
      call DATA.clr();
      call PWR.clr();
      post stopTask();
      return SUCCESS;
    }
  
    task void stopTask() {
      signal SplitControl.stopDone( SUCCESS );
    }
  }
-------------- next part --------------




More information about the Tinyos-2.0wg mailing list