MMEx with SPI Slave to allow legacy devices to communicate with modern media such as USB, SD cards, the internet and all of the mbed\'s other interfaces

Dependencies:   NetServices MSCUsbHost mbed TMP102 SDFileSystem

Revision:
0:67a55a82ce06
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spissp/spissp.cpp	Sun Feb 27 18:54:40 2011 +0000
@@ -0,0 +1,510 @@
+/* MMEx for MBED - SPI interfacing and ringbuffer functions
+ * Copyright (c) 2011 MK
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+  \file spissp.cpp
+  \brief SPI interfacing and ringbuffer functions
+*/
+
+#include "spissp.h"
+
+ /** Library for the interrupt driven SPI SLave
+ *
+ */
+spislirq *spislirq::instance;
+
+ /** Library for the interrupt driven SPI SLave
+ *
+ */
+spislirq::spislirq (int PortNum): smldl(p11, p12, p13, p14), 
+                                  pc(USBTX, USBRX, "pc"), _led4(LED4)
+  { }
+
+/** initialization of SPI Slave and buffers
+ *  
+ */
+void spislirq::init()   {
+    uint32_t tmp;
+
+    smldl.format(8,3);             // SPI format: 8 bits, mode 3
+                                   // mode | POL PHA
+                                   // -----+--------
+                                   //   0  |  0   0
+                                   //   1  |  0   1
+                                   //   2  |  1   0
+                                   //   3  |  1   1
+    smldl.frequency(1000000);      // more or less universal speed at 1 MHz
+                                   // determines time out IRQ time!
+
+    // initialize buffers
+    rx_in = 0;
+    rx_out = 0;
+    tx_in = 0;
+    tx_out = 0;
+
+    // add code here to set the SSP interrupt register and enable interrupts
+    // setup IMSC Interrupt Mask Set/Clear Register
+    // only enable interrupts for RX FIFO half full and RX timeout
+    tmp = (SSP_IMSC_RT | SSP_IMSC_RX) & SSP_IMSC_BITMASK;
+
+    LPC_SSP0->IMSC = tmp;
+
+    // attach our interrupt service routine
+    instance = this;
+    NVIC_SetVector(SSP0_IRQn, (uint32_t)&_spi_isr);
+    enable_irq();
+ }
+
+/** disable SSP0 interrupts
+ *  will discard the byte if buffer full
+ */
+ void spislirq::disable_irq(void)
+{
+    NVIC_DisableIRQ(SSP0_IRQn);
+}
+
+/** enable SSP0 interrupts
+ *  will discard the byte if buffer full
+ */
+void spislirq::enable_irq(void)
+{
+    NVIC_EnableIRQ(SSP0_IRQn);
+}
+
+/** instantiation of isr
+ *  will discard the byte if buffer full
+ */
+void spislirq::_spi_isr(void)
+{
+    instance->spi_isr();
+}
+
+/** add one byte to the transmit buffer, no blocking, no protocol
+ *  will discard the byte if buffer full
+ *
+ *  @param c byte to send
+ *
+ */
+void spislirq::txx_add(unsigned char c) {
+  if (!tx_full()) {
+    txxbuf[tx_in] = c;
+    tx_in = (tx_in + 1) % bufsize;
+  }
+}
+
+/** SPI SLave interrupt service routine
+ *
+ */
+void spislirq::spi_isr(void)
+{
+  uint16_t inp;
+
+  _led4 = !_led4;   // visualize we are in the isr
+
+  // we get in the ISR when there is at least one input char pending
+  // IRQ sources: - RX FIFO at least half full (4 chars)
+  //              - RX FIFO not empty and not read from a time-out period
+
+
+  // first we check if there is enough room left in the RX buffer
+  // if there is less than 10% left we signal this to the SPI Master
+  // is is very unlikely that the TX buffer is full when there is risk for
+  // overrun, but we can always send the '>B' the next time, as there will
+  // be more than 20 free chars in the buffer
+  if (rx_perc() > 90) {
+    txx_add('>');
+    txx_add('B');
+  }
+
+  // now do the real work, just like in spi_rw():
+  // 1. fill output FIFO with data from TX buffer
+
+  while ((!tx_empty()) && tx_fifo_notfull) {
+    // we can indeed try to transmit data
+    // put as much data as possible in the transmit FIFO
+    LPC_SSP0->DR =  (uint16_t)txxbuf[tx_out];
+    tx_out = (tx_out + 1) % bufsize;
+  }
+
+  // in case the TX FIFO and output buffer are empty, add 0x00
+  if (tx_empty() && tx_fifo_empty) {
+    LPC_SSP0->DR =  (uint16_t)0x00;
+  }
+
+  if irq_RTMIS {
+    // interrupt on timeout, clear RTIC to clear interrupt
+    LPC_SSP0->ICR = (SSP_ICR_RT & SSP_ICR_BITMASK);
+  }
+
+  // we must ensure that always the complete RX FIFO is read
+  // when the RX buffer is really full, characters are discarded
+  while (rx_fifo_notempty) {
+    inp = LPC_SSP0->DR;
+    if ((inp != 0x00) && !rx_full()) {
+      // received value only interesting when not 0x00
+      rxxbuf[rx_in] = inp;
+      rx_in = (rx_in + 1) % bufsize;
+    }
+  }
+}
+
+
+/** returns the number of bytes available in the transmit buffer
+ *
+ *  @return available space in transmit buffer
+ */
+int spislirq::tx_room() {
+  if (tx_in < tx_out) {
+    return(tx_out - tx_in - 1);
+  } else {
+    return(bufsize - tx_in + tx_out - 1);
+  }
+}
+
+/** returns the percentage used in the transmit buffer
+ *
+ *  @return percentage from 0 to 100
+ */
+int spislirq::tx_perc() {
+  // returns the percentage used in the transmit buffer
+  return(100 * tx_use() / bufsize);
+}
+
+/** returns the percentage used in the receive buffer
+ *
+ *  @return percentage from 0 to 100
+ */
+int spislirq::rx_perc() {
+  // returns the percentage used in the transmit buffer
+  return(100 * rx_use() / bufsize);
+}
+
+
+/** returns the amount af actual bytes used in the transmit buffer
+ *
+ *  @return bytes used in transmit buffer
+ */
+int spislirq::tx_use() {
+  return(bufsize - tx_room() - 1);
+}
+
+/** returns the number of bytes available in the receive buffer
+ *
+ *  @return available space in transmit buffer
+ */
+int spislirq::rx_room() {
+  if (rx_in < rx_out) {
+    return(rx_out - rx_in - 1);
+  } else {
+    return(bufsize - rx_in + rx_out -1);
+  }
+}
+
+/** returns the amount af actual bytes used in the receive buffer
+ *
+ *  @return bytes used in receive buffer
+ */
+int spislirq::rx_use() {
+  return(bufsize - rx_room() - 1);
+}
+
+/** empties the transmit buffer
+ */
+void spislirq::flush_tx() {
+  tx_out = tx_in = 0;
+}
+
+/** empties the receive buffer
+ */
+void spislirq::flush_rx() {
+  rx_out = rx_in = 0;
+}
+
+/** check if the TX buffer is empty
+ * @return true if the TX buffer is empty
+ */
+bool spislirq::tx_empty() {
+  return (tx_in == tx_out);
+}
+
+/** check if the TX buffer is full
+ * @return true if the TX buffer is full
+ */
+bool spislirq::tx_full() {
+  return(((tx_in + 1) % bufsize) == tx_out);
+}
+
+/** check if the RX buffer is empty
+ * @return true if the RX buffer is empty
+ */
+bool spislirq::rx_empty() {
+  return(rx_in == rx_out);
+}
+
+/** check if the RX buffer is full
+ * @return true if the RX buffer is full
+ */
+bool spislirq::rx_full() {
+  return(((rx_in + 1) % bufsize) == rx_out);
+}
+
+/** add one byte to the transmit buffer, no protocol handling is done
+ *
+ *  @param Bt byte to send
+ *  @return 0 when succesfull, will block until there is room in the buffer
+ *
+ */
+int spislirq::tx_add(unsigned char c) {
+  while (tx_full()) {
+    do_callback();
+    wait_ms(1);
+  }
+  do_callback();
+  disable_irq();
+  if (!tx_full()) {
+    txxbuf[tx_in] = c;
+    tx_in = (tx_in + 1) % bufsize;
+    enable_irq();
+    // DBG_outchar(c);
+    return 0;
+  } else {
+    enable_irq();
+    return -1;
+  }
+}
+
+/** add one byte to the transmit buffer with protocol handling,
+ *  function will block until there is room in the transmit buffer
+ *
+ *  @param Bt byte to send
+ */
+void spislirq::tx_addp(unsigned char c) {
+  // first empty buffer to at least 90%
+  switch (c) {
+    case c_arrow  : tx_add(c_escape);    // to send the > character
+                    tx_add(c_arrow);     // to send the > character
+                    break;
+    case NULL     : tx_add(c_escape);    // to send the > character
+                    tx_add(c_null);      // to send the N character
+                    break;
+    default       : tx_add(c);          // just send it ...
+                    break;
+  }
+}
+
+/** adds a complete string to the transmit buffer, with protocol processing.
+ *  function will block until there is room in the transmit buffer
+ *
+ *  @param S string to be transmitted
+ *  @return always returns 0
+ */
+int spislirq::tx_string(char S[]) {
+  int i = 0;
+
+  while ((S[i] != 0x00)) {
+    // add until end of string
+    tx_addp(S[i]);
+    i++;
+  }
+  return(0);
+}
+
+/** inserts one byte in the receive buffer without protocol processing
+ *  used for command batch processing
+ *
+ *  @return the character read, blocks if RX buffer is empty!
+ *
+ */
+int spislirq::rx_add(unsigned char c) {
+  while (rx_full()) {
+  do_callback();
+    wait_ms(1);  // wait until RC buffer full
+  }
+  disable_irq();
+  if (!rx_full()) {
+    rxxbuf[rx_in] = c;
+    rx_in = (rx_in + 1) % bufsize;
+    enable_irq();
+    return c;
+  }
+  enable_irq();
+  return c;
+}
+
+/** read one byte from the receive buffer without protocol processing
+ *
+ *  @return the character read, blocks if RX buffer is empty!
+ *
+ */
+int spislirq::rx_read() {
+  unsigned char c = 0;
+
+  while (rx_empty()) {
+    do_callback();
+    wait_us(500);
+  }
+  disable_irq();
+  if (!rx_empty()) {
+    c = rxxbuf[rx_out];
+    rx_out = (rx_out + 1) % bufsize;
+    enable_irq();
+    // DBG_inchar(c);
+    return c;
+  }
+  enable_irq();
+  return c;
+}
+
+/** peeks in receive buffer, reads next pending input char without advancing buffer pointer
+ *
+ *  @return the character read, blocks if RX buffer is empty!
+ *
+ */
+int spislirq::rx_peek() {
+  unsigned char c = 0;
+
+  while (rx_empty()) {
+    do_callback();
+    wait_us(500);
+  }
+  disable_irq();
+  if (!rx_empty()) {
+    c = rxxbuf[rx_out];
+  }
+  enable_irq();
+  return c;
+}
+
+/** read one byte, will wait until a valid char is read,
+ *  this function is blocking and will process escape characters,
+ *  when an escape is seen, will try to read the next char
+ *
+ *  @param mode command line processing or data read (ignored)
+ *  @return character read, or -1 when >F seen, -2 when >I seen
+ *
+ */
+int spislirq::rxx_read(bool mode) {
+  unsigned char bt1 = 0;
+  unsigned char bt2 = 0;
+  unsigned char h1  = 0;
+  unsigned char h2  = 0;
+  int rslt = 0;
+
+  bt1 = rx_read();
+
+  // we have now a character, check for escape codes
+  if (bt1 == c_escape) {
+    // this is an escape, now check for the next character
+    bt2 = rx_peek();        // peek in buffer, but do not read
+
+    switch (bt2) {
+      case c_arrow  : bt2 = rx_read();                   // read the char
+                      rslt = c_escape;
+                      break;
+
+      case c_eof    : bt2 = rx_read();                   // read the char
+                      rslt = -1;                         // <EOF>
+                      break;
+
+      case c_return : bt2 = rx_read();                   // read the char
+                      rslt = 0x0d;                       // carriage return
+                      break;
+
+      case c_null   :
+      case c_zero   : bt2 = rx_read();                   // read the char
+                      rslt = 0;                          // zero char
+                      break;
+
+      case c_hex    :
+      case c_hexalt : bt2 = rx_read();                    // read the char
+                      // now get next two chars, which must be hex digits
+                      h1 = rx_read();                     // get data from buffer
+                      h2 = rx_read();                     // get data from buffer
+
+                      rslt = 16 * hex2int(h1) + hex2int(h2); // calculate the result
+                      break;
+
+      case c_intrpt : bt2 = rx_read();                   // read the char
+                      rslt = -2;                         // interrupt
+                      break;
+
+      default       : rslt = bt1;                        // ignore
+
+    }  // end of switch(bt2)
+
+  } else {
+    // regular data send, no escape character
+    rslt = bt1;
+  }
+  return rslt;
+}
+
+void spislirq::DBG_set(int l) {
+  if ((l >= DBG_OFF) && (l <= DBG_FULL)) DBG_level = l;
+}
+
+
+/** show a debug message with a string and a character both a character and hex value
+ *
+ *  @param src this will typically be the name of the function
+ *  @param c character to be shown
+ */
+void spislirq::DBG_chr(char* src, char c) {
+  if (DBG_level != DBG_OFF) {
+    int cc = c;
+    if (c < 32) c = '.';
+    printf("%s : %c [%02x]\n", src, c, cc);
+  }
+}
+
+/** show one character in hex, input from SPI to the MBED
+ *
+ *  @param c input to be shown
+ */
+void spislirq::DBG_inchar(char C) {
+  // prints only one char in hex, preceded by a + for input followed by a space
+  if (DBG_level == DBG_FULL) {
+    printf("+%02x ", C);
+  }
+}
+
+/** show one character in hex, output from MBED to SPI
+ *
+ *  @param c output to be shown
+ */
+void spislirq::DBG_outchar(char C) {
+  if (DBG_level == DBG_FULL) {
+    printf("-%02x ", C);
+  }
+}
+
+/** convert a hex char '0' to 'F' in its decimal equivalent
+ *
+ *  @param C character
+ *  @return a value 0 to 15, 0 when C was not in '0'..'F'
+ *
+ */
+int spislirq::hex2int(char C) {
+  if ((C >= '0') && (C <= '9')) return(C - '0');
+  if ((C >= 'A') && (C <= 'F')) return(C - 'A' + 10);
+  return(0);
+}
\ No newline at end of file