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
Diff: spissp/spissp.cpp
- 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