⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 storage - tinyos documentation wiki.htm

📁 从官方网站上下载tinyos2.0的学习指南
💻 HTM
📖 第 1 页 / 共 3 页
字号:
memory. A volume table might look like: </P><PRE>&lt;volume_table&gt;
  &lt;volume name="CONFIGLOG" size="65536"/&gt;
  &lt;volume name="PACKETLOG" size="65536"/&gt;
  &lt;volume name="SENSORLOG" size="131072"/&gt;
  &lt;volume name="CAMERALOG" size="524288"/&gt;
&lt;/volume_table&gt;
</PRE>
<P>The volume table for a particular application must be placed in the 
application's directory (where one types 'make') and must be named 
<CODE>volumes-CHIPNAME.xml</CODE> where CHIPNAME is replaced with the 
platform-specific flash chip's name. For example, the Telos mote uses the ST 
Microelectronics M25P family of flash memories. The drivers for these chips can 
be found in the <CODE>tos/chips/stm25p</CODE> directory. Therefore, a 
Telos-based application that uses the storage abstractions needs a file named 
<CODE>volumes-stm25p.xml</CODE>. </P>
<P>Note that the size parameter is a multiple of the erase unit for a particular 
flash chip. See Section 4.1 in TEP 103 <SUP class=reference id=_ref-fn1_0><A 
title="" href="http://docs.tinyos.net/index.php/Storage#_note-fn1">[1]</A></SUP> 
for more details. </P><A name=Storing_Configuration_Data></A>
<H1><SPAN class=mw-headline>Storing Configuration Data</SPAN></H1>
<P>This lesson shows how configuration data can be written to and read from 
non-volatile storage. Configuration data typically exhibit some subset of the 
following properties. They are <B>limited in size</B>, ranging from a fews tens 
to a couple hundred bytes. Their values may be <B>non-uniform</B> across nodes. 
Sometimes, their values are <B>unknown</B> prior to deployment in the field and 
sometimes their values are <B>hardware-specific</B>, rather than being tied to 
the software running on a node. </P>
<P>Because configuration data can be non-uniform across nodes or unknown <I>a 
priori</I>, their values may be difficult to specify at compile-time and since 
the data are sometimes hardware-specific, their values must survive 
reprogramming, suggesting that encoding these values in the program image is not 
the simplest approach. Storing configuration data in volatile memory is also 
problematic since this data would not survive a reset or power cycle. </P>
<P>In summary, configuration data must persist through node resets, power 
cycles, or reprogramming, and then be restored afterward. The ability to persist 
and restore configuration data in this manner is useful in many scenarios. </P>
<UL>
  <LI><B>Calibration.</B> Calibration coefficients for sensors might be 
  factory-configured and persisted, so they are not lost when power is removed 
  for shipping or the node is reprogrammed post-calibration. For example, a 
  hypothetical temperature sensor might have an offset and gain that must be 
  calibrated, because these parameters are hardware-specific, and stored because 
  they are needed to convert the output voltage into the more useful units of 
  degrees Celcius. The calibration data for such a sensor might look like: <PRE>typedef struct calibration_config_t {
  int16_t temp_offset;
  int16_t temp_gain;
} calibration_config_t;
</PRE>
  <LI><B>Identification.</B> Device identification information, like 
  IEEE-compliant MAC addresses or the TinyOS TOS_NODE_ID parameters are 
  non-uniform across nodes although they are not hardware-specific, once they 
  are assigned to a node, these values should be <I>sticky</I> in that they are 
  persisted across reset, power cycle, and reprogramming operations (and not 
  lost or reassigned to another node). <PRE>typedef struct radio_config_t {
  ieee_mac_addr_t mac;
  uint16_t tos_node_id;
} radio_config_t;
</PRE>
  <LI><B>Location.</B> Node location data may be unknown at compile-time and 
  only become available during deployment. An application might, for example, 
  store node coordinates as follows and update these values in the field: <PRE>typedef struct coord_config_t {
  uint16_t x;
  uint16_t y;
  uint16_t z;
} coord_config_t;
</PRE>
  <LI><B>Sensing.</B> Sensing and signal processing parameters like sample 
  period, filter coefficients, and detection thresholds might be adjusted in the 
  field. The configuration data for such an application might look like: <PRE>typedef struct sense_config_t {
  uint16_t temp_sample_period_milli;
  uint16_t temp_ema_alpha_numerator;
  uint16_t temp_ema_alpha_denominator;
  uint16_t temp_high_threshold;
  uint16_t temp_low_threshold;
} sense_config_t;
</PRE></LI></UL>
<P>Now that we have discussed <I>why</I> one might use this type of storage, 
let's see <I>how</I> to use it. We will implement a simple demo application that 
illustrates how to use the <CODE>Mount</CODE> and <CODE>ConfigStorage</CODE> 
abstractions. A timer period will be read from flash, divided by two, and 
written back to flash. An LED is toggled each time the timer fires. But, before 
diving into code, let's discuss some high-level design considerations. </P>
<P>See <A class="external text" 
title=http://www.tinyos.net/tinyos-2.x/apps/tutorials/BlinkConfig/ 
href="http://www.tinyos.net/tinyos-2.x/apps/tutorials/BlinkConfig/" 
rel=nofollow><CODE>tinyos-2.x/apps/tutorials/BlinkConfig/</CODE></A> for the 
accompanying code. </P>
<P>Prior to its first usage, a volume does not contain any valid data. So, our 
code should detect the first usage of a volume and take any appropriate actions 
(e.g. preload it with default values). Similarly, when the data layout of the 
volume changes (for example, if the application requires new or different 
configuration variables), then application code should detect this and take 
appropriate actions (e.g. migrate the old data to the new layout or erase the 
volume and reload the defaults). These requirements suggest that we should have 
a way of keeping track of the volume version. We will use a version number for 
this purpose (and will need to maintain a discipline of updating the version 
number when the data layout changes incompatibly). Our configuration struct 
might have the following fields for the version number and blink period: </P><PRE>typedef struct config_t {
  uint16_t version;
  uint16_t period;
} config_t;
</PRE>
<OL>
  <LI>Create a <CODE>volumes-CHIPNAME.xml</CODE> file, enter the volume table in 
  this file, and place the file in the application directory. Note that 
  <CODE>CHIPNAME</CODE> is the flash chip used on your target plaform. For 
  example, <CODE>CHIPNAME</CODE> will be <CODE>stm25p</CODE> for the Telos 
  platform and <CODE>at45db</CODE> for the MicaZ platform. Our file will have 
  the following contents: <PRE>&lt;volume_table&gt;
  &lt;volume name="LOGTEST" size="262144"/&gt;
  &lt;volume name="CONFIGTEST" size="131072"/&gt;
