ricreato il link mancante
Diff: RoboClaw/UARTserial_mio/UARTSerial_mio.cpp
- Revision:
- 4:31695331ce17
diff -r 7912ab1400c4 -r 31695331ce17 RoboClaw/UARTserial_mio/UARTSerial_mio.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RoboClaw/UARTserial_mio/UARTSerial_mio.cpp Mon Jan 10 20:38:41 2022 +0000 @@ -0,0 +1,482 @@ +/* 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) \ No newline at end of file