mbed library sources. Supersedes mbed-src.
Dependents: SPIne CH_Communicatuin_Test CH_Communicatuin_Test2 MCP_SPIne ... more
Fork of mbed-dev-f303 by
drivers/UARTSerial.cpp
- Committer:
- AnnaBridge
- Date:
- 2017-07-06
- Revision:
- 168:9672193075cf
- Parent:
- 167:e84263d55307
- Child:
- 170:19eb464bc2be
File content as of revision 168:9672193075cf:
/* mbed Microcontroller Library * Copyright (c) 2006-2017 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if (DEVICE_SERIAL && DEVICE_INTERRUPTIN) #include <errno.h> #include "UARTSerial.h" #include "platform/mbed_poll.h" #include "platform/mbed_wait_api.h" namespace mbed { UARTSerial::UARTSerial(PinName tx, PinName rx, int baud) : SerialBase(tx, rx, baud), _blocking(true), _tx_irq_enabled(false), _dcd_irq(NULL) { /* Attatch IRQ routines to the serial device. */ SerialBase::attach(callback(this, &UARTSerial::rx_irq), RxIrq); } UARTSerial::~UARTSerial() { delete _dcd_irq; } void UARTSerial::dcd_irq() { wake(); } void UARTSerial::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, &UARTSerial::dcd_irq)); } else { _dcd_irq->rise(callback(this, &UARTSerial::dcd_irq)); } } } int UARTSerial::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 UARTSerial::isatty() { return 1; } off_t UARTSerial::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::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_ms(1); api_lock(); } api_unlock(); return 0; } void UARTSerial::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(); } ssize_t UARTSerial::write(const void* buffer, size_t length) { size_t data_written = 0; const char *buf_ptr = static_cast<const char *>(buffer); api_lock(); while (_txbuf.full()) { if (!_blocking) { api_unlock(); return -EAGAIN; } api_unlock(); wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ? api_lock(); } while (data_written < length && !_txbuf.full()) { _txbuf.push(*buf_ptr++); data_written++; } core_util_critical_section_enter(); if (!_tx_irq_enabled) { UARTSerial::tx_irq(); // only write to hardware in one place if (!_txbuf.empty()) { SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq); _tx_irq_enabled = true; } } core_util_critical_section_exit(); api_unlock(); return data_written; } ssize_t UARTSerial::read(void* buffer, size_t length) { size_t data_read = 0; char *ptr = static_cast<char *>(buffer); api_lock(); while (_rxbuf.empty()) { if (!_blocking) { api_unlock(); return -EAGAIN; } api_unlock(); wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ? api_lock(); } while (data_read < length && !_rxbuf.empty()) { _rxbuf.pop(*ptr++); data_read++; } api_unlock(); return data_read; } bool UARTSerial::hup() const { return _dcd_irq && _dcd_irq->read() != 0; } void UARTSerial::wake() { if (_sigio_cb) { _sigio_cb(); } } short UARTSerial::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::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::unlock() { // This is the override for SerialBase. } void UARTSerial::api_lock(void) { _mutex.lock(); } void UARTSerial::api_unlock(void) { _mutex.unlock(); } void UARTSerial::rx_irq(void) { bool was_empty = _rxbuf.empty(); /* Fill in the receive buffer if the peripheral is readable * and receive buffer is not full. */ while (SerialBase::readable()) { char data = SerialBase::_base_getc(); if (!_rxbuf.full()) { _rxbuf.push(data); } else { /* Drop - can we report in some way? */ } } /* Report the File handler that data is ready to be read from the buffer. */ if (was_empty && !_rxbuf.empty()) { wake(); } } // Also called from write to start transfer void UARTSerial::tx_irq(void) { bool was_full = _txbuf.full(); /* Write to the peripheral if there is something to write * and if the peripheral is available to write. */ while (!_txbuf.empty() && SerialBase::writeable()) { char data; _txbuf.pop(data); SerialBase::_base_putc(data); } if (_tx_irq_enabled && _txbuf.empty()) { SerialBase::attach(NULL, TxIrq); _tx_irq_enabled = false; } /* Report the File handler that data can be written to peripheral. */ if (was_full && !_txbuf.full() && !hup()) { wake(); } } } //namespace mbed #endif //(DEVICE_SERIAL && DEVICE_INTERRUPTIN)