]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
USCI I2C support.
authorR. Steve McKown <rsmckown@gmail.com>
Wed, 27 Oct 2010 23:54:17 +0000 (17:54 -0600)
committerR. Steve McKown <rsmckown@gmail.com>
Fri, 29 Oct 2010 20:19:45 +0000 (14:19 -0600)
Current limitations and features:

* Master mode only.
* No multi-master bus configurations.
* Will automatically reset a hung bus, if for example the master reset in the
  middle of a transaction and a slave currently is driving SDA low.
* Repeated starts are supported and can be very time efficient.
* An I2C transactions may be processed with multiple I2CPacket.read() or
  I2CPacket.write() commands, as appropriate.  The first must include I2C_START
  and the last must include I2C_STOP.
* Conditions exist during read where the hardware may clock out an extra byte
  from the slave before stop.  This byte is not passed to the user.

tos/chips/msp430/usci/HplMsp430UsciInt0P.nc
tos/chips/msp430/usci/HplMsp430UsciInt1P.nc
tos/chips/msp430/usci/HplMsp430UsciReg.nc
tos/chips/msp430/usci/HplMsp430UsciRegP.nc
tos/chips/msp430/usci/Msp430I2CB0C.nc [new file with mode: 0644]
tos/chips/msp430/usci/Msp430I2CB1C.nc [new file with mode: 0644]
tos/chips/msp430/usci/Msp430I2CP.nc [new file with mode: 0644]
tos/chips/msp430/usci/Msp430Usci.h

