Asynchronous Serial Library with flow control and tag detection.
source/AsyncSerial.cpp
- Committer:
- marcuschang
- Date:
- 2015-04-28
- Revision:
- 21:bdc3c69a1cd7
- Parent:
- 20:c36866a2d5a6
File content as of revision 21:bdc3c69a1cd7:
/* 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() { #if DEVICE_SERIAL_FC if ((rts != NC) && (cts != NC)) { SerialBase::set_flow_control(SerialBase::RTSCTS, rts, cts); } #endif } AsyncSerial::~AsyncSerial() { SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::RxIrq); SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::TxIrq); } /* Tx ISR */ void AsyncSerial::putDone() { // fill Tx buffers (in case the buffer can contain more than 1 byte) while(SerialBase::writeable()) { if(sendIndex < sendLength) { // send next character if there is still more to send SerialBase::_base_putc(sendBuffer[sendIndex]); // sendIndex points to the next byte to send sendIndex++; } else { // disable the TX interrupt when there is nothing left to send SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::TxIrq); // signal callback function sendHandler.call(); break; } } } /* Rx ISR */ void AsyncSerial::getReady() { // read while there are characters in buffer while(SerialBase::readable()) { // read single 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) { // Disable Rx interrupt SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::RxIrq); // Fire timeout to signal callback receiveStatus = AsyncSerial::RECEIVE_FOUND; timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, MINIMUM_TIMEOUT); break; } } 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)) { // Disable Rx interrupt SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::RxIrq); // Fire timeout to signal callback receiveStatus = AsyncSerial::RECEIVE_FULL; timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, MINIMUM_TIMEOUT); break; } } } /* 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; if(SerialBase::writeable()) { // make sure not to cause contention in the irq SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::TxIrq); // only write to hardware in one place AsyncSerial::putDone(); // enable TX interrupts SerialBase::attach<AsyncSerial>(this, &AsyncSerial::putDone, SerialBase::TxIrq); } 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; } // Arm timer timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, _timeoutMilli * 1000); // Arm Rx interrupts and start receiving SerialBase::attach<AsyncSerial>(this, &AsyncSerial::getReady, SerialBase::RxIrq); 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"); // Disable Rx interrupt (interrupts are not disabled if Rx timeouts) if (receiveStatus == AsyncSerial::RECEIVE_TIMEOUT) { SerialBase::attach<AsyncSerial>(NULL, NULL, SerialBase::RxIrq); } getDone(receiveStatus); }