From a4f296e45b6dca5370f596e8be299f40253eb946 Mon Sep 17 00:00:00 2001 From: mmaroti Date: Tue, 10 Mar 2009 21:43:26 +0000 Subject: [PATCH] initial RF212 support --- tos/chips/rf2xx/rf212/RF212.h | 162 ++++ tos/chips/rf2xx/rf212/RF212ActiveMessageC.nc | 148 ++++ tos/chips/rf2xx/rf212/RF212ActiveMessageP.nc | 298 +++++++ tos/chips/rf2xx/rf212/RF212DriverConfig.nc | 71 ++ tos/chips/rf2xx/rf212/RF212DriverLayerC.nc | 78 ++ .../rf2xx/rf212/RF212DriverLayerP-comp.nc | 819 ++++++++++++++++++ tos/chips/rf2xx/rf212/RF212DriverLayerP.nc | 819 ++++++++++++++++++ tos/chips/rf2xx/rf212/RF212Packet.h | 59 ++ tos/chips/rf2xx/rf212/RF212PacketC.nc | 62 ++ tos/chips/rf2xx/rf212/RF212PacketP.nc | 290 +++++++ 10 files changed, 2806 insertions(+) create mode 100644 tos/chips/rf2xx/rf212/RF212.h create mode 100644 tos/chips/rf2xx/rf212/RF212ActiveMessageC.nc create mode 100644 tos/chips/rf2xx/rf212/RF212ActiveMessageP.nc create mode 100644 tos/chips/rf2xx/rf212/RF212DriverConfig.nc create mode 100644 tos/chips/rf2xx/rf212/RF212DriverLayerC.nc create mode 100644 tos/chips/rf2xx/rf212/RF212DriverLayerP-comp.nc create mode 100644 tos/chips/rf2xx/rf212/RF212DriverLayerP.nc create mode 100644 tos/chips/rf2xx/rf212/RF212Packet.h create mode 100644 tos/chips/rf2xx/rf212/RF212PacketC.nc create mode 100644 tos/chips/rf2xx/rf212/RF212PacketP.nc diff --git a/tos/chips/rf2xx/rf212/RF212.h b/tos/chips/rf2xx/rf212/RF212.h new file mode 100644 index 00000000..7afb78f6 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212.h @@ -0,0 +1,162 @@ +/* + * 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__ diff --git a/tos/chips/rf2xx/rf212/RF212ActiveMessageC.nc b/tos/chips/rf2xx/rf212/RF212ActiveMessageC.nc new file mode 100644 index 00000000..6a79f853 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212ActiveMessageC.nc @@ -0,0 +1,148 @@ +/* + * 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 + +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 as PacketLinkQuality; + interface PacketField as PacketTransmitPower; + interface PacketField as PacketRSSI; + + interface PacketTimeStamp as PacketTimeStampRadio; + interface PacketTimeStamp 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; +} diff --git a/tos/chips/rf2xx/rf212/RF212ActiveMessageP.nc b/tos/chips/rf2xx/rf212/RF212ActiveMessageP.nc new file mode 100644 index 00000000..34391dad --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212ActiveMessageP.nc @@ -0,0 +1,298 @@ +/* + * 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 +#include +#include + +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() + { + } +} diff --git a/tos/chips/rf2xx/rf212/RF212DriverConfig.nc b/tos/chips/rf2xx/rf212/RF212DriverConfig.nc new file mode 100644 index 00000000..49d53b35 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212DriverConfig.nc @@ -0,0 +1,71 @@ +/* + * 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); +} diff --git a/tos/chips/rf2xx/rf212/RF212DriverLayerC.nc b/tos/chips/rf2xx/rf212/RF212DriverLayerC.nc new file mode 100644 index 00000000..d847e6fe --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212DriverLayerC.nc @@ -0,0 +1,78 @@ +/* + * 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; +} diff --git a/tos/chips/rf2xx/rf212/RF212DriverLayerP-comp.nc b/tos/chips/rf2xx/rf212/RF212DriverLayerP-comp.nc new file mode 100644 index 00000000..d86ae2af --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212DriverLayerP-comp.nc @@ -0,0 +1,819 @@ +/* + * 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 +#include +#include +#include +#include + +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; + + interface PacketField as PacketLinkQuality; + interface PacketField as PacketTransmitPower; + interface PacketField as PacketRSSI; + interface PacketField as PacketTimeSyncOffset; + + interface PacketTimeStamp; + interface LocalTime; + + 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(); + } +} diff --git a/tos/chips/rf2xx/rf212/RF212DriverLayerP.nc b/tos/chips/rf2xx/rf212/RF212DriverLayerP.nc new file mode 100644 index 00000000..e594bab1 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212DriverLayerP.nc @@ -0,0 +1,819 @@ +/* + * 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 +#include +#include +#include +#include + +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; + + interface PacketField as PacketLinkQuality; + interface PacketField as PacketTransmitPower; + interface PacketField as PacketRSSI; + interface PacketField as PacketTimeSyncOffset; + + interface PacketTimeStamp; + interface LocalTime; + + 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(); + } +} diff --git a/tos/chips/rf2xx/rf212/RF212Packet.h b/tos/chips/rf2xx/rf212/RF212Packet.h new file mode 100644 index 00000000..5b7a68b9 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212Packet.h @@ -0,0 +1,59 @@ +/* + * 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 + +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__ diff --git a/tos/chips/rf2xx/rf212/RF212PacketC.nc b/tos/chips/rf2xx/rf212/RF212PacketC.nc new file mode 100644 index 00000000..68eb9b29 --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212PacketC.nc @@ -0,0 +1,62 @@ +/* + * 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 as PacketLinkQuality; + interface PacketField as PacketTransmitPower; + interface PacketField as PacketRSSI; + interface PacketField as PacketSleepInterval; + interface PacketField as PacketTimeSyncOffset; + + interface PacketTimeStamp as PacketTimeStampRadio; + interface PacketTimeStamp 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; +} diff --git a/tos/chips/rf2xx/rf212/RF212PacketP.nc b/tos/chips/rf2xx/rf212/RF212PacketP.nc new file mode 100644 index 00000000..0fb8ceae --- /dev/null +++ b/tos/chips/rf2xx/rf212/RF212PacketP.nc @@ -0,0 +1,290 @@ +/* + * 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 +#include +#include + +module RF212PacketP +{ + provides + { + interface PacketAcknowledgements; + interface Packet; + interface PacketField as PacketLinkQuality; + interface PacketField as PacketTransmitPower; + interface PacketField as PacketRSSI; + interface PacketField as PacketSleepInterval; + interface PacketField as PacketTimeSyncOffset; + + interface PacketTimeStamp as PacketTimeStampRadio; + interface PacketTimeStamp as PacketTimeStampMilli; + } + + uses + { + interface IEEE154Packet; + + interface LocalTime as LocalTimeRadio; + interface LocalTime 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 + } +} -- 2.39.2