Skip to content

Commit

Permalink
Introducing a BufferedSerial
Browse files Browse the repository at this point in the history
BufferedSerial is a FileHandle and using SerialBase.
It keeps the SerialBase private however lets the user extend FileHandle
by keeping it public.

It is using CircularBuffer class for having circular buffers.

There are some minor amendments in CircularBuffer too.

Adding an entry for tx/rx buffer sizes in platform/mbed_lib.json.
Default size is 256 bytes.

For RTOS read(), write() calls yield for other threads to carry on with their stuff.
For non-RTOS blovking read or write would mean a loop where 100 percent resources are
consumed by this loop. Need to get a better implementation in. Currently no mechanism to
wake the mcu up after WFE.
  • Loading branch information
Hasnain Virk committed May 31, 2017
1 parent 09ae609 commit 2790d44
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 2 deletions.
211 changes: 211 additions & 0 deletions platform/BufferedSerial.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2017 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.
*/

#if DEVICE_SERIAL

#include <errno.h>
#include "platform/BufferedSerial.h"
#include "platform/mbed_poll.h"
#include "platform/mbed_wait_api.h"

namespace mbed {

BufferedSerial::BufferedSerial(PinName tx, PinName rx, int baud) :
SerialBase(tx, rx, baud),
_blocking(true),
_tx_irq_enabled(false)
{
/* Attatch IRQ routines to the serial device. */
SerialBase::attach(callback(this, &BufferedSerial::rx_irq), RxIrq);
}

int BufferedSerial::close()
{
/* Does not let us pass a file descriptor. So how to close ?
* Also, does it make sense to close a device type file descriptor*/
return 0;
}

int BufferedSerial::isatty()
{
return 1;

}

off_t BufferedSerial::seek(off_t offset, int whence)
{
/*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
* buffer system (from an interrupt context) */
return -ESPIPE;
}

int BufferedSerial::sync()
{
lock();

while (!_txbuf.empty()) {
unlock();
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
wait_ms(1);
lock();
}

unlock();

return 0;
}

ssize_t BufferedSerial::write(const void* buffer, size_t length)
{
size_t data_written = 0;
const char *buf_ptr = static_cast<const char *>(buffer);

lock();

while (_txbuf.full()) {
if (!_blocking) {
unlock();
return -EAGAIN;
}
unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
lock();
}

while (data_written < length && !_txbuf.full()) {
_txbuf.push(*buf_ptr++);
data_written++;
}

core_util_critical_section_enter();
if (!_tx_irq_enabled) {
BufferedSerial::tx_irq(); // only write to hardware in one place
if (!_txbuf.empty()) {
SerialBase::attach(callback(this, &BufferedSerial::tx_irq), TxIrq);
_tx_irq_enabled = true;
}
}
core_util_critical_section_exit();

unlock();

return data_written;
}

ssize_t BufferedSerial::read(void* buffer, size_t length)
{
size_t data_read = 0;

char *ptr = static_cast<char *>(buffer);

lock();

while (_rxbuf.empty()) {
if (!_blocking) {
unlock();
return -EAGAIN;
}
unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
lock();
}

while (data_read < length && !_rxbuf.empty()) {
_rxbuf.pop(*ptr++);
data_read++;
}

unlock();

return data_read;
}

short BufferedSerial::poll(short events) const {

short revents = 0;
/* Check the Circular Buffer if space available for writing out */
if (!_txbuf.full()) {
revents |= POLLOUT;
}

if (!_rxbuf.empty()) {
revents |= POLLIN;
}

/*TODO Handle other event types */

return revents;
}

void BufferedSerial::lock(void)
{
_mutex.lock();
}

void BufferedSerial::unlock(void)
{
_mutex.unlock();
}

void BufferedSerial::rx_irq(void)
{
bool was_empty = _rxbuf.empty();

/* Fill in the receive buffer if the peripheral is readable
* and receive buffer is not full. */
while (SerialBase::readable()) {
char data = SerialBase::_base_getc();
if (!_rxbuf.full()) {
_rxbuf.push(data);
} else {
/* Drop - can we report in some way? */
}
}

/* Report the File handler that data is ready to be read from the buffer. */
if (was_empty && !_rxbuf.empty()) {
_poll_change(this);
}
}

// Also called from write to start transfer
void BufferedSerial::tx_irq(void)
{
bool was_full = _txbuf.full();

/* Write to the peripheral if there is something to write
* and if the peripheral is available to write. */
while (!_txbuf.empty() && SerialBase::writeable()) {
char data;
_txbuf.pop(data);
SerialBase::_base_putc(data);
}

if (_tx_irq_enabled && _txbuf.empty()) {
SerialBase::attach(NULL, TxIrq);
_tx_irq_enabled = false;
}

/* Report the File handler that data can be written to peripheral. */
if (was_full && !_txbuf.full()) {
_poll_change(this);
}
}


} //namespace mbed

