This commit is contained in:
topicchi
2024-09-24 16:54:39 +00:00
parent e3ca99e4db
commit c7a68c0205
332 changed files with 6098 additions and 4139 deletions

View File

@@ -23,86 +23,106 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "SoftwareSerial.h"
#include <Arduino.h>
#ifdef ESP32
#define xt_rsil(a) (a)
#define xt_wsr_ps(a)
using namespace EspSoftwareSerial;
#ifndef ESP32
uint32_t UARTBase::m_savedPS = 0;
#else
portMUX_TYPE UARTBase::m_interruptsMux = portMUX_INITIALIZER_UNLOCKED;
#endif
ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::disableInterrupts()
{
#ifndef ESP32
m_savedPS = xt_rsil(15);
#else
taskENTER_CRITICAL(&m_interruptsMux);
#endif
}
ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::restoreInterrupts()
{
#ifndef ESP32
xt_wsr_ps(m_savedPS);
#else
taskEXIT_CRITICAL(&m_interruptsMux);
#endif
}
constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast<uint8_t>(0);
SoftwareSerial::SoftwareSerial() {
m_isrOverflow = false;
UARTBase::UARTBase() {
}
SoftwareSerial::SoftwareSerial(int8_t rxPin, int8_t txPin, bool invert)
UARTBase::UARTBase(int8_t rxPin, int8_t txPin, bool invert)
{
m_isrOverflow = false;
m_rxPin = rxPin;
m_txPin = txPin;
m_invert = invert;
}
SoftwareSerial::~SoftwareSerial() {
UARTBase::~UARTBase() {
end();
}
bool SoftwareSerial::isValidGPIOpin(int8_t pin) {
#if defined(ESP8266)
return (pin >= 0 && pin <= 16) && !isFlashInterfacePin(pin);
#elif defined(ESP32)
return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 19) ||
(pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 35);
#else
return true;
#endif
void UARTBase::setRxGPIOPinMode() {
if (m_rxValid) {
pinMode(m_rxPin, m_rxGPIOHasPullUp && m_rxGPIOPullUpEnabled ? INPUT_PULLUP : INPUT);
}
}
bool SoftwareSerial::isValidRxGPIOpin(int8_t pin) {
return isValidGPIOpin(pin)
#if defined(ESP8266)
&& (pin != 16)
#endif
;
void UARTBase::setTxGPIOPinMode() {
if (m_txValid) {
pinMode(m_txPin, m_txGPIOOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
}
}
void SoftwareSerial::begin(uint32_t baud, SoftwareSerialConfig config,
void UARTBase::begin(uint32_t baud, Config config,
int8_t rxPin, int8_t txPin,
bool invert, int bufCapacity, int isrBufCapacity) {
bool invert) {
if (-1 != rxPin) m_rxPin = rxPin;
if (-1 != txPin) m_txPin = txPin;
m_oneWire = (m_rxPin == m_txPin);
m_invert = invert;
m_dataBits = 5 + (config & 07);
m_parityMode = static_cast<SoftwareSerialParity>(config & 070);
m_parityMode = static_cast<Parity>(config & 070);
m_stopBits = 1 + ((config & 0300) ? 1 : 0);
m_pduBits = m_dataBits + static_cast<bool>(m_parityMode) + m_stopBits;
m_bitCycles = (ESP.getCpuFreqMHz() * 1000000UL + baud / 2) / baud;
m_bitTicks = (microsToTicks(1000000UL) + baud / 2) / baud;
m_intTxEnabled = true;
if (isValidRxGPIOpin(m_rxPin)) {
m_buffer.reset(new circular_queue<uint8_t>((bufCapacity > 0) ? bufCapacity : 64));
if (m_parityMode)
{
m_parityBuffer.reset(new circular_queue<uint8_t>((m_buffer->capacity() + 7) / 8));
m_parityInPos = m_parityOutPos = 1;
}
m_isrBuffer.reset(new circular_queue<uint32_t>((isrBufCapacity > 0) ?
isrBufCapacity : m_buffer->capacity() * (2 + m_dataBits + static_cast<bool>(m_parityMode))));
if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) {
m_rxValid = true;
pinMode(m_rxPin, INPUT_PULLUP);
}
}
if (isValidGPIOpin(m_txPin)) {
m_txValid = true;
if (!m_oneWire) {
pinMode(m_txPin, OUTPUT);
digitalWrite(m_txPin, !m_invert);
}
}
if (!m_rxEnabled) { enableRx(true); }
}
void SoftwareSerial::end()
void UARTBase::beginRx(bool hasPullUp, int bufCapacity, int isrBufCapacity) {
m_rxGPIOHasPullUp = hasPullUp;
m_rxReg = portInputRegister(digitalPinToPort(m_rxPin));
m_rxBitMask = digitalPinToBitMask(m_rxPin);
m_buffer.reset(new circular_queue<uint8_t>((bufCapacity > 0) ? bufCapacity : 64));
if (m_parityMode)
{
m_parityBuffer.reset(new circular_queue<uint8_t>((m_buffer->capacity() + 7) / 8));
m_parityInPos = m_parityOutPos = 1;
}
m_isrBuffer.reset(new circular_queue<uint32_t, UARTBase*>((isrBufCapacity > 0) ?
isrBufCapacity : m_buffer->capacity() * (2 + m_dataBits + static_cast<bool>(m_parityMode))));
if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) {
m_rxValid = true;
setRxGPIOPinMode();
}
}
void UARTBase::beginTx() {
#if !defined(ESP8266)
m_txReg = portOutputRegister(digitalPinToPort(m_txPin));
#endif
m_txBitMask = digitalPinToBitMask(m_txPin);
m_txValid = true;
if (!m_oneWire) {
setTxGPIOPinMode();
digitalWrite(m_txPin, !m_invert);
}
}
void UARTBase::end()
{
enableRx(false);
m_txValid = false;
@@ -115,12 +135,12 @@ void SoftwareSerial::end()
}
}
uint32_t SoftwareSerial::baudRate() {
return ESP.getCpuFreqMHz() * 1000000UL / m_bitCycles;
uint32_t UARTBase::baudRate() {
return 1000000UL / ticksToMicros(m_bitTicks);
}
void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) {
if (isValidGPIOpin(txEnablePin)) {
void UARTBase::setTransmitEnablePin(int8_t txEnablePin) {
if (-1 != txEnablePin) {
m_txEnableValid = true;
m_txEnablePin = txEnablePin;
pinMode(m_txEnablePin, OUTPUT);
@@ -131,31 +151,41 @@ void SoftwareSerial::setTransmitEnablePin(int8_t txEnablePin) {
}
}
void SoftwareSerial::enableIntTx(bool on) {
void UARTBase::enableIntTx(bool on) {
m_intTxEnabled = on;
}
void SoftwareSerial::enableTx(bool on) {
void UARTBase::enableRxGPIOPullUp(bool on) {
m_rxGPIOPullUpEnabled = on;
setRxGPIOPinMode();
}
void UARTBase::enableTxGPIOOpenDrain(bool on) {
m_txGPIOOpenDrain = on;
setTxGPIOPinMode();
}
void UARTBase::enableTx(bool on) {
if (m_txValid && m_oneWire) {
if (on) {
enableRx(false);
pinMode(m_txPin, OUTPUT);
setTxGPIOPinMode();
digitalWrite(m_txPin, !m_invert);
}
else {
pinMode(m_rxPin, INPUT_PULLUP);
setRxGPIOPinMode();
enableRx(true);
}
}
}
void SoftwareSerial::enableRx(bool on) {
if (m_rxValid) {
void UARTBase::enableRx(bool on) {
if (m_rxValid && on != m_rxEnabled) {
if (on) {
m_rxCurBit = m_pduBits - 1;
// Init to stop bit level and current cycle
m_isrLastCycle = (ESP.getCycleCount() | 1) ^ m_invert;
if (m_bitCycles >= (ESP.getCpuFreqMHz() * 1000000UL) / 74880UL)
m_rxLastBit = m_pduBits - 1;
// Init to stop bit level and current tick
m_isrLastTick = (microsToTicks(micros()) | 1) ^ m_invert;
if (m_bitTicks >= microsToTicks(1000000UL / 74880UL))
attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast<void (*)(void*)>(rxBitISR), this, CHANGE);
else
attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast<void (*)(void*)>(rxBitSyncISR), this, m_invert ? RISING : FALLING);
@@ -167,7 +197,7 @@ void SoftwareSerial::enableRx(bool on) {
}
}
int SoftwareSerial::read() {
int UARTBase::read() {
if (!m_rxValid) { return -1; }
if (!m_buffer->available()) {
rxBits();
@@ -187,9 +217,9 @@ int SoftwareSerial::read() {
return val;
}
size_t SoftwareSerial::read(uint8_t* buffer, size_t size) {
int UARTBase::read(uint8_t* buffer, size_t size) {
if (!m_rxValid) { return 0; }
size_t avail;
int avail;
if (0 == (avail = m_buffer->pop_n(buffer, size))) {
rxBits();
avail = m_buffer->pop_n(buffer, size);
@@ -204,7 +234,7 @@ size_t SoftwareSerial::read(uint8_t* buffer, size_t size) {
return avail;
}
size_t SoftwareSerial::readBytes(uint8_t* buffer, size_t size) {
size_t UARTBase::readBytes(uint8_t* buffer, size_t size) {
if (!m_rxValid || !size) { return 0; }
size_t count = 0;
auto start = millis();
@@ -212,13 +242,17 @@ size_t SoftwareSerial::readBytes(uint8_t* buffer, size_t size) {
auto readCnt = read(&buffer[count], size - count);
count += readCnt;
if (count >= size) break;
if (readCnt) start = millis();
else optimistic_yield(1000UL);
if (readCnt) {
start = millis();
}
else {
optimistic_yield(1000UL);
}
} while (millis() - start < _timeout);
return count;
}
int SoftwareSerial::available() {
int UARTBase::available() {
if (!m_rxValid) { return 0; }
rxBits();
int avail = m_buffer->available();
@@ -228,66 +262,90 @@ int SoftwareSerial::available() {
return avail;
}
void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool sync) {
if (!sync)
void UARTBase::lazyDelay() {
// Reenable interrupts while delaying to avoid other tasks piling up
if (!m_intTxEnabled) { restoreInterrupts(); }
const auto expired = microsToTicks(micros()) - m_periodStart;
const int32_t remaining = m_periodDuration - expired;
const uint32_t ms = remaining > 0 ? ticksToMicros(remaining) / 1000UL : 0;
if (ms > 0)
{
// Reenable interrupts while delaying to avoid other tasks piling up
if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); }
const auto expired = ESP.getCycleCount() - m_periodStart;
const auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL;
if (ms)
{
delay(ms);
}
else
{
do
{
optimistic_yield(10000UL);
}
while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration);
}
// Disable interrupts again
if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); }
delay(ms);
}
else
{
while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) {}
optimistic_yield(10000UL);
}
m_periodDuration = 0;
m_periodStart = ESP.getCycleCount();
// Assure that below-ms part of delays are not elided
preciseDelay();
// Disable interrupts again if applicable
if (!m_intTxEnabled) { disableInterrupts(); }
}
void ICACHE_RAM_ATTR SoftwareSerial::writePeriod(
void IRAM_ATTR UARTBase::preciseDelay() {
uint32_t ticks;
do {
ticks = microsToTicks(micros());
} while ((ticks - m_periodStart) < m_periodDuration);
m_periodDuration = 0;
m_periodStart = ticks;
}
void IRAM_ATTR UARTBase::writePeriod(
uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) {
preciseDelay(true);
preciseDelay();
if (dutyCycle)
{
digitalWrite(m_txPin, HIGH);
#if defined(ESP8266)
if (16 == m_txPin) {
GP16O = 1;
}
else {
GPOS = m_txBitMask;
}
#else
*m_txReg = *m_txReg | m_txBitMask;
#endif
m_periodDuration += dutyCycle;
if (offCycle || (withStopBit && !m_invert)) preciseDelay(!withStopBit || m_invert);
if (offCycle || (withStopBit && !m_invert)) {
if (!withStopBit || m_invert) {
preciseDelay();
}
else {
lazyDelay();
}
}
}
if (offCycle)
{
digitalWrite(m_txPin, LOW);
#if defined(ESP8266)
if (16 == m_txPin) {
GP16O = 0;
}
else {
GPOC = m_txBitMask;
}
#else
*m_txReg = *m_txReg & ~m_txBitMask;
#endif
m_periodDuration += offCycle;
if (withStopBit && m_invert) preciseDelay(false);
if (withStopBit && m_invert) lazyDelay();
}
}
size_t SoftwareSerial::write(uint8_t byte) {
size_t UARTBase::write(uint8_t byte) {
return write(&byte, 1);
}
size_t SoftwareSerial::write(uint8_t byte, SoftwareSerialParity parity) {
size_t UARTBase::write(uint8_t byte, Parity parity) {
return write(&byte, 1, parity);
}
size_t SoftwareSerial::write(const uint8_t* buffer, size_t size) {
size_t UARTBase::write(const uint8_t* buffer, size_t size) {
return write(buffer, size, m_parityMode);
}
size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size, SoftwareSerialParity parity) {
size_t IRAM_ATTR UARTBase::write(const uint8_t* buffer, size_t size, Parity parity) {
if (m_rxValid) { rxBits(); }
if (!m_txValid) { return -1; }
@@ -300,12 +358,12 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size,
uint32_t offCycle = 0;
if (!m_intTxEnabled) {
// Disable interrupts in order to get a clean transmit timing
m_savedPS = xt_rsil(15);
disableInterrupts();
}
const uint32_t dataMask = ((1UL << m_dataBits) - 1);
bool withStopBit = true;
m_periodDuration = 0;
m_periodStart = ESP.getCycleCount();
m_periodStart = microsToTicks(micros());
for (size_t cnt = 0; cnt < size; ++cnt) {
uint8_t byte = pgm_read_byte(buffer + cnt) & dataMask;
// push LSB start-data-parity-stop bit pattern into uint32_t
@@ -317,24 +375,24 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size,
uint32_t parityBit;
switch (parity)
{
case SWSERIAL_PARITY_EVEN:
case PARITY_EVEN:
// from inverted, so use odd parity
parityBit = byte;
parityBit ^= parityBit >> 4;
parityBit &= 0xf;
parityBit = (0x9669 >> parityBit) & 1;
break;
case SWSERIAL_PARITY_ODD:
case PARITY_ODD:
// from inverted, so use even parity
parityBit = byte;
parityBit ^= parityBit >> 4;
parityBit &= 0xf;
parityBit = (0x6996 >> parityBit) & 1;
break;
case SWSERIAL_PARITY_MARK:
case PARITY_MARK:
parityBit = 0;
break;
case SWSERIAL_PARITY_SPACE:
case PARITY_SPACE:
// suppresses warning parityBit uninitialized
default:
parityBit = 1;
@@ -356,18 +414,18 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size,
dutyCycle = offCycle = 0;
}
if (b) {
dutyCycle += m_bitCycles;
dutyCycle += m_bitTicks;
}
else {
offCycle += m_bitCycles;
offCycle += m_bitTicks;
}
}
withStopBit = true;
}
writePeriod(dutyCycle, offCycle, true);
if (!m_intTxEnabled) {
// restore the interrupt state
xt_wsr_ps(m_savedPS);
// restore the interrupt state if applicable
restoreInterrupts();
}
if (m_txEnableValid) {
digitalWrite(m_txEnablePin, LOW);
@@ -375,7 +433,7 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t* buffer, size_t size,
return size;
}
void SoftwareSerial::flush() {
void UARTBase::flush() {
if (!m_rxValid) { return; }
m_buffer->flush();
if (m_parityBuffer)
@@ -385,13 +443,13 @@ void SoftwareSerial::flush() {
}
}
bool SoftwareSerial::overflow() {
bool UARTBase::overflow() {
bool res = m_overflow;
m_overflow = false;
return res;
}
int SoftwareSerial::peek() {
int UARTBase::peek() {
if (!m_rxValid) { return -1; }
if (!m_buffer->available()) {
rxBits();
@@ -402,8 +460,7 @@ int SoftwareSerial::peek() {
return val;
}
void SoftwareSerial::rxBits() {
int isrAvail = m_isrBuffer->available();
void UARTBase::rxBits() {
#ifdef ESP8266
if (m_isrOverflow.load()) {
m_overflow = true;
@@ -415,139 +472,150 @@ void SoftwareSerial::rxBits() {
}
#endif
// stop bit can go undetected if leading data bits are at same level
// and there was also no next start bit yet, so one byte may be pending.
// low-cost check first
if (!isrAvail && m_rxCurBit >= -1 && m_rxCurBit < m_pduBits - m_stopBits) {
uint32_t detectionCycles = (m_pduBits - m_stopBits - m_rxCurBit) * m_bitCycles;
if (ESP.getCycleCount() - m_isrLastCycle > detectionCycles) {
m_isrBuffer->for_each(m_isrBufferForEachDel);
// A stop bit can go undetected if leading data bits are at same level
// and there was also no next start bit yet, so one word may be pending.
// Check that there was no new ISR data received in the meantime, inserting an
// extraneous stop level bit out of sequence breaks rx.
if (m_rxLastBit < m_pduBits - 1) {
const uint32_t detectionTicks = (m_pduBits - 1 - m_rxLastBit) * m_bitTicks;
if (!m_isrBuffer->available() && microsToTicks(micros()) - m_isrLastTick > detectionTicks) {
// Produce faux stop bit level, prevents start bit maldetection
// cycle's LSB is repurposed for the level bit
rxBits(((m_isrLastCycle + detectionCycles) | 1) ^ m_invert);
// tick's LSB is repurposed for the level bit
rxBits(((m_isrLastTick + detectionTicks) | 1) ^ m_invert);
}
}
m_isrBuffer->for_each([this](const uint32_t& isrCycle) { rxBits(isrCycle); });
}
void SoftwareSerial::rxBits(const uint32_t& isrCycle) {
bool level = (m_isrLastCycle & 1) ^ m_invert;
void UARTBase::rxBits(const uint32_t isrTick) {
const bool level = (m_isrLastTick & 1) ^ m_invert;
// error introduced by edge value in LSB of isrCycle is negligible
int32_t cycles = isrCycle - m_isrLastCycle;
m_isrLastCycle = isrCycle;
// error introduced by edge value in LSB of isrTick is negligible
uint32_t ticks = isrTick - m_isrLastTick;
m_isrLastTick = isrTick;
uint8_t bits = cycles / m_bitCycles;
if (cycles % m_bitCycles > (m_bitCycles >> 1)) ++bits;
uint32_t bits = ticks / m_bitTicks;
if (ticks % m_bitTicks > (m_bitTicks >> 1)) ++bits;
while (bits > 0) {
// start bit detection
if (m_rxCurBit >= (m_pduBits - 1)) {
// leading edge of start bit
if (m_rxLastBit >= (m_pduBits - 1)) {
// leading edge of start bit?
if (level) break;
m_rxCurBit = -1;
m_rxLastBit = -1;
--bits;
continue;
}
// data bits
if (m_rxCurBit >= -1 && m_rxCurBit < (m_dataBits - 1)) {
int8_t dataBits = min(bits, static_cast<uint8_t>(m_dataBits - 1 - m_rxCurBit));
m_rxCurBit += dataBits;
if (m_rxLastBit < (m_dataBits - 1)) {
uint8_t dataBits = min(bits, static_cast<uint32_t>(m_dataBits - 1 - m_rxLastBit));
m_rxLastBit += dataBits;
bits -= dataBits;
m_rxCurByte >>= dataBits;
if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); }
continue;
}
// parity bit
if (m_parityMode && m_rxCurBit == (m_dataBits - 1)) {
++m_rxCurBit;
if (m_parityMode && m_rxLastBit == (m_dataBits - 1)) {
++m_rxLastBit;
--bits;
m_rxCurParity = level;
continue;
}
// stop bits
if (m_rxCurBit < (m_pduBits - m_stopBits - 1)) {
++m_rxCurBit;
--bits;
continue;
}
if (m_rxCurBit == (m_pduBits - m_stopBits - 1)) {
// Store the received value in the buffer unless we have an overflow
// if not high stop bit level, discard word
if (level)
{
m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits);
if (!m_buffer->push(m_rxCurByte)) {
m_overflow = true;
}
else {
if (m_parityBuffer)
// Store the received value in the buffer unless we have an overflow
// if not high stop bit level, discard word
if (bits >= static_cast<uint32_t>(m_pduBits - 1 - m_rxLastBit) && level) {
m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits);
if (!m_buffer->push(m_rxCurByte)) {
m_overflow = true;
}
else {
if (m_parityBuffer)
{
if (m_rxCurParity) {
m_parityBuffer->pushpeek() |= m_parityInPos;
}
else {
m_parityBuffer->pushpeek() &= ~m_parityInPos;
}
m_parityInPos <<= 1;
if (!m_parityInPos)
{
if (m_rxCurParity) {
m_parityBuffer->pushpeek() |= m_parityInPos;
}
else {
m_parityBuffer->pushpeek() &= ~m_parityInPos;
}
m_parityInPos <<= 1;
if (!m_parityInPos)
{
m_parityBuffer->push();
m_parityInPos = 1;
}
m_parityBuffer->push();
m_parityInPos = 1;
}
}
}
m_rxCurBit = m_pduBits;
// reset to 0 is important for masked bit logic
m_rxCurByte = 0;
m_rxCurParity = false;
break;
}
m_rxLastBit = m_pduBits - 1;
// reset to 0 is important for masked bit logic
m_rxCurByte = 0;
m_rxCurParity = false;
break;
}
}
void ICACHE_RAM_ATTR SoftwareSerial::rxBitISR(SoftwareSerial* self) {
uint32_t curCycle = ESP.getCycleCount();
bool level = digitalRead(self->m_rxPin);
void IRAM_ATTR UARTBase::rxBitISR(UARTBase* self) {
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 cycle in the buffer unless we have an overflow
// cycle's LSB is repurposed for the level bit
if (!self->m_isrBuffer->push((curCycle | 1U) ^ !level)) self->m_isrOverflow.store(true);
// 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();
}
void ICACHE_RAM_ATTR SoftwareSerial::rxBitSyncISR(SoftwareSerial* self) {
uint32_t start = ESP.getCycleCount();
uint32_t wait = self->m_bitCycles - 172U;
void IRAM_ATTR UARTBase::rxBitSyncISR(UARTBase* self) {
bool level = self->m_invert;
// Store level and cycle in the buffer unless we have an overflow
// cycle's LSB is repurposed for the level bit
const uint32_t start = microsToTicks(micros());
uint32_t wait = self->m_bitTicks;
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(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true);
for (uint32_t i = 0; i < self->m_pduBits; ++i) {
while (ESP.getCycleCount() - start < wait) {};
wait += self->m_bitCycles;
while (microsToTicks(micros()) - start < wait) {};
wait += self->m_bitTicks;
// Store level and cycle in the buffer unless we have an overflow
// cycle's LSB is repurposed for the level bit
if (digitalRead(self->m_rxPin) != level)
// Store level and tick in the buffer unless we have an overflow
// tick's LSB is repurposed for the level bit
if (static_cast<bool>(*self->m_rxReg & self->m_rxBitMask) != level)
{
if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true);
level = !level;
}
}
// Trigger rx callback only when receiver is starved
if (empty) self->m_rxHandler();
}
void SoftwareSerial::onReceive(Delegate<void(int available), void*> handler) {
receiveHandler = handler;
void UARTBase::onReceive(const Delegate<void(), void*>& handler) {
disableInterrupts();
m_rxHandler = handler;
restoreInterrupts();
}
void SoftwareSerial::perform_work() {
if (!m_rxValid) { return; }
rxBits();
if (receiveHandler) {
int avail = m_buffer->available();
if (avail) { receiveHandler(avail); }
}
void UARTBase::onReceive(Delegate<void(), void*>&& handler) {
disableInterrupts();
m_rxHandler = std::move(handler);
restoreInterrupts();
}
#if __GNUC__ < 12
// The template member functions below must be in IRAM, but due to a bug GCC doesn't currently
// honor the attribute. Instead, it is possible to do explicit specialization and adorn
// these with the IRAM attribute:
// Delegate<>::operator (), circular_queue<>::available,
// circular_queue<>::available_for_push, circular_queue<>::push_peek, circular_queue<>::push
template void IRAM_ATTR delegate::detail::DelegateImpl<void*, void>::operator()() const;
template size_t IRAM_ATTR circular_queue<uint32_t, UARTBase*>::available() const;
template bool IRAM_ATTR circular_queue<uint32_t, UARTBase*>::push(uint32_t&&);
template bool IRAM_ATTR circular_queue<uint32_t, UARTBase*>::push(const uint32_t&);
#endif // __GNUC__ < 12