]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/msp430/usci/Msp430SpiP.nc
i2cNak() -> i2cNack()
[tinyos-2.x.git] / tos / chips / msp430 / usci / Msp430SpiP.nc
index 5a23279c4689d8a1e76a625d40f9216c4e03b3dd..93b942227bb5baf4f5be13d088a5ab0203b2f296 100644 (file)
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+
 /**
- * Spi implementation using a USCI device.
+ * Spi implementation using a USCI device.  When being used as a SPI slave, the
+ * CSn interface should be wired to the chip select driven by the SPI master so
+ * the module can know when a communications session is terminated unexpectedly.
  *
  * TODO: Implement error checking via UCxxSTAT
  *
- * @author R. Steve McKown <smckown@gmail.com>
+ * NOTE: Define NO_REN_ON_SPI to disable PxREN bits when SPI is acquired.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
  */
+
 generic module Msp430SpiP(uint16_t blockSize) {
   provides {
     interface SpiByte;
@@ -44,6 +48,7 @@ generic module Msp430SpiP(uint16_t blockSize) {
   uses {
     interface HplMsp430UsciReg as Registers;
     interface HplMsp430UsciInt as Interrupts;
+    interface GeneralIO as CSn;
     interface HplMsp430GeneralIO as STE;
     interface HplMsp430GeneralIO as SIMO;
     interface HplMsp430GeneralIO as SOMI;
@@ -54,49 +59,30 @@ generic module Msp430SpiP(uint16_t blockSize) {
 }
 implementation {
   enum {
-    BLOCKSIZE_DEFAULT = 64
+    BLOCKSIZE_DEFAULT = 64,
+
+    /* Bit positions in m_pins */
+    PINS_STE = 0,
+    PINS_SOMI,
+    PINS_SIMO,
+    PINS_CLK,
+#ifdef NO_REN_ON_SPI
+    PINS_RENADDR,      /* This gets added to store the PxREN bit */
+#endif
   };
 
-  #define saveBits(pin, pos, dir, out, ren) { \
-               if (call pin.isOutput()) \
-                       dir |= (1 << pos); \
-               if (call pin.getOut()) \
-                       out |= (1 << pos); \
-               if (call pin.isRen()) \
-                       ren |= (1 << pos); \
-       }
-
-  #define restoreBits(pin, pos, dir, out, ren) { \
-               if (ren & (1 << pos)) \
-                       call pin.enableRen(); \
-               else \
-                       call pin.disableRen(); \
-               if (out & (1 << pos)) \
-                       call pin.set(); \
-               else \
-                       call pin.clr(); \
-               if (dir & (1 << pos)) \
-                       call pin.makeOutput(); \
-               else \
-                       call pin.makeInput(); \
-       }
-
-  /* Pin IO configuration storage for later restoration */
-  uint8_t m_dir;
-  uint8_t m_out;
-  uint8_t m_ren;
-
-  uint8_t* m_txBuf;
-  uint8_t* m_rxBuf;
-  uint16_t m_len;
-  uint16_t m_pos;
-
-  inline bool is4pin() /* true if the SPI bus is in 4-pin mode */
+  uint8_t m_pins;
+  norace uint8_t* m_txBuf;
+  norace uint8_t* m_rxBuf;
+  norace uint16_t m_len;
+  norace uint16_t m_pos;
+
+  inline bool is4pin() /* TRUE if the SPI bus is in 4-pin mode */
   {
     return (call Registers.getCtl0(UCMODE_3)) != UCMODE_0;
   }
 
-  inline bool isBusy() /* true if a SPI transaction is in progress */
+  inline bool isBusy() /* TRUE if a SPI transaction is in progress */
   {
     atomic return m_len != 0;
   }
@@ -127,59 +113,44 @@ implementation {
       else
        call Registers.clrStat(UCLISTEN);
 
-      /* Save pin IO configuration */
-      m_dir = m_out = m_ren = 0;
-      if (is4pin())
-       saveBits(STE, 0, m_dir, m_out, m_ren);
-      saveBits(SIMO, 1, m_dir, m_out, m_ren);
-      saveBits(SOMI, 2, m_dir, m_out, m_ren);
-      saveBits(CLK, 3, m_dir, m_out, m_ren);
-
-      /* Configure pins for SPI use */
-      if (is4pin()) {
-#if 0 /* Unsure if REN on STE is a valid configuration */
-       /* Configure STE pin for SPI use */
-       if (config->ren & USCI_REN_STE) {
-         if (config->ren & USCI_REN_STE_PULLUP)
-           call STE.set();
-         else
-           call STE.clr();
-         call STE.enableRen();
-       }
+      /* Configure pins for SPI, saving prior pin states */
+      m_pins = 0;
+#ifdef NO_REN_ON_SPI
+      /* - First save off and disable PxREN bits */
+      if (is4pin() && call STE.isRen()) {
+       m_pins |= (1 << (PINS_STE + PINS_RENADDR));
+       call STE.disableRen();
+      }
+      if (call SOMI.isRen()) {
+       m_pins |= (1 << (PINS_SOMI + PINS_RENADDR));
+       call SOMI.disableRen();
+      }
+      if (call SIMO.isRen()) {
+       m_pins |= (1 << (PINS_SIMO + PINS_RENADDR));
+       call SIMO.disableRen();
+      }
+      if (call CLK.isRen()) {
+       m_pins |= (1 << (PINS_CLK + PINS_RENADDR));
+       call CLK.disableRen();
+      }
 #endif
+      /* - Then save off IOFunc state and enable ModuleFunc */
+      if (is4pin() && call STE.isIOFunc()) {
+       m_pins |= (1 << PINS_STE);
        call STE.selectModuleFunc();
       }
-      call SOMI.makeInput();
-      if (config->ren & USCI_REN_SOMI) {
-       if (config->ren & USCI_REN_SOMI_PULLUP)
-         call SOMI.set();
-       else
-         call SOMI.clr();
-       call SOMI.enableRen();
+      if (call SOMI.isIOFunc()) {
+       m_pins |= (1 << PINS_SOMI);
+       call SOMI.selectModuleFunc();
       }
-      call SOMI.selectModuleFunc();
-#if 0 /* Unsure if REN on SIMO is a valid configuration */
-      /* Configure SIMO pin for SPI use */
-      if (config->ren & USCI_REN_SIMO) {
-       if (config->ren & USCI_REN_SIMO_PULLUP)
-         call SIMO.set();
-       else
-         call SIMO.clr();
-       call SIMO.enableRen();
+      if (call SIMO.isIOFunc()) {
+       m_pins |= (1 << PINS_SIMO);
+       call SIMO.selectModuleFunc();
       }
-#endif
-      call SIMO.selectModuleFunc();
-#if 0 /* Unsure if REN on CLK is a valid configuration */
-      /* Configure CLK pin for SPI use */
-      if (config->ren & USCI_REN_CLK) {
-       if (config->ren & USCI_REN_CLK_PULLUP)
-         call CLK.set();
-       else
-         call CLK.clr();
-       call CLK.enableRen();
+      if (call CLK.isIOFunc()) {
+       m_pins |= (1 << PINS_CLK);
+       call CLK.selectModuleFunc();
       }
-#endif
-      call CLK.selectModuleFunc();
 
       /* Clear interrupts; we'll add them as needed */
       call Registers.clrIeRx();
@@ -190,66 +161,106 @@ implementation {
     }
   }
 
+  task void signalSendDone()
+  {
+    error_t error = (m_pos == m_len) ? SUCCESS : FAIL;
+
+    m_len = 0;
+    atomic signal SpiPacket.sendDone(m_txBuf, m_rxBuf, m_pos, error);
+  }
+
   async command void ResourceConfigure.unconfigure()
   {
     atomic {
       /* Disable the device */
-      call Registers.setCtl1(UCSYNC);
+      call Registers.setCtl1(UCSWRST);
+
+      /* Ensure SpiPacket.sendDone is posted if a trx was in progress */
+      if (m_len)
+       post signalSendDone();
 
       /* Clear interrupts and interrupt flags.  We only used Rx */
       call Registers.clrIeRx();
       call Registers.clrIfgRx();
 
-      /* Restore pins to their preconfigured state */
-#if 0
-      if (is4pin())
-       restoreBits(STE, 0, m_dir, m_out, m_ren);
-      restoreBits(SIMO, 1, m_dir, m_out, m_ren);
-      restoreBits(SOMI, 1, m_dir, m_out, m_ren);
-      restoreBits(CLK, 1, m_dir, m_out, m_ren);
-#endif
-      if (is4pin())
+      /* Restore pins to their pre-configure state */
+      /* - First restore IOFunc states */
+      if (is4pin() && (m_pins & (1 << PINS_STE)))
        call STE.selectIOFunc();
-      call SIMO.selectIOFunc();
-      call SOMI.selectIOFunc();
-      call CLK.selectIOFunc();
+      if (m_pins & (1 << PINS_SIMO))
+       call SIMO.selectIOFunc();
+      if (m_pins & (1 << PINS_SOMI))
+       call SOMI.selectIOFunc();
+      if (m_pins & (1 << PINS_CLK))
+       call CLK.selectIOFunc();
+      /* - Then restore PxREN bits */
+#ifdef NO_REN_ON_SPI
+      if (is4pin() && (m_pins & (1 << (PINS_STE + PINS_RENADDR))))
+       call STE.enableRen();
+      if (m_pins & (1 << (PINS_SIMO + PINS_RENADDR)))
+       call SIMO.enableRen();
+      if (m_pins & (1 << (PINS_SOMI + PINS_RENADDR)))
+       call SOMI.enableRen();
+      if (m_pins & (1 << (PINS_CLK + PINS_RENADDR)))
+       call CLK.enableRen();
+#endif
+    }
+  }
+
+  bool waitOnRx()
+  {
+    for (;;) {
+      if (call Registers.getIfgRx())
+       return TRUE;
+      if (call CSn.get())              /* SPI master has unselected us */
+       return FALSE;
+    }
+  }
+
+  bool waitOnTx()
+  {
+    for (;;) {
+      if (call Registers.getIfgTx())
+       return TRUE;
+      if (call CSn.get())              /* SPI master has unselected us */
+       return FALSE;
     }
   }
 
   async command uint8_t SpiByte.write(uint8_t byte)
   {
-    if (isBusy())
-      return 0;
-    else {
-      while (!call Registers.getIfgTx());
-      call Registers.setTxbuf(byte);
-      while(!call Registers.getIfgRx());
-      return call Registers.getRxbuf();
+    atomic {
+      if (isBusy())
+       return 0;
+      else {
+       waitOnTx();
+       call Registers.setTxbuf(byte);
+       waitOnRx();
+       return call Registers.getRxbuf();
+      }
     }
   }
 
-  void sendData()
+  /* If we are a slave, return FALSE if the master has unasserted CSn. */
+  bool sendData()
   {
-    /* We don't need to check Registers.getIfgTx() (aks UCxxTXIFG), as
-     * sendData() is only called after peripheral init or receipt of the rx()
-     * interrupt.  SPI on msp430 guarantees UCxxTXIFG is asserted in both of
-     * these cases.
-     */
     atomic {
       uint16_t end = m_pos + (blockSize ? blockSize : BLOCKSIZE_DEFAULT);
       uint8_t tmp;
 
       if (end > m_len)
        end = m_len;
-      call Registers.setTxbuf((m_txBuf) ? m_txBuf[m_pos] : 0);
+      waitOnTx(); /* Don't assume that the last tx is done already */
+      call Registers.setTxbuf(m_txBuf ? m_txBuf[m_pos] : 0);
       while (++m_pos < end) {
-       while (!call Registers.getIfgRx());
+       waitOnRx();
+       tmp = call Registers.getRxbuf();
        if (m_rxBuf)
-         m_rxBuf[m_pos - 1] = call Registers.getRxbuf();
-       else
-         tmp = call Registers.getRxbuf();
-       call Registers.setTxbuf((m_txBuf) ? m_txBuf[m_pos] : 0);
+         m_rxBuf[m_pos - 1] = tmp;
+       waitOnTx();
+       call Registers.setTxbuf(m_txBuf ? m_txBuf[m_pos] : 0);
       }
+      return call CSn.get() ? FALSE : TRUE;
     }
   }
 
@@ -259,41 +270,31 @@ implementation {
     if (isBusy() || (!txBuf && !rxBuf) || len == 0)
       return FAIL;
     else {
-      atomic {
-       m_txBuf = txBuf;
-       m_rxBuf = rxBuf;
-       m_len = len;
-       m_pos = 0;
+      m_txBuf = txBuf;
+      m_rxBuf = rxBuf;
+      m_len = len;
+      m_pos = 0;
+      if (sendData())
        call Registers.setIeRx();
-       sendData();
-       return SUCCESS;
-      }
+      else
+       post signalSendDone();
+      return SUCCESS;
     }
   }
 
   async event void Interrupts.tx() {}
 
-  task void signalSendDone()
-  {
-    atomic {
-      signal SpiPacket.sendDone(m_txBuf, m_rxBuf, m_len, SUCCESS);
-      m_len = 0;
-    }
-  }
-
   async event void Interrupts.rx(uint8_t byte)
   {
     if (m_rxBuf)
-      m_rxBuf[m_pos - 1] = call Registers.getRxbuf();
-    else
-      call Registers.getRxbuf();
+      m_rxBuf[m_pos - 1] = byte;
 
-    if (m_pos < m_len)
-      sendData();
-    else {
-      call Registers.clrIeRx();
-      post signalSendDone(); /* Don't signal from ISR context */
+    if (m_pos < m_len) {
+      if (sendData())
+       return;
     }
+    call Registers.clrIeRx();
+    post signalSendDone();
   }
 
   default async event void SpiPacket.sendDone(uint8_t*, uint8_t*, uint16_t,
@@ -301,7 +302,7 @@ implementation {
 
   default async command const msp430_usci_spi_t* Configure.get()
   {
-    const static msp430_usci_spi_t def = { 
+    const static msp430_usci_spi_t def = {
       ctl0: UCSYNC | UCMODE_0 | UCMST, /* 3-pin SPI mode 0, LSB first */
       ctl1: UCSWRST | UCSSEL_3,                /* SPI clock source is SMCLK */
       brx: 10,                 /* SPI clock=SMCLK/10; ~105KHz if SMCLK=1MHz */
@@ -316,5 +317,7 @@ implementation {
   async event void Interrupts.i2cStop() {}
   async event void Interrupts.i2cCal() {}
   async event void Interrupts.brk() {}
-  async event void Interrupts.i2cNak() {}
+  async event void Interrupts.i2cNack() {}
+
+  default async command bool CSn.get() { return FALSE; }
 }