diff options
Diffstat (limited to 'src/core/SoftwareSerial.cpp')
| -rwxr-xr-x | src/core/SoftwareSerial.cpp | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/core/SoftwareSerial.cpp b/src/core/SoftwareSerial.cpp new file mode 100755 index 0000000..a49dc4b --- /dev/null +++ b/src/core/SoftwareSerial.cpp @@ -0,0 +1,311 @@ +/* + * This class is porting arduino's SoftwareSerial for eXodusino + * + * Created on: 2012/03/17 + * @author nucho + */ + +/* + SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - + Multi-instance software serial library for Arduino/Wiring + -- Interrupt-driven receive and other improvements by ladyada + (http://ladyada.net) + -- Tuning, circular buffer, derivation from class Print/Stream, + multi-instance support, porting to 8MHz processors, + various optimizations, PROGMEM delay tables, inverse logic and + direct port writing by Mikal Hart (http://www.arduiniana.org) + -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) + -- 20MHz processor support by Garrett Mace (http://www.macetech.com) + -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + The latest version of this library can always be found at + http://arduiniana.org. + */ + +#include "SoftwareSerial.h" +#include "gpio.h" +#include "delay.h" +// +// Lookup table +// +typedef struct _DELAY_TABLE { + long baud; + unsigned short rx_delay_centering; + unsigned short rx_delay_intrabit; + unsigned short rx_delay_stopbit; + unsigned short tx_delay; +} DELAY_TABLE; + +static const DELAY_TABLE table[] = { +// baud rxcenter rxintra rxstop tx + { 38400, 11, 23, 23, 23, },//no support + { 19200, 23, 47, 47, 49, },//no support + { 9600, 50,100, 100, 100, }, + { 4800, 101, 202, 202, 202, }, +}; + + +// +// Debugging +// +// This function generates a brief pulse +// for debugging or measuring on an oscilloscope. +inline void DebugPulse(uint8_t pin, uint8_t count) { + //#if _DEBUG + // volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); + // + // uint8_t val = *pport; + // while (count--) + // { + // *pport = val | digitalPinToBitMask(pin); + // *pport = val; + // } + //#endif +} + +// +// Statics +// +SoftwareSerial *SoftwareSerial::active_object = 0; +char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; +volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; +volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; + +// +// Private methods +// + +// This function sets the current object as the "listening" +// one and returns true if it replaces another +bool SoftwareSerial::listen() { + if (active_object != this) { + _buffer_overflow = false; + + _receive_buffer_head = _receive_buffer_tail = 0; + active_object = this; + return true; + } + + return false; +} + +// +// The receive routine called by the interrupt handler +// +void SoftwareSerial::recv() { + uint8_t d = 0; + + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + if (_inverse_logic ? digitalRead(_receivePin) : !digitalRead(_receivePin)) { + // Wait approximately 1/2 of a bit width to "center" the sample + delayMicroseconds(_rx_delay_centering); + + DebugPulse(4, 1); + + // Read each of the 8 bits + for (uint8_t i = 0x1; i; i <<= 1) { + delayMicroseconds(_rx_delay_intrabit); + DebugPulse(4, 1); + uint8_t noti = ~i; + if (digitalRead(_receivePin)) + d |= i; + else + // else clause added to ensure function timing is ~balanced + d &= noti; + } + + // skip the stop bit + delayMicroseconds(_rx_delay_stopbit); + DebugPulse(4, 1); + + if (_inverse_logic) + d = ~d; + + // if buffer full, set the overflow flag and return + if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF + != _receive_buffer_head) { + // save new data in buffer: tail points to where byte goes + _receive_buffer[_receive_buffer_tail] = d; // save new byte + _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + } else { + _buffer_overflow = true; + } + } +} +// Interrupt handling +// + +/* static */ +inline void SoftwareSerial::handle_interrupt() { + if (active_object) { + active_object->recv(); + } +} + +// +// Constructor +// +SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, + bool inverse_logic /* = false */) : + _rx_delay_centering(0), _rx_delay_intrabit(0), _rx_delay_stopbit(0), + _tx_delay(0), _buffer_overflow(false), + _inverse_logic(inverse_logic) { + setTX(transmitPin); + setRX(receivePin); +} + +// +// Destructor +// +SoftwareSerial::~SoftwareSerial() { + end(); +} + +void SoftwareSerial::setTX(uint8_t tx) { + if (tx >= 0) { + pinMode(tx, OUTPUT); + digitalWrite(tx, HIGH); + } + _transmitPin = tx; +} + +void SoftwareSerial::setRX(uint8_t rx) { + if (rx >= 0) { + if (!_inverse_logic) { + //TODO:pin pullup + } else { + //TODO:pin pulldown + } + } + _receivePin = rx; +} + +// +// Public methods +// + +void SoftwareSerial::begin(long speed) { + _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay + = 0; + + for (unsigned i = 0; i < sizeof(table) / sizeof(table[0]); ++i) { + long baud = table[i].baud; + if (baud == speed) { + _rx_delay_centering = table[i].rx_delay_centering; + _rx_delay_intrabit = table[i].rx_delay_intrabit; + _rx_delay_stopbit = table[i].rx_delay_stopbit; + _tx_delay = table[i].tx_delay; + break; + } + } + // Set up RX interrupts, but only if we have a valid RX baud rate + if (_rx_delay_stopbit && _receivePin > 0) { + attachInterrupt(_receivePin, SoftwareSerial::handle_interrupt, FALLING); + delayMicroseconds(_tx_delay);// if we were low this establishes the end + } + + listen(); +} + +void SoftwareSerial::end() { + if (_receivePin > 0) { + detachInterrupt(_receivePin); + } +} + +// Read data from buffer +int SoftwareSerial::read() { + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte + _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; + return d; +} + +int SoftwareSerial::available() { + if (!isListening()) + return 0; + + return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) + % _SS_MAX_RX_BUFF; +} + +void SoftwareSerial::write(const char c) { + if (_tx_delay == 0) { + return; + } + if (_transmitPin < 0) { + return; + } + + // Write the start bit + digitalWrite(_transmitPin, _inverse_logic ? HIGH : LOW); + delayMicroseconds(_tx_delay); + + // Write each of the 8 bits + if (_inverse_logic) { + for (uint8_t mask = 0x01; mask; mask <<= 1) { + if (c & mask) // choose bit + digitalWrite(_transmitPin, LOW); // send 1 + else + digitalWrite(_transmitPin, HIGH); // send 0 + + delayMicroseconds(_tx_delay); + } + + digitalWrite(_transmitPin, LOW); // restore pin to natural state + } else { + for (uint8_t mask = 0x01; mask; mask <<= 1) { + if (c & mask) // choose bit + digitalWrite(_transmitPin, HIGH); // send 1 + else + digitalWrite(_transmitPin, LOW); // send 0 + + delayMicroseconds(_tx_delay); + } + + digitalWrite(_transmitPin, HIGH); // restore pin to natural state + } + + delayMicroseconds(_tx_delay); +} + +void SoftwareSerial::flush() { + if (!isListening()) + return; + + _receive_buffer_head = _receive_buffer_tail = 0; +} + +int SoftwareSerial::peek() { + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + return _receive_buffer[_receive_buffer_head]; +} |
