diff --git a/platform/BufferedSerial.cpp b/platform/BufferedSerial.cpp new file mode 100644 index 0000000000..5ab50d1990 --- /dev/null +++ b/platform/BufferedSerial.cpp @@ -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 +#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(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(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 diff --git a/platform/BufferedSerial.h b/platform/BufferedSerial.h new file mode 100644 index 0000000000..8ca445a07b --- /dev/null +++ b/platform/BufferedSerial.h @@ -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 _rxbuf; + CircularBuffer _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 */ diff --git a/platform/CircularBuffer.h b/platform/CircularBuffer.h index ef3fea7f65..bb7fd38c54 100644 --- a/platform/CircularBuffer.h +++ b/platform/CircularBuffer.h @@ -76,7 +76,7 @@ public: * * @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(); @@ -87,7 +87,7 @@ public: * * @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(); diff --git a/platform/mbed_lib.json b/platform/mbed_lib.json index 3185ed1886..b326b83b28 100644 --- a/platform/mbed_lib.json +++ b/platform/mbed_lib.json @@ -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": {