]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
Initial support for hardware acknowledgements and address recognition. Use RF230_HARD...
authormmaroti <mmaroti>
Fri, 4 Dec 2009 23:10:57 +0000 (23:10 +0000)
committermmaroti <mmaroti>
Fri, 4 Dec 2009 23:10:57 +0000 (23:10 +0000)
tos/chips/rf2xx/rf230/README
tos/chips/rf2xx/rf230/RF230DriverHwAckC.nc [new file with mode: 0644]
tos/chips/rf2xx/rf230/RF230DriverHwAckP.nc [new file with mode: 0644]
tos/chips/rf2xx/rf230/RF230RadioC.nc

index 0f4054c58789a55ff11c6fb4ba0d46260c47a428..cb4e9d94dcb876212c207a3f5d3f6b5be9b636bb 100644 (file)
@@ -33,7 +33,7 @@ RF230ActiveMessageC.
 RF230_CRCBYTE_COMMAND:
 
 This is the command used to calculate the CRC for the RF230 chip, since it 
-does not support hardware CRC checking (in rev A)
+does not support hardware CRC checking (in rev A).
 
 RF230_SLOW_SPI:
 
@@ -46,3 +46,11 @@ RF230_RSSI_ENERGY:
 If you define this, then the content of the RF230_PHY_ED_LEVEL is queried 
 instead of the RSSI value for eahc incoming message. This value can be 
 obtained with the PacketRSSI interface.
