MODSERIAL with support for more devices

Fork of MODSERIAL by Erik -

MODSERIAL.h

Committer:
AjK
Date:
2010-11-21
Revision:
3:0f10f536456e
Parent:
2:b936b4acbd92
Child:
4:28de979b77cf

File content as of revision 3:0f10f536456e:

/*
    Copyright (c) 2010 Andy Kirkham
 
    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.
*/

#ifndef MODSERIAL_H
#define MODSERIAL_H

/** @defgroup API The MODSERIAL API */
/** @defgroup MISC Misc MODSERIAL functions */
/** @defgroup INTERNALS MODSERIAL Internals */

#ifndef MODSERIAL_DEFAULT_RX_BUFFER_SIZE
#define MODSERIAL_DEFAULT_RX_BUFFER_SIZE    256
#endif

#ifndef MODSERIAL_DEFAULT_TX_BUFFER_SIZE
#define MODSERIAL_DEFAULT_TX_BUFFER_SIZE    256
#endif

#include "mbed.h"

namespace AjK {

/**
 * @author Andy Kirkham
 * @see http://mbed.org/cookbook/MODSERIAL
 * @see http://mbed.org/handbook/Serial
 * @see example.cpp
 * @see API 
 *
 * <b>MODSERIAL</b> extends the Mbed library <a href="/handbook/Serial">Serial</a> to provide fully buffered
 * TX and RX streams. Buffer length is fully customisable. 
 *
 * Before using MODSERIAL users should be familar with Mbed's standard <a href="/handbook/Serial">Serial</a>
 * library object. MODSERIAL is a direct "drop in" replacement for <a href="/handbook/Serial">Serial</a>. Where
 * previously Serial was used, MODSERIAL can be used as adirect replacement instantly offering standard
 * TX and RX buffering. By default, both TX and RX buffers are 256 bytes in length.
 *
 * @image html /media/uploads/mbedofficial/serial_interfaces.png
 *
 * Standard example:
 * @code
 * #include "mbed.h"
 * #include "MODSERIAL.h"
 *
 * MODSERIAL pc(USBTX, USBRX); // tx, rx
 *
 * int main() {
 *     pc.printf("Hello World!");
 *     while(1) {
 *         pc.putc(pc.getc() + 1);
 *     }
 * }
 * @endcode
 *
 * Example with alternate buffer length:
 * @code
 * #include "mbed.h"
 * #include "MODSERIAL.h"
 *
 * // Make TX and RX buffers 512byes in length
 * MODSERIAL pc(USBTX, USBRX, 512); // tx, rx
 *
 * int main() {
 *     pc.printf("Hello World!");
 *     while(1) {
 *         pc.putc(pc.getc() + 1);
 *     }
 * }
 * @endcode
 *
 * Example with alternate buffer length:
 * @code
 * #include "mbed.h"
 * #include "MODSERIAL.h"
 *
 * // Make TX 1024bytes and RX 512byes in length
 * MODSERIAL pc(USBTX, USBRX, 1024, 512); // tx, rx
 *
 * int main() {
 *     pc.printf("Hello World!");
 *     while(1) {
 *         pc.putc(pc.getc() + 1);
 *     }
 * }
 * @endcode
 */
class MODSERIAL : public Serial 
{
public:

    //! A copy of the Serial parity enum
    /** @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.format */
    enum Parity {
          None = 0
        , Odd
        , Even
        , Forced1   
        , Forced0
    };
    
    //! A copy of the Serial IrqType enum
    enum IrqType {
          RxIrq = 0
        , TxIrq
        , RxOvIrq
        , TxOvIrq
        , TxEmpty
    };
    
    //! Non-blocking functions return code.
    enum Result {
          Ok = 0                /*!< Ok. */
        , NoMemory       = -1   /*!< Memory allocation failed. */
        , NoChar         = -1   /*!< No character in buffer. */
        , BufferOversize = -2   /*!< Oversized buffer. */
    };
    
