/**
* 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 Msp430UsciSpiConfigure; /* maybe just Msp430UsciConfigure */
- interface Counter<T32khz,uint16_6>
+ interface HplMsp430GeneralIO as CLK;
+ interface AsyncConfigure<const msp430_usci_spi_t*> as Configure;
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;
+ /* Pin IO configuration storage for later restoration */
+ uint8_t m_dir;
+ uint8_t m_out;
+ uint8_t m_ren;
- async command void ResourceConfigure.configure();
+ 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 */
{
- call Registers.setCtl0(UCSYNC);
- /* Save pin states */
- dir = out = ren = 0;
- saveBits(STE, 0, dir, out, ren);
- saveBits(SIMO, 1, dir, out, ren);
- saveBits(SOMI, 2, dir, out, ren);
- saveBits(SCL, 3, dir, out, ren);
- /* FIXME: use Msp430UsciConfig to configure ports */
- /* FIXME: we may need to have REN/DIR stuff in the configuration... */
- if (call Registers.getCtl1(UCMODE_3) != UCMODE_0) {
- call STE.selectModuleFunc();
- call SIMO.selectModuleFunc();
- call SOMI.selectModuleFunc();
- call SCL.selectModuleFunc();
- /* Clear interrupts; we'll add them as needed */
- call Registers.clrCtl1(UCRXEIE|UCBRKIE);
- call Registers.clrIeRx();
- call Registers.clrIeTx();
- /* Enable the device */
- call Registers.clrCtl0(UCSYNC);
+ return (call Registers.getCtl0(UCMODE_3)) != UCMODE_0;
}
- async command void ResourceConfigure.unconfigure();
+ inline bool isBusy() /* true if a SPI transaction is in progress */
{
- /* Disable the device */
- call Registers.setCtl0(UCSYNC);
- /* Clear interrupts and interrupt flags */
- call Registers.clrIeRx();
- call Registers.clrIeTx();
- call Registers.clrIfgRx();
- call Registers.clrIfgTx();
- /* Restore pins to state just before configure() */
- restoreBits(SIMO, 1, dir, out, ren);
- restoreBits(SOMI, 2, dir, out, ren);
- restoreBits(SCL, 3, dir, out, ren);
- call SIMO.selectIOFunc();
- call SOMI.selectIOFunc();
- call SCL.selectIOFunc();
- /* Restore more if we were using 4-pin SPI */
- if (call Registers.getCtl1(UCMODE_3) != UCMODE_0) {
- restoreBits(STE, 0, dir, out, ren);
- call STE.selectIOFunc();
- }
+ atomic return m_len != 0;
}
+ async command void ResourceConfigure.configure()
+ {
+ atomic {
+ const msp430_usci_spi_t* config = call Configure.get();
+ uint8_t ctl0;
+
+ call Registers.setCtl1(UCSWRST);
+
+ /* 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);
+
+ /* 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);
+
+ /* 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();
+ }
+#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 SOMI.clr();
+ call SOMI.enableRen();
+ }
+ 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();
+ }
+#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();
+ call Registers.clrIeTx();
+
+ /* Enable the device */
+ call Registers.clrCtl1(UCSWRST);
+ }
+ }
- async event void Interrupts.tx()
+ async command void ResourceConfigure.unconfigure()
{
- while (slen && call Registers.getIfgTx()) {
- call Registers.setTxbuf(*sbuf);
- if (--slen)
- sbuf++;
+ atomic {
+ /* Disable the device */
+ call Registers.setCtl1(UCSYNC);
+
+ /* 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())
+ call STE.selectIOFunc();
+ call SIMO.selectIOFunc();
+ call SOMI.selectIOFunc();
+ call CLK.selectIOFunc();
}
- if (slen == 0 && sobuf) {
- call Registers.clrIeTx();
- call Registers.clrIfgTx();
- sobuf = 0;
- signal UartStream.sendDone(sobuf, solen, SUCCESS);
+ }
+
+ 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();
}
}
- async event void Interrupts.rx(uint8_t byte)
+ void sendData()
{
- if (robuf) {
- /* receive() takes precedence if active */
- while (rlen && call Registers.getIfgRx()) {
- *rbuf = byte;
- if (--rlen)
- rbuf++;
+ /* 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);
+ 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);
}
- if (rlen == 0 && robuf) {
- if (rxie) {
- call Registers.clrIeRx();
- call Registers.clrIfgRx();
- }
- robuf = 0;
- signal UartStream.receiveDone(robuf, rolen, SUCCESS);
+ }
+ }
+
+ async command error_t SpiPacket.send(uint8_t* txBuf, uint8_t* rxBuf,
+ uint16_t len)
+ {
+ if (isBusy() || (!txBuf && !rxBuf) || len == 0)
+ return FAIL;
+ else {
+ atomic {
+ m_txBuf = txBuf;
+ m_rxBuf = rxBuf;
+ m_len = len;
+ m_pos = 0;
+ call Registers.setIeRx();
+ sendData();
+ return SUCCESS;
}
- } else
- signal UartStream.receivedByte(byte);
+ }
+ }
+
+ 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();
+
+ 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: 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.i2cStart() {}
+ async event void Interrupts.i2cStop() {}
+ async event void Interrupts.i2cCal() {}
+ async event void Interrupts.brk() {}
+ async event void Interrupts.i2cNak() {}
}