#endif //DEVICE_SERIAL
94 changes: 94 additions & 0 deletions platform/BufferedSerial.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2017 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.
*/

#ifndef MBED_BUFFEREDSERIAL_H
#define MBED_BUFFEREDSERIAL_H

#include "platform/platform.h"

#if DEVICE_SERIAL

#include "FileHandle.h"
#include "SerialBase.h"
#include "PlatformMutex.h"
#include "serial_api.h"
#include "CircularBuffer.h"

namespace mbed {
class BufferedSerial : private SerialBase, public FileHandle {
public:
/** Create a BufferedSerial port, connected to the specified transmit and receive pins, with a particular baud rate.
* @param tx Transmit pin
* @param rx Receive pin
* @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE)
*/
BufferedSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);

/** Equivalent to POSIX poll(). Derived from FileHandle.
* Provides a mechanism to multiplex input/output over a set of file handles.
*/
virtual short poll(short events) const;

virtual ssize_t write(const void* buffer, size_t length);

virtual ssize_t read(void* buffer, size_t length);

/** Acquire mutex */
virtual void lock(void);

/** Release mutex */
virtual void unlock(void);

virtual int close();

virtual int isatty();

virtual off_t seek(off_t offset, int whence);

virtual int sync();

virtual int set_blocking(bool blocking) { _blocking = blocking; return 0; }

private:

/** Software serial buffers
* By default buffer size is 256 for TX and 256 for RX. Configurable through mbed_app.json
*/
CircularBuffer<char, MBED_CONF_PLATFORM_BUFFERED_SERIAL_RXBUF_SIZE> _rxbuf;
CircularBuffer<char, MBED_CONF_PLATFORM_BUFFERED_SERIAL_TXBUF_SIZE> _txbuf;

PlatformMutex _mutex;

bool _blocking;
bool _tx_irq_enabled;



/** ISRs for serial
* Routines to handle interrupts on serial pins.
* Copies data into Circular Buffer.
* Reports the state change to File handle.
*/
void tx_irq(void);
void rx_irq(void);


};
} //namespace mbed


#endif //DEVICE_SERIAL
#endif /* MBED_BUFFEREDSERIAL_H */
4 changes: 2 additions & 2 deletions platform/CircularBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CircularBuffer {
*
* @return True if the buffer is empty, false if not
*/
bool empty() {
bool empty() const {
core_util_critical_section_enter();
bool is_empty = (_head == _tail) && !_full;
core_util_critical_section_exit();
Expand All @@ -87,7 +87,7 @@ class CircularBuffer {
*
* @return True if the buffer is full, false if not
*/
bool full() {
bool full() const {
core_util_critical_section_enter();
bool full = _full;
core_util_critical_section_exit();
Expand Down
8 changes: 8 additions & 0 deletions platform/mbed_lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
"default-serial-baud-rate": {
"help": "Default baud rate for a Serial or RawSerial instance (if not specified in the constructor)",
"value": 9600
},
"buffered-serial-txbuf-size": {
"help": "Default TX buffer size for a BufferedSerial instance (unit Bytes))",
"value": 256
},
"buffered-serial-rxbuf-size": {
"help": "Default RX buffer size for a BufferedSerial instance (unit Bytes))",
"value": 256
}
},
"target_overrides": {
Expand Down

0 comments on commit 2790d44

Please sign in to comment.