    //! DMA channels.
    enum dmaChannel {
          NotInUse = -1     /*!< DMA not in use */
        , Channel0 =  0     /*!< Channel 0 */
        , Channel1          /*!< Channel 1 */
        , Channel2          /*!< Channel 2 */
        , Channel3          /*!< Channel 3 */
        , Channel4          /*!< Channel 4 */
        , Channel5          /*!< Channel 5 */
        , Channel6          /*!< Channel 6 */
        , Channel7          /*!< Channel 7 */
    };
    
    /**
     * The MODSERIAL constructor is used to initialise the serial object.
     *
     * @param tx PinName of the TX pin.
     * @param rx PinName of the TX pin.
     * @param name An option name for RPC usage.
     */    
    MODSERIAL(PinName tx, PinName rx, const char *name = NULL);
    
    /**
     * The MODSERIAL constructor is used to initialise the serial object.
     *
     * @param tx PinName of the TX pin.
     * @param rx PinName of the TX pin.
     * @param bufferSize Integer of the TX and RX buffer sizes.
     * @param name An option name for RPC usage.
     */    
    MODSERIAL(PinName tx, PinName rx, int bufferSize, const char *name = NULL);
    
    /**
     * The MODSERIAL constructor is used to initialise the serial object.
     *
     * @param tx PinName of the TX pin.
     * @param rx PinName of the TX pin.
     * @param txBufferSize Integer of the TX buffer sizes.
     * @param rxBufferSize Integer of the RX buffer sizes.
     * @param name An option name for RPC usage.
     */    
    MODSERIAL(PinName tx, PinName rx, int txBufferSize, int rxBufferSize, const char *name = NULL);
    
    virtual ~MODSERIAL();

    /**
     * Function: attach
     *  
     * The Mbed standard <a href="/handbook/Serial">Serial</a> library object allows an interrupt callback
     * to be made when a byte is received by the TX or RX UART hardware. MODSERIAL traps these interrupts
     * to enable it's buffering system. However, after the byte has been received/sent under interrupt control, 
     * MODSERIAL can callback a user function as a notification of the interrupt. Note, user code should not
     * directly interact with the Uart hardware, MODSERIAL does that, instead, MODSERIAL API functions should
     * be used.
     *
     * <b>Note</b>, a character is written out then, if there is room in the TX FIFO and the TX buffer is empty,
     * putc() will put the character directly into THR (the output holding register). If the TX FIFO is full and 
     * cannot accept the character, it is placed into the TX output buffer. The TX interrupts are then enabled
     * so that when the TX FIFO empties, the TX buffer is then transferred to the THR FIFO. The TxIrq will ONLY 
     * be activated when this transfer of a character from BUFFER to THR FIFO takes place. If your character 
     * throughput is not high bandwidth, then the 16 byte TX FIFO may be enough and the TX output buffer may 
     * never come into play.
     *
     * @code
     * #include "mbed.h"
     * #include "MODSERIAL.h"
     *
     * DigitalOut led1(LED1);
     * DigitalOut led2(LED2);
     * DigitalOut led3(LED3);
     *
     * // To test, connect p9 to p10 as a loopback.
     * MODSERIAL pc(p9, p10);
     *
     * // This function is called when a character goes into the TX buffer.
     * void txCallback(void) {
     *     led2 = !led2;
     * }
     *
     * // This function is called when a character goes into the RX buffer.
     * void rxCallback(void) {
     *     led3 = !led3;
     * }
     *
     * int main() {
     *     pc.baud(115200);
     *     pc.attach(&txCallback, MODSERIAL::TxIrq);
     *     pc.attach(&rxCallback, MODSERIAL::RxIrq);
     *
     *     while(1) {
     *         led1 = !led1;
     *         wait(0.5);
     *         pc.putc('A');
     *         wait(0.5);
     *     }
     * ]
     * @endcode
     *
     * @ingroup API
     * @param fptr A pointer to a void function, or 0 to set as none
     * @param type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)
     */  
    void attach(void (*fptr)(void), IrqType type = RxIrq) { _isr[type].attach(fptr); }
    
