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
spissp/spissp.cpp
- Committer:
- DeMein
- Date:
- 2011-02-27
- Revision:
- 0:67a55a82ce06
File content as of revision 0:67a55a82ce06:
/* 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); }