Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
UARTserial_mio/UARTSerial_mio.cpp
- Committer:
- anfontanelli
- Date:
- 2021-09-14
- Revision:
- 13:6a4969d25627
- Parent:
- 11:04d8899b5d82
File content as of revision 13:6a4969d25627:
/* mbed Microcontroller Library
* Copyright (c) 2006-2017 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 "UARTSerial_mio.h"
#if (DEVICE_SERIAL && DEVICE_INTERRUPTIN)
#include "platform/mbed_poll.h"
#if MBED_CONF_RTOS_PRESENT
#include "rtos/ThisThread.h"
#else
#include "platform/mbed_wait_api.h"
#endif
namespace mbed {
UARTSerial_mio::UARTSerial_mio(PinName tx, PinName rx, int baud) :
SerialBase(tx, rx, baud),
_blocking(true),
_tx_irq_enabled(false),
_rx_irq_enabled(false),
_tx_enabled(true),
_rx_enabled(true),
_dcd_irq(NULL)
{
/* Attatch IRQ routines to the serial device. */
enable_rx_irq();
}
UARTSerial_mio::~UARTSerial_mio()
{
delete _dcd_irq;
}
void UARTSerial_mio::dcd_irq()
{
wake();
}
void UARTSerial_mio::set_baud(int baud)
{
SerialBase::baud(baud);
}
void UARTSerial_mio::set_format(int bits, Parity parity, int stop_bits)
{
api_lock();
SerialBase::format(bits, parity, stop_bits);
api_unlock();
}
#if DEVICE_SERIAL_FC
void UARTSerial_mio::set_flow_control(Flow type, PinName flow1, PinName flow2)
{
api_lock();
SerialBase::set_flow_control(type, flow1, flow2);
api_unlock();
}
#endif
int UARTSerial_mio::close(){return 0;}
int UARTSerial_mio::isatty(){return 1;}
off_t UARTSerial_mio::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 UARTSerial_mio::sync()
{
api_lock();
while (!_txbuf.empty()) {
api_unlock();
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
wait_us(500);
api_lock();
}
api_unlock();
return 0;
}
int UARTSerial_mio::flush()
{
api_lock();
char c;
while (!_rxbuf.empty()) {
api_unlock();
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
wait_us(500);
_rxbuf.pop(c);
api_lock();
}
api_unlock();
return 0;
}
void UARTSerial_mio::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 UARTSerial_mio::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 UARTSerial_mio::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();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
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) {
UARTSerial_mio::tx_irq(); // only write to hardware in one place
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 UARTSerial_mio::read(void *buffer, size_t length)
{
size_t data_read = 0;
float timeout = 1.0; //ms
float tm = 0.0;
char *ptr = static_cast<char *>(buffer);
if (length == 0) {
return 0;
}
api_lock();
while (_rxbuf.size()!=length && tm <= timeout) {
if (!_blocking) {
api_unlock();
return -EAGAIN;
}
api_unlock();
wait_us(10); // XXX todo - proper wait, WFE for non-rtos ?
api_lock();
tm = tm + 0.01; //10/1000
}
while (data_read < length && !_rxbuf.empty()) {
_rxbuf.pop(*ptr++);
data_read++;
}
core_util_critical_section_enter();
if (_rx_enabled && !_rx_irq_enabled) {
UARTSerial_mio::rx_irq(); // only read from hardware in one place
if (!_rxbuf.full()) {
enable_rx_irq();
}
}
core_util_critical_section_exit();
api_unlock();
return data_read;
}
ssize_t UARTSerial_mio::read_timeout(void *buffer, size_t length, double _timeOut)
{
size_t data_read = 0;
double timeout = _timeOut; //ms
double tm = 0.0;
char *ptr = static_cast<char *>(buffer);
if (length == 0) {
return 0;
}
api_lock();
while (_rxbuf.size()!=length && tm<=timeout) {
if (!_blocking) {
api_unlock();
return -EAGAIN;
}
api_unlock();
wait_us(1); // XXX todo - proper wait, WFE for non-rtos ?
api_lock();
tm = tm + 0.001; //10/1000
}
//printf("tm: %f\r\n",tm);
tm = 0.0;
while (data_read < length && !_rxbuf.empty() && tm<=timeout) {
_rxbuf.pop(*ptr++);
data_read++;
tm = tm + 0.001; //10/1000
}
core_util_critical_section_enter();
if (_rx_enabled && !_rx_irq_enabled) {
UARTSerial_mio::rx_irq(); // only read from hardware in one place
if (!_rxbuf.full()) {
enable_rx_irq();
}
}
core_util_critical_section_exit();
api_unlock();
return data_read;
}
bool UARTSerial_mio::hup() const
{
return _dcd_irq && _dcd_irq->read() != 0;
}
void UARTSerial_mio::wake()
{
if (_sigio_cb) {
_sigio_cb();
}
}
short UARTSerial_mio::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;
}
/*TODO Handle other event types */
return revents;
}
void UARTSerial_mio::lock()
{
// This is the override for SerialBase.
// No lock required as we only use SerialBase from interrupt or from
// inside our own critical section.
}
void UARTSerial_mio::unlock()
{
// This is the override for SerialBase.
}
void UARTSerial_mio::api_lock(void)
{
//_mutex.lock();
}
void UARTSerial_mio::api_unlock(void)
{
//_mutex.unlock();
}
void UARTSerial_mio::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 UARTSerial_mio::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 */
void UARTSerial_mio::enable_rx_irq()
{
SerialBase::attach(callback(this, &UARTSerial_mio::rx_irq), RxIrq);
_rx_irq_enabled = true;
}
void UARTSerial_mio::disable_rx_irq()
{
SerialBase::attach(NULL, RxIrq);
_rx_irq_enabled = false;
}
void UARTSerial_mio::enable_tx_irq()
{
SerialBase::attach(callback(this, &UARTSerial_mio::tx_irq), TxIrq);
_tx_irq_enabled = true;
}
void UARTSerial_mio::disable_tx_irq()
{
SerialBase::attach(NULL, TxIrq);
_tx_irq_enabled = false;
}
int UARTSerial_mio::enable_input(bool enabled)
{
core_util_critical_section_enter();
if (_rx_enabled != enabled) {
if (enabled) {
UARTSerial_mio::rx_irq();
if (!_rxbuf.full()) {
enable_rx_irq();
}
} else {
disable_rx_irq();
}
_rx_enabled = enabled;
}
core_util_critical_section_exit();
return 0;
}
int UARTSerial_mio::enable_output(bool enabled)
{
core_util_critical_section_enter();
if (_tx_enabled != enabled) {
if (enabled) {
UARTSerial_mio::tx_irq();
if (!_txbuf.empty()) {
enable_tx_irq();
}
} else {
disable_tx_irq();
}
_tx_enabled = enabled;
}
core_util_critical_section_exit();
return 0;
}
void UARTSerial_mio::wait_ms(uint32_t millisec)
{
/* wait_ms implementation for RTOS spins until exact microseconds - we
* want to just sleep until next tick.
*/
#if MBED_CONF_RTOS_PRESENT
rtos::ThisThread::sleep_for(millisec);
#else
::wait_ms(millisec);
#endif
}
void UARTSerial_mio::wait_us(uint32_t microseconds)
{
/* wait_ms implementation for RTOS spins until exact microseconds - we
* want to just sleep until next tick.
*/
#if MBED_CONF_RTOS_PRESENT
rtos::ThisThread::sleep_for(microseconds/1000);
#else
::wait_us(microseconds);
#endif
}
} //namespace mbed
#endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN)