/**
* Spi implementation using a USCI device.
*
+ * TODO: Implement error checking via UCxxSTAT
+ *
* @author R. Steve McKown <smckown@gmail.com>
*/
-generic module Msp430SpiP() {
+generic module Msp430SpiP(uint16_t blockSize) {
provides {
interface SpiByte;
interface SpiPacket;
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 {
+ enum {
+ BLOCKSIZE_DEFAULT = 64
+ };
+
#define saveBits(pin, pos, dir, out, ren) { \
if (call pin.isOutput()) \
dir |= (1 << pos); \
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 */
+ /* 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 */
+ {
+ 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);
+ call Registers.assignMctl(0);
+ if (config->uclisten)
+ call Registers.setStat(UCLISTEN);
+ else
+ call Registers.clrStat(UCLISTEN);
- /* Save pin IO states */
- dir = out = ren = 0;
- saveBits(RXD, 0, dir, out, ren);
- saveBits(TXD, 1, dir, out, ren);
+ /* 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 RX pin for UART use */
- call RXD.makeInput();
- if (config->ren & USCI_REN_RX) {
- if (config->ren & USCI_REN_RX_PULLUP)
- call RXD.set();
+ /* 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();
+ }
+#endif
+ call STE.selectModuleFunc();
+ }
+ call SOMI.makeInput();
+ if (config->ren & USCI_REN_SOMI) {
+ if (config->ren & USCI_REN_SOMI_PULLUP)
+ call SOMI.set();
else
- call RXD.clr();
- call RXD.enableRen();
+ call SOMI.clr();
+ call SOMI.enableRen();
}
- call RXD.selectModuleFunc();
-
-#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();
+ 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 TXD.clr();
- call TXD.enableRen();
+ call SIMO.clr();
+ call SIMO.enableRen();
}
- call TXD.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();
+ }
+#endif
+ call CLK.selectModuleFunc();
/* Clear interrupts; we'll add them as needed */
call Registers.clrIeRx();
/* Disable the device */
call Registers.setCtl1(UCSYNC);
- /* 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);
+ 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
- call RXD.selectIOFunc();
- call TXD.selectIOFunc();
+ if (is4pin())
+ call STE.selectIOFunc();
+ call SIMO.selectIOFunc();
+ call SOMI.selectIOFunc();
+ call CLK.selectIOFunc();
}
}
- async command error_t UartByte.send(uint8_t byte)
+ async command uint8_t SpiByte.write(uint8_t byte)
{
- /* FIXME: race with UartStream.send() */
- atomic {
- if (sobuf)
- return FAIL;
- while (call Registers.getStat(UCBUSY));
+ if (isBusy())
+ return 0;
+ else {
+ while (!call Registers.getIfgTx());
call Registers.setTxbuf(byte);
- return SUCCESS;
+ while(!call Registers.getIfgRx());
+ return call Registers.getRxbuf();
}
}
- async command error_t UartStream.send(uint8_t* buf, uint16_t len)
+ void sendData()
{
- 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;
- }
+ /* 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;
- async event void Interrupts.tx()
- {
- 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);
+ if (end > m_len)
+ end = m_len;
+ call Registers.setTxbuf((m_txBuf) ? m_txBuf[m_pos] : 0);
+ while (++m_pos < end) {
+ while (!call Registers.getIfgRx());
+ 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);
+ }
}
}
- async command error_t UartStream.enableReceiveInterrupt()
- {
- if (!robuf)
- call Registers.clrIfgRx();
- call Registers.setIeRx();
- rxie = FALSE;
- return SUCCESS;
- }
-
- async command error_t UartStream.disableReceiveInterrupt()
+ async command error_t SpiPacket.send(uint8_t* txBuf, uint8_t* rxBuf,
+ uint16_t len)
{
- if (!robuf) {
- call Registers.clrIeRx();
- call Registers.clrIfgRx();
- } else
- rxie = TRUE;
- return SUCCESS;
- }
-
- async command error_t UartByte.receive(uint8_t* byte, uint8_t timeout)
- {
- uint16_t t;
-
- /* FIXME: race with UartStream.receive() */
- if (robuf || !byte)
+ if (isBusy() || (!txBuf && !rxBuf) || len == 0)
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();
+ else {
+ atomic {
+ m_txBuf = txBuf;
+ m_rxBuf = rxBuf;
+ m_len = len;
+ m_pos = 0;
+ call Registers.setIeRx();
+ sendData();
return SUCCESS;
}
}
- return FAIL;
}
- async command error_t UartStream.receive(uint8_t* buf, uint16_t len)
+ async event void Interrupts.tx() {}
+
+ task void signalSendDone()
{
- if (robuf || !buf || !len)
- return FAIL;
- robuf = rbuf = buf;
- rolen = rlen = len;
- if (!call Registers.getIeRx()) {
- call Registers.clrIfgRx();
- call Registers.setIeRx();
- rxie = TRUE;
- } else
- rxie = FALSE;
+ atomic {
+ signal SpiPacket.sendDone(m_txBuf, m_rxBuf, m_len, SUCCESS);
+ m_len = 0;
+ }
}
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] = call Registers.getRxbuf();
+ else
+ call Registers.getRxbuf();
+
+ if (m_pos < m_len)
+ sendData();
+ else {
+ call Registers.clrIeRx();
+ post signalSendDone(); /* Don't signal from ISR context */
+ }
}
+ 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, /* ??? */
+ 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 ) {}
}