From 6ca3dea835742afdbce13b84c2275cfb258d06e1 Mon Sep 17 00:00:00 2001 From: Scott Brynen Date: Mon, 13 Nov 2017 16:22:39 +0100 Subject: [PATCH] [SoftwareSerial] add half-duplex operation Squash and rebase of https://github.com/arduino/Arduino/pull/3053 --- libraries/SoftwareSerial/src/README.adoc | 27 ++++++++++ .../SoftwareSerial/src/SoftwareSerial.cpp | 54 +++++++++++-------- libraries/SoftwareSerial/src/SoftwareSerial.h | 8 ++- 3 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 libraries/SoftwareSerial/src/README.adoc diff --git a/libraries/SoftwareSerial/src/README.adoc b/libraries/SoftwareSerial/src/README.adoc new file mode 100644 index 0000000..7b79adf --- /dev/null +++ b/libraries/SoftwareSerial/src/README.adoc @@ -0,0 +1,27 @@ += SoftwareSerial Library for Arduino (formerly NewSoftSerial) = + +The SoftwareSerial library has been developed to allow serial communication (TX and/or RX) on other digital pins of the Arduino and other Atmel chips, using software to replicate the functionality (hence the name "SoftwareSerial"). It is possible to have multiple software serial ports with speeds up to 115200 bps. A parameter enables inverted signaling for devices which require that protocol. + +For more information about this library please visit us at +http://www.arduino.cc/en/Reference/SoftwareSerial + +== Written by == + +Mikal Hart; with improvements from: ladyada, Paul Stoffregen, Garrett Mace, Brett Hagman and Scott Brynen. + +== License == + +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 + diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.cpp b/libraries/SoftwareSerial/src/SoftwareSerial.cpp index 474fe4a..fad209f 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/src/SoftwareSerial.cpp @@ -1,15 +1,16 @@ /* -SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - +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 + 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/) +-- Transmit or Receive Only by Scott Brynen (http://github.com/sbrynen) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -35,6 +36,7 @@ http://arduiniana.org. #define _DEBUG 0 #define _DEBUG_PIN1 11 #define _DEBUG_PIN2 13 + // // Includes // @@ -48,7 +50,7 @@ http://arduiniana.org. // Statics // SoftwareSerial *SoftwareSerial::active_object = 0; -uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; +uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; @@ -78,7 +80,7 @@ inline void DebugPulse(uint8_t, uint8_t) {} // /* static */ -inline void SoftwareSerial::tunedDelay(uint16_t delay) { +inline void SoftwareSerial::tunedDelay(uint16_t delay) { _delay_loop_2(delay); } @@ -86,7 +88,7 @@ inline void SoftwareSerial::tunedDelay(uint16_t delay) { // one and returns true if it replaces another bool SoftwareSerial::listen() { - if (!_rx_delay_stopbit) + if ((_receivePin == SOFTWARESERIAL_UNUSED) || !_rx_delay_stopbit) return false; if (active_object != this) @@ -108,7 +110,7 @@ bool SoftwareSerial::listen() // Stop listening. Returns true if we were actually listening. bool SoftwareSerial::stopListening() { - if (active_object == this) + if ((_receivePin != SOFTWARESERIAL_UNUSED) && (active_object == this)) { setRxIntMsk(false); active_object = NULL; @@ -254,6 +256,7 @@ SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inv _buffer_overflow(false), _inverse_logic(inverse_logic) { + // passing SOFTWARESERIAL_UNUSED for either the TX or RX pin makes the object RX or TX only setTX(transmitPin); setRX(receivePin); } @@ -272,22 +275,27 @@ void SoftwareSerial::setTX(uint8_t tx) // the pin would be output low for a short while before switching to // output high. Now, it is input with pullup for a short while, which // is fine. With inverse logic, either order is fine. - digitalWrite(tx, _inverse_logic ? LOW : HIGH); - pinMode(tx, OUTPUT); - _transmitBitMask = digitalPinToBitMask(tx); - uint8_t port = digitalPinToPort(tx); - _transmitPortRegister = portOutputRegister(port); + _transmitPin = tx; + if (tx != SOFTWARESERIAL_UNUSED) { + digitalWrite(tx, _inverse_logic ? LOW : HIGH); + pinMode(tx, OUTPUT); + _transmitBitMask = digitalPinToBitMask(tx); + uint8_t port = digitalPinToPort(tx); + _transmitPortRegister = portOutputRegister(port); + } } void SoftwareSerial::setRX(uint8_t rx) { - pinMode(rx, INPUT); - if (!_inverse_logic) - digitalWrite(rx, HIGH); // pullup for normal logic! _receivePin = rx; - _receiveBitMask = digitalPinToBitMask(rx); - uint8_t port = digitalPinToPort(rx); - _receivePortRegister = portInputRegister(port); + if (rx != SOFTWARESERIAL_UNUSED) { + pinMode(rx, INPUT); + if (!_inverse_logic) + digitalWrite(rx, HIGH); // pullup for normal logic! + _receiveBitMask = digitalPinToBitMask(rx); + uint8_t port = digitalPinToPort(rx); + _receivePortRegister = portInputRegister(port); + } } uint16_t SoftwareSerial::subtract_cap(uint16_t num, uint16_t sub) { @@ -316,7 +324,7 @@ void SoftwareSerial::begin(long speed) _tx_delay = subtract_cap(bit_delay, 15 / 4); // Only setup rx when we have a valid PCINT for this pin - if (digitalPinToPCICR(_receivePin)) { + if ((_receivePin != SOFTWARESERIAL_UNUSED) && digitalPinToPCICR(_receivePin)) { #if GCC_VERSION > 40800 // Timings counted from gcc 4.8.2 output. This works up to 115200 on // 16Mhz and 57600 on 8Mhz. @@ -376,10 +384,10 @@ void SoftwareSerial::begin(long speed) void SoftwareSerial::setRxIntMsk(bool enable) { - if (enable) - *_pcint_maskreg |= _pcint_maskvalue; - else - *_pcint_maskreg &= ~_pcint_maskvalue; + if (enable) + *_pcint_maskreg |= _pcint_maskvalue; + else + *_pcint_maskreg &= ~_pcint_maskvalue; } void SoftwareSerial::end() @@ -414,7 +422,7 @@ int SoftwareSerial::available() size_t SoftwareSerial::write(uint8_t b) { - if (_tx_delay == 0) { + if ((_transmitPin == SOFTWARESERIAL_UNUSED) || (_tx_delay == 0)) { setWriteError(); return 0; } diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.h b/libraries/SoftwareSerial/src/SoftwareSerial.h index b1a37c4..ee0b1be 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.h +++ b/libraries/SoftwareSerial/src/SoftwareSerial.h @@ -10,6 +10,7 @@ Multi-instance software serial library for Arduino/Wiring -- 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/) +-- Transmit or Receive Only by Scott Brynen (http://github.com/sbrynen) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -43,6 +44,10 @@ http://arduiniana.org. #define _SS_MAX_RX_BUFF 64 // RX buffer size #endif +#ifndef SOFTWARESERIAL_UNUSED +#define SOFTWARESERIAL_UNUSED -1 // flag for unused TX or RX pins +#endif + #ifndef GCC_VERSION #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #endif @@ -54,6 +59,7 @@ private: uint8_t _receivePin; uint8_t _receiveBitMask; volatile uint8_t *_receivePortRegister; + uint8_t _transmitPin; uint8_t _transmitBitMask; volatile uint8_t *_transmitPortRegister; volatile uint8_t *_pcint_maskreg; @@ -94,7 +100,7 @@ public: void begin(long speed); bool listen(); void end(); - bool isListening() { return this == active_object; } + bool isListening() { return (_receivePin != SOFTWARESERIAL_UNUSED) && (this == active_object); } bool stopListening(); bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } int peek();