    /**
     * Function: attach
     *  
     * The Mbed standard <a href="/handbook/Serial">Serial</a> library object allows an interrupt callback
     * to be made when a byte is received by the TX or RX UART hardware. MODSERIAL traps these interrupts
     * to enable it's buffering system. However, after the byte has been received/sent under interrupt control, 
     * MODSERIAL can callback a user function as a notification of the interrupt. Note, user code should not
     * directly interact with the Uart hardware, MODSERIAL does that, instead, MODSERIAL API functions should
     * be used.
     *
     * <b>Note</b>, a character is written out then, if there is room in the TX FIFO and the TX buffer is empty,
     * putc() will put the character directly into THR (the output holding register). If the TX FIFO is full and 
     * cannot accept the character, it is placed into the TX output buffer. The TX interrupts are then enabled
     * so that when the TX FIFO empties, the TX buffer is then transferred to the THR FIFO. The TxIrq will ONLY 
     * be activated when this transfer of a character from BUFFER to THR FIFO takes place. If your character 
     * throughput is not high bandwidth, then the 16 byte TX FIFO may be enough and the TX output buffer may 
     * never come into play.
     *
     * @code
     * #include "mbed.h"
     * #include "MODSERIAL.h"
     *
     * DigitalOut led1(LED1);
     * DigitalOut led2(LED2);
     * DigitalOut led3(LED3);
     *
     * // To test, connect p9 to p10 as a loopback.
     * MODSERIAL pc(p9, p10);
     *
     * class Foo {
     * public:
     *     // This method is called when a character goes into the TX buffer.
     *     void txCallback(void) { led2 = !led2; }
     *
     *     // This method is called when a character goes into the RX buffer.
     *     void rxCallback(void) { led3 = !led3; }
     * };
     *
     * Foo foo;
     *
     * int main() {
     *     pc.baud(115200);
     *     pc.attach(&foo, &Foo::txCallback, MODSERIAL::TxIrq);
     *     pc.attach(&foo, &Foo::rxCallback, MODSERIAL::RxIrq);
     *
     *     while(1) {
     *         led1 = !led1;
     *         wait(0.5);
     *         pc.putc('A');
     *         wait(0.5);
     *     }
     * ]
     * @endcode
     *     
     * @ingroup API
     * @param  tptr A pointer to the object to call the member function on
     * @param  mptr A pointer to the member function to be called
     * @param  type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)
     */
    template<typename T>
    void attach(T* tptr, void (T::*mptr)(void), IrqType type = RxIrq) {
        if((mptr != NULL) && (tptr != NULL)) {
            _isr[type].attach(tptr, mptr);            
        }
    }

    /**
     * @see attach
     * @ingroup API
     */
    void connect(void (*fptr)(void), IrqType type = RxIrq) { _isr[RxIrq].attach(fptr); }
    
    /**
     * @see attach
     * @ingroup API
     */
    template<typename T>
    void connect(T* tptr, void (T::*mptr)(void), IrqType type = RxIrq) {
        if((mptr != NULL) && (tptr != NULL)) {
            _isr[type].attach(tptr, mptr);            
        }
    }
    
    /**
     * Function: writeable
     *  
     * Determine if there is space available to write a byte
     *
     * @ingroup API
     * @return 1 if there is space to write a character, else 0
     */
    int writeable() { return txBufferFull() ? 0 : 1; }
    
    /**
     * Function: readable
     *  
     * Determine if there is a byte available to read
     *
     * @ingroup API
     * @return 1 if there is a character available to read, else 0
     */
    int readable() { return rxBufferEmpty() ? 0 : 1; } 
    
