* (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() {
+
+generic module Msp430SpiP(uint16_t blockSize) {
provides {
interface SpiByte;
interface SpiPacket;
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;
- interface HplMsp430GeneralIO as SCL;
+ interface HplMsp430GeneralIO as CLK;
interface AsyncConfigure<const msp430_usci_spi_t*> as Configure;
- interface Counter<T32khz,uint16_6>
interface ArbiterInfo;
}
}
implementation {
- #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(); \
- }
-
- uint8_t dir; /* Pin state storage to allow for proper unconfiguration */
- uint8_t out;
- uint8_t ren;
- uint8_t* sobuf; /* Original buffer ptr from UartStream.send() */
- uint8_t solen; /* Original buffer len from UartStream.send() */
- uint8_t* sbuf; /* Position of next char to send */
- uint8_t slen; /* Len of chars in sbuf to send */
- bool rxie; /* Set if rxie has been enabled to UartStream.receive() */
- uint8_t* robuf; /* Original receive buffer */
- uint8_t rolen; /* Original (maximum) receive len */
- uint8_t* rbuf; /* Position of next byte in which to receive a char */
- uint8_t rlen; /* Remaining length in receive buffer */
+ enum {
+ 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
+ };
+
+ 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 */
+ {
+ atomic return m_len != 0;
+ }
async command void ResourceConfigure.configure()
{
call Registers.setCtl1(UCSWRST);
- /* Force 3-pin SPI if config says I2C */
+ /* UCMODE_3 is invalid for SPI. Presume the configuration data
+ * are wrong and force 3-pin SPI as a minimially safe alternative.
+ */
ctl0 = config->ctl0 | UCSYNC;
if ((ctl0 & UCMODE_3) == UCMODE_3)
ctl0 &= ~(UCMODE_3);
call Registers.assignCtl1(config->ctl1 | UCSWRST);
call Registers.assignBr0(config->brx & 0xff);
call Registers.assignBr1(config->brx >> 8);
- call Registers.assignMctl(config->mctl); /* ??? */
- call Registers.clrStat(UCLISTEN);
-
- /* Save pin IO states */
- dir = out = ren = 0;
- saveBits(RXD, 0, dir, out, ren);
- saveBits(TXD, 1, dir, out, ren);
-
- /* Configure RX pin for UART use */
- call RXD.makeInput();
- if (config->ren & USCI_REN_RX) {
- if (config->ren & USCI_REN_RX_PULLUP)
- call RXD.set();
- else
- call RXD.clr();
- call RXD.enableRen();
- }
- call RXD.selectModuleFunc();
+ call Registers.assignMctl(0);
+ if (config->uclisten)
+ call Registers.setStat(UCLISTEN);
+ else
+ call Registers.clrStat(UCLISTEN);
-#if 0 /* pull-ups don't make sense on TXD, since it doesn't appear that
- * enabling an open-drain emulation mode via USCI is possible.
- */
-
- /* Configure TX pin for UART use */
- if (config->ren & USCI_REN_TX) {
- if (config->ren & USCI_REN_TX_PULLUP)
- call TXD.set();
- else
- call TXD.clr();
- call TXD.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();
}
- call TXD.selectModuleFunc();
#endif
+ /* - Then save off IOFunc state and enable ModuleFunc */
+ if (is4pin() && call STE.isIOFunc()) {
+ m_pins |= (1 << PINS_STE);
+ call STE.selectModuleFunc();
+ }
+ if (call SOMI.isIOFunc()) {
+ m_pins |= (1 << PINS_SOMI);
+ call SOMI.selectModuleFunc();
+ }
+ if (call SIMO.isIOFunc()) {
+ m_pins |= (1 << PINS_SIMO);
+ call SIMO.selectModuleFunc();
+ }
+ if (call CLK.isIOFunc()) {
+ m_pins |= (1 << PINS_CLK);
+ 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 */
+ /* Clear interrupts and interrupt flags. We only used Rx */
call Registers.clrIeRx();
- call Registers.clrIeTx();
call Registers.clrIfgRx();
- call Registers.clrIfgTx();
- /* Restore pins to their preconfigured state */
-#if 0
- restoreBits(RXD, 0, dir, out, ren);
- restoreBits(TXD, 0, dir, out, ren);
+ /* Restore pins to their pre-configure state */
+ /* - First restore IOFunc states */
+ if (is4pin() && (m_pins & (1 << PINS_STE)))
+ call STE.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
- call RXD.selectIOFunc();
- call TXD.selectIOFunc();
}
}
- async command error_t UartByte.send(uint8_t byte)
+ bool waitOnRx()
{
- /* FIXME: race with UartStream.send() */
- atomic {
- if (sobuf)
- return FAIL;
- while (call Registers.getStat(UCBUSY));
- call Registers.setTxbuf(byte);
- return SUCCESS;
+ for (;;) {
+ if (call Registers.getIfgRx())
+ return TRUE;
+ if (call CSn.get()) /* SPI master has unselected us */
+ return FALSE;
}
}
- async command error_t UartStream.send(uint8_t* buf, uint16_t len)
- {
- if (sobuf || !buf || !len)
- return FAIL;
- sobuf = buf;
- solen = len;
- call Registers.setIeTx();
- call Registers.setTxbuf(*sobuf);
- slen = solen - 1;
- if (slen)
- sbuf = sobuf + 1;
- return SUCCESS;
- }
-
- async event void Interrupts.tx()
+ bool waitOnTx()
{
- while (slen && call Registers.getIfgTx()) {
- call Registers.setTxbuf(*sbuf);
- if (--slen)
- sbuf++;
- }
- if (slen == 0 && sobuf) {
- call Registers.clrIeTx();
- call Registers.clrIfgTx();
- sobuf = 0;
- signal UartStream.sendDone(sobuf, solen, SUCCESS);
+ for (;;) {
+ if (call Registers.getIfgTx())
+ return TRUE;
+ if (call CSn.get()) /* SPI master has unselected us */
+ return FALSE;
}
}
- async command error_t UartStream.enableReceiveInterrupt()
+ async command uint8_t SpiByte.write(uint8_t byte)
{
- if (!robuf)
- call Registers.clrIfgRx();
- call Registers.setIeRx();
- rxie = FALSE;
- return SUCCESS;
- }
-
- async command error_t UartStream.disableReceiveInterrupt()
- {
- if (!robuf) {
- call Registers.clrIeRx();
- call Registers.clrIfgRx();
- } else
- rxie = TRUE;
- return SUCCESS;
+ atomic {
+ if (isBusy())
+ return 0;
+ else {
+ waitOnTx();
+ call Registers.setTxbuf(byte);
+ waitOnRx();
+ return call Registers.getRxbuf();
+ }
+ }
}
- async command error_t UartByte.receive(uint8_t* byte, uint8_t timeout)
+ /* If we are a slave, return FALSE if the master has unasserted CSn. */
+ bool sendData()
{
- uint16_t t;
+ atomic {
+ uint16_t end = m_pos + (blockSize ? blockSize : BLOCKSIZE_DEFAULT);
+ uint8_t tmp;
- /* FIXME: race with UartStream.receive() */
- if (robuf || !byte)
- return FAIL;
- /* TODO: implement timeout, byte-time units. For now, 1-2 sec */
- t = TBR;
- while (t < TBR) {
- if (call Registers.getIfgRx()) {
- *byte = call Registers.getRxbuf();
- return SUCCESS;
+ if (end > m_len)
+ end = m_len;
+ 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) {
+ waitOnRx();
+ tmp = call Registers.getRxbuf();
+ if (m_rxBuf)
+ m_rxBuf[m_pos - 1] = tmp;
+ waitOnTx();
+ call Registers.setTxbuf(m_txBuf ? m_txBuf[m_pos] : 0);
}
+ return call CSn.get() ? FALSE : TRUE;
}
- return FAIL;
}
- async command error_t UartStream.receive(uint8_t* buf, uint16_t len)
+ async command error_t SpiPacket.send(uint8_t* txBuf, uint8_t* rxBuf,
+ uint16_t len)
{
- if (robuf || !buf || !len)
+ if (isBusy() || (!txBuf && !rxBuf) || len == 0)
return FAIL;
- robuf = rbuf = buf;
- rolen = rlen = len;
- if (!call Registers.getIeRx()) {
- call Registers.clrIfgRx();
- call Registers.setIeRx();
- rxie = TRUE;
- } else
- rxie = FALSE;
+ else {
+ m_txBuf = txBuf;
+ m_rxBuf = rxBuf;
+ m_len = len;
+ m_pos = 0;
+ if (sendData())
+ call Registers.setIeRx();
+ else
+ post signalSendDone();
+ return SUCCESS;
+ }
}
+ async event void Interrupts.tx() {}
+
async event void Interrupts.rx(uint8_t byte)
{
- if (robuf) {
- /* receive() takes precedence if active */
- while (rlen && call Registers.getIfgRx()) {
- *rbuf = byte;
- if (--rlen)
- rbuf++;
- }
- if (rlen == 0 && robuf) {
- if (rxie) {
- call Registers.clrIeRx();
- call Registers.clrIfgRx();
- }
- robuf = 0;
- signal UartStream.receiveDone(robuf, rolen, SUCCESS);
- }
- } else
- signal UartStream.receivedByte(byte);
+ if (m_rxBuf)
+ m_rxBuf[m_pos - 1] = byte;
+
+ 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,
+ error_t) {}
+
default async command const msp430_usci_spi_t* Configure.get()
{
- const static msp430_usci_spi_t def = {
- ctl0: UCMODE_0, /* async, lsb first, 8N1 */
- ctl1: UCSWRST|UCSSEL_1, /* clock spi from SMCLK */
- brx: UBRX_32768HZ_9600,
- mctl: UMCTL_32768HZ_9600, /* ??? */
+ 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 */
+ uclisten: FALSE,
ren: USCI_REN_NONE
};
async event void Interrupts.i2cCal() {}
async event void Interrupts.brk() {}
async event void Interrupts.i2cNak() {}
- async event void Counter.overflow() {}
- default async event void UartStream.sendDone( uint8_t* buf, uint16_t len,
- error_t error ) {}
- default async event void UartStream.receivedByte( uint8_t byte ) {}
- default async event void UartStream.receiveDone( uint8_t* buf, uint16_t len,
- error_t error ) {}
+ default async command bool CSn.get() { return FALSE; }
}