Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
source/AsyncSerial.cpp
- Committer:
- Marcus Chang 
- Date:
- 2015-04-17
- Revision:
- 17:5dd6bcc93a8a
- Parent:
- 16:55e6fbfc1e9d
- Child:
- 18:9127f9fae61f
File content as of revision 17:5dd6bcc93a8a:
/* mbed Microcontroller Library
 * Copyright (c) 2006-2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#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),
        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
    if ((rts != NC) && (cts != NC))
    {
        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()
{
    // 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)
{
    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* _receiveBuffer, uint16_t _maxLength,
                          const char* _conditionStartBuffer, uint16_t _conditionStartLength,
                          const char* _conditionEndBuffer, uint16_t _conditionEndLength,
                          uint32_t _timeoutMilli)
{
    receiveHandler.attach(_handler);
    /*  Signal callback function immediately if buffer and maxLength are invalid.
    */
    if ((_receiveBuffer == NULL) || (_maxLength == 0))
    {
        receiveResult.buffer = NULL;
        receiveResult.length = 0;
        receiveResult.status = AsyncSerial::RECEIVE_FULL;
        receiveHandler.call(&receiveResult);
    }
    else
    {
        receive(_receiveBuffer, _maxLength,
                _conditionStartBuffer, _conditionStartLength,
                _conditionEndBuffer, _conditionEndLength,
                _timeoutMilli);
    }
}
/*  Common receive function.
*/
void AsyncSerial::receive(uint8_t* _receiveBuffer, uint16_t _maxLength,
                          const char* _conditionStartBuffer, uint16_t _conditionStartLength,
                          const char* _conditionEndBuffer, uint16_t _conditionEndLength,
                          uint32_t _timeoutMilli)
{
    // Book keeping variables for reception
    receiveBuffer = _receiveBuffer;
    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);
    DEBUG("receive: %p\r\n", _receiveBuffer);
}
/*  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);
}
            
    
