Asynchronous Serial Library with flow control and tag detection.
Diff: source/AsyncSerial.cpp
- Revision:
- 12:b45908320b9c
- Parent:
- 11:6b99dbf1b65d
- Child:
- 13:dbb23efed611
--- a/source/AsyncSerial.cpp Fri Apr 10 14:27:21 2015 +0000 +++ b/source/AsyncSerial.cpp Fri Apr 10 17:02:09 2015 +0100 @@ -11,16 +11,13 @@ sendLength(0), sendIndex(0), - sendHandler(), - receiveHandler(), - receiveResult(), - waitHandler(), - isReceiving(false), receiveBuffer(NULL), receiveMaxLength(0), receiveIndex(0), receiveStatus(AsyncSerial::RECEIVE_TIMEOUT), + receiveResult(), + timeout() insideCondition(false), conditionStartBuffer(NULL), @@ -28,8 +25,12 @@ conditionEndBuffer(NULL), conditionEndLength(0), conditionIndex(0), - timeout() + + 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); @@ -38,36 +39,61 @@ #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 { - sendHandler.call(); + // 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; @@ -76,15 +102,23 @@ } 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; @@ -92,6 +126,8 @@ } } } + /* Start condition has not been met. + */ else { if (conditionStartBuffer != NULL) @@ -100,6 +136,8 @@ { conditionIndex++; + /* Set condition flag and reset index since it is reused. + */ if (conditionIndex == conditionStartLength) { insideCondition = true; @@ -108,6 +146,7 @@ } else { + // Character didn't match sequence. Start over. conditionIndex = 0; } } @@ -115,13 +154,18 @@ } } +/* 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); - if ((receiveBuffer == NULL) && (conditionStartBuffer == NULL)) + /* Check whether to call the wait handler or the receive handler. + */ + if (receiveBuffer == NULL) { waitHandler.call(status); } @@ -135,22 +179,30 @@ } } +/* Send block of data. Function pointer interface. +*/ void AsyncSerial::send(send_done_t handler, const char* buffer, uint16_t length) { - sendHandler.attach(handler); + 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 @@ -159,60 +211,82 @@ } } +/* 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) { - if (handler) - { - receiveHandler.attach(handler); + receiveHandler.attach(handler); - receive(buffer, maxLength, - conditionStartBuffer, conditionStartLength, - conditionEndBuffer, conditionEndLength, - timeoutMilli); - } + 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) { - receiveBuffer = buffer; - receiveMaxLength = maxLength; - receiveIndex = 0; + /* 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; - conditionStartBuffer = _conditionStartBuffer; - conditionStartLength = _conditionStartLength; - conditionEndBuffer = _conditionEndBuffer; - conditionEndLength = _conditionEndLength; - conditionIndex = 0; - - if ((_conditionStartBuffer != NULL) && (_conditionStartLength != 0)) - { - insideCondition = false; + receiveHandler.call(&receiveResult); } + /* Otherwise, setup book keeping variables for reception. + */ else { - insideCondition = true; - } + // 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; - while (SerialBase::readable()) - { - SerialBase::_base_getc(); + // 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; } - receiveStatus = AsyncSerial::RECEIVE_TIMEOUT; - timeout.attach_us<AsyncSerial>(this, &AsyncSerial::receiveTimeout, timeoutMilli * 1000); - isReceiving = true; - - DEBUG("receive: %p\r\n", receiveBuffer); + 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) @@ -225,6 +299,8 @@ timeoutMilli); } +/* Timeout fired. Call common receive done function. +*/ void AsyncSerial::receiveTimeout() { DEBUG("timeout\r\n"); @@ -232,14 +308,3 @@ getDone(receiveStatus); } -int AsyncSerial::getc() -{ - return SerialBase::_base_getc(); -} - -int AsyncSerial::putc(int c) -{ - return SerialBase::_base_putc(c); -} - -