[SoftwareSerial] add half-duplex operation

Squash and rebase of https://github.com/arduino/Arduino/pull/3053
This commit is contained in:
Scott Brynen 2017-11-13 16:22:39 +01:00 committed by Martino Facchin
parent fb7c2cc02e
commit 6ca3dea835
3 changed files with 65 additions and 24 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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();