From 18f677e91cfb0dd92968bc98941e0422f75157fe Mon Sep 17 00:00:00 2001 From: Hugues Kamba Date: Tue, 7 Jan 2020 13:23:36 +0000 Subject: [PATCH 1/3] Implement the BufferedSerial class to replace UARTSerial `BufferedSerial` is `UARTSerial` renamed to convey the original purpose of the class. --- TESTS/mbed_drivers/buffered_serial/main.cpp | 179 +++++++++ drivers/BufferedSerial.h | 362 +++++++++++++++++++ drivers/source/BufferedSerial.cpp | 379 ++++++++++++++++++++ mbed.h | 1 + 4 files changed, 921 insertions(+) create mode 100644 TESTS/mbed_drivers/buffered_serial/main.cpp create mode 100644 drivers/BufferedSerial.h create mode 100644 drivers/source/BufferedSerial.cpp diff --git a/TESTS/mbed_drivers/buffered_serial/main.cpp b/TESTS/mbed_drivers/buffered_serial/main.cpp new file mode 100644 index 0000000000..b96c77a58d --- /dev/null +++ b/TESTS/mbed_drivers/buffered_serial/main.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2019 Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#error [NOT_SUPPORTED] serial communication not supported for this target +#else + +#include "mbed.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "platform/FileHandle.h" +#include "drivers/BufferedSerial.h" + + +using namespace utest::v1; + + +/** + * Macros for setting console flow control. + */ +#define CONSOLE_FLOWCONTROL_RTS 1 +#define CONSOLE_FLOWCONTROL_CTS 2 +#define CONSOLE_FLOWCONTROL_RTSCTS 3 +#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x +#define mbed_console_concat(x) mbed_console_concat_(x) +#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) + + +#define MSG_KEY_ECHO_MESSAGE "echo_message" +#define MSG_VALUE_HELLO_WORLD "Hello, world!" + +#define EXPECTED_ECHOED_STRING "{{" MSG_KEY_ECHO_MESSAGE ";" MSG_VALUE_HELLO_WORLD "}}" +// The target is expected to transmit Greentea messages with \n (or \r\n) or they are not detected by the host +#define STRING_TO_SEND EXPECTED_ECHOED_STRING "\n" + + +static BufferedSerial buffered_serial_obj( + USBTX, USBRX, MBED_CONF_PLATFORM_STDIO_BAUD_RATE +); + + +FileHandle *mbed::mbed_override_console(int fd) +{ + return &buffered_serial_obj; +} + +static ssize_t buffered_serial_read(void *buffer, ssize_t length) +{ + if (length == 0) { + return 0; + } + + // Ignore the `\n` character previously sent to the host in the previous + // key-value pair that may not have been removed from the FIFO. + unsigned char *buf = static_cast(buffer); + buffered_serial_obj.read(buf, 1); + ssize_t i = (buf[0] == '{') ? 1 : 0; + + // Get the message sent by the host + for (; i < length; i++) { + TEST_ASSERT_EQUAL_UINT(1, buffered_serial_obj.read(buf + i, 1)); + } + + return length; +} + + +// Test that data sent using an UnbufferedSerial object is correctly sent. +// The test case sends a Greentea key-value pair message from the target to the +// host using an UnbufferedSerial object and expects the message +// to be echoed back by the host. The host response is received via the Greentea +// framework usual route using greentea_parse_kv(). Success is determined upon +// reception of the echoed message which indicates that the message was received +// by the host as it was sent by the target. +static void test_serial_write() +{ + char tx_msg[] = STRING_TO_SEND; + + TEST_ASSERT_EQUAL_UINT( + strlen(tx_msg) + 1, + buffered_serial_obj.write(tx_msg, strlen(tx_msg) + 1) + ); + + char rx_key[30] = {0}; + char rx_value[30] = {0}; + greentea_parse_kv(rx_key, rx_value, sizeof(rx_key), sizeof(rx_value)); + + TEST_ASSERT_EQUAL_STRING(MSG_KEY_ECHO_MESSAGE, rx_key); + TEST_ASSERT_EQUAL_STRING(MSG_VALUE_HELLO_WORLD, rx_value); +} + + +// Test that data received using an UnbufferedSerial object is correctly received. +// The test case sends a Greentea key-value pair message from the target to the +// host via the Greentea framework usual route using greentea_send_kv(). +// It expects the message to be echoed back to the target. An UnbufferedSerial +// object is used to handle the received message. Succes is determined upon +// reception of a key-value pair matching the key-value pair sent by the target. +static void test_serial_read() +{ + greentea_send_kv(MSG_KEY_ECHO_MESSAGE, MSG_VALUE_HELLO_WORLD); + + char rx_msg[sizeof(EXPECTED_ECHOED_STRING)] = {0}; + // Exclude the null terminator which is not read + ssize_t expected_rx_msg_length = sizeof(EXPECTED_ECHOED_STRING) - 1; + + buffered_serial_read(rx_msg, expected_rx_msg_length); + + TEST_ASSERT_EQUAL_STRING(EXPECTED_ECHOED_STRING, rx_msg); +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(12, "serial_comms"); + + return greentea_test_setup_handler(number_of_cases); +} + + +utest::v1::status_t greentea_failure_handler( + const Case *const source, const failure_t reason +) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + + +Case cases[] = { + Case( + "Bytes are correctly sent", + test_serial_write, greentea_failure_handler + ), + Case( + "Bytes are correctly received", + test_serial_read, greentea_failure_handler + ), +}; + + +Specification specification( + greentea_setup, cases, greentea_test_teardown_handler +); + + +int main() +{ +#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS + buffered_serial_obj.set_flow_control( + SerialBase::RTS, STDIO_UART_RTS, NC + ); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS + buffered_serial_obj.set_flow_control( + SerialBase::CTS, NC, STDIO_UART_CTS + ); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS + buffered_serial_obj.set_flow_control( + SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS + ); +#endif + return !Harness::run(specification); +} + +#endif // !DEVICE_SERIAL diff --git a/drivers/BufferedSerial.h b/drivers/BufferedSerial.h new file mode 100644 index 0000000000..5a8124e250 --- /dev/null +++ b/drivers/BufferedSerial.h @@ -0,0 +1,362 @@ +/* mbed Microcontroller Library + * Copyright (c) 2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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 && DEVICE_INTERRUPTIN) || defined(DOXYGEN_ONLY) + +#include "platform/FileHandle.h" +#include "drivers/SerialBase.h" +#include "drivers/InterruptIn.h" +#include "platform/PlatformMutex.h" +#include "platform/CircularBuffer.h" +#include "platform/NonCopyable.h" + +#ifndef MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE +#define MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE 256 +#endif + +#ifndef MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE +#define MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE 256 +#endif + +namespace mbed { +/** + * \defgroup drivers_BufferedSerial BufferedSerial class + * \ingroup drivers-public-api-uart + * @{ + */ + +/** Class providing buffered UART communication functionality using separate + * circular buffer for send and receive channels + * + */ + +class BufferedSerial: + private SerialBase, + public FileHandle, + private NonCopyable { + +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 + ); + + /** Create a BufferedSerial port, connected to the specified transmit and + * receive pins, with a particular baud rate. + * @param static_pinmap reference to structure which holds static pinmap + * @param baud The baud rate of the serial port (optional, defaults to + * MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE) + */ + BufferedSerial( + const serial_pinmap_t &static_pinmap, + int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE + ); + + virtual ~BufferedSerial(); + + /** Equivalent to POSIX poll(). Derived from FileHandle. + * Provides a mechanism to multiplex input/output over a set of file + * handles. + * The events that can be reported are POLLIN, POLLOUT, POLLHUP. + */ + virtual short poll(short events) const; + + /* Resolve ambiguities versus our private SerialBase + * (for writable, spelling differs, but just in case) + */ + using FileHandle::readable; + using FileHandle::writable; + + /** Write the contents of a buffer to a file + * + * Follows POSIX semantics: + * + * * if blocking, block until all data is written + * * if no data can be written, and non-blocking set, return -EAGAIN + * * if some data can be written, and non-blocking set, write partial + * + * @param buffer The buffer to write from + * @param length The number of bytes to write + * @return The number of bytes written, negative error on failure + */ + virtual ssize_t write(const void *buffer, size_t length); + + /** Read the contents of a file into a buffer + * + * Follows POSIX semantics: + * + * * if no data is available, and non-blocking set return -EAGAIN + * * if no data is available, and blocking set, wait until data is + * available + * * If any data is available, call returns immediately + * + * @param buffer The buffer to read in to + * @param length The number of bytes to read + * @return The number of bytes read, 0 at end of file, negative + * error on failure + */ + virtual ssize_t read(void *buffer, size_t length); + + /** Close a file + * + * @return 0 on success, negative error code on failure + */ + virtual int close(); + + /** Check if the file in an interactive terminal device + * + * @return True if the file is a terminal + * @return False if the file is not a terminal + * @return Negative error code on failure + */ + virtual int isatty(); + + /** Move the file position to a given offset from from a given location + * + * Not valid for a device type FileHandle like BufferedSerial. + * In case of BufferedSerial, returns ESPIPE + * + * @param offset The offset from whence to move to + * @param whence The start of where to seek + * SEEK_SET to start from beginning of file, + * SEEK_CUR to start from current position in file, + * SEEK_END to start from end of file + * @return The new offset of the file, negative error code on + * failure + */ + virtual off_t seek(off_t offset, int whence); + + /** Flush any buffers associated with the file + * + * @return 0 on success, negative error code on failure + */ + virtual int sync(); + + /** Set blocking or non-blocking mode + * The default is blocking. + * + * @param blocking true for blocking mode, false for non-blocking mode. + */ + virtual int set_blocking(bool blocking) + { + _blocking = blocking; + return 0; + } + + /** Check current blocking or non-blocking mode for file operations. + * + * @return true for blocking mode, false for non-blocking mode. + */ + virtual bool is_blocking() const + { + return _blocking; + } + + /** Enable or disable input + * + * Control enabling of device for input. This is primarily intended + * for temporary power-saving; the overall ability of the device to operate + * for input and/or output may be fixed at creation time, but this call can + * allow input to be temporarily disabled to permit power saving without + * losing device state. + * + * @param enabled true to enable input, false to disable. + * + * @return 0 on success + * @return Negative error code on failure + */ + virtual int enable_input(bool enabled); + + /** Enable or disable output + * + * Control enabling of device for output. This is primarily intended + * for temporary power-saving; the overall ability of the device to operate + * for input and/or output may be fixed at creation time, but this call can + * allow output to be temporarily disabled to permit power saving without + * losing device state. + * + * @param enabled true to enable output, false to disable. + * + * @return 0 on success + * @return Negative error code on failure + */ + virtual int enable_output(bool enabled); + + /** Register a callback on state change of the file. + * + * The specified callback will be called on state changes such as when + * the file can be written to or read from. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations. + * + * Note! This is not intended as an attach-like asynchronous api, but + * rather as a building block for constructing such functionality. + * + * The exact timing of when the registered function + * is called is not guaranteed and susceptible to change. It should be + * used as a cue to make read/write/poll calls to find the current state. + * + * @param func Function to call on state change + */ + virtual void sigio(Callback func); + + /** Setup interrupt handler for DCD line + * + * If DCD line is connected, an IRQ handler will be setup. + * Does nothing if DCD is NC, i.e., not connected. + * + * @param dcd_pin Pin-name for DCD + * @param active_high a boolean set to true if DCD polarity is active + * low + */ + void set_data_carrier_detect(PinName dcd_pin, bool active_high = false); + + /** Set the baud rate + * + * @param baud The baud rate + */ + void set_baud(int baud); + + // Expose private SerialBase::Parity as BufferedSerial::Parity + using SerialBase::Parity; + + /** Set the transmission format used by the serial port + * + * @param bits The number of bits in a word (5-8; default = 8) + * @param parity The parity used (None, Odd, Even, Forced1, Forced0; + * default = None) + * @param stop_bits The number of stop bits (1 or 2; default = 1) + */ + void set_format( + int bits = 8, Parity parity = BufferedSerial::None, int stop_bits = 1 + ); + +#if DEVICE_SERIAL_FC + // For now use the base enum - but in future we may have extra options + // such as XON/XOFF or manual GPIO RTSCTS. + using SerialBase::Flow; + + /** Set the flow control type on the serial port + * + * @param type the flow control type (Disabled, RTS, CTS, RTSCTS) + * @param flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for + * CTS) + * @param flow2 the second flow control pin (CTS for RTSCTS) + */ + void set_flow_control(Flow type, PinName flow1 = NC, PinName flow2 = NC); +#endif + +private: + + /** Acquire mutex + */ + virtual void api_lock(void); + + /** Release mutex + */ + virtual void api_unlock(void); + + /** Unbuffered write - invoked when write called from critical section + * @param buf_ptr The buffer to write from + * @param length The number of bytes to write + * @return The number of bytes written, negative error on failure + */ + ssize_t write_unbuffered(const char *buf_ptr, size_t length); + + /** Enable processing of byte reception IRQs and register a callback to + * process them. + */ + void enable_rx_irq(); + + /** Disable processing of byte reception IRQs and de-register callback to + * process them. + */ + void disable_rx_irq(); + + /** Enable processing of byte transmission IRQs and register a callback to + * process them. + */ + void enable_tx_irq(); + + /** Disable processing of byte transmission IRQs and de-register callback to + * process them. + */ + void disable_tx_irq(); + + /** 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; + + Callback _sigio_cb; + + bool _blocking = true; + bool _tx_irq_enabled = false; + bool _rx_irq_enabled = false; + bool _tx_enabled = true; + bool _rx_enabled = true; + InterruptIn *_dcd_irq = nullptr; + + /** Device Hanged up + * Determines if the device hanged up on us. + * + * @return True, if hanged up + */ + bool hup() const; + + /** 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); + + /** Execute a callback previously registered for state change of the file. + */ + void wake(void); + + /** Wake on data carrier detected. + */ + void dcd_irq(void); +}; + +/** @}*/ + +} //namespace mbed + +#endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN) || defined(DOXYGEN_ONLY) +#endif //MBED_BUFFEREDSERIAL_H diff --git a/drivers/source/BufferedSerial.cpp b/drivers/source/BufferedSerial.cpp new file mode 100644 index 0000000000..a2c8873b03 --- /dev/null +++ b/drivers/source/BufferedSerial.cpp @@ -0,0 +1,379 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "drivers/BufferedSerial.h" + +#if (DEVICE_SERIAL && DEVICE_INTERRUPTIN) + +#include "platform/mbed_poll.h" +#include "platform/mbed_thread.h" + +namespace mbed { + +BufferedSerial::BufferedSerial(PinName tx, PinName rx, int baud): + SerialBase(tx, rx, baud) +{ + enable_rx_irq(); +} + +BufferedSerial::BufferedSerial(const serial_pinmap_t &static_pinmap, int baud): + SerialBase(static_pinmap, baud) +{ + enable_rx_irq(); +} + +BufferedSerial::~BufferedSerial() +{ + delete _dcd_irq; +} + +void BufferedSerial::dcd_irq() +{ + wake(); +} + +void BufferedSerial::set_baud(int baud) +{ + SerialBase::baud(baud); +} + +void BufferedSerial::set_data_carrier_detect(PinName dcd_pin, bool active_high) +{ + delete _dcd_irq; + _dcd_irq = NULL; + + if (dcd_pin != NC) { + _dcd_irq = new InterruptIn(dcd_pin); + if (active_high) { + _dcd_irq->fall(callback(this, &BufferedSerial::dcd_irq)); + } else { + _dcd_irq->rise(callback(this, &BufferedSerial::dcd_irq)); + } + } +} + +void BufferedSerial::set_format(int bits, Parity parity, int stop_bits) +{ + api_lock(); + SerialBase::format(bits, parity, stop_bits); + api_unlock(); +} + +#if DEVICE_SERIAL_FC +void BufferedSerial::set_flow_control(Flow type, PinName flow1, PinName flow2) +{ + api_lock(); + SerialBase::set_flow_control(type, flow1, flow2); + api_unlock(); +} +#endif + +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) +{ + // 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() +{ + api_lock(); + + while (!_txbuf.empty()) { + api_unlock(); + // Doing better than wait would require TxIRQ to also do wake() when + // becoming empty. Worth it? + thread_sleep_for(1); + api_lock(); + } + + api_unlock(); + + return 0; +} + +void BufferedSerial::sigio(Callback func) +{ + core_util_critical_section_enter(); + _sigio_cb = func; + if (_sigio_cb) { + short current_events = poll(0x7FFF); + if (current_events) { + _sigio_cb(); + } + } + core_util_critical_section_exit(); +} + +/* Special synchronous write designed to work from critical section, such + * as in mbed_error_vprintf. + */ +ssize_t BufferedSerial::write_unbuffered(const char *buf_ptr, size_t length) +{ + while (!_txbuf.empty()) { + tx_irq(); + } + + for (size_t data_written = 0; data_written < length; data_written++) { + SerialBase::_base_putc(*buf_ptr++); + } + + return length; +} + +ssize_t BufferedSerial::write(const void *buffer, size_t length) +{ + size_t data_written = 0; + const char *buf_ptr = static_cast(buffer); + + if (length == 0) { + return 0; + } + + if (core_util_in_critical_section()) { + return write_unbuffered(buf_ptr, length); + } + + api_lock(); + + // Unlike read, we should write the whole thing if blocking. POSIX only + // allows partial as a side-effect of signal handling; it normally tries to + // write everything if blocking. Without signals we can always write all. + while (data_written < length) { + + if (_txbuf.full()) { + if (!_blocking) { + break; + } + do { + api_unlock(); + // Should we have a proper wait? + thread_sleep_for(1); + api_lock(); + } while (_txbuf.full()); + } + + while (data_written < length && !_txbuf.full()) { + _txbuf.push(*buf_ptr++); + data_written++; + } + + core_util_critical_section_enter(); + if (_tx_enabled && !_tx_irq_enabled) { + // only write to hardware in one place + BufferedSerial::tx_irq(); + if (!_txbuf.empty()) { + enable_tx_irq(); + } + } + core_util_critical_section_exit(); + } + + api_unlock(); + + return data_written != 0 ? (ssize_t) data_written : (ssize_t) - EAGAIN; +} + +ssize_t BufferedSerial::read(void *buffer, size_t length) +{ + size_t data_read = 0; + + char *ptr = static_cast(buffer); + + if (length == 0) { + return 0; + } + + api_lock(); + + while (_rxbuf.empty()) { + if (!_blocking) { + api_unlock(); + return -EAGAIN; + } + api_unlock(); + // Do we need a proper wait? + thread_sleep_for(1); + api_lock(); + } + + while (data_read < length && !_rxbuf.empty()) { + _rxbuf.pop(*ptr++); + data_read++; + } + + core_util_critical_section_enter(); + if (_rx_enabled && !_rx_irq_enabled) { + // only read from hardware in one place + BufferedSerial::rx_irq(); + if (!_rxbuf.full()) { + enable_rx_irq(); + } + } + core_util_critical_section_exit(); + + api_unlock(); + + return data_read; +} + +bool BufferedSerial::hup() const +{ + return _dcd_irq && _dcd_irq->read() != 0; +} + +void BufferedSerial::wake() +{ + if (_sigio_cb) { + _sigio_cb(); + } +} + +short BufferedSerial::poll(short events) const +{ + + short revents = 0; + // Check the Circular Buffer if space available for writing out + + + if (!_rxbuf.empty()) { + revents |= POLLIN; + } + + // POLLHUP and POLLOUT are mutually exclusive + if (hup()) { + revents |= POLLHUP; + } else if (!_txbuf.full()) { + revents |= POLLOUT; + } + + return revents; +} + +void BufferedSerial::api_lock(void) +{ + _mutex.lock(); +} + +void BufferedSerial::api_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 (!_rxbuf.full() && SerialBase::readable()) { + char data = SerialBase::_base_getc(); + _rxbuf.push(data); + } + + if (_rx_irq_enabled && _rxbuf.full()) { + disable_rx_irq(); + } + + // Report the File handler that data is ready to be read from the buffer. + if (was_empty && !_rxbuf.empty()) { + wake(); + } +} + +// Also called from write to start transfer +void BufferedSerial::tx_irq(void) +{ + bool was_full = _txbuf.full(); + char data; + + // Write to the peripheral if there is something to write + // and if the peripheral is available to write. + while (SerialBase::writeable() && _txbuf.pop(data)) { + SerialBase::_base_putc(data); + } + + if (_tx_irq_enabled && _txbuf.empty()) { + disable_tx_irq(); + } + + // Report the File handler that data can be written to peripheral. + if (was_full && !_txbuf.full() && !hup()) { + wake(); + } +} + +/* These are all called from critical section + * Attatch IRQ routines to the serial device. + */ +void BufferedSerial::enable_rx_irq() +{ + SerialBase::attach(callback(this, &BufferedSerial::rx_irq), RxIrq); + _rx_irq_enabled = true; +} + +void BufferedSerial::disable_rx_irq() +{ + SerialBase::attach(NULL, RxIrq); + _rx_irq_enabled = false; +} + +void BufferedSerial::enable_tx_irq() +{ + SerialBase::attach(callback(this, &BufferedSerial::tx_irq), TxIrq); + _tx_irq_enabled = true; +} + +void BufferedSerial::disable_tx_irq() +{ + SerialBase::attach(NULL, TxIrq); + _tx_irq_enabled = false; +} + +int BufferedSerial::enable_input(bool enabled) +{ + api_lock(); + SerialBase::enable_input(enabled); + api_unlock(); + + return 0; +} + +int BufferedSerial::enable_output(bool enabled) +{ + api_lock(); + SerialBase::enable_output(enabled); + api_unlock(); + + return 0; +} + +} // namespace mbed + +#endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN) diff --git a/mbed.h b/mbed.h index 4e1c8c5528..7f8258c2df 100644 --- a/mbed.h +++ b/mbed.h @@ -72,6 +72,7 @@ #include "drivers/RawSerial.h" #include "drivers/UnbufferedSerial.h" #include "drivers/UARTSerial.h" +#include "drivers/BufferedSerial.h" #include "drivers/FlashIAP.h" #include "drivers/MbedCRC.h" #include "drivers/QSPI.h" From 5fa76279ef511e7d548351119d5049f9d5030ba6 Mon Sep 17 00:00:00 2001 From: Hugues Kamba Date: Tue, 7 Jan 2020 14:53:36 +0000 Subject: [PATCH 2/3] Mark `UARTSerial` as deprecated --- drivers/UARTSerial.h | 108 ++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/drivers/UARTSerial.h b/drivers/UARTSerial.h index 85cffa8849..d876a55344 100644 --- a/drivers/UARTSerial.h +++ b/drivers/UARTSerial.h @@ -48,28 +48,39 @@ namespace mbed { * */ -class UARTSerial : private SerialBase, public FileHandle, private NonCopyable { +class + MBED_DEPRECATED_SINCE( + "mbed-os-6.0.0", + "Use BufferedSerial instead." + ) UARTSerial : private SerialBase, public FileHandle, private NonCopyable { public: - /** Create a UARTSerial port, connected to the specified transmit and receive pins, with a particular baud rate. + /** @deprecated + * Create a UARTSerial 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) */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") UARTSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE); - /** Create a UARTSerial port, connected to the specified transmit and receive pins, with a particular baud rate. + /** @deprecated + * Create a UARTSerial port, connected to the specified transmit and receive pins, with a particular baud rate. * @param static_pinmap reference to structure which holds static pinmap * @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE) */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") UARTSerial(const serial_pinmap_t &static_pinmap, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE); + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual ~UARTSerial(); - /** Equivalent to POSIX poll(). Derived from FileHandle. + /** @deprecated + * Equivalent to POSIX poll(). Derived from FileHandle. * Provides a mechanism to multiplex input/output over a set of file handles. */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual short poll(short events) const; /* Resolve ambiguities versus our private SerialBase @@ -78,7 +89,8 @@ public: using FileHandle::readable; using FileHandle::writable; - /** Write the contents of a buffer to a file + /** @deprecated + * Write the contents of a buffer to a file * * Follows POSIX semantics: * @@ -90,9 +102,11 @@ public: * @param length The number of bytes to write * @return The number of bytes written, negative error on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual ssize_t write(const void *buffer, size_t length); - /** Read the contents of a file into a buffer + /** @deprecated + * Read the contents of a file into a buffer * * Follows POSIX semantics: * @@ -104,23 +118,29 @@ public: * @param length The number of bytes to read * @return The number of bytes read, 0 at end of file, negative error on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual ssize_t read(void *buffer, size_t length); - /** Close a file + /** @deprecated + * Close a file * * @return 0 on success, negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int close(); - /** Check if the file in an interactive terminal device + /** @deprecated + * Check if the file in an interactive terminal device * * @return True if the file is a terminal * @return False if the file is not a terminal * @return Negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int isatty(); - /** Move the file position to a given offset from from a given location + /** @deprecated + * Move the file position to a given offset from from a given location * * Not valid for a device type FileHandle like UARTSerial. * In case of UARTSerial, returns ESPIPE @@ -132,35 +152,43 @@ public: * SEEK_END to start from end of file * @return The new offset of the file, negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual off_t seek(off_t offset, int whence); - /** Flush any buffers associated with the file + /** @deprecated + * Flush any buffers associated with the file * * @return 0 on success, negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int sync(); - /** Set blocking or non-blocking mode + /** @deprecated + * Set blocking or non-blocking mode * The default is blocking. * * @param blocking true for blocking mode, false for non-blocking mode. */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int set_blocking(bool blocking) { _blocking = blocking; return 0; } - /** Check current blocking or non-blocking mode for file operations. + /** @deprecated + * Check current blocking or non-blocking mode for file operations. * * @return true for blocking mode, false for non-blocking mode. */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual bool is_blocking() const { return _blocking; } - /** Enable or disable input + /** @deprecated + * Enable or disable input * * Control enabling of device for input. This is primarily intended * for temporary power-saving; the overall ability of the device to operate for @@ -173,9 +201,11 @@ public: * @return 0 on success * @return Negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int enable_input(bool enabled); - /** Enable or disable output + /** @deprecated + * Enable or disable output * * Control enabling of device for output. This is primarily intended * for temporary power-saving; the overall ability of the device to operate for @@ -188,9 +218,11 @@ public: * @return 0 on success * @return Negative error code on failure */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual int enable_output(bool enabled); - /** Register a callback on state change of the file. + /** @deprecated + * Register a callback on state change of the file. * * The specified callback will be called on state changes such as when * the file can be written to or read from. @@ -207,9 +239,11 @@ public: * * @param func Function to call on state change */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") virtual void sigio(Callback func); - /** Setup interrupt handler for DCD line + /** @deprecated + * Setup interrupt handler for DCD line * * If DCD line is connected, an IRQ handler will be setup. * Does nothing if DCD is NC, i.e., not connected. @@ -217,12 +251,15 @@ public: * @param dcd_pin Pin-name for DCD * @param active_high a boolean set to true if DCD polarity is active low */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") void set_data_carrier_detect(PinName dcd_pin, bool active_high = false); - /** Set the baud rate + /** @deprecated + * Set the baud rate * * @param baud The baud rate */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") void set_baud(int baud); // Expose private SerialBase::Parity as UARTSerial::Parity @@ -234,12 +271,14 @@ public: using SerialBase::Forced1; using SerialBase::Forced0; - /** Set the transmission format used by the serial port + /** @deprecated + * Set the transmission format used by the serial port * * @param bits The number of bits in a word (5-8; default = 8) * @param parity The parity used (None, Odd, Even, Forced1, Forced0; default = None) * @param stop_bits The number of stop bits (1 or 2; default = 1) */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") void set_format(int bits = 8, Parity parity = UARTSerial::None, int stop_bits = 1); #if DEVICE_SERIAL_FC @@ -252,30 +291,42 @@ public: using SerialBase::CTS; using SerialBase::RTSCTS; - /** Set the flow control type on the serial port + /** @deprecated + * Set the flow control type on the serial port * * @param type the flow control type (Disabled, RTS, CTS, RTSCTS) * @param flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for CTS) * @param flow2 the second flow control pin (CTS for RTSCTS) */ + MBED_DEPRECATED("The class has been deprecated and will be removed in the future.") void set_flow_control(Flow type, PinName flow1 = NC, PinName flow2 = NC); #endif private: - /** SerialBase lock override */ + /** @deprecated + * SerialBase lock override + */ virtual void lock(void); - /** SerialBase unlock override */ + /** @deprecated + * SerialBase unlock override + */ virtual void unlock(void); - /** Acquire mutex */ + /** @deprecated + * Acquire mutex + */ virtual void api_lock(void); - /** Release mutex */ + /** @deprecated + * Release mutex + */ virtual void api_unlock(void); - /** Unbuffered write - invoked when write called from critical section */ + /** @deprecated + * Unbuffered write - invoked when write called from critical section + */ ssize_t write_unbuffered(const char *buf_ptr, size_t length); void enable_rx_irq(); @@ -283,7 +334,8 @@ private: void enable_tx_irq(); void disable_tx_irq(); - /** Software serial buffers + /** @deprecated + * Software serial buffers * By default buffer size is 256 for TX and 256 for RX. Configurable through mbed_app.json */ CircularBuffer _rxbuf; @@ -300,14 +352,16 @@ private: bool _rx_enabled; InterruptIn *_dcd_irq; - /** Device Hanged up + /** @deprecated + * Device Hanged up * Determines if the device hanged up on us. * * @return True, if hanged up */ bool hup() const; - /** ISRs for serial + /** @deprecated + * ISRs for serial * Routines to handle interrupts on serial pins. * Copies data into Circular Buffer. * Reports the state change to File handle. From a74ffacf81aae2a915d556be6e0eea8119d3d8d0 Mon Sep 17 00:00:00 2001 From: Hugues Kamba Date: Tue, 7 Jan 2020 14:56:39 +0000 Subject: [PATCH 3/3] Remove usage of UARTSerial in Mbed OS Core diretories Replace with BufferedSerial as UARTSerial has been deprecated. --- TEST_APPS/device/exampleapp/main.cpp | 2 +- TEST_APPS/device/nfcapp/uart.cpp | 7 ++++--- TEST_APPS/device/socket_app/main.cpp | 2 +- drivers/SerialBase.h | 2 +- drivers/mbed_lib.json | 4 ++-- platform/ATCmdParser.h | 2 +- platform/mbed_lib.json | 2 +- platform/mbed_retarget.h | 6 +++--- platform/source/mbed_board.c | 2 +- platform/source/mbed_retarget.cpp | 6 +++--- 10 files changed, 18 insertions(+), 17 deletions(-) diff --git a/TEST_APPS/device/exampleapp/main.cpp b/TEST_APPS/device/exampleapp/main.cpp index 8e1654e07f..99987036cd 100644 --- a/TEST_APPS/device/exampleapp/main.cpp +++ b/TEST_APPS/device/exampleapp/main.cpp @@ -56,7 +56,7 @@ int main() FileHandle *mbed::mbed_override_console(int) { - static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); + static BufferedSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS diff --git a/TEST_APPS/device/nfcapp/uart.cpp b/TEST_APPS/device/nfcapp/uart.cpp index fa6bdf99ae..807d0d2aee 100644 --- a/TEST_APPS/device/nfcapp/uart.cpp +++ b/TEST_APPS/device/nfcapp/uart.cpp @@ -16,7 +16,7 @@ #include #include #include "platform/FileHandle.h" -#include "drivers/UARTSerial.h" +#include "drivers/BufferedSerial.h" /** * Macros for setting console flow control. @@ -32,8 +32,9 @@ mbed::FileHandle *mbed::mbed_override_console(int) { - static mbed::UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, - SERIAL_CONSOLE_BAUD_RATE); + static mbed::BufferedSerial console( + STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE + ); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS mbed::console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS diff --git a/TEST_APPS/device/socket_app/main.cpp b/TEST_APPS/device/socket_app/main.cpp index 3bc1365613..7c24bdc33a 100644 --- a/TEST_APPS/device/socket_app/main.cpp +++ b/TEST_APPS/device/socket_app/main.cpp @@ -57,7 +57,7 @@ int main() FileHandle *mbed::mbed_override_console(int) { - static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); + static BufferedSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS diff --git a/drivers/SerialBase.h b/drivers/SerialBase.h index e57483ed93..86e78a77d6 100644 --- a/drivers/SerialBase.h +++ b/drivers/SerialBase.h @@ -39,7 +39,7 @@ namespace mbed { */ /** A base class for serial port implementations - * Can't be instantiated directly (use UnbufferedSerial or UARTSerial) + * Can't be instantiated directly (use UnbufferedSerial or BufferedSerial) * * @note Synchronization level: Set by subclass */ diff --git a/drivers/mbed_lib.json b/drivers/mbed_lib.json index 4a5400817d..614baa955e 100644 --- a/drivers/mbed_lib.json +++ b/drivers/mbed_lib.json @@ -2,11 +2,11 @@ "name": "drivers", "config": { "uart-serial-txbuf-size": { - "help": "Default TX buffer size for a UARTSerial instance (unit Bytes))", + "help": "Default TX buffer size for a BufferedSerial instance (unit Bytes))", "value": 256 }, "uart-serial-rxbuf-size": { - "help": "Default RX buffer size for a UARTSerial instance (unit Bytes))", + "help": "Default RX buffer size for a BufferedSerial instance (unit Bytes))", "value": 256 }, "crc-table-size": { diff --git a/platform/ATCmdParser.h b/platform/ATCmdParser.h index 48d82ac276..02c7a6e6b4 100644 --- a/platform/ATCmdParser.h +++ b/platform/ATCmdParser.h @@ -39,7 +39,7 @@ namespace mbed { * * Here are some examples: * @code - * UARTSerial serial = UARTSerial(D1, D0); + * BufferedSerial serial = BufferedSerial(D1, D0); * ATCmdParser at = ATCmdParser(&serial, "\r\n"); * int value; * char buffer[100]; diff --git a/platform/mbed_lib.json b/platform/mbed_lib.json index 14604557e7..2afefca17e 100644 --- a/platform/mbed_lib.json +++ b/platform/mbed_lib.json @@ -12,7 +12,7 @@ }, "stdio-buffered-serial": { - "help": "(Applies if target.console-uart is true and stdio-minimal-console-only is false.) Use UARTSerial driver to obtain buffered serial I/O on stdin/stdout/stderr. If false, unbuffered serial_getc and serial_putc are used directly.", + "help": "(Applies if target.console-uart is true and stdio-minimal-console-only is false.) Use BufferedSerial driver to obtain buffered serial I/O on stdin/stdout/stderr. If false, unbuffered serial_getc and serial_putc are used directly.", "value": false }, diff --git a/platform/mbed_retarget.h b/platform/mbed_retarget.h index 247609122a..a2be2bdf39 100644 --- a/platform/mbed_retarget.h +++ b/platform/mbed_retarget.h @@ -100,7 +100,7 @@ class DirHandle; * to give the target a chance to specify a FileHandle for the console. * * If this is not provided or returns NULL, the console will be: - * - UARTSerial if configuration option "platform.stdio-buffered-serial" is + * - BufferedSerial if configuration option "platform.stdio-buffered-serial" is * true and the target has DEVICE_SERIAL; * - Raw HAL serial via serial_getc and serial_putc if * "platform.stdio-buffered-serial" is false and the target has DEVICE_SERIAL; @@ -121,10 +121,10 @@ FileHandle *mbed_target_override_console(int fd); * by mbed_target_override_console, else will default to serial - see * mbed_target_override_console for more details. * - * Example using UARTSerial: + * Example using BufferedSerial: * @code * FileHandle *mbed::mbed_override_console(int) { - * static UARTSerial my_serial(D0, D1); + * static BufferedSerial my_serial(D0, D1); * return &my_serial; * } * @endcode diff --git a/platform/source/mbed_board.c b/platform/source/mbed_board.c index 164e4f1625..b40047a517 100644 --- a/platform/source/mbed_board.c +++ b/platform/source/mbed_board.c @@ -75,7 +75,7 @@ void mbed_error_vprintf(const char *format, va_list arg) void mbed_error_puts(const char *str) { // Writing the string to the console in a critical section is - // potentially beneficial - for example in UARTSerial it + // potentially beneficial - for example in BufferedSerial it // forces the "unbuffered" mode that makes sure all characters // go out now. If we made the call not in a critical section, // it would go to the software buffer and we would be reliant diff --git a/platform/source/mbed_retarget.cpp b/platform/source/mbed_retarget.cpp index cee0c57436..32e88e0917 100644 --- a/platform/source/mbed_retarget.cpp +++ b/platform/source/mbed_retarget.cpp @@ -30,7 +30,7 @@ #include "platform/mbed_atomic.h" #include "platform/mbed_critical.h" #include "platform/mbed_poll.h" -#include "drivers/UARTSerial.h" +#include "drivers/BufferedSerial.h" #include "hal/us_ticker_api.h" #include "hal/lp_ticker_api.h" #include "hal/static_pinmap.h" @@ -150,7 +150,7 @@ extern serial_t stdio_uart; /* Private FileHandle to implement backwards-compatible functionality of * direct HAL serial access for default stdin/stdout/stderr. * This is not a particularly well-behaved FileHandle for a stream, which - * is why it's not public. People should be using UARTSerial. + * is why it's not public. People should be using BufferedSerial. */ class DirectSerial : public FileHandle { public: @@ -337,7 +337,7 @@ static FileHandle *default_console() # if MBED_CONF_PLATFORM_STDIO_BUFFERED_SERIAL static const serial_pinmap_t console_pinmap = get_uart_pinmap(STDIO_UART_TX, STDIO_UART_RX); - static UARTSerial console(console_pinmap, MBED_CONF_PLATFORM_STDIO_BAUD_RATE); + static BufferedSerial console(console_pinmap, MBED_CONF_PLATFORM_STDIO_BAUD_RATE); # if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS static const serial_fc_pinmap_t fc_pinmap = get_uart_fc_pinmap(STDIO_UART_RTS, NC); console.serial_set_flow_control(SerialBase::RTS, fc_pinmap);