+
+RF230_HARDWARE_ACK
+
+If you define this, then the radio stack will use hardware acknowledgements
+and address recognition. In particular, you will not be able to snoop on
+onther messages. The load on the CPU is reduced, you should be able to send
+more messages under heavy load. Note, that the PacketRSSI interface will
+return the energy level (and not the rssi value) of received packet.
diff --git a/tos/chips/rf2xx/rf230/RF230DriverHwAckC.nc b/tos/chips/rf2xx/rf230/RF230DriverHwAckC.nc
new file mode 100644 (file)
index 0000000..271d322
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+* Copyright (c) 2009, University of Szeged
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* - Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* - Neither the name of University of Szeged nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+* OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Author: Miklos Maroti
+*/
+
+#include <RadioConfig.h>
+#include <RF230DriverLayer.h>
+
+configuration RF230DriverHwAckC
+{
+       provides
+       {
+               interface RadioState;
+               interface RadioSend;
+               interface RadioReceive;
+               interface RadioCCA;
+               interface RadioPacket;
+
+               interface PacketField<uint8_t> as PacketTransmitPower;
+               interface PacketField<uint8_t> as PacketRSSI;
+               interface PacketField<uint8_t> as PacketTimeSyncOffset;
+               interface PacketField<uint8_t> as PacketLinkQuality;
+
+               interface LocalTime<TRadio> as LocalTimeRadio;
+
+               interface PacketAcknowledgements;
+       }
+
+       uses
+       {
+               interface RF230DriverConfig as Config;
+               interface PacketTimeStamp<TRadio, uint32_t>;
+               interface Ieee154PacketLayer;
+       }
+}
+
+implementation
+{
+       components RF230DriverHwAckP, HplRF230C, BusyWaitMicroC, TaskletC, MainC, RadioAlarmC;
+
+       RadioState = RF230DriverHwAckP;
+       RadioSend = RF230DriverHwAckP;
+       RadioReceive = RF230DriverHwAckP;
+       RadioCCA = RF230DriverHwAckP;
+       RadioPacket = RF230DriverHwAckP;
+
+       LocalTimeRadio = HplRF230C;
+
+       Config = RF230DriverHwAckP;
+
+       PacketTransmitPower = RF230DriverHwAckP.PacketTransmitPower;
+       components new MetadataFlagC() as TransmitPowerFlagC;
+       RF230DriverHwAckP.TransmitPowerFlag -> TransmitPowerFlagC;
+
+       PacketRSSI = RF230DriverHwAckP.PacketRSSI;
+       components new MetadataFlagC() as RSSIFlagC;
+       RF230DriverHwAckP.RSSIFlag -> RSSIFlagC;
+
+       PacketTimeSyncOffset = RF230DriverHwAckP.PacketTimeSyncOffset;
+       components new MetadataFlagC() as TimeSyncFlagC;
+       RF230DriverHwAckP.TimeSyncFlag -> TimeSyncFlagC;
+
+       PacketLinkQuality = RF230DriverHwAckP.PacketLinkQuality;
+       PacketTimeStamp = RF230DriverHwAckP.PacketTimeStamp;
+
+       RF230DriverHwAckP.LocalTime -> HplRF230C;
+
+       RF230DriverHwAckP.RadioAlarm -> RadioAlarmC.RadioAlarm[unique("RadioAlarm")];
+       RadioAlarmC.Alarm -> HplRF230C.Alarm;
+
+       RF230DriverHwAckP.SELN -> HplRF230C.SELN;
+       RF230DriverHwAckP.SpiResource -> HplRF230C.SpiResource;
+       RF230DriverHwAckP.FastSpiByte -> HplRF230C;
+
+       RF230DriverHwAckP.SLP_TR -> HplRF230C.SLP_TR;
+       RF230DriverHwAckP.RSTN -> HplRF230C.RSTN;
+
+       RF230DriverHwAckP.IRQ -> HplRF230C.IRQ;
+       RF230DriverHwAckP.Tasklet -> TaskletC;
+       RF230DriverHwAckP.BusyWait -> BusyWaitMicroC;
+
+#ifdef RADIO_DEBUG
+       components DiagMsgC;
+       RF230DriverHwAckP.DiagMsg -> DiagMsgC;
+#endif
+
+       MainC.SoftwareInit -> RF230DriverHwAckP.SoftwareInit;
+
+       components RealMainP;
+       RealMainP.PlatformInit -> RF230DriverHwAckP.PlatformInit;
+
+       components new MetadataFlagC(), ActiveMessageAddressC;
+       RF230DriverHwAckP.AckReceivedFlag -> MetadataFlagC;
+       RF230DriverHwAckP.ActiveMessageAddress -> ActiveMessageAddressC;
+       PacketAcknowledgements = RF230DriverHwAckP;
+       Ieee154PacketLayer = RF230DriverHwAckP;
+}
diff --git a/tos/chips/rf2xx/rf230/RF230DriverHwAckP.nc b/tos/chips/rf2xx/rf230/RF230DriverHwAckP.nc
new file mode 100644 (file)
index 0000000..2bd762d
--- /dev/null
@@ -0,0 +1,1005 @@
+/*
+* Copyright (c) 2009, University of Szeged
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* - Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* - Neither the name of University of Szeged nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+* OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* Author: Miklos Maroti
+*/
+
+#include <RF230DriverLayer.h>
+#include <Tasklet.h>
+#include <RadioAssert.h>
+#include <TimeSyncMessageLayer.h>
+#include <RadioConfig.h>
+
+module RF230DriverHwAckP
+{
+       provides
+       {
+               interface Init as PlatformInit @exactlyonce();
+               interface Init as SoftwareInit @exactlyonce();
+
+               interface RadioState;
+               interface RadioSend;
+               interface RadioReceive;
+               interface RadioCCA;
+               interface RadioPacket;
+
+               interface PacketField<uint8_t> as PacketTransmitPower;
+               interface PacketField<uint8_t> as PacketRSSI;
+               interface PacketField<uint8_t> as PacketTimeSyncOffset;
+               interface PacketField<uint8_t> as PacketLinkQuality;
+
+               interface PacketAcknowledgements;
+       }
+
+       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 LocalTime<TRadio>;
+
+               interface RF230DriverConfig as Config;
+
+               interface PacketFlag as TransmitPowerFlag;
+               interface PacketFlag as RSSIFlag;
+               interface PacketFlag as TimeSyncFlag;
+
+               interface PacketTimeStamp<TRadio, uint32_t>;
+
+               interface Tasklet;
+               interface RadioAlarm;
+
+               interface PacketFlag as AckReceivedFlag;
+               interface Ieee154PacketLayer;
+               interface ActiveMessageAddress;
+
+#ifdef RADIO_DEBUG
+               interface DiagMsg;
+#endif
+       }
+}
+
+implementation
+{
+       rf230_header_t* getHeader(message_t* msg)
+       {
+               return ((void*)msg) + call Config.headerLength(msg);
+       }
+
+       void* getPayload(message_t* msg)
+       {
+               return ((void*)msg) + call RadioPacket.headerLength(msg);
+       }
+
+       rf230_metadata_t* getMeta(message_t* msg)
+       {
+               return ((void*)msg) + sizeof(message_t) - call RadioPacket.metadataLength(msg);
+       }
+
+/*----------------- 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,
+       };
+
+       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_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
+
+/*----------------- 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),
+               PLL_CALIBRATION_TIME = (uint16_t)(180 * RADIO_ALARM_MICROSEC),
+               CCA_REQUEST_TIME = (uint16_t)(140 * RADIO_ALARM_MICROSEC),
+
+               TX_SFD_DELAY = (uint16_t)((128 + 5*32 + 16) * RADIO_ALARM_MICROSEC),
+               RX_SFD_DELAY = (uint16_t)((16 + 32) * RADIO_ALARM_MICROSEC),
+       };
+
+       tasklet_async event void RadioAlarm.fired()
+       {
+               if( state == STATE_SLEEP_2_TRX_OFF )
+                       state = STATE_TRX_OFF;
+               else if( state == STATE_TRX_OFF_2_RX_ON )
+               {
+                       ASSERT( cmd == CMD_TURNON || cmd == CMD_CHANNEL );
+
+                       state = STATE_RX_ON;
+                       cmd = CMD_SIGNAL_DONE;
+               }
+               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_AACK_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;
+
+               return SUCCESS;
+       }
+
+       command error_t SoftwareInit.init()
+       {
+               // for powering up the radio
+               return call SpiResource.request();
+       }
+
+       void initRadio()
+       {
+               uint16_t temp;
+
+               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_TRX_END );
+               writeRegister(RF230_CCA_THRES, RF230_CCA_THRES_VALUE);
+               writeRegister(RF230_PHY_TX_PWR, RF230_TX_AUTO_CRC_ON | (RF230_DEF_RFPOWER & RF230_TX_PWR_MASK));
+
+               txPower = RF230_DEF_RFPOWER & RF230_TX_PWR_MASK;
+               channel = RF230_DEF_CHANNEL & RF230_CHANNEL_MASK;
+               writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
+
+               writeRegister(RF230_XAH_CTRL, 0);
+               writeRegister(RF230_CSMA_SEED_1, 0);
+
+               temp = call ActiveMessageAddress.amGroup();
+               writeRegister(RF230_PAN_ID_0, temp);
+               writeRegister(RF230_PAN_ID_1, temp >> 8);
+
+               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() && call RadioAlarm.isFree() )
+               {
+                       writeRegister(RF230_PHY_CC_CCA, RF230_CCA_MODE_VALUE | channel);
+
+                       if( state == STATE_RX_ON )
+                       {
+                               call RadioAlarm.wait(PLL_CALIBRATION_TIME);
+                               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() && call RadioAlarm.isFree() )
+               {
+                       uint16_t temp;
+       
+                       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);
+
+                       temp = call ActiveMessageAddress.amAddress();
+                       writeRegister(RF230_SHORT_ADDR_0, temp);
+                       writeRegister(RF230_SHORT_ADDR_1, temp >> 8);
+
+                       call RadioAlarm.wait(PLL_CALIBRATION_TIME);
+                       writeRegister(RF230_TRX_STATE, RF230_RX_AACK_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() { }
+
+       task void changeAddress()
+       {
+               call Tasklet.suspend();
+
+               if( isSpiAcquired() )
+               {
+                       uint16_t temp = call ActiveMessageAddress.amAddress();
+                       writeRegister(RF230_SHORT_ADDR_0, temp);
+                       writeRegister(RF230_SHORT_ADDR_1, temp >> 8);
+               }
+               else
+                       post changeAddress();
+
+               call Tasklet.resume();
+       }
+
+       async event void ActiveMessageAddress.changed()
+       {
+               post changeAddress();
+       }
+
+/*----------------- TRANSMIT -----------------*/
+
+       tasklet_norace message_t* txMsg;
+
+       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) & RF230_TX_PWR_MASK;
+
+               if( length != txPower )
+               {
+                       txPower = length;
+                       writeRegister(RF230_PHY_TX_PWR, RF230_TX_AUTO_CRC_ON | txPower);
+               }
+
+               writeRegister(RF230_TRX_STATE, RF230_TX_ARET_ON);
+
+               // do something useful, just to wait a little
+               time32 = call LocalTime.get();
+               timesync = call PacketTimeSyncOffset.isSet(msg) ? ((void*)msg) + 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_TX_ARET_ON )
+               {
+                       ASSERT( (readRegister(RF230_TRX_STATUS) & RF230_TRX_STATUS_MASK) == RF230_BUSY_RX_AACK );
+
+                       writeRegister(RF230_TRX_STATE, RF230_RX_AACK_ON);
+                       return EBUSY;
+               }
+
+#ifndef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+
+               ASSERT( ! radioIrq );
+
+               call SELN.clr();
+               call FastSpiByte.splitWrite(RF230_CMD_FRAME_WRITE);
+
+               data = getPayload(msg);
+               length = getHeader(msg)->length;
+
+               // length | data[0] ... data[length-3] | automatically generated FCS
+               call FastSpiByte.splitReadWrite(length);
+
+               // the FCS is atomatically generated (2 bytes)
+               length -= 2;
+
+               header = call Config.headerPreloadLength();
+               if( header > length )
+                       header = length;
+
+               length -= header;
+
+               // first upload the header to gain some time
+               do {
+                       call FastSpiByte.splitReadWrite(*(data++));
+               }
+               while( --header != 0 );
+
+#ifdef RF230_SLOW_SPI
+               atomic
+               {
+                       call SLP_TR.set();
+                       time = call RadioAlarm.getNow();
+               }
+               call SLP_TR.clr();
+#endif
+
+               time32 += (int16_t)(time + TX_SFD_DELAY) - (int16_t)(time32);
+
+               if( timesync != 0 )
+                       *(timesync_relative_t*)timesync = (*(timesync_absolute_t*)timesync) - time32;
+
+               while( length-- != 0 )
+                       call FastSpiByte.splitReadWrite(*(data++));
+
+               // 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_AACK_ON);
+
+               if( timesync != 0 )
+                       *(timesync_absolute_t*)timesync = (*(timesync_relative_t*)timesync) + time32;
+
+               call PacketTimeStamp.set(msg, time32);
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(msg)->length;
+
+                       call DiagMsg.chr('t');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(length);
+                       call DiagMsg.hex8s(getPayload(msg), length - 2);
+                       call DiagMsg.send();
+               }
+#endif
+
+               // wait for the TRX_END interrupt
+               txMsg = msg;
+               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_AACK_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;
+               bool crcValid = FALSE;
+
+               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 RadioPacket.maxPayloadLength() + 2 )
+               {
+                       uint8_t read;
+                       uint8_t* data;
+
+                       // initiate the reading
+                       call FastSpiByte.splitWrite(0);
+
+                       data = getPayload(rxMsg);
+                       getHeader(rxMsg)->length = length;
+
+                       // we do not store the CRC field
+                       length -= 2;
+
+                       read = call Config.headerPreloadLength();
+                       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.splitRead());
+
+                               // we should have no other incoming message or buffer underflow
+                               crcValid = ! radioIrq;
+                       }
+               }
+
+               call SELN.set();
+
+               if( crcValid && call PacketTimeStamp.isValid(rxMsg) )
+               {
+                       uint32_t time32 = call PacketTimeStamp.timestamp(rxMsg);
+                       time32 -= RX_SFD_DELAY + (length << (RADIO_ALARM_MILLI_EXP - 5));
+                       call PacketTimeStamp.set(rxMsg, time32);
+               }
+
+#ifdef RADIO_DEBUG_MESSAGES
+               if( call DiagMsg.record() )
+               {
+                       length = getHeader(rxMsg)->length;
+
+                       call DiagMsg.chr('r');
+                       call DiagMsg.uint32(call PacketTimeStamp.isValid(rxMsg) ? call PacketTimeStamp.timestamp(rxMsg) : 0);
+                       call DiagMsg.uint16(call RadioAlarm.getNow());
+                       call DiagMsg.int8(crcValid ? length : -length);
+                       call DiagMsg.hex8s(getPayload(rxMsg), length - 2);
+                       call DiagMsg.int8(call PacketRSSI.isSet(rxMsg) ? call PacketRSSI.get(rxMsg) : -1);
+                       call DiagMsg.uint8(call PacketLinkQuality.isSet(rxMsg) ? call PacketLinkQuality.get(rxMsg) : 0);
+                       call DiagMsg.send();
+               }
+#endif
+
+               state = STATE_RX_ON;
+               cmd = CMD_NONE;
+
+               // signal only if it has passed the CRC check
+               if( crcValid )
+                       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_TRX_END )
+                       {
+                               if( cmd == CMD_TRANSMIT )
+                               {
+                                       ASSERT( state == STATE_BUSY_TX_2_RX_ON );
+
+                                       temp = readRegister(RF230_TRX_STATE) & RF230_TRAC_STATUS_MASK;
+
+                                       if( call Ieee154PacketLayer.getAckRequired(txMsg) )
+                                               call AckReceivedFlag.setValue(txMsg, temp != RF230_TRAC_NO_ACK);
+
+                                       state = STATE_RX_ON;
+                                       cmd = CMD_NONE;
+
+                                       signal RadioSend.sendDone(temp != RF230_TRAC_CHANNEL_ACCESS_FAILURE ? SUCCESS : EBUSY);
+
+                                       // TODO: we could have missed a received message
+                                       ASSERT( ! (irq & RF230_IRQ_RX_START) );
+                               }
+                               else if( cmd == CMD_NONE )
+                               {
+                                       ASSERT( state == STATE_RX_ON );
+
+                                       if( irq == RF230_IRQ_TRX_END )
+                                       {
+                                               call PacketRSSI.set(rxMsg, readRegister(RF230_PHY_ED_LEVEL));
+
+                                               // TODO: compensate for packet transmission time when downloading
+                                               time32 = call LocalTime.get();
+                                               time32 += (int16_t)(time) - (int16_t)(time32);
+                                               call PacketTimeStamp.set(rxMsg, time32);
+                                       }
+                                       else
+                                       {
+                                               call PacketRSSI.clear(rxMsg);
+                                               call PacketTimeStamp.clear(rxMsg);
+                                       }
+
+                                       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();
+       }
+
+/*----------------- RadioPacket -----------------*/
+       
+       async command uint8_t RadioPacket.headerLength(message_t* msg)
+       {
+               return call Config.headerLength(msg) + sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.payloadLength(message_t* msg)
+       {
+               return getHeader(msg)->length - 2;
+       }
+
+       async command void RadioPacket.setPayloadLength(message_t* msg, uint8_t length)
+       {
+               ASSERT( 1 <= length && length <= 125 );
+               ASSERT( call RadioPacket.headerLength(msg) + length + call RadioPacket.metadataLength(msg) <= sizeof(message_t) );
+
+               // we add the length of the CRC, which is automatically generated
+               getHeader(msg)->length = length + 2;
+       }
+
+       async command uint8_t RadioPacket.maxPayloadLength()
+       {
+               ASSERT( call Config.maxPayloadLength() <= 125 );
+
+               return call Config.maxPayloadLength() - sizeof(rf230_header_t);
+       }
+
+       async command uint8_t RadioPacket.metadataLength(message_t* msg)
+       {
+               return call Config.metadataLength(msg) + sizeof(rf230_metadata_t);
+       }
+
+       async command void RadioPacket.clear(message_t* msg)
+       {
+               // all flags are automatically cleared
+       }
+
+/*----------------- PacketTransmitPower -----------------*/
+
+       async command bool PacketTransmitPower.isSet(message_t* msg)
+       {
+               return call TransmitPowerFlag.get(msg);
+       }
+
+       async command uint8_t PacketTransmitPower.get(message_t* msg)
+       {
+               return getMeta(msg)->power;
+       }
+
+       async command void PacketTransmitPower.clear(message_t* msg)
+       {
+               call TransmitPowerFlag.clear(msg);
+       }
+
+       async command void PacketTransmitPower.set(message_t* msg, uint8_t value)
+       {
+               call TransmitPowerFlag.set(msg);
+               getMeta(msg)->power = value;
+       }
+
+/*----------------- PacketRSSI -----------------*/
+
+       async command bool PacketRSSI.isSet(message_t* msg)
+       {
+               return call RSSIFlag.get(msg);
+       }
+
+       async command uint8_t PacketRSSI.get(message_t* msg)
+       {
+               return getMeta(msg)->rssi;
+       }
+
+       async command void PacketRSSI.clear(message_t* msg)
+       {
+               call RSSIFlag.clear(msg);
+       }
+
+       async command void PacketRSSI.set(message_t* msg, uint8_t value)
+       {
+               // just to be safe if the user fails to clear the packet
+               call TransmitPowerFlag.clear(msg);
+
+               call RSSIFlag.set(msg);
+               getMeta(msg)->rssi = value;
+       }
+
+/*----------------- PacketTimeSyncOffset -----------------*/
+
+       async command bool PacketTimeSyncOffset.isSet(message_t* msg)
+       {
+               return call TimeSyncFlag.get(msg);
+       }
+
+       async command uint8_t PacketTimeSyncOffset.get(message_t* msg)
+       {
+               return call RadioPacket.headerLength(msg) + call RadioPacket.payloadLength(msg) - sizeof(timesync_absolute_t);
+       }
+
+       async command void PacketTimeSyncOffset.clear(message_t* msg)
+       {
+               call TimeSyncFlag.clear(msg);
+       }
+
+       async command void PacketTimeSyncOffset.set(message_t* msg, uint8_t value)
+       {
+               // we do not store the value, the time sync field is always the last 4 bytes
+               ASSERT( call PacketTimeSyncOffset.get(msg) == value );
+
+               call TimeSyncFlag.set(msg);
+       }
+
+/*----------------- 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;
+       }
+
+/*----------------- PacketAcknowledgements -----------------*/
+
+       async command error_t PacketAcknowledgements.requestAck(message_t* msg)
+       {
+               call Ieee154PacketLayer.setAckRequired(msg, TRUE);
+
+               return SUCCESS;
+       }
+
+       async command error_t PacketAcknowledgements.noAck(message_t* msg)
+       {
+               call Ieee154PacketLayer.setAckRequired(msg, FALSE);
+
+               return SUCCESS;
+       }
+
+       async command bool PacketAcknowledgements.wasAcked(message_t* msg)
+       {
+               return call AckReceivedFlag.get(msg);
+       }
+}
index dbec5f5a7e30d42858a422c7f7484663d6f22e6e..45270039436df5d613cf531b7546c3f7e809db47 100644 (file)
@@ -158,7 +158,11 @@ implementation
        #warning "*** USING LOW POWER LISTENING LAYER"
        components LowPowerListeningLayerC;
        LowPowerListeningLayerC.Config -> RF230RadioP;
+#ifdef RF230_HARDWARE_ACK
+       LowPowerListeningLayerC.PacketAcknowledgements -> RF230DriverLayerC;
+#else
        LowPowerListeningLayerC.PacketAcknowledgements -> SoftwareAckLayerC;
+#endif
 #else  
        components LowPowerListeningDummyC as LowPowerListeningLayerC;
 #endif
@@ -174,7 +178,11 @@ implementation
 #ifdef PACKET_LINK
        components PacketLinkLayerC;
        PacketLink = PacketLinkLayerC;
+#ifdef RF230_HARDWARE_ACK
+       PacketLinkLayerC.PacketAcknowledgements -> RF230DriverLayerC;
+#else
        PacketLinkLayerC.PacketAcknowledgements -> SoftwareAckLayerC;
+#endif
 #else
        components new DummyLayerC() as PacketLinkLayerC;
 #endif
@@ -213,16 +221,23 @@ implementation
        components RandomCollisionLayerC as CollisionAvoidanceLayerC;
 #endif
        CollisionAvoidanceLayerC.Config -> RF230RadioP;
+#ifdef RF230_HARDWARE_ACK
+       CollisionAvoidanceLayerC.SubSend -> CsmaLayerC;
+       CollisionAvoidanceLayerC.SubReceive -> RF230DriverLayerC;
+#else
        CollisionAvoidanceLayerC.SubSend -> SoftwareAckLayerC;
        CollisionAvoidanceLayerC.SubReceive -> SoftwareAckLayerC;
+#endif
 
 // -------- SoftwareAcknowledgement
 
+#ifndef RF230_HARDWARE_ACK
        components SoftwareAckLayerC;
        SoftwareAckLayerC.Config -> RF230RadioP;
        SoftwareAckLayerC.SubSend -> CsmaLayerC;
        SoftwareAckLayerC.SubReceive -> RF230DriverLayerC;
        PacketAcknowledgements = SoftwareAckLayerC;
+#endif
 
 // -------- Carrier Sense
 
@@ -246,7 +261,13 @@ implementation
 
 // -------- RF230 Driver
 
+#ifdef RF230_HARDWARE_ACK
+       components RF230DriverHwAckC as RF230DriverLayerC;
+       PacketAcknowledgements = RF230DriverLayerC;
+       RF230DriverLayerC.Ieee154PacketLayer -> Ieee154PacketLayerC;
+#else
        components RF230DriverLayerC;
+#endif
        RF230DriverLayerC.Config -> RF230RadioP;
        RF230DriverLayerC.PacketTimeStamp -> TimeStampingLayerC;
        PacketTransmitPower = RF230DriverLayerC.PacketTransmitPower;