[Tinyos-2-commits] CVS: tinyos-2.x/tos/chips/rf2xx/rf212 RF212.h, NONE, 1.1 RF212ActiveMessageC.nc, NONE, 1.1 RF212ActiveMessageP.nc, NONE, 1.1 RF212DriverConfig.nc, NONE, 1.1 RF212DriverLayerC.nc, NONE, 1.1 RF212DriverLayerP-comp.nc, NONE, 1.1 RF212DriverLayerP.nc, NONE, 1.1 RF212Packet.h, NONE, 1.1 RF212PacketC.nc, NONE, 1.1 RF212PacketP.nc, NONE, 1.1
Miklos Maroti
mmaroti at users.sourceforge.net
Tue Mar 10 14:43:28 PDT 2009
Update of /cvsroot/tinyos/tinyos-2.x/tos/chips/rf2xx/rf212
In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv587
Added Files:
RF212.h RF212ActiveMessageC.nc RF212ActiveMessageP.nc
RF212DriverConfig.nc RF212DriverLayerC.nc
RF212DriverLayerP-comp.nc RF212DriverLayerP.nc RF212Packet.h
RF212PacketC.nc RF212PacketP.nc
Log Message:
initial RF212 support
--- NEW FILE: RF212.h ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#ifndef __RF212_H__
#define __RF212_H__
enum rf212_registers_enum
{
RF212_TRX_STATUS = 0x01,
RF212_TRX_STATE = 0x02,
RF212_TRX_CTRL_0 = 0x03,
RF212_TRX_CTRL_1 = 0x04,
RF212_PHY_TX_PWR = 0x05,
RF212_PHY_RSSI = 0x06,
RF212_PHY_ED_LEVEL = 0x07,
RF212_PHY_CC_CCA = 0x08,
RF212_CCA_THRES = 0x09,
RF212_IRQ_MASK = 0x0E,
RF212_IRQ_STATUS = 0x0F,
RF212_VREG_CTRL = 0x10,
RF212_BATMON = 0x11,
RF212_XOSC_CTRL = 0x12,
RF212_PLL_CF = 0x1A,
RF212_PLL_DCU = 0x1B,
RF212_PART_NUM = 0x1C,
RF212_VERSION_NUM = 0x1D,
RF212_MAN_ID_0 = 0x1E,
RF212_MAN_ID_1 = 0x1F,
RF212_SHORT_ADDR_0 = 0x20,
RF212_SHORT_ADDR_1 = 0x21,
RF212_PAN_ID_0 = 0x22,
RF212_PAN_ID_1 = 0x23,
RF212_IEEE_ADDR_0 = 0x24,
RF212_IEEE_ADDR_1 = 0x25,
RF212_IEEE_ADDR_2 = 0x26,
RF212_IEEE_ADDR_3 = 0x27,
RF212_IEEE_ADDR_4 = 0x28,
RF212_IEEE_ADDR_5 = 0x29,
RF212_IEEE_ADDR_6 = 0x2A,
RF212_IEEE_ADDR_7 = 0x2B,
RF212_XAH_CTRL = 0x2C,
RF212_CSMA_SEED_0 = 0x2D,
RF212_CSMA_SEED_1 = 0x2E,
};
enum rf212_trx_status_enums
{
RF212_CCA_DONE = 1 << 7,
RF212_CCA_STATUS = 1 << 6,
RF212_TRX_STATUS_MASK = 0x1F,
RF212_P_ON = 0,
RF212_BUSY_RX = 1,
RF212_BUSY_TX = 2,
RF212_RX_ON = 6,
RF212_TRX_OFF = 8,
RF212_PLL_ON = 9,
RF212_SLEEP = 15,
RF212_BUSY_RX_AACK = 16,
RF212_BUSR_TX_ARET = 17,
RF212_RX_AACK_ON = 22,
RF212_TX_ARET_ON = 25,
RF212_RX_ON_NOCLK = 28,
RF212_AACK_ON_NOCLK = 29,
RF212_BUSY_RX_AACK_NOCLK = 30,
RF212_STATE_TRANSITION_IN_PROGRESS = 31,
};
enum rf212_trx_state_enums
{
RF212_TRAC_STATUS_MASK = 0xE0,
RF212_TRAC_SUCCESS = 0,
RF212_TRAC_CHANNEL_ACCESS_FAILURE = 3 << 5,
RF212_TRAC_NO_ACK = 5 << 5,
RF212_TRX_CMD_MASK = 0x1F,
RF212_NOP = 0,
RF212_TX_START = 2,
RF212_FORCE_TRX_OFF = 3,
};
enum rf212_phy_rssi_enums
{
RF212_RX_CRC_VALID = 1 << 7,
RF212_RSSI_MASK = 0x1F,
};
enum rf212_phy_cc_cca_enums
{
RF212_CCA_REQUEST = 1 << 7,
RF212_CCA_MODE_0 = 0 << 5,
RF212_CCA_MODE_1 = 1 << 5,
RF212_CCA_MODE_2 = 2 << 5,
RF212_CCA_MODE_3 = 3 << 5,
RF212_CHANNEL_DEFAULT = 11,
RF212_CHANNEL_MASK = 0x1F,
};
enum rf212_irq_register_enums
{
RF212_IRQ_BAT_LOW = 1 << 7,
RF212_IRQ_TRX_UR = 1 << 6,
RF212_IRQ_AMI = 1 << 5,
RF212_IRQ_CCA_ED_DONE = 1 << 4,
RF212_IRQ_TRX_END = 1 << 3,
RF212_IRQ_RX_START = 1 << 2,
RF212_IRQ_PLL_UNLOCK = 1 << 1,
RF212_IRQ_PLL_LOCK = 1 << 0,
};
enum rf212_batmon_enums
{
RF212_BATMON_OK = 1 << 5,
RF212_BATMON_VHR = 1 << 4,
RF212_BATMON_VTH_MASK = 0x0F,
};
enum rf212_vreg_ctrl_enums
{
RF212_AVREG_EXT = 1 << 7,
RF212_AVDD_OK = 1 << 6,
RF212_DVREG_EXT = 1 << 3,
RF212_DVDD_OK = 1 << 2,
};
enum rf212_xosc_ctrl_enums
{
RF212_XTAL_MODE_OFF = 0 << 4,
RF212_XTAL_MODE_EXTERNAL = 4 << 4,
RF212_XTAL_MODE_INTERNAL = 15 << 4,
};
enum rf212_spi_command_enums
{
RF212_CMD_REGISTER_READ = 0x80,
RF212_CMD_REGISTER_WRITE = 0xC0,
RF212_CMD_REGISTER_MASK = 0x3F,
RF212_CMD_FRAME_READ = 0x20,
RF212_CMD_FRAME_WRITE = 0x60,
RF212_CMD_SRAM_READ = 0x00,
RF212_CMD_SRAM_WRITE = 0x40,
};
#endif//__RF212_H__
--- NEW FILE: RF212ActiveMessageC.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#include <RadioConfig.h>
configuration RF212ActiveMessageC
{
provides
{
interface SplitControl;
interface AMSend[am_id_t id];
interface Receive[am_id_t id];
interface Receive as Snoop[am_id_t id];
interface Packet;
interface AMPacket;
interface PacketAcknowledgements;
interface LowPowerListening;
interface RadioChannel;
interface PacketField<uint8_t> as PacketLinkQuality;
interface PacketField<uint8_t> as PacketTransmitPower;
interface PacketField<uint8_t> as PacketRSSI;
interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
}
}
implementation
{
components RF212ActiveMessageP, RF212PacketC, IEEE154PacketC, RadioAlarmC;
#ifdef RADIO_DEBUG
components AssertC;
#endif
RF212ActiveMessageP.IEEE154Packet -> IEEE154PacketC;
RF212ActiveMessageP.Packet -> RF212PacketC;
RF212ActiveMessageP.RadioAlarm -> RadioAlarmC.RadioAlarm[unique("RadioAlarm")];
Packet = RF212PacketC;
AMPacket = RF212PacketC;
PacketAcknowledgements = RF212PacketC;
PacketLinkQuality = RF212PacketC.PacketLinkQuality;
PacketTransmitPower = RF212PacketC.PacketTransmitPower;
PacketRSSI = RF212PacketC.PacketRSSI;
PacketTimeStampRadio = RF212PacketC;
PacketTimeStampMilli = RF212PacketC;
LowPowerListening = LowPowerListeningLayerC;
RadioChannel = MessageBufferLayerC;
components ActiveMessageLayerC;
#ifdef TFRAMES_ENABLED
components new DummyLayerC() as IEEE154NetworkLayerC;
#else
components IEEE154NetworkLayerC;
#endif
#ifdef LOW_POWER_LISTENING
components LowPowerListeningLayerC;
#else
components new DummyLayerC() as LowPowerListeningLayerC;
#endif
components MessageBufferLayerC;
components UniqueLayerC;
components TrafficMonitorLayerC;
#ifdef SLOTTED_MAC
components SlottedCollisionLayerC as CollisionAvoidanceLayerC;
#else
components RandomCollisionLayerC as CollisionAvoidanceLayerC;
#endif
components SoftwareAckLayerC;
components new DummyLayerC() as CsmaLayerC;
components RF212DriverLayerC;
SplitControl = LowPowerListeningLayerC;
AMSend = ActiveMessageLayerC;
Receive = ActiveMessageLayerC.Receive;
Snoop = ActiveMessageLayerC.Snoop;
ActiveMessageLayerC.Config -> RF212ActiveMessageP;
ActiveMessageLayerC.AMPacket -> IEEE154PacketC;
ActiveMessageLayerC.SubSend -> IEEE154NetworkLayerC;
ActiveMessageLayerC.SubReceive -> IEEE154NetworkLayerC;
IEEE154NetworkLayerC.SubSend -> UniqueLayerC;
IEEE154NetworkLayerC.SubReceive -> LowPowerListeningLayerC;
// the UniqueLayer is wired at two points
UniqueLayerC.Config -> RF212ActiveMessageP;
UniqueLayerC.SubSend -> LowPowerListeningLayerC;
LowPowerListeningLayerC.SubControl -> MessageBufferLayerC;
LowPowerListeningLayerC.SubSend -> MessageBufferLayerC;
LowPowerListeningLayerC.SubReceive -> MessageBufferLayerC;
#ifdef LOW_POWER_LISTENING
LowPowerListeningLayerC.PacketSleepInterval -> RF212PacketC;
LowPowerListeningLayerC.IEEE154Packet -> IEEE154PacketC;
LowPowerListeningLayerC.PacketAcknowledgements -> RF212PacketC;
#endif
MessageBufferLayerC.Packet -> RF212PacketC;
MessageBufferLayerC.RadioSend -> TrafficMonitorLayerC;
MessageBufferLayerC.RadioReceive -> UniqueLayerC;
MessageBufferLayerC.RadioState -> TrafficMonitorLayerC;
UniqueLayerC.SubReceive -> TrafficMonitorLayerC;
TrafficMonitorLayerC.Config -> RF212ActiveMessageP;
TrafficMonitorLayerC.SubSend -> CollisionAvoidanceLayerC;
TrafficMonitorLayerC.SubReceive -> CollisionAvoidanceLayerC;
TrafficMonitorLayerC.SubState -> RF212DriverLayerC;
CollisionAvoidanceLayerC.Config -> RF212ActiveMessageP;
CollisionAvoidanceLayerC.SubSend -> SoftwareAckLayerC;
CollisionAvoidanceLayerC.SubReceive -> SoftwareAckLayerC;
SoftwareAckLayerC.Config -> RF212ActiveMessageP;
SoftwareAckLayerC.SubSend -> CsmaLayerC;
SoftwareAckLayerC.SubReceive -> RF212DriverLayerC;
CsmaLayerC.Config -> RF212ActiveMessageP;
CsmaLayerC -> RF212DriverLayerC.RadioSend;
CsmaLayerC -> RF212DriverLayerC.RadioCCA;
RF212DriverLayerC.RF212DriverConfig -> RF212ActiveMessageP;
}
--- NEW FILE: RF212ActiveMessageP.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#include <RF212Packet.h>
#include <RadioConfig.h>
#include <Tasklet.h>
module RF212ActiveMessageP
{
provides
{
interface RF212DriverConfig;
interface SoftwareAckConfig;
interface UniqueConfig;
interface CsmaConfig;
interface TrafficMonitorConfig;
interface RandomCollisionConfig;
interface SlottedCollisionConfig;
interface ActiveMessageConfig;
interface DummyConfig;
}
uses
{
interface IEEE154Packet;
interface Packet;
interface RadioAlarm;
}
}
implementation
{
/*----------------- RF212DriverConfig -----------------*/
async command uint8_t RF212DriverConfig.getLength(message_t* msg)
{
return call IEEE154Packet.getLength(msg);
}
async command void RF212DriverConfig.setLength(message_t* msg, uint8_t len)
{
call IEEE154Packet.setLength(msg, len);
}
async command uint8_t* RF212DriverConfig.getPayload(message_t* msg)
{
return ((uint8_t*)(call IEEE154Packet.getHeader(msg))) + 1;
}
inline rf212packet_metadata_t* getMeta(message_t* msg)
{
return (rf212packet_metadata_t*)(msg->metadata);
}
async command uint8_t RF212DriverConfig.getHeaderLength()
{
// we need the fcf, dsn, destpan and dest
return 7;
}
async command uint8_t RF212DriverConfig.getMaxLength()
{
// note, that the ieee154_footer_t is not stored, but we should include it here
return sizeof(rf212packet_header_t) - 1 + TOSH_DATA_LENGTH + sizeof(ieee154_footer_t);
}
async command uint8_t RF212DriverConfig.getDefaultChannel()
{
return RF212_DEF_CHANNEL;
}
async command bool RF212DriverConfig.requiresRssiCca(message_t* msg)
{
return call IEEE154Packet.isDataFrame(msg);
}
/*----------------- SoftwareAckConfig -----------------*/
async command bool SoftwareAckConfig.requiresAckWait(message_t* msg)
{
return call IEEE154Packet.requiresAckWait(msg);
}
async command bool SoftwareAckConfig.isAckPacket(message_t* msg)
{
return call IEEE154Packet.isAckFrame(msg);
}
async command bool SoftwareAckConfig.verifyAckPacket(message_t* data, message_t* ack)
{
return call IEEE154Packet.verifyAckReply(data, ack);
}
async command bool SoftwareAckConfig.requiresAckReply(message_t* msg)
{
return call IEEE154Packet.requiresAckReply(msg);
}
async command void SoftwareAckConfig.createAckPacket(message_t* data, message_t* ack)
{
call IEEE154Packet.createAckReply(data, ack);
}
async command void SoftwareAckConfig.setAckReceived(message_t* msg, bool acked)
{
if( acked )
getMeta(msg)->flags |= RF212PACKET_WAS_ACKED;
else
getMeta(msg)->flags &= ~RF212PACKET_WAS_ACKED;
}
async command uint16_t SoftwareAckConfig.getAckTimeout()
{
return (uint16_t)(800 * RADIO_ALARM_MICROSEC);
}
tasklet_async command void SoftwareAckConfig.reportChannelError()
{
signal TrafficMonitorConfig.channelError();
}
/*----------------- UniqueConfig -----------------*/
async command uint8_t UniqueConfig.getSequenceNumber(message_t* msg)
{
return call IEEE154Packet.getDSN(msg);
}
async command void UniqueConfig.setSequenceNumber(message_t* msg, uint8_t dsn)
{
call IEEE154Packet.setDSN(msg, dsn);
}
async command am_addr_t UniqueConfig.getSender(message_t* msg)
{
return call IEEE154Packet.getSrcAddr(msg);
}
tasklet_async command void UniqueConfig.reportChannelError()
{
signal TrafficMonitorConfig.channelError();
}
/*----------------- ActiveMessageConfig -----------------*/
command error_t ActiveMessageConfig.checkPacket(message_t* msg)
{
// the user forgot to call clear, we should return EINVAL
if( ! call IEEE154Packet.isDataFrame(msg) )
call Packet.clear(msg);
return SUCCESS;
}
/*----------------- CsmaConfig -----------------*/
async command bool CsmaConfig.requiresSoftwareCCA(message_t* msg)
{
return call IEEE154Packet.isDataFrame(msg);
}
/*----------------- TrafficMonitorConfig -----------------*/
enum
{
TRAFFIC_UPDATE_PERIOD = 100, // in milliseconds
TRAFFIC_MAX_BYTES = (uint16_t)(TRAFFIC_UPDATE_PERIOD * 1000UL / 32), // 3125
};
async command uint16_t TrafficMonitorConfig.getUpdatePeriod()
{
return TRAFFIC_UPDATE_PERIOD;
}
async command uint16_t TrafficMonitorConfig.getChannelTime(message_t* msg)
{
/* We count in bytes, one byte is 32 microsecond. We are conservative here.
*
* pure airtime: preable (4 bytes), SFD (1 byte), length (1 byte), payload + CRC (len bytes)
* frame separation: 5-10 bytes
* ack required: 8-16 byte separation, 11 bytes airtime, 5-10 bytes separation
*/
uint8_t len = call IEEE154Packet.getLength(msg);
return call IEEE154Packet.getAckRequired(msg) ? len + 6 + 16 + 11 + 10 : len + 6 + 10;
}
async command am_addr_t TrafficMonitorConfig.getSender(message_t* msg)
{
return call IEEE154Packet.getSrcAddr(msg);
}
tasklet_async command void TrafficMonitorConfig.timerTick()
{
signal SlottedCollisionConfig.timerTick();
}
/*----------------- RandomCollisionConfig -----------------*/
/*
* We try to use the same values as in CC2420
*
* CC2420_MIN_BACKOFF = 10 jiffies = 320 microsec
* CC2420_BACKOFF_PERIOD = 10 jiffies
* initial backoff = 0x1F * CC2420_BACKOFF_PERIOD = 310 jiffies = 9920 microsec
* congestion backoff = 0x7 * CC2420_BACKOFF_PERIOD = 70 jiffies = 2240 microsec
*/
async command uint16_t RandomCollisionConfig.getMinimumBackoff()
{
return (uint16_t)(320 * RADIO_ALARM_MICROSEC);
}
async command uint16_t RandomCollisionConfig.getInitialBackoff(message_t* msg)
{
return (uint16_t)(9920 * RADIO_ALARM_MICROSEC);
}
async command uint16_t RandomCollisionConfig.getCongestionBackoff(message_t* msg)
{
return (uint16_t)(2240 * RADIO_ALARM_MICROSEC);
}
async command uint16_t RandomCollisionConfig.getTransmitBarrier(message_t* msg)
{
uint16_t time;
// TODO: maybe we should use the embedded timestamp of the message
time = call RadioAlarm.getNow();
// estimated response time (download the message, etc) is 5-8 bytes
if( call IEEE154Packet.requiresAckReply(msg) )
time += (uint16_t)(32 * (-5 + 16 + 11 + 5) * RADIO_ALARM_MICROSEC);
else
time += (uint16_t)(32 * (-5 + 5) * RADIO_ALARM_MICROSEC);
return time;
}
tasklet_async event void RadioAlarm.fired() { }
/*----------------- SlottedCollisionConfig -----------------*/
async command uint16_t SlottedCollisionConfig.getInitialDelay()
{
return 300;
}
async command uint8_t SlottedCollisionConfig.getScheduleExponent()
{
return 1 + RADIO_ALARM_MILLI_EXP;
}
async command uint16_t SlottedCollisionConfig.getTransmitTime(message_t* msg)
{
// TODO: check if the timestamp is correct
return getMeta(msg)->timestamp;
}
async command uint16_t SlottedCollisionConfig.getCollisionWindowStart(message_t* msg)
{
// the preamble (4 bytes), SFD (1 byte), plus two extra for safety
return getMeta(msg)->timestamp - (uint16_t)(7 * 32 * RADIO_ALARM_MICROSEC);
}
async command uint16_t SlottedCollisionConfig.getCollisionWindowLength(message_t* msg)
{
return (uint16_t)(2 * 7 * 32 * RADIO_ALARM_MICROSEC);
}
default tasklet_async event void SlottedCollisionConfig.timerTick() { }
/*----------------- Dummy -----------------*/
async command void DummyConfig.nothing()
{
}
}
--- NEW FILE: RF212DriverConfig.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
/**
* This interface needs to be implemented by the MAC to control the behaviour
* of the RF212DriverLayerC component.
*/
interface RF212DriverConfig
{
/**
* Returns the length of the PHY payload (including the FCF field).
* This value must be in the range [3, 127].
*/
async command uint8_t getLength(message_t* msg);
/**
* Sets the length of the PHY payload.
*/
async command void setLength(message_t* msg, uint8_t len);
/**
* Returns a pointer to the start of the PHY payload that contains
* getLength()-2 number of bytes. The FCF field (CRC-16) is not stored,
* but automatically appended / verified.
*/
async command uint8_t* getPayload(message_t* msg);
/**
* Gets the number of bytes we should read before the RadioReceive.header
* event is fired. If the length of the packet is less than this amount,
* then that event is fired earlier. The header length must be at least one.
*/
async command uint8_t getHeaderLength();
/**
* Returns the maximum PHY length that can be set via the setLength command
*/
async command uint8_t getMaxLength();
/**
* This command is used at power up to set the default channel.
* The default CC2420 channel is 26.
*/
async command uint8_t getDefaultChannel();
/**
* Returns TRUE if before sending this message we should make sure that
* the channel is clear via a very basic (and quick) RSSI check.
*/
async command bool requiresRssiCca(message_t* msg);
}
--- NEW FILE: RF212DriverLayerC.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
configuration RF212DriverLayerC
{
provides
{
interface RadioState;
interface RadioSend;
interface RadioReceive;
interface RadioCCA;
}
uses interface RF212DriverConfig;
}
implementation
{
components RF212DriverLayerP, HplRF212C, BusyWaitMicroC, TaskletC, MainC, RadioAlarmC, RF212PacketC, LocalTimeMicroC as LocalTimeRadioC;
RadioState = RF212DriverLayerP;
RadioSend = RF212DriverLayerP;
RadioReceive = RF212DriverLayerP;
RadioCCA = RF212DriverLayerP;
RF212DriverConfig = RF212DriverLayerP;
RF212DriverLayerP.PacketLinkQuality -> RF212PacketC.PacketLinkQuality;
RF212DriverLayerP.PacketTransmitPower -> RF212PacketC.PacketTransmitPower;
RF212DriverLayerP.PacketRSSI -> RF212PacketC.PacketRSSI;
RF212DriverLayerP.PacketTimeSyncOffset -> RF212PacketC.PacketTimeSyncOffset;
RF212DriverLayerP.PacketTimeStamp -> RF212PacketC;
RF212DriverLayerP.LocalTime -> LocalTimeRadioC;
RF212DriverLayerP.RadioAlarm -> RadioAlarmC.RadioAlarm[unique("RadioAlarm")];
RadioAlarmC.Alarm -> HplRF212C.Alarm;
RF212DriverLayerP.SELN -> HplRF212C.SELN;
RF212DriverLayerP.SpiResource -> HplRF212C.SpiResource;
RF212DriverLayerP.FastSpiByte -> HplRF212C;
RF212DriverLayerP.SLP_TR -> HplRF212C.SLP_TR;
RF212DriverLayerP.RSTN -> HplRF212C.RSTN;
RF212DriverLayerP.IRQ -> HplRF212C.IRQ;
RF212DriverLayerP.Tasklet -> TaskletC;
RF212DriverLayerP.BusyWait -> BusyWaitMicroC;
#ifdef RADIO_DEBUG
components DiagMsgC;
RF212DriverLayerP.DiagMsg -> DiagMsgC;
#endif
MainC.SoftwareInit -> RF212DriverLayerP.SoftwareInit;
components RealMainP;
RealMainP.PlatformInit -> RF212DriverLayerP.PlatformInit;
}
--- NEW FILE: RF212DriverLayerP-comp.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#include <RF230.h>
#include <Tasklet.h>
#include <RadioAssert.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>
module RF230DriverLayerP
{
provides
{
interface Init as PlatformInit @exactlyonce();
interface Init as SoftwareInit @exactlyonce();
interface RadioState;
interface RadioSend;
interface RadioReceive;
interface RadioCCA;
}
uses
{
interface GeneralIO as SELN;
interface Resource as SpiResource;
interface FastSpiByte;
interface GeneralIO as SLP_TR;
interface GeneralIO as RSTN;
interface GpioCapture as IRQ;
interface BusyWait<TMicro, uint16_t>;
interface PacketField<uint8_t> as PacketLinkQuality;
interface PacketField<uint8_t> as PacketTransmitPower;
interface PacketField<uint8_t> as PacketRSSI;
interface PacketField<uint8_t> as PacketTimeSyncOffset;
interface PacketTimeStamp<TRadio, uint32_t>;
interface LocalTime<TRadio>;
interface RF230DriverConfig;
interface Tasklet;
interface RadioAlarm;
#ifdef RADIO_DEBUG
interface DiagMsg;
#endif
}
}
implementation
{
/*----------------- STATE -----------------*/
tasklet_norace uint8_t state;
enum
{
STATE_P_ON = 0,
STATE_SLEEP = 1,
STATE_SLEEP_2_TRX_OFF = 2,
STATE_TRX_OFF = 3,
STATE_TRX_OFF_2_RX_ON = 4,
STATE_RX_ON = 5,
STATE_BUSY_TX_2_RX_ON = 6,
STATE_PLL_ON_2_RX_ON = 7,
};
tasklet_norace uint8_t cmd;
enum
{
CMD_NONE = 0, // the state machine has stopped
CMD_TURNOFF = 1, // goto SLEEP state
CMD_STANDBY = 2, // goto TRX_OFF state
CMD_TURNON = 3, // goto RX_ON state
CMD_TRANSMIT = 4, // currently transmitting a message
CMD_RECEIVE = 5, // currently receiving a message
CMD_CCA = 6, // performing clear chanel assesment
CMD_CHANNEL = 7, // changing the channel
CMD_SIGNAL_DONE = 8, // signal the end of the state transition
CMD_DOWNLOAD = 9, // download the received message
};
norace bool radioIrq;
tasklet_norace uint8_t txPower;
tasklet_norace uint8_t channel;
tasklet_norace message_t* rxMsg;
message_t rxMsgBuffer;
uint16_t capturedTime; // the current time when the last interrupt has occured
tasklet_norace uint8_t rssiClear;
tasklet_norace uint8_t rssiBusy;
/*----------------- REGISTER -----------------*/
inline void writeRegister(uint8_t reg, uint8_t value)
{
ASSERT( call SpiResource.isOwner() );
ASSERT( reg == (reg & RF230_CMD_REGISTER_MASK) );
call SELN.clr();
call FastSpiByte.splitWrite(RF230_CMD_REGISTER_WRITE | reg);
call FastSpiByte.splitReadWrite(value);
call FastSpiByte.splitRead();
call SELN.set();
}
inline uint8_t readRegister(uint8_t reg)
{
ASSERT( call SpiResource.isOwner() );
ASSERT( reg == (reg & RF230_CMD_REGISTER_MASK) );
call SELN.clr();
call FastSpiByte.splitWrite(RF230_CMD_REGISTER_READ | reg);
call FastSpiByte.splitReadWrite(0);
reg = call FastSpiByte.splitRead();
call SELN.set();
return reg;
}
/*----------------- ALARM -----------------*/
enum
{
SLEEP_WAKEUP_TIME = (uint16_t)(880 * RADIO_ALARM_MICROSEC),
CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),
TX_SFD_DELAY = (uint16_t)(176 * RADIO_ALARM_MICROSEC),
RX_SFD_DELAY = (uint16_t)(8 * RADIO_ALARM_MICROSEC),
};
tasklet_async event void RadioAlarm.fired()
{
if( state == STATE_SLEEP_2_TRX_OFF )
state = STATE_TRX_OFF;
else if( cmd == CMD_CCA )
{
uint8_t cca;
ASSERT( state == STATE_RX_ON );
cmd = CMD_NONE;
cca = readRegister(RF230_TRX_STATUS);
ASSERT( (cca & RF230_TRX_STATUS_MASK) == RF230_RX_ON );
signal RadioCCA.done( (cca & RF230_CCA_DONE) ? ((cca & RF230_CCA_STATUS) ? SUCCESS : EBUSY) : FAIL );
}
else
ASSERT(FALSE);
// make sure the rest of the command processing is called
call Tasklet.schedule();
}
/*----------------- INIT -----------------*/
command error_t PlatformInit.init()
{
call SELN.makeOutput();
call SELN.set();
call SLP_TR.makeOutput();
call SLP_TR.clr();
call RSTN.makeOutput();
call RSTN.set();
rxMsg = &rxMsgBuffer;
// these are just good approximates
rssiClear = 0;
rssiBusy = 90;
return SUCCESS;
}
command error_t SoftwareInit.init()
{
// for powering up the radio
return call SpiResource.request();
}
void initRadio()
{
call BusyWait.wait(510);
call RSTN.clr();
call SLP_TR.clr();
call BusyWait.wait(6);
call RSTN.set();
writeRegister(RF230_TRX_CTRL_0, RF230_TRX_CTRL_0_VALUE);
writeRegister(RF230_TRX_STATE, RF230_TRX_OFF);
call BusyWait.wait(510);
writeRegister(RF230_IRQ_MASK, RF230_IRQ_TRX_UR | RF230_IRQ_PLL_LOCK | RF230_IRQ_TRX_END | RF230_IRQ_RX_START);
writeRegister(RF230_CCA_THRES, RF230_CCA_THRES_VALUE);
writeRegister(RF230_PHY_TX_PWR, RF230_DEF_RFPOWER);
txPower = RF230_DEF_RFPOWER;
channel = call RF230DriverConfig.getDefaultChannel() & RF230_CHANNEL_MASK;
writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
call SLP_TR.set();
state = STATE_SLEEP;
}
/*----------------- SPI -----------------*/
event void SpiResource.granted()
{
call SELN.makeOutput();
call SELN.set();
if( state == STATE_P_ON )
{
initRadio();
call SpiResource.release();
}
else
call Tasklet.schedule();
}
bool isSpiAcquired()
{
if( call SpiResource.isOwner() )
return TRUE;
if( call SpiResource.immediateRequest() == SUCCESS )
{
call SELN.makeOutput();
call SELN.set();
return TRUE;
}
call SpiResource.request();
return FALSE;
}
/*----------------- CHANNEL -----------------*/
tasklet_async command uint8_t RadioState.getChannel()
{
return channel;
}
tasklet_async command error_t RadioState.setChannel(uint8_t c)
{
c &= RF230_CHANNEL_MASK;
if( cmd != CMD_NONE )
return EBUSY;
else if( channel == c )
return EALREADY;
channel = c;
cmd = CMD_CHANNEL;
call Tasklet.schedule();
return SUCCESS;
}
inline void changeChannel()
{
ASSERT( cmd == CMD_CHANNEL );
ASSERT( state == STATE_SLEEP || state == STATE_TRX_OFF || state == STATE_RX_ON );
if( isSpiAcquired() )
{
writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
if( state == STATE_RX_ON )
state = STATE_TRX_OFF_2_RX_ON;
else
cmd = CMD_SIGNAL_DONE;
}
}
/*----------------- TURN ON/OFF -----------------*/
inline void changeState()
{
if( (cmd == CMD_STANDBY || cmd == CMD_TURNON)
&& state == STATE_SLEEP && call RadioAlarm.isFree() )
{
call SLP_TR.clr();
call RadioAlarm.wait(SLEEP_WAKEUP_TIME);
state = STATE_SLEEP_2_TRX_OFF;
}
else if( cmd == CMD_TURNON && state == STATE_TRX_OFF && isSpiAcquired() )
{
ASSERT( ! radioIrq );
readRegister(RF230_IRQ_STATUS); // clear the interrupt register
call IRQ.captureRisingEdge();
// setChannel was ignored in SLEEP because the SPI was not working, so do it here
writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
writeRegister(RF230_TRX_STATE, RF230_RX_ON);
state = STATE_TRX_OFF_2_RX_ON;
}
else if( (cmd == CMD_TURNOFF || cmd == CMD_STANDBY)
&& state == STATE_RX_ON && isSpiAcquired() )
{
writeRegister(RF230_TRX_STATE, RF230_FORCE_TRX_OFF);
call IRQ.disable();
radioIrq = FALSE;
state = STATE_TRX_OFF;
}
if( cmd == CMD_TURNOFF && state == STATE_TRX_OFF )
{
call SLP_TR.set();
state = STATE_SLEEP;
cmd = CMD_SIGNAL_DONE;
}
else if( cmd == CMD_STANDBY && state == STATE_TRX_OFF )
cmd = CMD_SIGNAL_DONE;
}
tasklet_async command error_t RadioState.turnOff()
{
if( cmd != CMD_NONE )
return EBUSY;
else if( state == STATE_SLEEP )
return EALREADY;
cmd = CMD_TURNOFF;
call Tasklet.schedule();
return SUCCESS;
}
tasklet_async command error_t RadioState.standby()
{
if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
return EBUSY;
else if( state == STATE_TRX_OFF )
return EALREADY;
cmd = CMD_STANDBY;
call Tasklet.schedule();
return SUCCESS;
}
tasklet_async command error_t RadioState.turnOn()
{
if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
return EBUSY;
else if( state == STATE_RX_ON )
return EALREADY;
cmd = CMD_TURNON;
call Tasklet.schedule();
return SUCCESS;
}
default tasklet_async event void RadioState.done() { }
/*----------------- TRANSMIT -----------------*/
tasklet_async command error_t RadioSend.send(message_t* msg)
{
uint16_t time;
uint8_t length;
uint8_t* data;
uint8_t header;
uint32_t time32;
void* timesync;
if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || radioIrq )
return EBUSY;
length = call PacketTransmitPower.isSet(msg) ?
call PacketTransmitPower.get(msg) : RF230_DEF_RFPOWER;
if( length != txPower )
{
txPower = length;
writeRegister(RF230_PHY_TX_PWR, txPower);
}
if( call RF230DriverConfig.requiresRssiCca(msg)
&& (readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK) > ((rssiClear + rssiBusy) >> 3) )
return EBUSY;
writeRegister(RF230_TRX_STATE, RF230_PLL_ON);
// do something useful, just to wait a little
time32 = call LocalTime.get();
timesync = call PacketTimeSyncOffset.isSet(msg) ? msg->data + call PacketTimeSyncOffset.get(msg) : 0;
// we have missed an incoming message in this short amount of time
if( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) != RF230_PLL_ON )
{
ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_BUSY_RX );
state = STATE_PLL_ON_2_RX_ON;
return EBUSY;
}
atomic
{
call SLP_TR.set();
time = call RadioAlarm.getNow() + TX_SFD_DELAY;
}
call SLP_TR.clr();
ASSERT( ! radioIrq );
call SELN.clr();
call FastSpiByte.splitWrite(RF230_CMD_FRAME_WRITE);
length = call RF230DriverConfig.getLength(msg);
data = call RF230DriverConfig.getPayload(msg);
// length | data[0] ... data[length-3] | automatically generated FCS
call FastSpiByte.splitReadWrite(length);
// the FCS is atomatically generated (2 bytes)
length -= 2;
header = call RF230DriverConfig.getHeaderLength();
if( header > length )
header = length;
length -= header;
// first upload the header to gain some time
do {
call FastSpiByte.splitReadWrite(*(data++));
}
while( --header != 0 );
time32 += (int16_t)(time) - (int16_t)(time32);
if( timesync != 0 )
*(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;
do {
call FastSpiByte.splitReadWrite(*(data++));
}
while( --length != 0 );
// wait for the SPI transfer to finish
call FastSpiByte.splitRead();
call SELN.set();
/*
* There is a very small window (~1 microsecond) when the RF230 went
* into PLL_ON state but was somehow not properly initialized because
* of an incoming message and could not go into BUSY_TX. I think the
* radio can even receive a message, and generate a TRX_UR interrupt
* because of concurrent access, but that message probably cannot be
* recovered.
*
* TODO: this needs to be verified, and make sure that the chip is
* not locked up in this case.
*/
// go back to RX_ON state when finished
writeRegister(RF230_TRX_STATE, RF230_RX_ON);
#ifdef RADIO_DEBUG_MESSAGES
if( call DiagMsg.record() )
{
length = call RF230DriverConfig.getLength(msg);
call DiagMsg.str("tx");
call DiagMsg.uint16(time);
call DiagMsg.uint8(length);
call DiagMsg.hex8s(data, length - 2);
call DiagMsg.send();
}
#endif
if( timesync != 0 )
*(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;
call PacketTimeStamp.set(msg, time32);
// wait for the TRX_END interrupt
state = STATE_BUSY_TX_2_RX_ON;
cmd = CMD_TRANSMIT;
return SUCCESS;
}
default tasklet_async event void RadioSend.sendDone(error_t error) { }
default tasklet_async event void RadioSend.ready() { }
/*----------------- CCA -----------------*/
tasklet_async command error_t RadioCCA.request()
{
if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || ! call RadioAlarm.isFree() )
return EBUSY;
// see Errata B7 of the datasheet
// writeRegister(RF230_TRX_STATE, RF230_PLL_ON);
// writeRegister(RF230_TRX_STATE, RF230_RX_ON);
writeRegister(RF230_PHY_CC_CCA, RF230_CCA_REQUEST | RF230_CCA_MODE_VALUE | channel);
call RadioAlarm.wait(CCA_REQUEST_TIME);
cmd = CMD_CCA;
return SUCCESS;
}
default tasklet_async event void RadioCCA.done(error_t error) { }
/*----------------- RECEIVE -----------------*/
inline void downloadMessage()
{
uint8_t length;
uint8_t crc;
call SELN.clr();
call FastSpiByte.write(RF230_CMD_FRAME_READ);
// read the length byte
length = call FastSpiByte.write(0);
// if correct length
if( length >= 3 && length <= call RF230DriverConfig.getMaxLength() )
{
uint8_t read;
uint8_t* data;
// initiate the reading
call FastSpiByte.splitWrite(0);
call RF230DriverConfig.setLength(rxMsg, length);
data = call RF230DriverConfig.getPayload(rxMsg);
crc = 0;
// we do not store the CRC field
length -= 2;
read = call RF230DriverConfig.getHeaderLength();
if( length < read )
read = length;
length -= read;
do {
*(data++) = call FastSpiByte.splitReadWrite(0);
}
while( --read != 0 );
if( signal RadioReceive.header(rxMsg) )
{
while( length-- != 0 )
*(data++) = call FastSpiByte.splitReadWrite(0);
call FastSpiByte.splitReadWrite(0); // two CRC bytes
call FastSpiByte.splitReadWrite(0);
call PacketLinkQuality.set(rxMsg, call FastSpiByte.splitReadWrite(0));
call FastSpiByte.splitReadWrite(0); // ED
crc = call FastSpiByte.splitRead() & RF230_RX_CRC_VALID; // RX_STATUS
}
}
call SELN.set();
state = STATE_RX_ON;
#ifdef RADIO_DEBUG_MESSAGES
if( call DiagMsg.record() )
{
length = call RF230DriverConfig.getLength(rxMsg);
call DiagMsg.str("rx");
call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
call DiagMsg.uint16(call RadioAlarm.getNow());
call DiagMsg.uint8(crc != 0);
call DiagMsg.uint8(length);
call DiagMsg.hex8s(call RF230DriverConfig.getPayload(rxMsg), length - 2);
call DiagMsg.send();
}
#endif
cmd = CMD_NONE;
// signal only if it has passed the CRC check
if( crc != 0 )
rxMsg = signal RadioReceive.receive(rxMsg);
}
/*----------------- IRQ -----------------*/
async event void IRQ.captured(uint16_t time)
{
ASSERT( ! radioIrq );
atomic
{
capturedTime = time;
radioIrq = TRUE;
}
call Tasklet.schedule();
}
void serviceRadio()
{
if( isSpiAcquired() )
{
uint16_t time;
uint32_t time32;
uint8_t irq;
uint8_t temp;
atomic time = capturedTime;
radioIrq = FALSE;
irq = readRegister(RF230_IRQ_STATUS);
#ifdef RADIO_DEBUG
// TODO: handle this interrupt
if( irq & RF230_IRQ_TRX_UR )
{
if( call DiagMsg.record() )
{
call DiagMsg.str("assert ur");
call DiagMsg.uint16(call RadioAlarm.getNow());
call DiagMsg.hex8(readRegister(RF230_TRX_STATUS));
call DiagMsg.hex8(readRegister(RF230_TRX_STATE));
call DiagMsg.hex8(irq);
call DiagMsg.uint8(state);
call DiagMsg.uint8(cmd);
call DiagMsg.send();
}
}
#endif
if( irq & RF230_IRQ_PLL_LOCK )
{
if( cmd == CMD_TURNON || cmd == CMD_CHANNEL )
{
ASSERT( state == STATE_TRX_OFF_2_RX_ON );
state = STATE_RX_ON;
cmd = CMD_SIGNAL_DONE;
}
else if( cmd == CMD_TRANSMIT )
{
ASSERT( state == STATE_BUSY_TX_2_RX_ON );
}
else
ASSERT(FALSE);
}
if( irq & RF230_IRQ_RX_START )
{
if( cmd == CMD_CCA )
{
signal RadioCCA.done(FAIL);
cmd = CMD_NONE;
}
if( cmd == CMD_NONE )
{
ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
// the most likely place for busy channel, with no TRX_END interrupt
if( irq == RF230_IRQ_RX_START )
{
temp = readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK;
rssiBusy += temp - (rssiBusy >> 2);
#ifndef RF230_RSSI_ENERGY
call PacketRSSI.set(rxMsg, temp);
}
else
{
call PacketRSSI.clear(rxMsg);
#endif
}
/*
* The timestamp corresponds to the first event which could not
* have been a PLL_LOCK because then cmd != CMD_NONE, so we must
* have received a message (and could also have received the
* TRX_END interrupt in the mean time, but that is fine. Also,
* we could not be after a transmission, because then cmd =
* CMD_TRANSMIT.
*/
if( irq == RF230_IRQ_RX_START ) // just to be cautious
{
time32 = call LocalTime.get();
time32 += (int16_t)(time - RX_SFD_DELAY) - (int16_t)(time32);
call PacketTimeStamp.set(rxMsg, time32);
}
else
call PacketTimeStamp.clear(rxMsg);
cmd = CMD_RECEIVE;
}
else
ASSERT( cmd == CMD_TURNOFF );
}
if( irq & RF230_IRQ_TRX_END )
{
if( cmd == CMD_TRANSMIT )
{
ASSERT( state == STATE_BUSY_TX_2_RX_ON );
state = STATE_RX_ON;
cmd = CMD_NONE;
signal RadioSend.sendDone(SUCCESS);
// TODO: we could have missed a received message
ASSERT( ! (irq & RF230_IRQ_RX_START) );
}
else if( cmd == CMD_RECEIVE )
{
ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
#ifdef RF230_RSSI_ENERGY
if( irq == RF230_IRQ_TRX_END )
call PacketRSSI.set(rxMsg, readRegister(RF230_PHY_ED_LEVEL));
else
call PacketRSSI.clear(rxMsg);
#endif
if( state == STATE_PLL_ON_2_RX_ON )
{
ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_PLL_ON );
writeRegister(RF230_TRX_STATE, RF230_RX_ON);
state = STATE_RX_ON;
}
else
{
// the most likely place for clear channel (hope to avoid acks)
rssiClear += (readRegister(RF230_PHY_RSSI) & RF230_RSSI_MASK) - (rssiClear >> 2);
}
cmd = CMD_DOWNLOAD;
}
else
ASSERT(FALSE);
}
}
}
default tasklet_async event bool RadioReceive.header(message_t* msg)
{
return TRUE;
}
default tasklet_async event message_t* RadioReceive.receive(message_t* msg)
{
return msg;
}
/*----------------- TASKLET -----------------*/
tasklet_async event void Tasklet.run()
{
if( radioIrq )
serviceRadio();
if( cmd != CMD_NONE )
{
if( cmd == CMD_DOWNLOAD )
downloadMessage();
else if( CMD_TURNOFF <= cmd && cmd <= CMD_TURNON )
changeState();
else if( cmd == CMD_CHANNEL )
changeChannel();
if( cmd == CMD_SIGNAL_DONE )
{
cmd = CMD_NONE;
signal RadioState.done();
}
}
if( cmd == CMD_NONE && state == STATE_RX_ON && ! radioIrq )
signal RadioSend.ready();
if( cmd == CMD_NONE )
call SpiResource.release();
}
}
--- NEW FILE: RF212DriverLayerP.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#include <RF212.h>
#include <Tasklet.h>
#include <RadioAssert.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>
module RF212DriverLayerP
{
provides
{
interface Init as PlatformInit @exactlyonce();
interface Init as SoftwareInit @exactlyonce();
interface RadioState;
interface RadioSend;
interface RadioReceive;
interface RadioCCA;
}
uses
{
interface GeneralIO as SELN;
interface Resource as SpiResource;
interface FastSpiByte;
interface GeneralIO as SLP_TR;
interface GeneralIO as RSTN;
interface GpioCapture as IRQ;
interface BusyWait<TMicro, uint16_t>;
interface PacketField<uint8_t> as PacketLinkQuality;
interface PacketField<uint8_t> as PacketTransmitPower;
interface PacketField<uint8_t> as PacketRSSI;
interface PacketField<uint8_t> as PacketTimeSyncOffset;
interface PacketTimeStamp<TRadio, uint32_t>;
interface LocalTime<TRadio>;
interface RF212DriverConfig;
interface Tasklet;
interface RadioAlarm;
#ifdef RADIO_DEBUG
interface DiagMsg;
#endif
}
}
implementation
{
/*----------------- STATE -----------------*/
tasklet_norace uint8_t state;
enum
{
STATE_P_ON = 0,
STATE_SLEEP = 1,
STATE_SLEEP_2_TRX_OFF = 2,
STATE_TRX_OFF = 3,
STATE_TRX_OFF_2_RX_ON = 4,
STATE_RX_ON = 5,
STATE_BUSY_TX_2_RX_ON = 6,
STATE_PLL_ON_2_RX_ON = 7,
};
tasklet_norace uint8_t cmd;
enum
{
CMD_NONE = 0, // the state machine has stopped
CMD_TURNOFF = 1, // goto SLEEP state
CMD_STANDBY = 2, // goto TRX_OFF state
CMD_TURNON = 3, // goto RX_ON state
CMD_TRANSMIT = 4, // currently transmitting a message
CMD_RECEIVE = 5, // currently receiving a message
CMD_CCA = 6, // performing clear chanel assesment
CMD_CHANNEL = 7, // changing the channel
CMD_SIGNAL_DONE = 8, // signal the end of the state transition
CMD_DOWNLOAD = 9, // download the received message
};
norace bool radioIrq;
tasklet_norace uint8_t txPower;
tasklet_norace uint8_t channel;
tasklet_norace message_t* rxMsg;
message_t rxMsgBuffer;
uint16_t capturedTime; // the current time when the last interrupt has occured
tasklet_norace uint8_t rssiClear;
tasklet_norace uint8_t rssiBusy;
/*----------------- REGISTER -----------------*/
inline void writeRegister(uint8_t reg, uint8_t value)
{
ASSERT( call SpiResource.isOwner() );
ASSERT( reg == (reg & RF212_CMD_REGISTER_MASK) );
call SELN.clr();
call FastSpiByte.splitWrite(RF212_CMD_REGISTER_WRITE | reg);
call FastSpiByte.splitReadWrite(value);
call FastSpiByte.splitRead();
call SELN.set();
}
inline uint8_t readRegister(uint8_t reg)
{
ASSERT( call SpiResource.isOwner() );
ASSERT( reg == (reg & RF212_CMD_REGISTER_MASK) );
call SELN.clr();
call FastSpiByte.splitWrite(RF212_CMD_REGISTER_READ | reg);
call FastSpiByte.splitReadWrite(0);
reg = call FastSpiByte.splitRead();
call SELN.set();
return reg;
}
/*----------------- ALARM -----------------*/
enum
{
SLEEP_WAKEUP_TIME = (uint16_t)(880 * RADIO_ALARM_MICROSEC),
CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),
TX_SFD_DELAY = (uint16_t)(176 * RADIO_ALARM_MICROSEC),
RX_SFD_DELAY = (uint16_t)(8 * RADIO_ALARM_MICROSEC),
};
tasklet_async event void RadioAlarm.fired()
{
if( state == STATE_SLEEP_2_TRX_OFF )
state = STATE_TRX_OFF;
else if( cmd == CMD_CCA )
{
uint8_t cca;
ASSERT( state == STATE_RX_ON );
cmd = CMD_NONE;
cca = readRegister(RF212_TRX_STATUS);
ASSERT( (cca & RF212_TRX_STATUS_MASK) == RF212_RX_ON );
signal RadioCCA.done( (cca & RF212_CCA_DONE) ? ((cca & RF212_CCA_STATUS) ? SUCCESS : EBUSY) : FAIL );
}
else
ASSERT(FALSE);
// make sure the rest of the command processing is called
call Tasklet.schedule();
}
/*----------------- INIT -----------------*/
command error_t PlatformInit.init()
{
call SELN.makeOutput();
call SELN.set();
call SLP_TR.makeOutput();
call SLP_TR.clr();
call RSTN.makeOutput();
call RSTN.set();
rxMsg = &rxMsgBuffer;
// these are just good approximates
rssiClear = 0;
rssiBusy = 90;
return SUCCESS;
}
command error_t SoftwareInit.init()
{
// for powering up the radio
return call SpiResource.request();
}
void initRadio()
{
call BusyWait.wait(510);
call RSTN.clr();
call SLP_TR.clr();
call BusyWait.wait(6);
call RSTN.set();
writeRegister(RF212_TRX_CTRL_0, RF212_TRX_CTRL_0_VALUE);
writeRegister(RF212_TRX_STATE, RF212_TRX_OFF);
call BusyWait.wait(510);
writeRegister(RF212_IRQ_MASK, RF212_IRQ_TRX_UR | RF212_IRQ_PLL_LOCK | RF212_IRQ_TRX_END | RF212_IRQ_RX_START);
writeRegister(RF212_CCA_THRES, RF212_CCA_THRES_VALUE);
writeRegister(RF212_PHY_TX_PWR, RF212_DEF_RFPOWER);
txPower = RF212_DEF_RFPOWER;
channel = call RF212DriverConfig.getDefaultChannel() & RF212_CHANNEL_MASK;
writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);
call SLP_TR.set();
state = STATE_SLEEP;
}
/*----------------- SPI -----------------*/
event void SpiResource.granted()
{
call SELN.makeOutput();
call SELN.set();
if( state == STATE_P_ON )
{
initRadio();
call SpiResource.release();
}
else
call Tasklet.schedule();
}
bool isSpiAcquired()
{
if( call SpiResource.isOwner() )
return TRUE;
if( call SpiResource.immediateRequest() == SUCCESS )
{
call SELN.makeOutput();
call SELN.set();
return TRUE;
}
call SpiResource.request();
return FALSE;
}
/*----------------- CHANNEL -----------------*/
tasklet_async command uint8_t RadioState.getChannel()
{
return channel;
}
tasklet_async command error_t RadioState.setChannel(uint8_t c)
{
c &= RF212_CHANNEL_MASK;
if( cmd != CMD_NONE )
return EBUSY;
else if( channel == c )
return EALREADY;
channel = c;
cmd = CMD_CHANNEL;
call Tasklet.schedule();
return SUCCESS;
}
inline void changeChannel()
{
ASSERT( cmd == CMD_CHANNEL );
ASSERT( state == STATE_SLEEP || state == STATE_TRX_OFF || state == STATE_RX_ON );
if( isSpiAcquired() )
{
writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);
if( state == STATE_RX_ON )
state = STATE_TRX_OFF_2_RX_ON;
else
cmd = CMD_SIGNAL_DONE;
}
}
/*----------------- TURN ON/OFF -----------------*/
inline void changeState()
{
if( (cmd == CMD_STANDBY || cmd == CMD_TURNON)
&& state == STATE_SLEEP && call RadioAlarm.isFree() )
{
call SLP_TR.clr();
call RadioAlarm.wait(SLEEP_WAKEUP_TIME);
state = STATE_SLEEP_2_TRX_OFF;
}
else if( cmd == CMD_TURNON && state == STATE_TRX_OFF && isSpiAcquired() )
{
ASSERT( ! radioIrq );
readRegister(RF212_IRQ_STATUS); // clear the interrupt register
call IRQ.captureRisingEdge();
// setChannel was ignored in SLEEP because the SPI was not working, so do it here
writeRegister(RF212_PHY_CC_CCA, RF212_CCA_MODE_VALUE | channel);
writeRegister(RF212_TRX_STATE, RF212_RX_ON);
state = STATE_TRX_OFF_2_RX_ON;
}
else if( (cmd == CMD_TURNOFF || cmd == CMD_STANDBY)
&& state == STATE_RX_ON && isSpiAcquired() )
{
writeRegister(RF212_TRX_STATE, RF212_FORCE_TRX_OFF);
call IRQ.disable();
radioIrq = FALSE;
state = STATE_TRX_OFF;
}
if( cmd == CMD_TURNOFF && state == STATE_TRX_OFF )
{
call SLP_TR.set();
state = STATE_SLEEP;
cmd = CMD_SIGNAL_DONE;
}
else if( cmd == CMD_STANDBY && state == STATE_TRX_OFF )
cmd = CMD_SIGNAL_DONE;
}
tasklet_async command error_t RadioState.turnOff()
{
if( cmd != CMD_NONE )
return EBUSY;
else if( state == STATE_SLEEP )
return EALREADY;
cmd = CMD_TURNOFF;
call Tasklet.schedule();
return SUCCESS;
}
tasklet_async command error_t RadioState.standby()
{
if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
return EBUSY;
else if( state == STATE_TRX_OFF )
return EALREADY;
cmd = CMD_STANDBY;
call Tasklet.schedule();
return SUCCESS;
}
tasklet_async command error_t RadioState.turnOn()
{
if( cmd != CMD_NONE || (state == STATE_SLEEP && ! call RadioAlarm.isFree()) )
return EBUSY;
else if( state == STATE_RX_ON )
return EALREADY;
cmd = CMD_TURNON;
call Tasklet.schedule();
return SUCCESS;
}
default tasklet_async event void RadioState.done() { }
/*----------------- TRANSMIT -----------------*/
tasklet_async command error_t RadioSend.send(message_t* msg)
{
uint16_t time;
uint8_t length;
uint8_t* data;
uint8_t header;
uint32_t time32;
void* timesync;
if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || radioIrq )
return EBUSY;
length = call PacketTransmitPower.isSet(msg) ?
call PacketTransmitPower.get(msg) : RF212_DEF_RFPOWER;
if( length != txPower )
{
txPower = length;
writeRegister(RF212_PHY_TX_PWR, txPower);
}
if( call RF212DriverConfig.requiresRssiCca(msg)
&& (readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK) > ((rssiClear + rssiBusy) >> 3) )
return EBUSY;
writeRegister(RF212_TRX_STATE, RF212_PLL_ON);
// do something useful, just to wait a little
time32 = call LocalTime.get();
timesync = call PacketTimeSyncOffset.isSet(msg) ? msg->data + call PacketTimeSyncOffset.get(msg) : 0;
// we have missed an incoming message in this short amount of time
if( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) != RF212_PLL_ON )
{
ASSERT( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) == RF212_BUSY_RX );
state = STATE_PLL_ON_2_RX_ON;
return EBUSY;
}
atomic
{
call SLP_TR.set();
time = call RadioAlarm.getNow() + TX_SFD_DELAY;
}
call SLP_TR.clr();
ASSERT( ! radioIrq );
call SELN.clr();
call FastSpiByte.splitWrite(RF212_CMD_FRAME_WRITE);
length = call RF212DriverConfig.getLength(msg);
data = call RF212DriverConfig.getPayload(msg);
// length | data[0] ... data[length-3] | automatically generated FCS
call FastSpiByte.splitReadWrite(length);
// the FCS is atomatically generated (2 bytes)
length -= 2;
header = call RF212DriverConfig.getHeaderLength();
if( header > length )
header = length;
length -= header;
// first upload the header to gain some time
do {
call FastSpiByte.splitReadWrite(*(data++));
}
while( --header != 0 );
time32 += (int16_t)(time) - (int16_t)(time32);
if( timesync != 0 )
*(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;
do {
call FastSpiByte.splitReadWrite(*(data++));
}
while( --length != 0 );
// wait for the SPI transfer to finish
call FastSpiByte.splitRead();
call SELN.set();
/*
* There is a very small window (~1 microsecond) when the RF212 went
* into PLL_ON state but was somehow not properly initialized because
* of an incoming message and could not go into BUSY_TX. I think the
* radio can even receive a message, and generate a TRX_UR interrupt
* because of concurrent access, but that message probably cannot be
* recovered.
*
* TODO: this needs to be verified, and make sure that the chip is
* not locked up in this case.
*/
// go back to RX_ON state when finished
writeRegister(RF212_TRX_STATE, RF212_RX_ON);
#ifdef RADIO_DEBUG_MESSAGES
if( call DiagMsg.record() )
{
length = call RF212DriverConfig.getLength(msg);
call DiagMsg.str("tx");
call DiagMsg.uint16(time);
call DiagMsg.uint8(length);
call DiagMsg.hex8s(data, length - 2);
call DiagMsg.send();
}
#endif
if( timesync != 0 )
*(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;
call PacketTimeStamp.set(msg, time32);
// wait for the TRX_END interrupt
state = STATE_BUSY_TX_2_RX_ON;
cmd = CMD_TRANSMIT;
return SUCCESS;
}
default tasklet_async event void RadioSend.sendDone(error_t error) { }
default tasklet_async event void RadioSend.ready() { }
/*----------------- CCA -----------------*/
tasklet_async command error_t RadioCCA.request()
{
if( cmd != CMD_NONE || state != STATE_RX_ON || ! isSpiAcquired() || ! call RadioAlarm.isFree() )
return EBUSY;
// see Errata B7 of the datasheet
// writeRegister(RF212_TRX_STATE, RF212_PLL_ON);
// writeRegister(RF212_TRX_STATE, RF212_RX_ON);
writeRegister(RF212_PHY_CC_CCA, RF212_CCA_REQUEST | RF212_CCA_MODE_VALUE | channel);
call RadioAlarm.wait(CCA_REQUEST_TIME);
cmd = CMD_CCA;
return SUCCESS;
}
default tasklet_async event void RadioCCA.done(error_t error) { }
/*----------------- RECEIVE -----------------*/
inline void downloadMessage()
{
uint8_t length;
uint8_t crc;
call SELN.clr();
call FastSpiByte.write(RF212_CMD_FRAME_READ);
// read the length byte
length = call FastSpiByte.write(0);
// if correct length
if( length >= 3 && length <= call RF212DriverConfig.getMaxLength() )
{
uint8_t read;
uint8_t* data;
// initiate the reading
call FastSpiByte.splitWrite(0);
call RF212DriverConfig.setLength(rxMsg, length);
data = call RF212DriverConfig.getPayload(rxMsg);
crc = 0;
// we do not store the CRC field
length -= 2;
read = call RF212DriverConfig.getHeaderLength();
if( length < read )
read = length;
length -= read;
do {
*(data++) = call FastSpiByte.splitReadWrite(0);
}
while( --read != 0 );
if( signal RadioReceive.header(rxMsg) )
{
while( length-- != 0 )
*(data++) = call FastSpiByte.splitReadWrite(0);
call FastSpiByte.splitReadWrite(0); // two CRC bytes
call FastSpiByte.splitReadWrite(0);
call PacketLinkQuality.set(rxMsg, call FastSpiByte.splitReadWrite(0));
call FastSpiByte.splitReadWrite(0); // ED
crc = call FastSpiByte.splitRead() & RF212_RX_CRC_VALID; // RX_STATUS
}
}
call SELN.set();
state = STATE_RX_ON;
#ifdef RADIO_DEBUG_MESSAGES
if( call DiagMsg.record() )
{
length = call RF212DriverConfig.getLength(rxMsg);
call DiagMsg.str("rx");
call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
call DiagMsg.uint16(call RadioAlarm.getNow());
call DiagMsg.uint8(crc != 0);
call DiagMsg.uint8(length);
call DiagMsg.hex8s(call RF212DriverConfig.getPayload(rxMsg), length - 2);
call DiagMsg.send();
}
#endif
cmd = CMD_NONE;
// signal only if it has passed the CRC check
if( crc != 0 )
rxMsg = signal RadioReceive.receive(rxMsg);
}
/*----------------- IRQ -----------------*/
async event void IRQ.captured(uint16_t time)
{
ASSERT( ! radioIrq );
atomic
{
capturedTime = time;
radioIrq = TRUE;
}
call Tasklet.schedule();
}
void serviceRadio()
{
if( isSpiAcquired() )
{
uint16_t time;
uint32_t time32;
uint8_t irq;
uint8_t temp;
atomic time = capturedTime;
radioIrq = FALSE;
irq = readRegister(RF212_IRQ_STATUS);
#ifdef RADIO_DEBUG
// TODO: handle this interrupt
if( irq & RF212_IRQ_TRX_UR )
{
if( call DiagMsg.record() )
{
call DiagMsg.str("assert ur");
call DiagMsg.uint16(call RadioAlarm.getNow());
call DiagMsg.hex8(readRegister(RF212_TRX_STATUS));
call DiagMsg.hex8(readRegister(RF212_TRX_STATE));
call DiagMsg.hex8(irq);
call DiagMsg.uint8(state);
call DiagMsg.uint8(cmd);
call DiagMsg.send();
}
}
#endif
if( irq & RF212_IRQ_PLL_LOCK )
{
if( cmd == CMD_TURNON || cmd == CMD_CHANNEL )
{
ASSERT( state == STATE_TRX_OFF_2_RX_ON );
state = STATE_RX_ON;
cmd = CMD_SIGNAL_DONE;
}
else if( cmd == CMD_TRANSMIT )
{
ASSERT( state == STATE_BUSY_TX_2_RX_ON );
}
else
ASSERT(FALSE);
}
if( irq & RF212_IRQ_RX_START )
{
if( cmd == CMD_CCA )
{
signal RadioCCA.done(FAIL);
cmd = CMD_NONE;
}
if( cmd == CMD_NONE )
{
ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
// the most likely place for busy channel, with no TRX_END interrupt
if( irq == RF212_IRQ_RX_START )
{
temp = readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK;
rssiBusy += temp - (rssiBusy >> 2);
#ifndef RF212_RSSI_ENERGY
call PacketRSSI.set(rxMsg, temp);
}
else
{
call PacketRSSI.clear(rxMsg);
#endif
}
/*
* The timestamp corresponds to the first event which could not
* have been a PLL_LOCK because then cmd != CMD_NONE, so we must
* have received a message (and could also have received the
* TRX_END interrupt in the mean time, but that is fine. Also,
* we could not be after a transmission, because then cmd =
* CMD_TRANSMIT.
*/
if( irq == RF212_IRQ_RX_START ) // just to be cautious
{
time32 = call LocalTime.get();
time32 += (int16_t)(time - RX_SFD_DELAY) - (int16_t)(time32);
call PacketTimeStamp.set(rxMsg, time32);
}
else
call PacketTimeStamp.clear(rxMsg);
cmd = CMD_RECEIVE;
}
else
ASSERT( cmd == CMD_TURNOFF );
}
if( irq & RF212_IRQ_TRX_END )
{
if( cmd == CMD_TRANSMIT )
{
ASSERT( state == STATE_BUSY_TX_2_RX_ON );
state = STATE_RX_ON;
cmd = CMD_NONE;
signal RadioSend.sendDone(SUCCESS);
// TODO: we could have missed a received message
ASSERT( ! (irq & RF212_IRQ_RX_START) );
}
else if( cmd == CMD_RECEIVE )
{
ASSERT( state == STATE_RX_ON || state == STATE_PLL_ON_2_RX_ON );
#ifdef RF212_RSSI_ENERGY
if( irq == RF212_IRQ_TRX_END )
call PacketRSSI.set(rxMsg, readRegister(RF212_PHY_ED_LEVEL));
else
call PacketRSSI.clear(rxMsg);
#endif
if( state == STATE_PLL_ON_2_RX_ON )
{
ASSERT( (readRegister(RF212_TRX_STATUS) & RF212_TRX_STATUS_MASK) == RF212_PLL_ON );
writeRegister(RF212_TRX_STATE, RF212_RX_ON);
state = STATE_RX_ON;
}
else
{
// the most likely place for clear channel (hope to avoid acks)
rssiClear += (readRegister(RF212_PHY_RSSI) & RF212_RSSI_MASK) - (rssiClear >> 2);
}
cmd = CMD_DOWNLOAD;
}
else
ASSERT(FALSE);
}
}
}
default tasklet_async event bool RadioReceive.header(message_t* msg)
{
return TRUE;
}
default tasklet_async event message_t* RadioReceive.receive(message_t* msg)
{
return msg;
}
/*----------------- TASKLET -----------------*/
tasklet_async event void Tasklet.run()
{
if( radioIrq )
serviceRadio();
if( cmd != CMD_NONE )
{
if( cmd == CMD_DOWNLOAD )
downloadMessage();
else if( CMD_TURNOFF <= cmd && cmd <= CMD_TURNON )
changeState();
else if( cmd == CMD_CHANNEL )
changeChannel();
if( cmd == CMD_SIGNAL_DONE )
{
cmd = CMD_NONE;
signal RadioState.done();
}
}
if( cmd == CMD_NONE && state == STATE_RX_ON && ! radioIrq )
signal RadioSend.ready();
if( cmd == CMD_NONE )
call SpiResource.release();
}
}
--- NEW FILE: RF212Packet.h ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#ifndef __RF212PACKET_H__
#define __RF212PACKET_H__
#include <IEEE154Packet.h>
typedef ieee154_header_t rf212packet_header_t;
typedef nx_struct rf212packet_footer_t
{
// the time stamp is not recorded here, time stamped messaged cannot have max length
} rf212packet_footer_t;
typedef struct rf212packet_metadata_t
{
uint8_t flags;
uint8_t lqi;
uint8_t power; // shared between TXPOWER and RSSI
#ifdef LOW_POWER_LISTENING
uint16_t lpl_sleepint;
#endif
uint32_t timestamp;
} rf212packet_metadata_t;
enum rf212packet_metadata_flags
{
RF212PACKET_WAS_ACKED = 0x01, // PacketAcknowledgements
RF212PACKET_TIMESTAMP = 0x02, // PacketTimeStamp
RF212PACKET_TXPOWER = 0x04, // PacketTransmitPower
RF212PACKET_RSSI = 0x08, // PacketRSSI
RF212PACKET_TIMESYNC = 0x10, // PacketTimeSync (update timesync_footer)
RF212PACKET_LPL_SLEEPINT = 0x20, // LowPowerListening
RF212PACKET_CLEAR_METADATA = 0x00,
};
#endif//__RF212PACKET_H__
--- NEW FILE: RF212PacketC.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
configuration RF212PacketC
{
provides
{
interface Packet;
interface AMPacket;
interface PacketAcknowledgements;
interface PacketField<uint8_t> as PacketLinkQuality;
interface PacketField<uint8_t> as PacketTransmitPower;
interface PacketField<uint8_t> as PacketRSSI;
interface PacketField<uint16_t> as PacketSleepInterval;
interface PacketField<uint8_t> as PacketTimeSyncOffset;
interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
}
}
implementation
{
components RF212PacketP, IEEE154PacketC, LocalTimeMicroC, LocalTimeMilliC;
RF212PacketP.IEEE154Packet -> IEEE154PacketC;
RF212PacketP.LocalTimeRadio -> LocalTimeMicroC;
RF212PacketP.LocalTimeMilli -> LocalTimeMilliC;
Packet = RF212PacketP;
AMPacket = IEEE154PacketC;
PacketAcknowledgements = RF212PacketP;
PacketLinkQuality = RF212PacketP.PacketLinkQuality;
PacketTransmitPower = RF212PacketP.PacketTransmitPower;
PacketRSSI = RF212PacketP.PacketRSSI;
PacketSleepInterval = RF212PacketP.PacketSleepInterval;
PacketTimeSyncOffset = RF212PacketP.PacketTimeSyncOffset;
PacketTimeStampRadio = RF212PacketP;
PacketTimeStampMilli = RF212PacketP;
}
--- NEW FILE: RF212PacketP.nc ---
/*
* Copyright (c) 2007, Vanderbilt University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice, the following
* two paragraphs and the author appear in all copies of this software.
*
* IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author: Miklos Maroti
*/
#include <RF212Packet.h>
#include <GenericTimeSyncMessage.h>
#include <RadioConfig.h>
module RF212PacketP
{
provides
{
interface PacketAcknowledgements;
interface Packet;
interface PacketField<uint8_t> as PacketLinkQuality;
interface PacketField<uint8_t> as PacketTransmitPower;
interface PacketField<uint8_t> as PacketRSSI;
interface PacketField<uint16_t> as PacketSleepInterval;
interface PacketField<uint8_t> as PacketTimeSyncOffset;
interface PacketTimeStamp<TRadio, uint32_t> as PacketTimeStampRadio;
interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
}
uses
{
interface IEEE154Packet;
interface LocalTime<TRadio> as LocalTimeRadio;
interface LocalTime<TMilli> as LocalTimeMilli;
}
}
implementation
{
enum
{
PACKET_LENGTH_INCREASE =
sizeof(rf212packet_header_t) - 1 // the 8-bit length field is not counted
+ sizeof(ieee154_footer_t), // the CRC is not stored in memory
};
inline rf212packet_metadata_t* getMeta(message_t* msg)
{
return (rf212packet_metadata_t*)(msg->metadata);
}
/*----------------- Packet -----------------*/
command void Packet.clear(message_t* msg)
{
call IEEE154Packet.createDataFrame(msg);
getMeta(msg)->flags = RF212PACKET_CLEAR_METADATA;
}
inline command void Packet.setPayloadLength(message_t* msg, uint8_t len)
{
call IEEE154Packet.setLength(msg, len + PACKET_LENGTH_INCREASE);
}
inline command uint8_t Packet.payloadLength(message_t* msg)
{
return call IEEE154Packet.getLength(msg) - PACKET_LENGTH_INCREASE;
}
inline command uint8_t Packet.maxPayloadLength()
{
return TOSH_DATA_LENGTH;
}
command void* Packet.getPayload(message_t* msg, uint8_t len)
{
if( len > TOSH_DATA_LENGTH )
return NULL;
return msg->data;
}
/*----------------- PacketAcknowledgements -----------------*/
async command error_t PacketAcknowledgements.requestAck(message_t* msg)
{
call IEEE154Packet.setAckRequired(msg, TRUE);
return SUCCESS;
}
async command error_t PacketAcknowledgements.noAck(message_t* msg)
{
call IEEE154Packet.setAckRequired(msg, FALSE);
return SUCCESS;
}
async command bool PacketAcknowledgements.wasAcked(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_WAS_ACKED;
}
/*----------------- PacketLinkQuality -----------------*/
async command bool PacketLinkQuality.isSet(message_t* msg)
{
return TRUE;
}
async command uint8_t PacketLinkQuality.get(message_t* msg)
{
return getMeta(msg)->lqi;
}
async command void PacketLinkQuality.clear(message_t* msg)
{
}
async command void PacketLinkQuality.set(message_t* msg, uint8_t value)
{
getMeta(msg)->lqi = value;
}
/*----------------- PacketTimeStampRadio -----------------*/
async command bool PacketTimeStampRadio.isValid(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_TIMESTAMP;
}
async command uint32_t PacketTimeStampRadio.timestamp(message_t* msg)
{
return getMeta(msg)->timestamp;
}
async command void PacketTimeStampRadio.clear(message_t* msg)
{
getMeta(msg)->flags &= ~RF212PACKET_TIMESTAMP;
}
async command void PacketTimeStampRadio.set(message_t* msg, uint32_t value)
{
getMeta(msg)->flags |= RF212PACKET_TIMESTAMP;
getMeta(msg)->timestamp = value;
}
/*----------------- PacketTimeStampMilli -----------------*/
async command bool PacketTimeStampMilli.isValid(message_t* msg)
{
return call PacketTimeStampRadio.isValid(msg);
}
async command uint32_t PacketTimeStampMilli.timestamp(message_t* msg)
{
int32_t offset = call PacketTimeStampRadio.timestamp(msg) - call LocalTimeRadio.get();
return (offset >> RADIO_ALARM_MILLI_EXP) + call LocalTimeMilli.get();
}
async command void PacketTimeStampMilli.clear(message_t* msg)
{
call PacketTimeStampRadio.clear(msg);
}
async command void PacketTimeStampMilli.set(message_t* msg, uint32_t value)
{
int32_t offset = (value - call LocalTimeMilli.get()) << RADIO_ALARM_MILLI_EXP;
call PacketTimeStampRadio.set(msg, offset + call LocalTimeRadio.get());
}
/*----------------- PacketTransmitPower -----------------*/
async command bool PacketTransmitPower.isSet(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_TXPOWER;
}
async command uint8_t PacketTransmitPower.get(message_t* msg)
{
return getMeta(msg)->power;
}
async command void PacketTransmitPower.clear(message_t* msg)
{
getMeta(msg)->flags &= ~RF212PACKET_TXPOWER;
}
async command void PacketTransmitPower.set(message_t* msg, uint8_t value)
{
getMeta(msg)->flags &= ~RF212PACKET_RSSI;
getMeta(msg)->flags |= RF212PACKET_TXPOWER;
getMeta(msg)->power = value;
}
/*----------------- PacketRSSI -----------------*/
async command bool PacketRSSI.isSet(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_RSSI;
}
async command uint8_t PacketRSSI.get(message_t* msg)
{
return getMeta(msg)->power;
}
async command void PacketRSSI.clear(message_t* msg)
{
getMeta(msg)->flags &= ~RF212PACKET_RSSI;
}
async command void PacketRSSI.set(message_t* msg, uint8_t value)
{
getMeta(msg)->flags &= ~RF212PACKET_TXPOWER;
getMeta(msg)->flags |= RF212PACKET_RSSI;
getMeta(msg)->power = value;
}
/*----------------- PacketTimeSyncOffset -----------------*/
async command bool PacketTimeSyncOffset.isSet(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_TIMESYNC;
}
async command uint8_t PacketTimeSyncOffset.get(message_t* msg)
{
return call IEEE154Packet.getLength(msg) - PACKET_LENGTH_INCREASE - sizeof(timesync_absolute_t);
}
async command void PacketTimeSyncOffset.clear(message_t* msg)
{
getMeta(msg)->flags &= ~RF212PACKET_TIMESYNC;
}
async command void PacketTimeSyncOffset.set(message_t* msg, uint8_t value)
{
// the value is ignored, the offset always points to the timesync footer at the end of the payload
getMeta(msg)->flags |= RF212PACKET_TIMESYNC;
}
/*----------------- PacketSleepInterval -----------------*/
async command bool PacketSleepInterval.isSet(message_t* msg)
{
return getMeta(msg)->flags & RF212PACKET_LPL_SLEEPINT;
}
async command uint16_t PacketSleepInterval.get(message_t* msg)
{
#ifdef LOW_POWER_LISTENING
return getMeta(msg)->lpl_sleepint;
#else
return 0;
#endif
}
async command void PacketSleepInterval.clear(message_t* msg)
{
getMeta(msg)->flags &= ~RF212PACKET_LPL_SLEEPINT;
}
async command void PacketSleepInterval.set(message_t* msg, uint16_t value)
{
getMeta(msg)->flags |= RF212PACKET_LPL_SLEEPINT;
#ifdef LOW_POWER_LISTENING
getMeta(msg)->lpl_sleepint = value;
#endif
}
}
More information about the Tinyos-2-commits
mailing list