Merge pull request #12207 from hugueskamba/hk-add-buffered_serial

Add BufferedSerial class to replace UARTSerial
pull/12213/head
Martin Kojtal 2020-01-16 10:06:22 +00:00 committed by GitHub
commit 18c941cc84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1020 additions and 44 deletions

View File

@ -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<unsigned char *>(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

View File

@ -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

View File

@ -16,7 +16,7 @@
#include <stdio.h>
#include <stdarg.h>
#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

View File

@ -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

362
drivers/BufferedSerial.h Normal file
View File

@ -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<BufferedSerial> {
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<void()> 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<char, MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE> _rxbuf;
CircularBuffer<char, MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE> _txbuf;
PlatformMutex _mutex;
Callback<void()> _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

View File

@ -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
*/

View File

@ -48,28 +48,39 @@ namespace mbed {
*
*/
class UARTSerial : private SerialBase, public FileHandle, private NonCopyable<UARTSerial> {
class
MBED_DEPRECATED_SINCE(
"mbed-os-6.0.0",
"Use BufferedSerial instead."
) UARTSerial : private SerialBase, public FileHandle, private NonCopyable<UARTSerial> {
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<void()> 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<char, MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE> _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.

View File

@ -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": {

View File

@ -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<void()> 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<const char *>(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<char *>(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)

1
mbed.h
View File

@ -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"

View File

@ -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];

View File

@ -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
},

View File

@ -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

View File

@ -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

View File

@ -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);