]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/atm128/i2c/Atm128I2CMasterPacketP.nc
Merge devel code into the trunk.
[tinyos-2.x.git] / tos / chips / atm128 / i2c / Atm128I2CMasterPacketP.nc
diff --git a/tos/chips/atm128/i2c/Atm128I2CMasterPacketP.nc b/tos/chips/atm128/i2c/Atm128I2CMasterPacketP.nc
new file mode 100644 (file)
index 0000000..347853c
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2006 Stanford University.
+ * 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 the Stanford University 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 STANFORD
+ * UNIVERSITY 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.
+ */
+
+#include "Atm128I2C.h"
+
+/**
+ * This driver implements an interupt driven I2C Master controller 
+ * Hardware Abstraction Layer (HAL) to the ATmega128 
+ * two-wire-interface (TWI) hardware subsystem.
+ *
+ * @author Philip Levis
+ *
+ * @version $Id$
+ */
+
+generic module Atm128I2CMasterPacketP() {
+  provides interface AsyncStdControl;
+  provides interface I2CPacket<TI2CBasicAddr>;
+  provides interface Atm128I2C;
+  
+  uses interface HplAtm128I2CBus as I2C;
+  uses interface Leds as ReadDebugLeds;
+  uses interface Leds as WriteDebugLeds;
+}
+implementation {
+
+  enum {
+    I2C_OFF          = 0,
+    I2C_IDLE         = 1,
+    I2C_BUSY         = 2,      
+    I2C_ADDR         = 3,
+    I2C_DATA         = 4,
+    I2C_STARTING     = 5,
+    I2C_STOPPING     = 6,
+  } atm128_i2c_state_t;
+
+  uint8_t state = I2C_OFF;
+  i2c_flags_t packetFlags; 
+  uint8_t* packetPtr;
+  uint8_t packetLen;
+  uint8_t index;
+  uint16_t packetAddr;
+  bool reading = FALSE;
+
+  void i2c_abort(error_t err) {
+    atomic {
+      // Cycle the I2C
+      call I2C.readCurrent();
+      call I2C.enableInterrupt(FALSE);
+      call I2C.enable(FALSE);
+      call I2C.sendCommand();
+      call I2C.readCurrent();
+      call I2C.enable(TRUE);
+      call I2C.sendCommand();
+      state = I2C_IDLE;
+      if (reading) {
+       signal I2CPacket.readDone(err, packetAddr, packetLen, packetPtr);
+      }
+      else {
+       signal I2CPacket.writeDone(err, packetAddr, packetLen, packetPtr);
+      }
+    }
+  }
+  
+  async command error_t AsyncStdControl.start() {
+    atomic {
+      if (state == I2C_OFF) {
+       call I2C.init(FALSE);
+       call I2C.readCurrent();
+       call I2C.enable(TRUE);
+       call I2C.enableInterrupt(FALSE);
+       call I2C.sendCommand();
+       state = I2C_IDLE;
+       return SUCCESS;
+      }
+      else {
+       return FAIL;
+      }
+    }
+  }
+
+  async command error_t AsyncStdControl.stop() {
+    atomic {
+      if (state == I2C_IDLE) {
+       call I2C.readCurrent();
+       call I2C.enable(FALSE);
+       call I2C.enableInterrupt(FALSE);
+       call I2C.setInterruptPending(FALSE);
+       call I2C.off();
+       state = I2C_OFF;
+       return SUCCESS;
+      }
+      else {
+       return FAIL;
+      }
+    }
+  }
+
+  async command error_t I2CPacket.read(i2c_flags_t flags, uint16_t addr, uint8_t len, uint8_t* data) {
+    atomic {
+      if (state == I2C_IDLE) {
+       state = I2C_BUSY;
+      }
+      else if (state == I2C_OFF) {
+       return EOFF;
+      }
+      else {
+       return EBUSY;
+      }
+    }
+    /* This follows the procedure described on page 209 of the atmega128L
+     * data sheet. It is synchronous (does not handle interrupts).*/
+    atomic {
+      packetAddr = addr;
+      packetPtr = data;
+      packetLen = len;
+      packetFlags = flags;
+      index = 0;
+      reading = TRUE;
+    }
+    /* Clear interrupt pending, send the I2C start command and abort
+       if we're not in the start state.*/
+    call I2C.readCurrent();
+    atomic {
+      call I2C.enableInterrupt(TRUE);
+      call I2C.setInterruptPending(TRUE);
+      call I2C.enableAck(FALSE);
+      
+      if (flags & I2C_START) {
+        call I2C.setStart(TRUE);
+        state = I2C_STARTING;
+      }
+      else if (len > 1 || (len > 0 && flags & I2C_ACK_END)) {
+        call I2C.enableAck(TRUE);
+        state = I2C_DATA;
+      }
+      else if (len == 1) { // length is 1
+        state = I2C_DATA;
+      }
+      else if (flags & I2C_STOP) {
+        state = I2C_STOPPING;
+        call I2C.enableAck(FALSE);
+        call I2C.setStop(TRUE);
+      }
+      call I2C.sendCommand();
+    }
+    return SUCCESS;
+  }
+
+  async command error_t I2CPacket.write(i2c_flags_t flags, uint16_t addr, uint8_t len, uint8_t* data) {
+    atomic {
+      if (state == I2C_IDLE) {
+       state = I2C_BUSY;
+      }
+      else if (state == I2C_OFF) {
+       return EOFF;
+      }
+      else {
+       return EBUSY;
+      }
+    }
+    /* This follows the procedure described on page 209 of the atmega128L
+     * data sheet. It is synchronous (does not handle interrupts).*/
+    atomic {
+      packetAddr = addr;
+      packetPtr = data;
+      packetLen = len;
+      packetFlags = flags;
+      index = 0;
+      reading = FALSE;
+    }
+    call I2C.readCurrent();
+    atomic {
+      call I2C.setInterruptPending(TRUE);
+      call I2C.enableAck(TRUE);
+      call I2C.enableInterrupt(TRUE);
+      
+      if (flags & I2C_START) {
+        call I2C.setStart(TRUE);
+       //      call WriteDebugLeds.led0On();
+        state = I2C_STARTING;
+      }
+      else if (len > 0) {
+        state = I2C_DATA;
+       call I2C.write(data[index]);
+      }
+      else if (flags & I2C_STOP) {
+        state = I2C_STOPPING;
+        call I2C.enableAck(FALSE);
+        call I2C.setStop(TRUE);
+      }
+      else { // A 0-length packet with no start and no stop....
+       state = I2C_IDLE;
+       return FAIL;
+      }
+      call I2C.sendCommand();
+    }
+    return SUCCESS;
+  }
+  
+  /**
+   * A command has been sent over the I2C.
+   * The diversity of I2C options and modes means that there are a
+   * plethora of cases to consider. To simplify reading the code,
+   * they're described here and the corresponding statements
+   * are only labelled with identifying comments.
+   *
+   * When reading:
+   *  R1) A start condition has been requested. This requires 
+   *      sending the start signal. When the interrupt comes in,
+   *      send the first byte of the packet. This driver
+   *      detects this condition by the START flag being set. 
+   *      A successful send of the start clears the local copy of
+   *      the flag. The driver does not distinguish between start
+   *      and repeated start.
+   *  R2) Sending the address byte with the read bit set.
+   *  R3) Sending the first byte of a two-byte address with the
+   *      read bit set. 
+   *  R4) Sending the second byte of a two-byte address.
+   *  R5) Reading any byte except the last byte of a packet.
+   *  R6) Reading the last byte of the packet, with ACK_END requested.
+   *  R7) Reading the last byte of the packet, with ACK_END cleared.
+   *  R8) Sending a stop condition.
+   */
+  async event void I2C.commandComplete() {
+    call I2C.readCurrent();
+    atomic {
+      if (state == I2C_DATA) {
+       if (reading == TRUE) {
+         if (index < packetLen) {
+           packetPtr[index] = call I2C.read();
+           if (index == packetLen - 1 &&
+                !(packetFlags & I2C_ACK_END)) { 
+              call I2C.enableAck(FALSE);
+            }
+          }
+         else {
+           call I2C.enableInterrupt(FALSE);
+           if (packetFlags & I2C_STOP) {
+             packetFlags &= ~I2C_STOP;
+             call I2C.setStop(TRUE);
+             call I2C.status();
+           }
+           else {
+             call I2C.setInterruptPending(FALSE);
+           }
+           call I2C.sendCommand();
+           state = I2C_IDLE;
+           signal I2CPacket.readDone(SUCCESS, packetAddr, packetLen, packetPtr);
+           return;
+         }
+         index++;
+         call I2C.sendCommand();
+         return;
+        }
+        else { // Writing
+         if (index < packetLen) {
+           call I2C.write(packetPtr[index]);
+           index++;
+           call I2C.sendCommand();
+         }
+         else {
+           call I2C.enableInterrupt(FALSE);
+           if (packetFlags & I2C_STOP) {
+             packetFlags &= ~I2C_STOP;
+             call I2C.setStop(TRUE);
+             call WriteDebugLeds.led1On();
+           }
+           else {
+             call I2C.setInterruptPending(FALSE);
+           }
+           call I2C.sendCommand();
+           state = I2C_IDLE;
+           call WriteDebugLeds.led2On();
+           signal I2CPacket.writeDone(SUCCESS, packetAddr, packetLen, packetPtr);
+           return;
+         }
+       }
+      }
+      else if (state == I2C_STARTING) {
+       packetFlags &= ~I2C_START;
+       call I2C.setStart(FALSE);
+       if (call I2C.status() != ATM128_I2C_START &&
+           call I2C.status() != ATM128_I2C_RSTART) {
+         if (reading) {
+           //call ReadDebugLeds.set(call I2C.status() >> 4);
+         }
+         //call ReadDebugLeds.led2On();
+         i2c_abort(FAIL);
+         return;
+       }
+       state = I2C_ADDR;
+       call I2C.enableAck(TRUE);
+      }
+      if (state == I2C_ADDR) {
+       if (reading == TRUE) {
+         call I2C.write((packetAddr & 0xff) | ATM128_I2C_SLA_READ);
+       }
+       else
+         call I2C.write((packetAddr & 0xff) | ATM128_I2C_SLA_WRITE);
+       state = I2C_DATA;
+       call I2C.sendCommand();
+      }
+    }
+  }
+
+  async command void Atm128I2C.stop() {
+    atomic {
+      call I2C.readCurrent();
+      call I2C.enableInterrupt(FALSE);
+      call I2C.setStop(TRUE);
+      call I2C.setInterruptPending(TRUE);
+      call I2C.sendCommand();
+    }
+  }
+}