    /**
     * Function: txBufferSane
     *  
     * Determine if the TX buffer has been initialized.
     *
     * @ingroup API
     * @return true if the buffer is initialized, else false
     */
    bool txBufferSane(void) { return buffer[TxIrq] != (char *)NULL ? true : false; }
    
    /**
     * Function: rxBufferSane
     *  
     * Determine if the RX buffer has been initialized.
     *
     * @ingroup API
     * @return true if the buffer is initialized, else false
     */
    bool rxBufferSane(void) { return buffer[TxIrq] != (char *)NULL ? true : false; }
    
    /**
     * Function: txBufferGetCount
     *  
     * Returns how many bytes are in the TX buffer
     *
     * @ingroup API
     * @return The number of bytes in the TX buffer
     */
    int txBufferGetCount(void)    { return buffer_count[TxIrq]; }
    
    /**
     * Function: rxBufferGetCount
     *  
     * Returns how many bytes are in the RX buffer
     *
     * @ingroup API
     * @return The number of bytes in the RX buffer
     */
    int rxBufferGetCount(void)    { return buffer_count[RxIrq]; }
    
    /**
     * Function: txBufferGetSize
     *  
     * Returns the current size of the TX buffer
     *
     * @ingroup API
     * @return The length iof the TX buffer in bytes
     */
    int txBufferGetSize(int size) { return buffer_size[TxIrq]; } 
    
    /**
     * Function: rxBufferGetSize
     *  
     * Returns the current size of the RX buffer
     *
     * @ingroup API
     * @return The length iof the RX buffer in bytes
     */
    int rxBufferGetSize(int size) { return buffer_size[RxIrq]; } 
    
    /**
     * Function: txBufferFull
     *  
     * Is the TX buffer full?
     *
     * @ingroup API
     * @return true if the TX buffer is full, otherwise false
     */
    bool txBufferFull(void);
    
    /**
     * Function: rxBufferFull
     *  
     * Is the RX buffer full?
     *
     * @ingroup API
     * @return true if the RX buffer is full, otherwise false
     */
    bool rxBufferFull(void);
    
    /**
     * Function: txBufferEmpty
     *  
     * Is the TX buffer empty?
     *
     * @ingroup API
     * @return true if the TX buffer is empty, otherwise false
     */
    bool txBufferEmpty(void);
    
    /**
     * Function: rxBufferEmpty
     *  
     * Is the RX buffer empty?
     *
     * @ingroup API
     * @return true if the RX buffer is empty, otherwise false
     */
    bool rxBufferEmpty(void);
    
    /**
     * Function: txBufferSetSize
     *  
     * Change the TX buffer size.
     *
     * @see Result
     * @ingroup API
     * @param size The new TX buffer size in bytes.
     * @param m Perform a memory sanity check. Errs the Mbed if memory alloc fails.
     * @return Result Ok on success.
     */
    int txBufferSetSize(int size, bool m) { return resizeBuffer(size, TxIrq, m); } 
    
    /**
     * Function: rxBufferSetSize
     *  
     * Change the RX buffer size.
     *
     * @see Result
     * @ingroup API
     * @param size The new RX buffer size in bytes.
     * @param m Perform a memory sanity check. Errs the Mbed if memory alloc fails.
     * @return Result Ok on success.
     */
    int rxBufferSetSize(int size, bool m) { return resizeBuffer(size, RxIrq, m); } 
    
    /**
     * Function: txBufferSetSize
     *  
     * Change the TX buffer size.
     * Always performs a memory sanity check, halting the Mbed on failure.
     *
     * @see Result
     * @ingroup API
     * @param size The new TX buffer size in bytes.
     * @return Result Ok on success.
     */
    int txBufferSetSize(int size) { return resizeBuffer(size, TxIrq, true); } 
    
