]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/atm128/spi/Atm128SpiP.nc
Merge devel code into the trunk.
[tinyos-2.x.git] / tos / chips / atm128 / spi / Atm128SpiP.nc
diff --git a/tos/chips/atm128/spi/Atm128SpiP.nc b/tos/chips/atm128/spi/Atm128SpiP.nc
new file mode 100644 (file)
index 0000000..43b95f1
--- /dev/null
@@ -0,0 +1,313 @@
+/// $Id$
+
+/*
+ * "Copyright (c) 2005 Stanford 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 STANFORD 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 STANFORD UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * 
+ * STANFORD 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 STANFORD UNIVERSITY
+ * HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
+ * ENHANCEMENTS, OR MODIFICATIONS."
+ *
+ *  Copyright (c) 2004-2005 Crossbow Technology, Inc.
+ *  Copyright (c) 2000-2005 The Regents of the University  of California.
+ *  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 (updated) modification history and the author appear in
+ *  all copies of this source code.
+ *
+ *  Permission is also granted to distribute this software under the
+ *  standard BSD license as contained in the TinyOS distribution.
+ *
+ *  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 INTEL OR ITS
+ *  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.
+ *
+ *
+ */
+
+/**
+ * Primitives for accessing the SPI module on ATmega128
+ * microcontroller.  This module assumes the bus has been reserved and
+ * checks that the bus owner is in fact the person using the bus.
+ * SpiPacket provides an asynchronous send interface where the
+ * transmit data length is equal to the receive data length, while
+ * SpiByte provides an interface for sending a single byte
+ * synchronously. SpiByte allows a component to send a few bytes
+ * in a simple fashion: if more than a handful need to be sent,
+ * SpiPacket should be used.
+ *
+ *
+ * <pre>
+ *  $Id$
+ * </pre>
+ *
+ * @author Philip Levis
+ * @author Joe Polastre
+ * @author Martin Turon <mturon@xbow.com>
+ *
+ */
+
+module Atm128SpiP {
+  provides {
+    interface Init;
+    interface SpiByte;
+    interface SpiPacket;
+    interface Resource[uint8_t id];
+  }
+  uses {
+    interface Atm128Spi as Spi;
+    interface Resource as ResourceArbiter[uint8_t id];
+    interface ArbiterInfo;
+    interface McuPowerState;
+  }
+}
+implementation {
+  uint8_t* txBuffer;
+  uint8_t* rxBuffer;
+  uint16_t len;
+  uint16_t pos;
+  
+  enum {
+    SPI_IDLE,
+    SPI_BUSY,
+    SPI_ATOMIC_SIZE = 10,
+  };
+
+  command error_t Init.init() {
+    return SUCCESS;
+  }
+  bool started;
+  
+  void startSpi() {
+    call Spi.enableSpi(FALSE);
+    atomic {
+      call Spi.initMaster();
+      call Spi.enableInterrupt(FALSE);
+      call Spi.setMasterDoubleSpeed(TRUE);
+      call Spi.setClockPolarity(FALSE);
+      call Spi.setClockPhase(FALSE);
+      call Spi.setClock(0);
+      call Spi.enableSpi(TRUE);
+    }
+    call McuPowerState.update();
+  }
+
+  void stopSpi() {
+    call Spi.enableSpi(FALSE);
+    started = FALSE;
+    atomic {
+      call Spi.sleep();
+    }
+    call McuPowerState.update();
+  }
+  
+  async command void SpiByte.write( uint8_t tx, uint8_t* rx ) {
+    call Spi.write( tx );
+    while ( !( SPSR & 0x80 ) );
+    *rx = call Spi.read();
+  }
+
+
+  /**
+   * This component sends SPI packets in chunks of size SPI_ATOMIC_SIZE
+   * (which is normally 5). The tradeoff is between SPI performance
+   * (throughput) and how much the component limits concurrency in the
+   * rest of the system. Handling an interrupt on each byte is
+   * very expensive: the context saving/register spilling constrains
+   * the rate at which one can write out bytes. A more efficient
+   * approach is to write out a byte and wait for a few cycles until
+   * the byte is written (a tiny spin loop). This leads to greater
+   * throughput, but blocks the system and prevents it from doing
+   * useful work.
+   *
+   * This component takes a middle ground. When asked to transmit X
+   * bytes in a packet, it transmits those X bytes in 10-byte parts.
+   * <tt>sendNextPart()</tt> is responsible for sending one such
+   * part. It transmits bytes with the SpiByte interface, which
+   * disables interrupts and spins on the SPI control register for
+   * completion. On the last byte, however, <tt>sendNextPart</tt>
+   * re-enables SPI interrupts and sends the byte through the
+   * underlying split-phase SPI interface. When this component handles
+   * the SPI transmit completion event (handles the SPI interrupt),
+   * it calls sendNextPart() again. As the SPI interrupt does
+   * not disable interrupts, this allows processing in the rest of the
+   * system to continue.
+   */
+   
+  error_t sendNextPart() {
+    uint16_t end;
+    uint16_t tmpPos;
+    uint8_t* tx;
+    uint8_t* rx;
+    
+    atomic {
+      tx = txBuffer;
+      rx = rxBuffer;
+      tmpPos = pos;
+      end = pos + SPI_ATOMIC_SIZE;
+      end = (end > len)? len:end;
+    }
+
+    for (;tmpPos < (end - 1) ; tmpPos++) {
+      uint8_t val;
+      if (tx != NULL) 
+       call SpiByte.write( tx[tmpPos], &val );
+      else
+       call SpiByte.write( 0, &val );
+    
+      if (rx != NULL) {
+       rx[tmpPos] = val;
+      }
+    }
+
+    // For the last byte, we re-enable interrupts.
+
+   call Spi.enableInterrupt(TRUE);
+   atomic {
+     if (tx != NULL)
+       call Spi.write(tx[tmpPos]);
+     else
+       call Spi.write(0);
+     
+     pos = tmpPos;
+      // The final increment will be in the interrupt
+      // handler.
+    }
+    return SUCCESS;
+  }
+
+  /**
+   * Send bufLen bytes in <tt>writeBuf</tt> and receive bufLen bytes
+   * into <tt>readBuf</tt>. If <tt>readBuf</tt> is NULL, bytes will be
+   * read out of the SPI, but they will be discarded. A byte is read
+   * from the SPI before writing and discarded (to clear any buffered
+   * bytes that might have been left around).
+   *
+   * This command only sets up the state variables and clears the SPI:
+   * <tt>sendNextPart()</tt> does the real work.
+   *
+   */
+  
+  
+  async command error_t SpiPacket.send(uint8_t* writeBuf, 
+                                      uint8_t* readBuf, 
+                                      uint16_t  bufLen) {
+    uint8_t discard;
+    atomic {
+      txBuffer = writeBuf;
+      rxBuffer = readBuf;
+      len = bufLen;
+      pos = 0;
+    }
+    
+    discard = call Spi.read();
+    
+    return sendNextPart();
+  }
+
+ default async event void SpiPacket.sendDone
+      (uint8_t* _txbuffer, uint8_t* _rxbuffer, 
+       uint16_t _length, error_t _success) { }
+
+ async event void Spi.dataReady(uint8_t data) {
+   bool again;
+   
+   atomic {
+     if (rxBuffer != NULL) {
+       rxBuffer[pos] = data;
+       // Increment position
+     }
+     pos++;
+   }
+   call Spi.enableInterrupt(FALSE);
+   
+   atomic {
+     again = (pos < len);
+   }
+   
+   if (again) {
+     sendNextPart();
+   }
+   else {
+     uint8_t* rx;
+     uint8_t* tx;
+     uint16_t  myLen;
+     uint8_t discard;
+     
+     atomic {
+       rx = rxBuffer;
+       tx = txBuffer;
+       myLen = len;
+       rxBuffer = NULL;
+       txBuffer = NULL;
+       len = 0;
+       pos = 0;
+     }
+     discard = call Spi.read();
+
+     signal SpiPacket.sendDone(tx, rx, myLen, SUCCESS);
+   }
+ }
+
+ async command error_t Resource.immediateRequest[ uint8_t id ]() {
+   error_t result = call ResourceArbiter.immediateRequest[ id ]();
+   if ( result == SUCCESS ) {
+     startSpi();
+   }
+   return result;
+ }
+ async command error_t Resource.request[ uint8_t id ]() {
+   atomic {
+     if (!call ArbiterInfo.inUse()) {
+       startSpi();
+     }
+   }
+   return call ResourceArbiter.request[ id ]();
+ }
+
+ async command void Resource.release[ uint8_t id ]() {
+   call ResourceArbiter.release[ id ]();
+   atomic {
+     if (!call ArbiterInfo.inUse()) {
+       stopSpi();
+     }
+   }
+ }
+
+ async command uint8_t Resource.isOwner[uint8_t id]() {
+   return call ResourceArbiter.isOwner[id]();
+ }
+ event void ResourceArbiter.granted[ uint8_t id ]() {
+   signal Resource.granted[ id ]();
+ }
+ default event void Resource.granted[ uint8_t id ]() {}
+}