&lt;/volume_table&gt;
This volume information is used by the toolchain to create an include file. The auto-generated file, however, has to be included manually. 
Place the following line in the configuration file which declares the ConfigStorageC component (e.g. <CODE>BlinkConfigAppC.nc</CODE>):  
 #include "StorageVolumes.h"
 
</PRE>
  <LI>BlinkConfigC, the application code for this simple demo, <I>uses</I> the 
  <CODE>Mount</CODE> and <CODE>ConfigStorage</CODE> interfaces (note that we 
  rename <CODE>ConfigStorage</CODE> to <CODE>Config</CODE>). <PRE>module BlinkConfigC {
  uses {
    ...
    interface ConfigStorage as Config;
    interface Mount;
    ...
  }
}
</PRE>
  <LI>Each interface must be wired to an <I>implementation</I> that will provide 
  it: <PRE>configuration BlinkConfigAppC {
}
implementation {
  components BlinkConfigC as App;
  components new ConfigStorageC(VOLUME_CONFIGTEST);
  ...

  App.Config     -&gt; ConfigStorageC.ConfigStorage;
  App.Mount      -&gt; ConfigStorageC.Mount;
  ...
}
</PRE>
  <LI>Before the flash chip can be used, it must be mounted using the two-phase 
  mount/mountDone command. Here we show how this might be chained into the boot 
  sequence: <PRE>  event void Boot.booted() {
    conf.period = DEFAULT_PERIOD;

    if (call Mount.mount()&nbsp;!= SUCCESS) {
      // Handle failure
    }
  }
