Marcus Chang / AsyncSerial

source/AsyncSerial.cpp

Committer:
Marcus Chang
Date:
2015-04-10
Revision:
12:b45908320b9c
Parent:
11:6b99dbf1b65d
Child:
13:dbb23efed611

File content as of revision 12:b45908320b9c:

#include "AsyncSerial/AsyncSerial.h"

#include "mbed.h"

#define MINIMUM_TIMEOUT 1

AsyncSerial::AsyncSerial(PinName tx, PinName rx, PinName rts, PinName cts)
    :   SerialBase(tx, rx),

        sendBuffer(NULL),
        sendLength(0),
        sendIndex(0),

        isReceiving(false),
        receiveBuffer(NULL),
        receiveMaxLength(0),
        receiveIndex(0),
        receiveStatus(AsyncSerial::RECEIVE_TIMEOUT),
        receiveResult(),
        timeout()

        insideCondition(false),
        conditionStartBuffer(NULL),
        conditionStartLength(0),
        conditionEndBuffer(NULL),
        conditionEndLength(0),
        conditionIndex(0),

        sendHandler(),
        receiveHandler(),
        waitHandler(),
{
    // register ISR for receiving and sending
    SerialBase::attach<AsyncSerial>(this, &AsyncSerial::getReady, SerialBase::RxIrq);
    SerialBase::attach<AsyncSerial>(this, &AsyncSerial::putDone, SerialBase::TxIrq);

#if DEVICE_SERIAL_FC
//    SerialBase::set_flow_control(SerialBase::RTSCTS, rts, cts);
#endif
}

/*  Tx ISR
*/
void AsyncSerial::putDone()
{
    // sendIndex points to the next byte to send
    sendIndex++;

    if (sendIndex < sendLength)
    {
        // send next character if there is still more to send
        SerialBase::_base_putc(sendBuffer[sendIndex]);
    }
    else
    {
        // else signal callback function
        sendHandler.call();
    }
}

/*  Rx ISR
*/
void AsyncSerial::getReady()
{
    /*  Only read characters from buffer if we are receiving.
        On platforms with flow control, the full buffer will
        force the sender to pause. This will prevent loss of
        data between calls to receive.
    */
    if (isReceiving)
    {
        // read character from buffer
        uint8_t input = SerialBase::_base_getc();

        DEBUG("%c", input);

        // check if start condition has been met
        if (insideCondition)
        {
            /*  If stop condition has been set, check if the
                character matches. If it does increment counter
                to point to next character in sequence. Otherwise
                reset sequence search.
            */
            if (conditionEndBuffer != NULL)
            {
                if (input == conditionEndBuffer[conditionIndex])
                {
                    conditionIndex++;

                    /*  End condition has been met.
                        Set receive status to indicate sequence has been found
                        and re-arm timer. The timeout is responsible for
                        signaling the callback function and is useful for
                        decoupling the callback from the receive ISR.
                    */
                    if (conditionIndex == conditionEndLength)
                    {
                        receiveStatus = AsyncSerial::RECEIVE_FOUND;
                        timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, MINIMUM_TIMEOUT);
                    }
                }
                else
                {
                    // Character didn't match sequence. Start over.
                    conditionIndex = 0;
                }
            }

            /*  A receive buffer is available.
                Store character in buffer and check if buffer is full,
                set receive status and re-arm timeout if it is.
            */
            if (receiveBuffer != NULL)
            {
                receiveBuffer[receiveIndex] = input;
                receiveIndex++;

                /*  If end condition has been met we still store the character
                    but we do not change the receive status nor re-arm the timer.
                */
                if ((receiveIndex == receiveMaxLength) && (receiveStatus != AsyncSerial::RECEIVE_FOUND))
                {
                    receiveStatus = AsyncSerial::RECEIVE_FULL;
                    timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, MINIMUM_TIMEOUT);
                }
            }
        }
        /*  Start condition has not been met.
        */
        else
        {
            if (conditionStartBuffer != NULL)
            {
                if (input == conditionStartBuffer[conditionIndex])
                {
                    conditionIndex++;

                    /*  Set condition flag and reset index since it is reused.
                    */
                    if (conditionIndex == conditionStartLength)
                    {
                        insideCondition = true;
                        conditionIndex = 0;
                    }
                }
                else
                {
                    // Character didn't match sequence. Start over.
                    conditionIndex = 0;
                }
            }
        }
    }
}