index 9d75d9d9f940bebe5f3d90f27856e1cc760a2253..d802a0bf3b66e2163998866bb63580d8934ff57f 100644 (file)
@@ -106,9 +106,13 @@ implementation
    * modes.
    */
   TOSH_SIGNAL(USCIAB0TX_VECTOR) {
-    if (READ_FLAG(UC0IFG & UC0IE, UCB0RXIE))
-      signal IntB.rx(UCB0RXBUF); /* I2C receive */
-    else if (READ_FLAG(UC0IFG & UC0IE, UCA0TXIFG))
+    if (READ_FLAG(UC0IFG & UC0IE, UCB0RXIE)) {
+      /* I2C receive.  Do not read UCB0RXBUF here, as the code receiving
+       * IntB.rx() may first need to set stop and/or start bits.  The receiver
+       * must read UCB0RXBUF.
+       */
+      signal IntB.rx(0);
+    } else if (READ_FLAG(UC0IFG & UC0IE, UCA0TXIFG))
       signal IntA.tx();
     else if (READ_FLAG(UC0IFG & UC0IE, UCB0TXIFG))
       signal IntB.tx();
index 45eada48e2e7a5972823b945deef99492b28311d..1e18eb70bf9605c83845b19f4e219bb2a557b63b 100644 (file)
@@ -100,7 +100,11 @@ implementation
    */
   TOSH_SIGNAL(USCIAB1TX_VECTOR) {
     if (READ_FLAG(UC1IFG & UC1IE, UCB1RXIE))
-      signal IntB.rx(UCB1RXBUF); /* I2C receive */
+      /* I2C receive.  Do not read UCB1RXBUF here, as the code receiving
+       * IntB.rx() may first need to set stop and/or start bits.  The receiver
+       * must read UCB1RXBUF.
+       */
+      signal IntB.rx(0);
     else if (READ_FLAG(UC1IFG & UC1IE, UCA1TXIFG))
       signal IntA.tx();
     else if (READ_FLAG(UC1IFG & UC1IE, UCB1TXIFG))
index cf49708ada3b8bda03da1acbb913a1f5f93b01d0..b3a876fc8bd1eed4d20a86f2f6bf5670d2458ee8 100644 (file)
@@ -224,28 +224,18 @@ interface HplMsp430UsciReg {
   async command void assignIrrctl(uint8_t value);
 
   /**
-   * Manipulate bits in the UCxxI2COA register.  See the methods that
-   * manipulate UCxxCTL0 for more information.  These methods perform no
-   * operation on providers implementing a USCI_Ax device.
+   * Access to the UCBxI2COA register.
    */
   async command volatile uint8_t* ptrI2Coa();
-  async command uint8_t getI2Coa(uint8_t mask);
-  async command void setI2Coa(uint8_t mask);
-  async command void clrI2Coa(uint8_t mask);
-  //async command void assignI2Coa(uint8_t mask, uint8_t value);
-  async command void assignI2Coa(uint8_t value);
+  async command uint16_t readI2Coa();
+  async command void assignI2Coa(uint16_t addr);
 
   /**
-   * Manipulate bits in the UCxxI2COA register.  See the methods that
-   * manipulate UCxxCTL0 for more information.  These methods perform no
-   * operation on providers implementing a USCI_Ax device.
+   * Access to the UCBxI2SA register.
    */
   async command volatile uint8_t* ptrI2Csa();
-  async command uint8_t getI2Csa(uint8_t mask);
-  async command void setI2Csa(uint8_t mask);
-  async command void clrI2Csa(uint8_t mask);
-  //async command void assignI2Csa(uint8_t mask, uint8_t value);
-  async command void assignI2Csa(uint8_t value);
+  async command uint16_t readI2Csa();
+  async command void assignI2Csa(uint16_t addr);
 
   /**
    * Manipulate bits in the UCxxIE register.  See the methods that manipulate
@@ -265,4 +255,5 @@ interface HplMsp430UsciReg {
   async command bool getIfgRx();
   async command void clrIfgRx();
   async command bool getIfgTx();
+  async command void clrIfgTx();
 }
index 5cd2e6c290cd4471228749249384f26c12fe0b28..32f3de12af105417770420d077ecb9dd1c0ffc95 100644 (file)
@@ -236,8 +236,38 @@ implementation
   RENDER_A(Abctl);
   RENDER_A(Irtctl);
   RENDER_A(Irrctl);
-  RENDER_B(I2Coa);
-  RENDER_B(I2Csa);
+
+  /* RENDER_B(I2Coa); */
+  async command volatile uint8_t* Registers.ptrI2Coa()
+  {
+    return &UCBxI2Coa;
+  }
+
+  async command uint16_t Registers.readI2Coa()
+  {
+    return UCBxI2Coa;
+  }
+
+  async command void Registers.assignI2Coa(uint16_t addr)
+  {
+    UCBxI2Coa = addr;
+  }
+
+  /* RENDER_B(I2Csa); */
+  async command volatile uint8_t* Registers.ptrI2Csa()
+  {
+    return &UCBxI2Csa;
+  }
+
+  async command uint16_t Registers.readI2Csa()
+  {
+    return UCBxI2Csa;
+  }
+
+  async command void Registers.assignI2Csa(uint16_t addr)
+  {
+    UCBxI2Csa = addr;
+  }
 
   /* RENDER(Ie); */
   async command bool Registers.getIeRx()
@@ -285,4 +315,9 @@ implementation
   {
     return READ_FLAG(UCxxIfg, UCxxTXIFG);
   }
+
+  async command void Registers.clrIfgTx()
+  {
+    CLR_FLAG(UCxxIfg, UCxxTXIFG);
+  }
 }
diff --git a/tos/chips/msp430/usci/Msp430I2CB0C.nc b/tos/chips/msp430/usci/Msp430I2CB0C.nc
new file mode 100644 (file)
index 0000000..9b76c0f
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * 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 Technische Universität Berlin 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.
+ */
+
+/**
+ * This configuration provides the interface for using USCI_B0 in its I2C
+ * mode.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#if !defined(__MSP430_HAS_USCI_AB0__)
+#error "Target does not have a USCI_B0 peripheral (I2C)"
+#endif
+
+#include "I2C.h"
+#include "Msp430Usci.h"
+
+generic configuration Msp430I2CB0C() {
+  provides {
+    interface Resource;
+    interface ResourceRequested;
+    /* interface SomethingToSetOwnAddress */
+    /* interface SomethingToDenoteMode */
+    interface I2CPacket<TI2CBasicAddr> as I2CBasicPacket;
+    interface I2CPacket<TI2CExtdAddr> as I2CExtdPacket;
+    interface ArbiterInfo; /* ??? */
+  }
+  uses interface AsyncConfigure<const msp430_usci_i2c_t*> as Configure;
+}
+implementation {
+  enum {
+    CLIENT_ID = unique(MSP430_USCIB0_RESOURCE)
+  };
+
+  components new Msp430I2CP() as I2CP;
+  I2CBasicPacket = I2CP;
+  I2CExtdPacket = I2CP;
+  Configure = I2CP;
+
+  components Msp430UsciB0C as UsciC;
+  Resource = UsciC.Resource[CLIENT_ID];
+  ResourceRequested = UsciC.ResourceRequested[CLIENT_ID];
+  ArbiterInfo = UsciC.ArbiterInfo;
+  I2CP -> UsciC.Registers;
+  I2CP -> UsciC.Interrupts[CLIENT_ID];
+  I2CP -> UsciC.ArbiterInfo;
+  UsciC.ResourceConfigure[CLIENT_ID] -> I2CP;
+
+  components HplMsp430GeneralIOC as IOC;
+  I2CP.SCL -> IOC.UCB0SCL;
+  I2CP.SDA -> IOC.UCB0SDA;
+
+#if 0 /* FIXME: no virtualized alarm for msp430 */
+  comonents new SomeVirtualizedAlarmClientC() as AlarmC;
+  I2CP.Alarm -> AlarmC;
+#endif
+
+  components BusyWaitMicroC;
+  I2CP.BusyWait -> BusyWaitMicroC;
+
+  components Msp430CounterMicroC;
+  I2CP.Counter -> Msp430CounterMicroC;
+}
diff --git a/tos/chips/msp430/usci/Msp430I2CB1C.nc b/tos/chips/msp430/usci/Msp430I2CB1C.nc
new file mode 100644 (file)
index 0000000..47cab8d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * 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 Technische Universität Berlin 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.
+ */
+
+/**
+ * This configuration provides the interface for using USCI_B1 in its I2C
+ * mode.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#if !defined(__MSP430_HAS_USCI_AB1__)
+#error "Target does not have a USCI_B1 peripheral (I2C)"
+#endif
+
+#include "I2C.h"
+#include "Msp430Usci.h"
+
+generic configuration Msp430I2CB1C() {
+  provides {
+    interface Resource;
+    interface ResourceRequested;
+    /* interface SomethingToSetOwnAddress */
+    /* interface SomethingToDenoteMode */
+    interface I2CPacket<TI2CBasicAddr> as I2CBasicPacket;
+    interface I2CPacket<TI2CExtdAddr> as I2CExtdPacket;
+    interface ArbiterInfo; /* ??? */
+  }
+  uses interface AsyncConfigure<const msp430_usci_i2c_t*> as Configure;
+}
+implementation {
+  enum {
+    CLIENT_ID = unique(MSP430_USCIB1_RESOURCE)
+  };
+
+  components new Msp430I2CP() as I2CP;
+  I2CBasicPacket = I2CP;
+  I2CExtdPacket = I2CP;
+  Configure = I2CP;
+
+  components Msp430UsciB1C as UsciC;
+  Resource = UsciC.Resource[CLIENT_ID];
+  ResourceRequested = UsciC.ResourceRequested[CLIENT_ID];
+  ArbiterInfo = UsciC.ArbiterInfo;
+  I2CP -> UsciC.Registers;
+  I2CP -> UsciC.Interrupts[CLIENT_ID];
+  I2CP -> UsciC.ArbiterInfo;
+  UsciC.ResourceConfigure[CLIENT_ID] -> I2CP;
+
+  components HplMsp430GeneralIOC as IOC;
+  I2CP.SCL -> IOC.UCB1SCL;
+  I2CP.SDA -> IOC.UCB1SDA;
+
+#if 0 /* FIXME: no virtualized alarm for msp430 */
+  comonents new SomeVirtualizedAlarmClientC() as AlarmC;
+  I2CP.Alarm -> AlarmC;
+#endif
+
+  components BusyWaitMicroC;
+  I2CP.BusyWait -> BusyWaitMicroC;
+
+  components Msp430CounterMicroC;
+  I2CP.Counter -> Msp430CounterMicroC;
+}
diff --git a/tos/chips/msp430/usci/Msp430I2CP.nc b/tos/chips/msp430/usci/Msp430I2CP.nc
new file mode 100644 (file)
index 0000000..8ce827d
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2008, Titanium Mirror, Inc.
+ * 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 Technische Universität Berlin 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.
+ */
+
+/**
+ * USCI I2C implementation.  Currently supports only single master operation.
+ * Repeated start operations are supported, as are using multiple I2CPacket
+ * read or write commands to satisfy a single I2C read or write transaction.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+generic module Msp430I2CP() {
+  provides {
+    interface I2CPacket<TI2CBasicAddr> as I2CBasicPacket;
+    interface I2CPacket<TI2CExtdAddr> as I2CExtdPacket;
+    interface ResourceConfigure;
+  }
+  uses {
+    interface HplMsp430UsciReg as Registers;
+    interface HplMsp430UsciInt as Interrupts;
+    interface HplMsp430GeneralIO as SDA;
+    interface HplMsp430GeneralIO as SCL;
+    interface AsyncConfigure<const msp430_usci_i2c_t*> as Configure;
+    interface ArbiterInfo;
+    interface BusyWait<TMicro, uint16_t>;
+    interface Counter<TMicro, uint16_t>;
+  }
+}
+implementation {
+  enum {
+    /* Activity timing */
+    SCL_WAIT_TIME = 1024,      /* micros */
+    BUSY_CHECK_TIME = 1024,    /* micros */
+    START_CHECK_TIME = 102,    /* micros */
+    STOP_CHECK_TIME = 102,     /* micros */
+
+    /* Setting new I2C flags is dangerous; manually ensure the bit definitions
+     * below do not overlap with those defined in tos/types/I2C.h.
+     */
+    I2C_EXTENDED = 0x10,
+  };
+
+  i2c_flags_t m_flags;
+  uint8_t* m_buf;
+  uint16_t m_len;
+  uint16_t m_pos;
+
+  inline void setSDA()
+  {
+    call SDA.makeInput();
+  }
+
+  inline void clrSDA()
+  {
+    call SDA.makeOutput();
+  }
+
+  inline void setSCL()
+  {
+    call SCL.makeInput();
+  }
+
+  inline void clrSCL()
+  {
+    call SCL.makeOutput();
+  }
+
+  bool idleBus()
+  {
+    /* Look for bus idle when the I2C pins are in IO mode. */
+    return (call SDA.get() && call SCL.get());
+  }
+
+  bool resetBus()
+  {
+    /* When the I2C pins are in IO mode, verify the I2C bus is idle or attempt
+     * to get the bus into an idle state.  This code is only valid if we are
+     * the only master on the I2C bus and is not suitable for multi-master
+     * setups.
+     *
+     * Return TRUE if the bus was idle or was successfully brought to idle.
+     * Return FALSE if the bus could not be made idle in a reasonable time.
+     */
+    if (idleBus())
+      return TRUE;
+    else {
+      uint16_t i;
+
+      /* Wait a bit if SCL is low.  A save might be clock stretching. */
+      i = call Counter.get();
+      while (call Counter.get() - i <= SCL_WAIT_TIME) {
+       if (!(call SCL.get()))
+         return FALSE;
+      }
+
+      /* If SDA is low, clock SCL in an attempt to release it. */
+      for (i = 0; i < 10 && !call SDA.get(); i++) {
+       clrSCL();
+       call BusyWait.wait(10);
+       setSCL();
+       call BusyWait.wait(10);
+      }
+      if (!idleBus())
+       return FALSE;
+
+      /* Drive a stop condition on the bus to stop any active slaves. */
+      clrSCL();
+      clrSDA();
+      call BusyWait.wait(10);
+      setSCL();
+      call BusyWait.wait(10);
+      setSDA();
+      call BusyWait.wait(10);
+      return idleBus();
+    }
+  }
+
+  bool isConfigured()
+  {
+    return !(call Registers.getCtl1(UCSWRST));
+  }
+
+  /* TRUE if a transaction (we initiated, single master) is in progress. */
+  bool isBusy()
+  {
+    return m_len;
+  }
+
+  /* Wait for an I2C start, if in progress, to complete.  Return FALSE if a
+   * start is pending and did not finish within the allotted time.  Return TRUE
+   * if no start was pending, or it finished within the allotted time.
+   */
+  bool waitStart()
+  {
+    uint16_t t0 = call Counter.get();
+
+    do {
+      if (!(call Registers.getCtl1(UCTXSTT)))
+       return TRUE;
+    } while (call Counter.get() - t0 <= START_CHECK_TIME);
+    return FALSE;
+  }
+
+  /* Wait for an I2C stop, if in progress, to complete.  Return FALSE if a
+   * stop is pending and did not finish within the allotted time.  Return TRUE
+   * if no stop was pending, or it finished within the allotted time.
+   */
+  bool waitStop()
+  {
+    uint16_t t0 = call Counter.get();
+
+    do {
+      if (!(call Registers.getCtl1(UCTXSTP)))
+       return TRUE;
+    } while (call Counter.get() - t0 <= STOP_CHECK_TIME);
+    return FALSE;
+  }
+
+  async command void ResourceConfigure.configure()
+  {
+    atomic {
+      const msp430_usci_i2c_t* config = call Configure.get();
+
+      call Registers.setCtl1(UCSWRST);
+
+      /* Several conditions might find a slave driving the bus at this point.
+       * One such condition is a badly timed PUC of this slave.  Use resetBus()
+       * to attempt rectification of such conditions.
+       */
+      if (!resetBus())
+       return;
+
+      /* Configure USCI registers.  I2C requires UCMODE_3 and UCSYNC */
+      call Registers.assignCtl0(config->ctl0 | UCMODE_3 | UCSYNC);
+      call Registers.assignCtl1(config->ctl1 | UCSWRST);
+      call Registers.assignBr0(config->brx & 0xff);
+      call Registers.assignBr1(config->brx >> 8);
+      call Registers.assignMctl(0);
+      call Registers.assignI2Coa(config->i2coa);
+      call Registers.assignI2Csa(0);
+
+      /* Configure pins for I2C */
+      call SDA.selectModuleFunc();
+      call SCL.selectModuleFunc();
+
+      /* Clear interrupts; we'll add them as needed */
+      call Registers.assignI2Cie(0);
+      call Registers.clrIeRx();
+      call Registers.clrIeTx();
+
+      /* Enable the device */
+      call Registers.clrCtl1(UCSWRST);
+    }
+  }
+
+  void signalDone()
+  {
+    error_t error = (m_pos == m_len) ? SUCCESS : FAIL;
+    uint16_t len = m_len;
+    uint16_t addr;
+
+    /* No more I2C interrupts until the next I2C request arrives */
+    call Registers.assignI2Cie(0);
+    call Registers.clrIeTx();
+    call Registers.clrIeRx();
+
+    /* Wait for stop to finish, if it is in progress. */
+    if (!waitStop())
+      error = FAIL;
+
+    m_len = 0;
+    addr = call Registers.readI2Csa();
+    switch ((call Registers.getCtl0(UCSLA10) ? 2 : 0) +        /* extended addr */
+           (call Registers.getCtl1(UCTR) ? 1 : 0)) {   /* write */
+      case 0:
+       atomic signal I2CBasicPacket.readDone(error, addr, len, m_buf);
+       break;
+      case 1:
+       atomic signal I2CBasicPacket.writeDone(error, addr, len, m_buf);
+       break;
+      case 2:
+       atomic signal I2CExtdPacket.readDone(error, addr, len, m_buf);
+       break;
+      case 3:
+       atomic signal I2CExtdPacket.writeDone(error, addr, len, m_buf);
+       break;
+    }
+  }
+
+  async command void ResourceConfigure.unconfigure()
+  {
+    atomic {
+      /* Signal done if a pending operation */
+      if (isBusy())
+       signalDone();
+
+      /* Disable the device */
+      call Registers.setCtl1(UCSWRST);
+
+      /* Clear interrupts and interrupt flags. */
+      call Registers.assignI2Cie(0);
+      call Registers.clrIeRx();
+      call Registers.clrIfgRx();
+
+      /* Restore I2C pins to IO function */
+      call SDA.selectIOFunc();
+      call SCL.selectIOFunc();
+    }
+  }
+
+  error_t read(i2c_flags_t flags, uint16_t addr, uint8_t len, uint8_t* rxBuf)
+  {
+    /* From the TI family guide SDAU144D, page 17-17:
+     * "Data is received from the slave as long as UCTXSTP or UCTXSTT is not
+     * set.  If UCBxRXBUF is not read the master holds the bus during reception
+     * of the last data bit and until the UCBxRXBUF is read."
+     *
+     * Since the I2C bus can only be suspended during read by *not* reading a
+     * byte out of RXBUF, the only reasonable command after a read() that
+     * did not include I2C_STOP is a read that does not include I2C_START.  A
+     * read that includes I2C_START, or a write (which by definition must
+     * include I2C_START) will effectively create the situation where the
+     * prior read caused the slave to send one more byte than was delivered via
+     * readDone() to the user code.  In many cases, this won't be a problem, as
+     * triggering the slave to read an extra byte will do no harm.  But this
+     * may not always be the case, if for example the slave maintains state
+     * that is changed upon the read of that final byte the user code never
+     * sees.
+     */
+    if (!isConfigured() || isBusy() || len == 0 || !rxBuf)
+      return FAIL;
+
+    m_flags = flags;
+    m_len = len;
+    m_buf = rxBuf;
+    m_pos = 0;
+
+#if 0 /* FIXME: No virtualized alarm for msp430 */
+    /* We require an alarm because we aren't looking at UCALIFG, arbitration
+     * lost.  If this were to happen, the state machine hangs.
+     */
+    call Alarm.start(I2C_TIMEOUT);
+#endif
+    if (m_flags & I2C_EXTENDED)
+      call Registers.setCtl0(UCSLA10);
+    else
+      call Registers.clrCtl0(UCSLA10);
+    call Registers.assignI2Csa(addr);
+    call Registers.clrCtl1(UCTR);
+    if (m_flags & I2C_START) {
+      call Registers.clrIfgRx(); /* see above re: suspending reads */
+      call Registers.setCtl1(UCTXSTT);
+    }
+    if (m_len == 1 && (m_flags & I2C_STOP)) {
+      /* UCTXSTP must assert before hw finishes clocking in the last byte.
+       * FIXME: in reading a single I2C trx using multiple I2C...read() calls,
+       * too much time spent by the user processing the readDone() can cause
+       * the I2C hardware to clock an extra byte's worth of clocks, thereby
+       * issuing 1 byte more of read with the slave, before the stop event.
+       */
+      waitStart();
+      call Registers.setCtl1(UCTXSTP);
+    }
+    call Registers.setI2Cie(UCNACKIE);
+    call Registers.setIeRx();
+    return SUCCESS;
+  }
+
+  async command error_t I2CBasicPacket.read(i2c_flags_t flags, uint16_t addr,
+      uint8_t length, uint8_t* data)
+  {
+    atomic return read(flags & ~I2C_EXTENDED, addr, length, data);
+  }
+
+  async command error_t I2CExtdPacket.read(i2c_flags_t flags, uint16_t addr,
+      uint8_t length, uint8_t* data)
+  {
+    atomic return read(flags | I2C_EXTENDED, addr, length, data);
+  }
+
+  error_t write(i2c_flags_t flags, uint16_t addr, uint8_t len, uint8_t* txBuf)
+  {
+    if (!isConfigured() || isBusy() || len == 0 || !txBuf)
+      return FAIL;
+
+    m_flags = flags;
+    m_len = len;
+    m_buf = txBuf;
+    m_pos = 0;
+
+#if 0 /* FIXME: No virtualized alarm for msp430 */
+    /* We require an alarm because we aren't looking at UCALIFG, arbitration
+     * lost.  If this were to happen, the state machine hangs.
+     */
+    call Alarm.start(I2C_TIMEOUT);
+#endif
+    if (m_flags & I2C_EXTENDED)
+      call Registers.setCtl0(UCSLA10);
+    else
+      call Registers.clrCtl0(UCSLA10);
+    call Registers.assignI2Csa(addr);
+    call Registers.setCtl1((m_flags & I2C_START) ? UCTR + UCTXSTT : UCTR);
+    call Registers.setI2Cie(UCNACKIE);
+    call Registers.setIeTx();
+    return SUCCESS;
+  }
+
+  async command error_t I2CBasicPacket.write(i2c_flags_t flags, uint16_t addr,
+      uint8_t length, uint8_t* data)
+  {
+    atomic return write(flags & ~I2C_EXTENDED, addr, length, data);
+  }
+
+  async command error_t I2CExtdPacket.write(i2c_flags_t flags, uint16_t addr,
+      uint8_t length, uint8_t* data)
+  {
+    atomic return write(flags | I2C_EXTENDED, addr, length, data);
+  }
+
+#if 0 /* FIXME: need a virtualized alarm for msp430 */
+  event void Alarm.fired()
+  {
+    if (m_flags & I2C_STOP) {
+      call Registers.setCtl1(UCTXSTP);
+      call Registers.clrIfgTx();
+    }
+    signalDone();
+  }
+#endif
+
+  async event void Interrupts.tx()
+  {
+    if (m_pos == m_len) {
+      if (m_flags & I2C_STOP) {
+       call Registers.setCtl1(UCTXSTP);
+       call Registers.clrIfgTx();
+      }
+      signalDone();
+    } else
+      call Registers.setTxbuf(m_buf[m_pos++]);
+  }
+
+  async event void Interrupts.rx(uint8_t nobyte)
+  {
+    if (m_len - m_pos == 2 && (m_flags & I2C_STOP)) {
+      /* As soon as we read RXBUF, the hw will begin clocking in the next byte.
+       * To guarantee that a slow uC can still set UCTXSTP before the last
+       * byte is fully clocked, we set it before the second to last byte is
+       * read from RXBUF, when the hw has I2C communications suspended.
+       */
+      call Registers.setCtl1(UCTXSTP);
+    }
+    m_buf[m_pos++] = call Registers.getRxbuf();
+    if (m_pos == m_len)
+      signalDone();
+  }
+
+  async event void Interrupts.i2cNack()
+  {
+    call Registers.setCtl1(UCTXSTP);
+    call Registers.clrStat(UCNACKIFG);
+    signalDone();
+  }
+
+  default async event void I2CBasicPacket.readDone(error_t error, uint16_t addr,
+      uint8_t length, uint8_t* data) {}
+  default async event void I2CBasicPacket.writeDone(error_t error,
+      uint16_t addr, uint8_t length, uint8_t* data) {}
+  default async event void I2CExtdPacket.readDone(error_t error, uint16_t addr,
+      uint8_t length, uint8_t* data) {}
+  default async event void I2CExtdPacket.writeDone(error_t error, uint16_t addr,
+      uint8_t length, uint8_t* data) {}
+
+  default async command const msp430_usci_i2c_t* Configure.get()
+  {
+    const static msp430_usci_i2c_t def = {
+      ctl0: UCSYNC | UCMODE_3 | UCMST, /* I2C master */
+      ctl1: UCSWRST | UCSSEL_3,                /* I2C clock source is SMCLK */
+      brx: 10,                 /* I2C clock=SMCLK/10; ~95KHz if SMCLK=2^20Hz */
+      ren: USCI_REN_NONE
+    };
+
+    return &def;
+  }
+
+  async event void Interrupts.i2cStart() {}
+  async event void Interrupts.i2cStop() {}
+  async event void Interrupts.i2cCal() {}
+  async event void Interrupts.brk() {}
+  async event void Counter.overflow() {}
+}
index 31ba96ae37ece6023b3c55bd7208a5d9077d5904..575889d1bd5b85ebd9ad7a48d370ed40b3d90444 100644 (file)
@@ -130,10 +130,7 @@ typedef struct {
   uint8_t ctl0;
   uint8_t ctl1;
   uint16_t brx;
-  uint8_t i2cie;
   uint8_t i2coa;
-  uint8_t i2csa;
-  bool uclisten;
   msp430_ren_t ren;
 } msp430_usci_i2c_t;