Skip to content

Commit 3bfd18d

Browse files
committed
Drop waveform-generating write code in favor of maintainable inverse logic implementation - performance was a zero-sum game.
1 parent b2ed06a commit 3bfd18d

File tree

6 files changed

+101
-130
lines changed

6 files changed

+101
-130
lines changed

examples/loopback/loopback.ino

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#ifdef ESP32
3434
constexpr int IUTBITRATE = 57600;
3535
#else
36-
constexpr int IUTBITRATE = 74880;
36+
constexpr int IUTBITRATE = 153600;
3737
#endif
3838

3939
#if defined(ESP8266)
@@ -189,14 +189,10 @@ void loop() {
189189
inCnt = 0;
190190
while ((ESP.getCycleCount() - deadlineStart) < (1000000 * 10 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) {
191191
int avail = hwSerial.available();
192-
if (0 >= avail) {
193-
delay(1);
194-
continue;
195-
}
196192
inCnt += hwSerial.readBytes(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite())));
197193
if (inCnt >= BLOCKSIZE) { break; }
198194
// wait for more outstanding bytes to trickle in
199-
deadlineStart = ESP.getCycleCount();
195+
if (avail) deadlineStart = ESP.getCycleCount();
200196
}
201197
hwSerial.write(inBuf, inCnt);
202198
#endif
@@ -217,17 +213,19 @@ void loop() {
217213
++rxErrors;
218214
expected = -1;
219215
}
216+
#ifndef HWSOURCESINK
220217
if ((serialIUT.readParity() ^ static_cast<bool>(swSerialConfig & 010)) != serialIUT.parityEven(r))
221218
{
222219
++rxParityErrors;
223220
}
221+
#endif
224222
++rxCount;
225223
++inCnt;
226224
}
227225

228226
if (inCnt >= BLOCKSIZE) { break; }
229227
// wait for more outstanding bytes to trickle in
230-
deadlineStart = ESP.getCycleCount();
228+
if (avail) deadlineStart = ESP.getCycleCount();
231229
}
232230

233231
const uint32_t interval = micros() - start;

examples/repeater/repeater.ino

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#ifdef ESP32
2424
constexpr int IUTBITRATE = 57600;
2525
#else
26-
constexpr int IUTBITRATE = 74880;
26+
constexpr int IUTBITRATE = 153600;
2727
#endif
2828

2929
constexpr SoftwareSerialConfig swSerialConfig = SWSERIAL_8N1;
@@ -54,83 +54,83 @@ HardwareSerial& logger(Serial);
5454
void setup() {
5555
#ifdef HWLOOPBACK
5656
#if defined(ESP8266)
57-
repeater.begin(IUTBITRATE);
58-
repeater.setRxBufferSize(2 * BLOCKSIZE);
59-
repeater.swap();
60-
logger.begin(9600, swSerialConfig, RX, TX);
57+
repeater.begin(IUTBITRATE);
58+
repeater.setRxBufferSize(2 * BLOCKSIZE);
59+
repeater.swap();
60+
logger.begin(9600, swSerialConfig, RX, TX);
6161
#elif defined(ESP32)
62-
repeater.begin(IUTBITRATE, SERIAL_8N1, D7, D8);
63-
repeater.setRxBufferSize(2 * BLOCKSIZE);
64-
logger.begin(9600);
62+
repeater.begin(IUTBITRATE, SERIAL_8N1, D7, D8);
63+
repeater.setRxBufferSize(2 * BLOCKSIZE);
64+
logger.begin(9600);
6565
#endif
6666
#else
6767
#if defined(ESP8266)
68-
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, false, 2 * BLOCKSIZE);
68+
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, false, 2 * BLOCKSIZE);
6969
#elif defined(ESP32)
70-
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, false, 2 * BLOCKSIZE);
70+
repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, false, 2 * BLOCKSIZE);
7171
#endif
7272
#ifdef HALFDUPLEX
73-
repeater.enableIntTx(false);
73+
repeater.enableIntTx(false);
7474
#endif
75-
Serial.begin(9600);
75+
Serial.begin(9600);
7676
#endif
7777