/*  Common function for signaling receive done handler or wait done handler.
*/
void AsyncSerial::getDone(uint8_t status)
{
    // stop reception
    isReceiving = false;

    DEBUG("getDone: %d\r\n", status);

    /*  Check whether to call the wait handler or the receive handler.
    */
    if (receiveBuffer == NULL)
    {
        waitHandler.call(status);
    }
    else
    {
        receiveResult.buffer = receiveBuffer;
        receiveResult.length = receiveIndex;
        receiveResult.status = status;

        receiveHandler.call(&receiveResult);
    }
}

/*  Send block of data. Function pointer interface.
*/
void AsyncSerial::send(send_done_t handler, const char* buffer, uint16_t length)
{
    sendHandler.attach(handler);
    send(buffer, length);
}

/*  Common send block of data function.
*/
void AsyncSerial::send(const char* buffer, uint16_t length)
{
    /*  Signal callback function immediately if there is nothing to send.
    */
    if ((buffer != NULL) && (length != 0))
    {
        // Store book keeping variables
        sendBuffer = buffer;
        sendLength = length;
        sendIndex = 0;

        // Send first character. ISR sends the rest.
        SerialBase::_base_putc(sendBuffer[sendIndex]);

        DEBUG("send: %p %d\r\n", buffer, length);
    }
    else
    {
        sendHandler.call();
    }
}

/*  Receiving block of data. Function pointer interface.
*/
void AsyncSerial::receive(receive_done_t handler,
                          uint8_t* buffer, uint16_t maxLength,
                          const char* conditionStartBuffer, uint16_t conditionStartLength,
                          const char* conditionEndBuffer, uint16_t conditionEndLength,
                          uint32_t timeoutMilli)
{
    receiveHandler.attach(handler);

    receive(buffer, maxLength,
            conditionStartBuffer, conditionStartLength,
            conditionEndBuffer, conditionEndLength,
            timeoutMilli);
}

/*  Common receive function.
*/
void AsyncSerial::receive(uint8_t* buffer, uint16_t maxLength,
                          const char* _conditionStartBuffer, uint16_t _conditionStartLength,
                          const char* _conditionEndBuffer, uint16_t _conditionEndLength,
                          uint32_t timeoutMilli)
{
    /*  Signal callback function immediately if buffer and maxLength are invalid.
    */
    if ((buffer == NULL) || (maxLength == 0))
    {
        receiveResult.buffer = NULL;
        receiveResult.length = 0;
        receiveResult.status = AsyncSerial::RECEIVE_FULL;

        receiveHandler.call(&receiveResult);
    }
    /*  Otherwise, setup book keeping variables for reception.
    */
    else
    {
        // Book keeping variables for reception
        receiveBuffer = buffer;
        receiveMaxLength = maxLength;
        receiveIndex = 0;
        receiveStatus = AsyncSerial::RECEIVE_TIMEOUT;

        // Book keeping variables for conditions
        conditionStartBuffer = _conditionStartBuffer;
        conditionStartLength = _conditionStartLength;
        conditionEndBuffer = _conditionEndBuffer;
        conditionEndLength = _conditionEndLength;
        conditionIndex = 0;

        // Check if optional start condition is set
        if ((_conditionStartBuffer != NULL) && (_conditionStartLength != 0))
        {
            insideCondition = false;
        }
        else
        {
            insideCondition = true;
        }

        // Clear buffer. This re-arms the rx interrupts.
        while (SerialBase::readable())
        {
            SerialBase::_base_getc();
        }

        // Arm timer and start receiving.
        timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, timeoutMilli * 1000);
        isReceiving = true;
    }

    DEBUG("receive: %p\r\n", buffer);
}

/*  Wait until timeout or sequence is detected.
*/
void AsyncSerial::wait(wait_done_t handler,
                       const char* conditionEndBuffer, uint16_t conditionEndLength,
                       uint32_t timeoutMilli)
{
    waitHandler.attach(handler);

    receive(NULL, 0,
            NULL, 0,
            conditionEndBuffer, conditionEndLength,
            timeoutMilli);
}

/*  Timeout fired. Call common receive done function.
*/
void AsyncSerial::receiveTimeout()
{
    DEBUG("timeout\r\n");

    getDone(receiveStatus);
}