*/
/**
- * 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) {
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;
}
implementation {
enum {
- BLOCKSIZE_DEFAULT = 64
- };
-
- #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(); \
- }
+ BLOCKSIZE_DEFAULT = 64,
- /* Pin IO configuration storage for later restoration */
- uint8_t m_dir;
- uint8_t m_out;
- uint8_t m_ren;
+ /* 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
+ };
- uint8_t* m_txBuf;
- uint8_t* m_rxBuf;
- uint16_t m_len;
- uint16_t m_pos;
+ 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 */
+ 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;
}
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();
}
}
+ 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);
+ 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;
}
}
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,
async event void Interrupts.i2cCal() {}
async event void Interrupts.brk() {}
async event void Interrupts.i2cNak() {}
+
+ default async command bool CSn.get() { return FALSE; }
}