78-
start = micros();
79-
rxCount = 0;
80-
seqErrors = 0;
78+
start = micros();
79+
rxCount = 0;
80+
seqErrors = 0;
8181
}
8282

8383
void loop() {
8484
#ifdef HALFDUPLEX
85-
unsigned char block[BLOCKSIZE];
85+
unsigned char block[BLOCKSIZE];
8686
#endif
87-
int inCnt = 0;
88-
// starting deadline for the first bytes to come in
89-
uint32_t deadline = micros() + 200000;
90-
while (static_cast<int32_t>(deadline - micros()) > 0) {
91-
if (0 >= repeater.available()) {
92-
delay(1);
93-
continue;
94-
}
95-
int r = repeater.read();
96-
if (r == -1) { logger.println("read() == -1"); }
97-
if (expected == -1) { expected = r; }
98-
else {
99-
expected = (expected + 1) % 256;
100-
}
101-
if (r != (expected & ((1 << (5 + swSerialConfig % 4)) - 1))) {
102-
++seqErrors;
103-
expected = -1;
104-
}
105-
++rxCount;
87+
// starting deadline for the first bytes to come in
88+
uint32_t deadlineStart = ESP.getCycleCount();
89+
int inCnt = 0;
90+
while ((ESP.getCycleCount() - deadlineStart) < (1000000 * 10 * BLOCKSIZE) / IUTBITRATE * 2 * ESP.getCpuFreqMHz()) {
91+
int avail = repeater.available();
92+
for (int i = 0; i < avail; ++i)
93+
{
94+
int r = repeater.read();
95+
if (r == -1) { logger.println("read() == -1"); }
96+
if (expected == -1) { expected = r; }
97+
else {
98+
expected = (expected + 1) % 256;
99+
}
100+
if (r != (expected & ((1 << (5 + swSerialConfig % 4)) - 1))) {
101+
++seqErrors;
102+
expected = -1;
103+
}
104+
++rxCount;
106105
#ifdef HALFDUPLEX
107-
block[inCnt] = r;
106+
block[inCnt] = r;
108107
#else
109-
repeater.write(r);
108+
repeater.write(r);
110109
#endif
111-
if (++inCnt >= BLOCKSIZE) { break; }
112-
// wait for more outstanding bytes to trickle in
113-
deadline = micros() + static_cast<uint32_t>(1000000 * 10 * BLOCKSIZE / IUTBITRATE * 32);
114-
}
110+
}
111+
if (++inCnt >= BLOCKSIZE) { break; }
112+
// wait for more outstanding bytes to trickle in
113+
if (avail) deadlineStart = ESP.getCycleCount();
114+
}
115115

116116
#ifdef HALFDUPLEX
117-
repeater.write(block, inCnt);
117+
repeater.write(block, inCnt);
118118
#endif
119119

120-
if (inCnt != 0 && inCnt != BLOCKSIZE) {
121-
logger.print("Got "); logger.print(inCnt); logger.println(" bytes during buffer interval");
122-
}
120+
if (inCnt != 0 && inCnt != BLOCKSIZE) {
121+
logger.print("Got "); logger.print(inCnt); logger.println(" bytes during buffer interval");
122+
}
123123

124-
if (rxCount >= ReportInterval) {
125-
auto end = micros();
126-
unsigned long interval = end - start;
127-
long cps = rxCount * (1000000.0 / interval);
128-
long seqErrorsps = seqErrors * (1000000.0 / interval);
129-
logger.println(bitRateTxt + 10 * cps + "bps, "
130-
+ seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)");
131-
start = end;
132-
rxCount = 0;
133-
seqErrors = 0;
134-
expected = -1;
135-
}
124+
if (rxCount >= ReportInterval) {
125+
auto end = micros();
126+
unsigned long interval = end - start;
127+
long cps = rxCount * (1000000.0 / interval);
128+
long seqErrorsps = seqErrors * (1000000.0 / interval);
129+
logger.println(bitRateTxt + 10 * cps + "bps, "
130+
+ seqErrorsps + "cps seq. errors (" + 100.0 * seqErrors / rxCount + "%)");
131+
start = end;
132+
rxCount = 0;
133+
seqErrors = 0;
134+
expected = -1;
135+
}
136136
}

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "EspSoftwareSerial",
3-
"version": "6.2.2",
3+
"version": "6.3.0",
44
"keywords": [
55
"serial", "io", "softwareserial"
66
],

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=EspSoftwareSerial
2-
version=6.2.2
2+
version=6.3.0
33
author=Peter Lerup, Dirk Kaar
44
maintainer=Peter Lerup <peter@lerup.com>
55
sentence=Implementation of the Arduino software serial for ESP8266/ESP32.