    /**
     * Function: rxBufferSetSize
     *  
     * Change the RX buffer size.
     * Always performs a memory sanity check, halting the Mbed on failure.
     *
     * @see Result
     * @ingroup API
     * @param size The new RX buffer size in bytes.
     * @return Result Ok on success.
     */
    int rxBufferSetSize(int size) { return resizeBuffer(size, RxIrq, true); } 
    
    /**
     * Function: txBufferFlush
     *  
     * Remove all bytes from the TX buffer.
     * @ingroup API
     */
    void txBufferFlush(void) { flushBuffer(TxIrq); }
    
    /**
     * Function: rxBufferFlush
     *  
     * Remove all bytes from the RX buffer.
     * @ingroup API
     */
    void rxBufferFlush(void) { flushBuffer(RxIrq); }
    
    /**
     * Function: dmaSend
     *  
     * Remove all bytes from the RX buffer.
     * @ingroup API
     */
    int dmaSend(char *buffer, int length, dmaChannel q = Channel7);
    
    /**
     * Function: getcNb
     *
     * Like getc() but is non-blocking. If no bytes are in the RX buffer this
     * function returns Result::NoChar (-1)
     *
     * @ingroup API
     * @return A byte from the RX buffer or Result::NoChar (-1) if bufer empty.
     */
    int getcNb() { return __getc(false); }
    
    /**
     * Function: getc
     *
     * Overloaded version of Serial::getc()
     * 
     * This function blocks (if the RX buffer is empty the function will wait for a
     * character to arrive and then return that character).
     *
     * @ingroup API
     * @return A byte from the RX buffer
     */
    int getc()   { return __getc(true);  }
    
    /**
     * Function: txGetLastChar
     *
     * Rteurn the last byte to pass through the TX interrupt handler.
     *
     * @ingroup MISC
     * @return The byte
     */
    char txGetLastChar(void) { return txc; }
    
    /**
     * Function: rxGetLastChar
     *
     * Return the last byte to pass through the RX interrupt handler.
     *
     * @ingroup MISC
     * @return The byte
     */
    char rxGetLastChar(void) { return rxc; }
    
    /**
     * Function: txIsBusy
     *
     * If the Uart is still actively sending characters this
     * function will return true.
     *
     * @ingroup API
     * @return bool
     */
    bool txIsBusy(void);
    
    #if 0 // Inhereted from Serial/Stream, for documentation only
    /**
     * Function: putc
     * 
     * Write a character
     * Inhereted from Serial/Stream
     *
     * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.putc
     * @ingroup API
     * @param c The character to write to the serial port
     */
    int putc(int c);
    #endif
    
    #if 0 // Inhereted from Serial/Stream, for documentation only
    /**
     * Function: printf
     *  
     * Write a formated string
     * Inhereted from Serial/Stream
     *
     * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.printf
     * @ingroup API
     * @param format A printf-style format string, followed by the variables to use in formating the string.
     */
    int printf(const char* format, ...);
    #endif
    
    #if 0 // Inhereted from Serial/Stream, for documentation only
    /**
     * Function: scanf
     *  
     * Read a formated string
     * Inhereted from Serial/Stream
     *
     * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.scanf
     * @ingroup API
     * @param format - A scanf-style format string, followed by the pointers to variables to store the results.
     */
    int scanf(const char* format, ...);
    #endif
    
    /**
     * DMA channel in use.
     * @ingroup INTERNALS
     */
    int dmaInUse[2];
    
    void isr_rx_dma(void);
    void isr_tx_dma(void);
    
protected:

    /**
     * A pointer to the UART peripheral base address being used.
     * @ingroup INTERNALS
     */
    void *_base;
    
    /**
     * The last byte to pass through the TX IRQ handler.
     * @ingroup INTERNALS
     */
    volatile char txc;
    
    /**
     * The last byte to pass through the RX IRQ handler.
     * @ingroup INTERNALS
     */
    volatile char rxc;
    
    /**
     * Pointers to the TX and RX buffers.
     * @ingroup INTERNALS
     */
    volatile char *buffer[2];
    
