/**
* Spi implementation using a USCI device.
*
- * @author R. Steve McKown <smckown@gmail.com>
+ * TODO: Implement error checking via UCxxSTAT
+ *
+ * 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;
interface HplMsp430GeneralIO as STE;
interface HplMsp430GeneralIO as SIMO;
interface HplMsp430GeneralIO as SOMI;
- interface HplMsp430GeneralIO as SCL;
- interface AsyncConfigure<const msp430_usci_config_t*> as
- Msp430UsciConfigure;
- interface Counter<T32khz,uint16_6>
+ interface HplMsp430GeneralIO as CLK;
+ interface AsyncConfigure<const msp430_usci_spi_t*> as Configure;
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); \
- }
+ 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
+ };
- #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 m_pins;
+ 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;
+ }
- 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 */
+ inline bool isBusy() /* true if a SPI transaction is in progress */
+ {
+ atomic return m_len != 0;
+ }
async command void ResourceConfigure.configure()
{
atomic {
- const msp430_usci_config_t* config = call Msp430UsciConfigure.get();
+ const msp430_usci_spi_t* config = call Configure.get();
+ uint8_t ctl0;
call Registers.setCtl1(UCSWRST);
- /* Configure USCI registers */
- call Registers.assignCtl0(config->spi.ctl0);
- call Registers.assignCtl1(config->spi.ctl1|UCSWRST);
- call Registers.assignBr0(config->spi.brx & 0xff);
- call Registers.assignBr1(config->spi.brx >> 8);
- call Registers.assignMctl(config->spi.mctl);
- call Registers.assignIrtctl(config->spi.irtctl);
- call Registers.assignIrrctl(config->spi.irrctl);
- call Registers.assignAbctl(config->spi.abctl);
- call Registers.clrStat(UCLISTEN);
+ /* 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);
- /* Save pin IO states */
- dir = out = ren = 0;
- saveBits(RXD, 0, dir, out, ren);
- saveBits(TXD, 1, dir, out, ren);
+ /* Configure USCI registers */
+ call Registers.assignCtl0(ctl0);
+ call Registers.assignCtl1(config->ctl1 | UCSWRST);
+ call Registers.assignBr0(config->brx & 0xff);
+ call Registers.assignBr1(config->brx >> 8);
+ call Registers.assignMctl(0);
+ if (config->uclisten)
+ call Registers.setStat(UCLISTEN);
+ else
+ call Registers.clrStat(UCLISTEN);
- /* Configure RX pin for UART use */
- call RXD.makeInput();
- if (config->spi.ren & USCI_REN_RX) {
- if (config->spi.ren & USCI_REN_RX_PULLUP)
- call RXD.set();
- else
- call RXD.clr();
- call RXD.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();
}
- 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->spi.ren & USCI_REN_TX) {
- if (config->spi.ren & USCI_REN_TX_PULLUP)
- call TXD.set();
- else
- call TXD.clr();
- call TXD.enableRen();
+ 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();
{
atomic {
/* Disable the device */
- call Registers.setCtl1(UCSYNC);
+ call Registers.setCtl1(UCSWRST);
- /* 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)
+ 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.getCtl1(UCSWRST));
call Registers.setTxbuf(byte);
- return SUCCESS;
+ while(!call Registers.getIfgRx() && !call Registers.getCtl1(UCSWRST));
+ 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;
- }
+ 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() && !call Registers.getCtl1(UCSWRST));
+ tmp = call Registers.getRxbuf();
+ if (m_rxBuf)
+ m_rxBuf[m_pos - 1] = call Registers.getRxbuf();
+ while (!call Registers.getIfgTx() && !call Registers.getCtl1(UCSWRST));
+ 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 {
+ uint16_t len = m_len;
+ m_len = 0;
+ signal SpiPacket.sendDone(m_txBuf, m_rxBuf, len, SUCCESS);
+ }
}
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)
+ sendData();
+ else {
+ call Registers.clrIeRx();
+ post signalSendDone(); /* Don't signal from ISR context */
+ }
}
- default async command const msp430_usci_config_t* Msp430UsciConfigure.get()
+ 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_config_t def = {
- spi: {
- ctl0: UCMODE_0, /* async, lsb first, 8N1 */
- ctl1: UCSWRST|UCSSEL_1, /* clock spi from SMCLK */
- brx: UBRX_32768HZ_9600,
- mctl: UMCTL_32768HZ_9600,
- irtctl: 0,
- irrctl: 0,
- abctl: 0,
- ren: USCI_REN_NONE
- }
+ 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
};
return &def;
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 ) {}
}