src/SoftwareSerial.cpp

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -210,38 +210,26 @@ int SoftwareSerial::available() {
210210
return avail;
211211
}
212212

213-
void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool asyn, uint32_t savedPS) {
214-
if (asyn)
213+
void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(uint32_t cycles, bool relaxed)
214+
{
215+
m_periodDuration += cycles;
216+
if (relaxed)
215217
{
216218
// Reenable interrupts while delaying to avoid other tasks piling up
217-
if (!m_intTxEnabled) { xt_wsr_ps(savedPS); }
219+
if (!m_intTxEnabled) { xt_wsr_ps(m_savedPS); }
218220
auto expired = ESP.getCycleCount() - m_periodStart;
219-
auto micro_s = expired < m_periodDuration ? (m_periodDuration - expired) / ESP.getCpuFreqMHz() : 0;
220-
delay(micro_s / 1000);
221+
if (expired < m_periodDuration)
222+
{
223+
auto ms = (m_periodDuration - expired) / ESP.getCpuFreqMHz() / 1000UL;
224+
if (ms) delay(ms);
225+
}
221226
}
222-
while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { if (asyn) optimistic_yield(10000); }
223-
if (asyn)
227+
while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { if (relaxed) optimistic_yield(10000); }
228+
// Disable interrupts again
229+
if (relaxed)
224230
{
225231
resetPeriodStart();
226-
// Disable interrupts again
227-
if (!m_intTxEnabled) { savedPS = xt_rsil(15); }
228-
}
229-
}
230-
231-
void ICACHE_RAM_ATTR SoftwareSerial::writePeriod(
232-
uint32_t dutyCycle, uint32_t offCycle, bool withStopBit, uint32_t savedPS) {
233-
preciseDelay(false, savedPS);
234-
if (dutyCycle) {
235-
digitalWrite(m_txPin, HIGH);
236-
m_periodDuration += dutyCycle;
237-
bool asyn = withStopBit && !m_invert;
238-
if (asyn || offCycle) preciseDelay(asyn, savedPS);
239-
}
240-
if (offCycle) {
241-
digitalWrite(m_txPin, LOW);
242-
m_periodDuration += offCycle;
243-
bool asyn = withStopBit && m_invert;
244-
if (asyn) preciseDelay(asyn, savedPS);
232+
if (!m_intTxEnabled) { m_savedPS = xt_rsil(15); }
245233
}
246234
}
247235

@@ -264,24 +252,20 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size
264252
if (m_txEnableValid) {
265253
digitalWrite(m_txEnablePin, HIGH);
266254
}
267-
// Stop bit : LOW if inverted logic, otherwise HIGH
268-
bool b = !m_invert;
269-
// Force line level on entry
270-
uint32_t dutyCycle = b;
271-
uint32_t offCycle = m_invert;
272-
uint32_t savedPS = 0;
255+
// Stop bit: HIGH
256+
bool b = true;
257+
uint32_t curBitCycles = 0;
273258
if (!m_intTxEnabled) {
274259
// Disable interrupts in order to get a clean transmit timing
275-
savedPS = xt_rsil(15);
260+
m_savedPS = xt_rsil(15);
276261
}
277262
resetPeriodStart();
278263
const uint32_t dataMask = ((1UL << m_dataBits) - 1);
279-
for (size_t cnt = 0; cnt < size; ++cnt, ++buffer) {
280-
bool withStopBit = true;
281-
uint8_t byte = (m_invert ? ~*buffer : *buffer) & dataMask;
264+
for (size_t cnt = 0; cnt < size; ++cnt) {
265+
uint8_t byte = buffer[cnt] & dataMask;
282266
// push LSB start-data-parity-stop bit pattern into uint32_t
283-
// Stop bits : LOW if inverted logic, otherwise HIGH
284-
uint32_t word = m_invert ? 0UL : ~0UL << (m_dataBits + static_cast<bool>(parity));
267+
// Stop bits: HIGH
268+
uint32_t word = ~0UL << (m_dataBits + static_cast<bool>(parity));
285269
// parity bit, if any
286270
if (parity)
287271
{
@@ -295,42 +279,36 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size
295279
parityBit = !parityEven(byte);
296280
break;
297281
case SWSERIAL_PARITY_MARK:
298-
parityBit = !m_invert;
282+
parityBit = 1;
299283
break;
300284
case SWSERIAL_PARITY_SPACE:
301-
parityBit = m_invert;
302-
break;
303-
default:
304285
// suppresses warning parityBit uninitialized
286+
default:
305287
parityBit = 0;
306288
break;
307289
}
308290
word |= parityBit << m_dataBits;
309291
}
310292
word |= byte;
311-
// Start bit : HIGH if inverted logic, otherwise LOW
293+
// Start bit: LOW
312294
word <<= 1;
313-
word |= m_invert;
314295
for (int i = 0; i <= m_pduBits; ++i) {
315296
bool pb = b;
316297
b = word & (1 << i);
317-
if (!pb && b) {
318-
writePeriod(dutyCycle, offCycle, withStopBit, savedPS);
319-
withStopBit = false;
320-
dutyCycle = offCycle = 0;
321-
}
322-
if (b) {
323-
dutyCycle += m_bitCycles;
324-
}
325-
else {
326-
offCycle += m_bitCycles;
298+
if (pb != b)
299+
{
300+
preciseDelay(curBitCycles, false);
301+
curBitCycles = 0;
302+
digitalWrite(m_txPin, (b ^ m_invert) ? HIGH : LOW);
327303
}
304+
curBitCycles += m_bitCycles;
328305
}
306+
preciseDelay(curBitCycles, true);
307+
curBitCycles = 0;
329308
}
330-
writePeriod(dutyCycle, offCycle, true, savedPS);
331309
if (!m_intTxEnabled) {
332310
// restore the interrupt state
333-
xt_wsr_ps(savedPS);
311+
xt_wsr_ps(m_savedPS);
334312
}
335313
if (m_txEnableValid) {
336314
digitalWrite(m_txEnablePin, LOW);

src/SoftwareSerial.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,8 @@ class SoftwareSerial : public Stream {
195195
m_periodDuration = 0;
196196
m_periodStart = ESP.getCycleCount();
197197
}
198-
// If asyn, it's legal to exceed the deadline, for instance,
199-
// by enabling interrupts.
200-
void preciseDelay(bool asyn, uint32_t savedPS);
201-
// If withStopBit is set, either cycle contains a stop bit.
202-
// If dutyCycle == 0, the level is not forced to HIGH.
203-
// If offCycle == 0, the level remains unchanged from dutyCycle.
204-
void writePeriod(
205-
uint32_t dutyCycle, uint32_t offCycle, bool withStopBit, uint32_t savedPS);
198+
// If relaxed is true, may relax timing to exceed cycle counts, by yielding.
199+
void preciseDelay(uint32_t cycles, bool relaxed);
206200
bool isValidGPIOpin(int8_t pin);
207201
/* check m_rxValid that calling is safe */
208202
void rxBits();
@@ -232,6 +226,7 @@ class SoftwareSerial : public Stream {
232226
uint32_t m_periodStart;
233227
uint32_t m_periodDuration;
234228
bool m_intTxEnabled;
229+
uint32_t m_savedPS = 0;
235230
std::unique_ptr<circular_queue<uint8_t> > m_buffer;
236231
std::unique_ptr<circular_queue<uint8_t> > m_parityBuffer;
237232
uint8_t m_parityInPos;

0 commit comments

Comments
 (0)