    /**
     * Buffer in pointers.
     * @ingroup INTERNALS
     */
    volatile int   buffer_in[2];
    
    /**
     * Buffer out pointers.
     * @ingroup INTERNALS
     */
    volatile int   buffer_out[2];
    
    /**
     * Buffer lengths.
     * @ingroup INTERNALS
     */
    volatile int   buffer_size[2];
    
    /**
     * Buffer content counters.
     * @ingroup INTERNALS
     */
    volatile int   buffer_count[2];
    
    /**
     * Buffer overflow.
     * @ingroup INTERNALS
     */
    volatile int   buffer_overflow[2];
    
    /**
     * Callback system.
     * @ingroup INTERNALS
     */
    FunctionPointer _isr[5];
    
    /**
     * TX Interrupt Service Routine.
     * @ingroup INTERNALS
     */
    void isr_tx(void);
    
    /**
     * RX Interrupt Service Routine.
     * @ingroup INTERNALS
     */
    void isr_rx(void);
    
    /**
     * Disable the interrupts for this Uart.
     * @ingroup INTERNALS
     */
    void disableIrq(void);
    
    /**
     * Enable the interrupts for this Uart.
     * @ingroup INTERNALS
     */
    void enableIrq(void);

    /**
     * Get a character from the RX buffer
     * @ingroup INTERNALS
     * @param bool True to block (wait for input)
     * @return A byte from the buffer.
     */
    int __getc(bool);
    
    /**
     * Put a character from the TX buffer
     * @ingroup INTERNALS
     * @param bool True to block (wait for space in the TX buffer if full)
     * @return 0 on success
     */
    int __putc(int c, bool);
    
    /**
     * Function: _putc 
     * Overloaded virtual function.
     */
    virtual int _putc(int c) { return __putc(c, true); }
    
    /**
     * Function: _getc 
     * Overloaded virtual function.
     */
    virtual int _getc()      { return __getc(true); }
        
    /** 
     * Function: init
     * Initialize the MODSERIAL object
     * @ingroup INTERNALS
     */
    void init(int txSize, int rxSize);
    
    /** 
     * Function: flushBuffer
     * @ingroup INTERNALS
     */
    void flushBuffer(IrqType type);

    /** 
     * Function: resizeBuffer
     * @ingroup INTERNALS
     */
    int resizeBuffer(int size, IrqType type = RxIrq, bool memory_check = true);   
    
    /** 
     * Function: downSizeBuffer
     * @ingroup INTERNALS
     */
    int downSizeBuffer(int size, IrqType type, bool memory_check); 
    
    /** 
     * Function: upSizeBuffer
     * @ingroup INTERNALS
     */
    int upSizeBuffer(int size, IrqType type, bool memory_check); 
    
    /** 
     * Function: this_reset
     * @see DMA.cpp
     * @ingroup INTERNALS
     */
    void this_reset(void);

    /** 
     * Function: dmaSetup
     * @see DMA.cpp
     * @ingroup INTERNALS
     */
    LPC_GPDMACH_TypeDef * dmaSetup(dmaChannel q);

    /** 
     * Function: dmaSelectChannel
     * @see DMA.cpp
     * @ingroup INTERNALS
     */
    LPC_GPDMACH_TypeDef * dmaSelectChannel(dmaChannel q = Channel7) {
        switch (q) {
            case Channel0: return LPC_GPDMACH0;
            case Channel1: return LPC_GPDMACH1;
            case Channel2: return LPC_GPDMACH2;
            case Channel3: return LPC_GPDMACH3;
            case Channel4: return LPC_GPDMACH4;
            case Channel5: return LPC_GPDMACH5;
            case Channel6: return LPC_GPDMACH6;
            case Channel7: return LPC_GPDMACH7;
        }
        return (LPC_GPDMACH_TypeDef *)NULL;
    }
    
};

}; // namespace AjK ends

using namespace AjK;

#endif