</PRE>
  <LI>If the Mount.mount succeeds, then the <CODE>Mount.mountDone</CODE> event 
  will be signaled. The following code shows how to check if the volume is 
  valid, and if it is, how to initiate a read from the volume using the 
  <CODE>ConfigStore.read</CODE> command. If the volume is invalid, calling 
  <CODE>Config.commit</CODE> will make it valid (this call is also used to flush 
  buffered data to flash much like the UNIX fsync system call is supposed to 
  flush buffered writes to disk): <PRE>  event void Mount.mountDone(error_t error) {
    if (error == SUCCESS) {
      if (call Config.valid() == TRUE) {
        if (call Config.read(CONFIG_ADDR, &amp;conf, sizeof(conf))&nbsp;!= SUCCESS) {
          // Handle failure
        }
      }
      else {
        // Invalid volume.  Commit to make valid.
        call Leds.led1On();
        if (call Config.commit() == SUCCESS) {
          call Leds.led0On();
        }
        else {
          // Handle failure
        }
      }
    }
    else{
      // Handle failure
    }
  }
</PRE>
  <LI>If the read is successful, then a <CODE>Config.readDone</CODE> event will 
  occur. In this case, we first check for a successful read, and if successful, 
  we then check the version number. If the version number matches what we 
  expected, we copy of the configuration data to a local variable, and adjust 
  its values. If there is a version mismatch, we set the value of the 
  configuration information to a default value. Finally, we call the the 
  <CODE>Config.write</CODE> function: <PRE>  event void Config.readDone(storage_addr_t addr, void* buf,
    storage_len_t len, error_t err) __attribute__((noinline)) {

    if (err == SUCCESS) {
      memcpy(&amp;conf, buf, len);
      if (conf.version == CONFIG_VERSION) {
        conf.period = conf.period/2;
        conf.period = conf.period &gt; MAX_PERIOD&nbsp;? MAX_PERIOD&nbsp;: conf.period;
        conf.period = conf.period &lt; MIN_PERIOD&nbsp;? MAX_PERIOD&nbsp;: conf.period;
      }
      else {
        // Version mismatch. Restore default.
        call Leds.led1On();
        conf.version = CONFIG_VERSION;
        conf.period = DEFAULT_PERIOD;
      }
      call Leds.led0On();
      call Config.write(CONFIG_ADDR, &amp;conf, sizeof(conf));
    }
    else {
      // Handle failure.
    }
  }
</PRE>
  <LI>Data is not necessarily "written" to flash when 
  <CODE>ConfigStore.write</CODE> is called and <CODE>Config.writeDone</CODE> is 
  signaled. To ensure data is persisted to flash, a 
  <CODE>ConfigStore.commit</CODE> call is required: <PRE>  event void Config.writeDone(storage_addr_t addr, void *buf,
    storage_len_t len, error_t err) {
    // Verify addr and len

    if (err == SUCCESS) {
      if (call Config.commit()&nbsp;!= SUCCESS) {
        // Handle failure
      }
    }
    else {
      // Handle failure
    }
  }
</PRE>
  <LI>Finally, when the <CODE>Config.commitDone</CODE> event is signaled, data 
  has been durably written to flash and will survive a node power cycle: <PRE>  event void Config.commitDone(error_t err) {
    call Leds.led0Off();
    call Timer0.startPeriodic(conf.period);
    if (err == SUCCESS) {
      // Handle failure
    }
  }
</PRE></LI></OL><A name=Logging_Data></A>
<H1><SPAN class=mw-headline>Logging Data</SPAN></H1>
<P>Reliable (atomic) logging of events and small data items is a common 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -