From 56d19bf5fe9b38cdd438e06e1396b4a341d86274 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" <19971886+dok-net@users.noreply.github.com> Date: Sat, 31 Dec 2022 19:15:30 +0100 Subject: [PATCH] Release 7.0.0 (#267) * Breaking change, rx interrupt triggered receive callback instead of polling. * uint rollover (regression) fixed * GCC and constexpr member function fix. --- keywords.txt | 1 - library.json | 2 +- library.properties | 2 +- src/SoftwareSerial.cpp | 58 +++++++++++++++--------------------------- src/SoftwareSerial.h | 24 +++++++++-------- 5 files changed, 36 insertions(+), 51 deletions(-) diff --git a/keywords.txt b/keywords.txt index 52d48ab..d668cc2 100644 --- a/keywords.txt +++ b/keywords.txt @@ -30,7 +30,6 @@ end KEYWORD2 isListening KEYWORD2 stopListening KEYWORD2 onReceive KEYWORD2 -perform_work KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/library.json b/library.json index f6b50b5..8ffc2b3 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "EspSoftwareSerial", - "version": "6.17.1", + "version": "7.0.0", "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", "keywords": [ "serial", "io", "softwareserial" diff --git a/library.properties b/library.properties index 833e99b..7cbc82a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=EspSoftwareSerial -version=6.17.1 +version=7.0.0 author=Dirk Kaar, Peter Lerup maintainer=Dirk Kaar sentence=Implementation of the Arduino software serial for ESP8266/ESP32. diff --git a/src/SoftwareSerial.cpp b/src/SoftwareSerial.cpp index 73f56a1..b4a8648 100644 --- a/src/SoftwareSerial.cpp +++ b/src/SoftwareSerial.cpp @@ -69,10 +69,7 @@ SoftwareSerial::~SoftwareSerial() { end(); } -#if __GNUC__ >= 10 -constexpr -#endif -bool SoftwareSerial::isValidGPIOpin(int8_t pin) const { +constexpr bool SoftwareSerial::isValidGPIOpin(int8_t pin) { #if defined(ESP8266) return (pin >= 0 && pin <= 16) && !isFlashInterfacePin(pin); #elif defined(ESP32) @@ -80,7 +77,7 @@ bool SoftwareSerial::isValidGPIOpin(int8_t pin) const { // Remmove the flash memory pins on related devices, since using these causes memory access issues. #ifdef CONFIG_IDF_TARGET_ESP32 // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf, - // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/esp32-devkitC-v4-pinout.jpg + // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/esp32-devkitC-v4-pinout.jpg return (pin == 1) || (pin >= 3 && pin <= 5) || (pin >= 12 && pin <= 15) || (!psramFound() && pin >= 16 && pin <= 17) || @@ -91,10 +88,10 @@ bool SoftwareSerial::isValidGPIOpin(int8_t pin) const { // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/_images/esp32-s2_saola1-pinout.jpg return (pin >= 1 && pin <= 21) || (pin >= 33 && pin <= 44); #elif CONFIG_IDF_TARGET_ESP32C3 - // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf, + // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf, // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/_images/esp32-c3-devkitm-1-v1-pinout.jpg return (pin >= 0 && pin <= 1) || (pin >= 3 && pin <= 7) || (pin >= 18 && pin <= 21); -#else +#else return pin >= 0; #endif #else @@ -102,10 +99,7 @@ bool SoftwareSerial::isValidGPIOpin(int8_t pin) const { #endif } -#if __GNUC__ >= 10 -constexpr -#endif -bool SoftwareSerial::isValidRxGPIOpin(int8_t pin) const { +constexpr bool SoftwareSerial::isValidRxGPIOpin(int8_t pin) { return isValidGPIOpin(pin) #if defined(ESP8266) && (pin != 16) @@ -113,10 +107,7 @@ bool SoftwareSerial::isValidRxGPIOpin(int8_t pin) const { ; } -#if __GNUC__ >= 10 -constexpr -#endif -bool SoftwareSerial::isValidTxGPIOpin(int8_t pin) const { +constexpr bool SoftwareSerial::isValidTxGPIOpin(int8_t pin) { return isValidGPIOpin(pin) #if defined(ESP32) #ifdef CONFIG_IDF_TARGET_ESP32 @@ -130,10 +121,7 @@ bool SoftwareSerial::isValidTxGPIOpin(int8_t pin) const { ; } -#if __GNUC__ >= 10 -constexpr -#endif -bool SoftwareSerial::hasRxGPIOPullUp(int8_t pin) const { +constexpr bool SoftwareSerial::hasRxGPIOPullUp(int8_t pin) { #if defined(ESP32) return !(pin >= 34 && pin <= 39); #else @@ -342,7 +330,7 @@ void SoftwareSerial::lazyDelay() { if (!m_intTxEnabled) { restoreInterrupts(); } const auto expired = microsToTicks(micros()) - m_periodStart; const int32_t remaining = m_periodDuration - expired; - const int32_t ms = remaining > 0 ? static_cast(ticksToMicros(remaining) / 1000L) : 0; + const uint32_t ms = remaining > 0 ? ticksToMicros(remaining) / 1000UL : 0; if (ms > 0) { delay(ms); @@ -359,11 +347,9 @@ void SoftwareSerial::lazyDelay() { void IRAM_ATTR SoftwareSerial::preciseDelay() { uint32_t ticks; - uint32_t expired; do { ticks = microsToTicks(micros()); - expired = ticks - m_periodStart; - } while (static_cast(m_periodDuration - expired) > 0); + } while ((ticks - m_periodStart) < m_periodDuration); m_periodDuration = 0; m_periodStart = ticks; } @@ -634,19 +620,23 @@ void SoftwareSerial::rxBits(const uint32_t isrTick) { } void IRAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial* self) { - uint32_t curTick = microsToTicks(micros()); - bool level = *self->m_rxReg & self->m_rxBitMask; + const bool level = *self->m_rxReg & self->m_rxBitMask; + const uint32_t curTick = microsToTicks(micros()); + const bool empty = !self->m_isrBuffer->available(); // Store level and tick in the buffer unless we have an overflow // tick's LSB is repurposed for the level bit if (!self->m_isrBuffer->push((curTick | 1U) ^ !level)) self->m_isrOverflow.store(true); + // Trigger rx callback only when receiver is starved + if (empty && self->m_rxHandler) self->m_rxHandler(); } void IRAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial* self) { - uint32_t start = microsToTicks(micros()); + bool level = self->m_invert; + const uint32_t start = microsToTicks(micros()); uint32_t wait = self->m_bitTicks - microsToTicks(2U); + const bool empty = !self->m_isrBuffer->available(); - bool level = self->m_invert; // Store level and tick in the buffer unless we have an overflow // tick's LSB is repurposed for the level bit if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); @@ -663,17 +653,11 @@ void IRAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial* self) { level = !level; } } + // Trigger rx callback only when receiver is starved + if (empty && self->m_rxHandler) self->m_rxHandler(); } -void SoftwareSerial::onReceive(Delegate handler) { - receiveHandler = handler; +void SoftwareSerial::onReceive(Delegate handler) { + m_rxHandler = handler; } -void SoftwareSerial::perform_work() { - if (!m_rxValid) { return; } - rxBits(); - if (receiveHandler) { - int avail = m_buffer->available(); - if (avail) { receiveHandler(avail); } - } -} diff --git a/src/SoftwareSerial.h b/src/SoftwareSerial.h index de5a89c..8ba7103 100644 --- a/src/SoftwareSerial.h +++ b/src/SoftwareSerial.h @@ -203,12 +203,13 @@ class SoftwareSerial : public Stream { bool isListening() { return m_rxEnabled; } bool stopListening() { enableRx(false); return true; } - /// Set an event handler for received data. - void onReceive(Delegate handler); - - /// Run the internal processing and event engine. Can be iteratively called - /// from loop, or otherwise scheduled. - void perform_work(); + /// onReceive sets a callback that will be called in interrupt context + /// when data is received. + /// More precisely, the callback is triggered when EspSoftwareSerial detects + /// a new reception, which may not yet have completed on invocation. + /// Reading - never from this interrupt context - should therefore be + /// delayed for the duration of one incoming word. + void onReceive(Delegate handler); using Print::write; @@ -223,11 +224,11 @@ class SoftwareSerial : public Stream { // If offCycle == 0, the level remains unchanged from dutyCycle. void writePeriod( uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); - constexpr bool isValidGPIOpin(int8_t pin) const; - constexpr bool isValidRxGPIOpin(int8_t pin) const; - constexpr bool isValidTxGPIOpin(int8_t pin) const; + static constexpr bool isValidGPIOpin(int8_t pin); + static constexpr bool isValidRxGPIOpin(int8_t pin); + static constexpr bool isValidTxGPIOpin(int8_t pin); // result is only defined for a valid Rx GPIO pin - constexpr bool hasRxGPIOPullUp(int8_t pin) const; + static constexpr bool hasRxGPIOPullUp(int8_t pin); // safely set the pin mode for the Rx GPIO pin void setRxGPIOPinMode(); // safely set the pin mode for the Tx GPIO pin @@ -295,7 +296,8 @@ class SoftwareSerial : public Stream { std::atomic m_isrOverflow; uint32_t m_isrLastTick; bool m_rxCurParity = false; - Delegate receiveHandler; + Delegate m_rxHandler; }; #endif // __SoftwareSerial_h +