Skip to content

Commit b9fe2ec

Browse files
committed
TX period bias improves timings. Bias is device dependent.
Example loopback.ino now defaults to one-device SoftwareSerial <-> (hw) Serial roundtrip test. Connect pins accordingly.
1 parent 8f85d64 commit b9fe2ec

File tree

5 files changed

+124
-122
lines changed

5 files changed

+124
-122
lines changed

examples/loopback/loopback.ino

Lines changed: 108 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@
1616
// Hardware Serial2 defaults to D4 (rx), D3 (tx).
1717
// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx).
1818

19-
#if !defined(D5)
19+
#if defined(ESP8266) && !defined(D5)
2020
#define D5 (14)
2121
#define D6 (12)
2222
#define D7 (13)
2323
#define D8 (15)
2424
#endif
2525

2626
// Pick only one of HWLOOPBACK OR HWSENDNSINK
27-
//#define HWLOOPBACK 1
27+
#define HWLOOPBACK 1
2828
//#define HWSENDNSINK 1
29-
//#define HALFDUPLEX 1
29+
#define HALFDUPLEX 1
3030

3131
#ifdef ESP32
32-
constexpr int IUTBITRATE = 19200;
32+
constexpr int IUTBITRATE = 115200;
3333
#else
34-
constexpr int IUTBITRATE = 19200;
34+
constexpr int IUTBITRATE = 115200;
3535
#endif
3636

3737
#if defined(ESP8266) || defined(ESP32)
@@ -81,159 +81,151 @@ HardwareSerial& logger(Serial);
8181
void setup() {
8282
#if defined(ESP8266)
8383
#if defined(HWLOOPBACK) || defined(HWSENDNSINK)
84-
Serial.begin(IUTBITRATE);
85-
Serial.swap();
86-
Serial.setRxBufferSize(4 * BLOCKSIZE);
87-
logger.begin(9600, RX, TX);
84+
Serial.begin(IUTBITRATE);
85+
Serial.swap();
86+
Serial.setRxBufferSize(2 * BLOCKSIZE);
87+
logger.begin(9600, -1, TX);
8888
#else
89-
Serial.begin(9600);
89+
Serial.begin(9600);
9090
#endif
9191
#elif defined(ESP32)
9292
#if defined(HWLOOPBACK)
93-
Serial2.begin(IUTBITRATE);
94-
Serial2.setRxBufferSize(4 * BLOCKSIZE);
95-
logger.begin(9600);
93+
Serial2.begin(IUTBITRATE);
94+
Serial2.setRxBufferSize(2 * BLOCKSIZE);
95+
logger.begin(9600);
9696
#elif defined(HWSENDNSINK)
97-
serialIUT.begin(IUTBITRATE, SERIAL_8N1, D5, D6);
98-
serialIUT.setRxBufferSize(4 * BLOCKSIZE);
99-
logger.begin(9600);
97+
serialIUT.begin(IUTBITRATE, SERIAL_8N1, D5, D6);
98+
serialIUT.setRxBufferSize(2 * BLOCKSIZE);
99+
logger.begin(9600);
100100
#else
101-
Serial.begin(9600);
101+
Serial.begin(9600);
102102
#endif
103103
#else
104-
Serial.begin(9600);
104+
Serial.begin(9600);
105105
#endif
106106

107107
#if !defined(HWSENDNSINK)
108108
#if defined(ESP8266)
109-
serialIUT.begin(IUTBITRATE, D5, D6, swSerialConfig, false, 4 * BLOCKSIZE);
109+
serialIUT.begin(IUTBITRATE, D5, D6, swSerialConfig, false, 4 * BLOCKSIZE);
110110
#ifdef HALFDUPLEX
111-
serialIUT.enableIntTx(false);
111+
serialIUT.enableIntTx(false);
112112
#endif
113113
#elif defined(ESP32)
114-
serialIUT.begin(IUTBITRATE, D5, D6, swSerialConfig, false, 4 * BLOCKSIZE);
114+
serialIUT.begin(IUTBITRATE, D5, D6, swSerialConfig, false, 2 * BLOCKSIZE);
115115
#ifdef HALFDUPLEX
116-
serialIUT.enableIntTx(false);
116+
serialIUT.enableIntTx(false);
117117
#endif
118118
#else
119-
serialIUT.begin(IUTBITRATE);
119+
serialIUT.begin(IUTBITRATE);
120120
#endif
121121
#endif
122122

123-
start = micros();
124-
txCount = 0;
125-
rxCount = 0;
126-
rxErrors = 0;
123+
start = micros();
124+
txCount = 0;
125+
rxCount = 0;
126+
rxErrors = 0;
127127

128-
logger.println("Loopback example for EspSoftwareSerial");
128+
logger.println("Loopback example for EspSoftwareSerial");
129129
}
130130

131131
unsigned char c = 0;
132132

133133
void loop() {
134134
#ifdef HALFDUPLEX
135-
unsigned char block[BLOCKSIZE];
135+
unsigned char block[2 * BLOCKSIZE];
136136
#endif
137-
unsigned char inBuf[BLOCKSIZE];
138-
for (int i = 0; i < BLOCKSIZE; ++i) {
137+
unsigned char inBuf[2 * BLOCKSIZE];
138+
for (int i = 0; i < BLOCKSIZE; ++i) {
139139
#ifndef HALFDUPLEX
140-
serialIUT.write(c);
140+
serialIUT.write(c);
141141
#ifdef HWLOOPBACK
142-
int avail = hwLoopback.available();
143-
while ((0 == (i % 8)) && avail > 0) {
144-
int inCnt = hwLoopback.readBytes(inBuf, min(avail, min(BLOCKSIZE, hwLoopback.availableForWrite())));
145-
hwLoopback.write(inBuf, inCnt);
146-
avail -= inCnt;
147-
}
142+
int avail = hwLoopback.available();
143+
while ((0 == (i % 8)) && avail > 0) {
144+
int inCnt = hwLoopback.readBytes(inBuf, min(avail, min(BLOCKSIZE, hwLoopback.availableForWrite())));
145+
hwLoopback.write(inBuf, inCnt);
146+
avail -= inCnt;
147+
}
148148
#endif
149149
#else
150-
block[i] = c;
150+
block[i] = c;
151151
#endif
152-
c = (c + 1) % 256;
153-
++txCount;
154-
}
152+
c = (c + 1) % 256;
153+
++txCount;
154+
}
155155
#ifdef HALFDUPLEX
156-
serialIUT.write(block, BLOCKSIZE);
156+
serialIUT.write(block, BLOCKSIZE);
157157
#endif
158158
#ifdef HWSENDNSINK
159159
#if defined(ESP8266)
160-
if (Serial.hasOverrun()) { logger.println("Serial::overrun"); }
160+
if (Serial.hasOverrun()) { logger.println("Serial::overrun"); }
161161
#endif
162162
#else
163-
if (serialIUT.overflow()) { logger.println("SoftwareSerial::overflow"); }
163+
if (serialIUT.overflow()) { logger.println("SoftwareSerial::overflow"); }
164164
#endif
165165

166-
uint32_t deadline;
167-
int inCnt;
166+
int inCnt;
167+
uint32_t deadlineStart;
168168

169169
#ifdef HWLOOPBACK
170-
// starting deadline for the first bytes to become readable
171-
deadline = micros() + static_cast<uint32_t>(1000000 * 10 * BLOCKSIZE / IUTBITRATE * 8);
172-
inCnt = 0;
173-
while (static_cast<int32_t>(deadline - micros()) > 0) {
174-
int avail = hwLoopback.available();
175-
if (0 >= avail) {
176-
delay(1);
177-
continue;
178-
}
179-
inCnt += hwLoopback.readBytes(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwLoopback.availableForWrite())));
180-
if (inCnt >= BLOCKSIZE) { break; }
181-
// wait for more outstanding bytes to trickle in
182-
deadline = micros() + static_cast<uint32_t>(1000000 * 10 * BLOCKSIZE / IUTBITRATE * 8);
183-
}
184-
hwLoopback.write(inBuf, inCnt);
185-
#endif
186-
187-
// starting deadline for the first bytes to come in
188-
deadline = micros() + static_cast<uint32_t>(1000000 * 10 * BLOCKSIZE / IUTBITRATE * 16);
189-
inCnt = 0;
190-
while (static_cast<int32_t>(deadline - micros()) > 0) {
191-
int avail;
192-
if (0 >= (avail = serialIUT.available())) {
193-
delay(1);
194-
continue;
195-
}
196-
avail = serialIUT.readBytes(inBuf, BLOCKSIZE);
197-
for (int i = 0; i < avail; ++i) {
198-
unsigned char r = inBuf[i];
199-
if (expected == -1) { expected = r; }
200-
else {
201-
expected = (expected + 1) % 256;
202-
}
203-
if (r != (expected & ((1 << (5 + swSerialConfig % 4)) - 1))) {
204-
++rxErrors;
205-
expected = -1;
206-
}
207-
++rxCount;
208-
++inCnt;
209-
}
210-
if (inCnt >= BLOCKSIZE) { break; }
211-
// wait for more outstanding bytes to trickle in
212-
deadline = micros() + static_cast<uint32_t>(1000000 * 10 * BLOCKSIZE / IUTBITRATE * 16);
213-
}
214-
215-
#ifdef HALFDUPLEX
216-
if (inCnt != BLOCKSIZE) {
217-
logger.print("Got "); logger.print(inCnt); logger.println(" bytes during block loopback interval");
218-
}
219-
#endif
220-
221-
if (txCount >= ReportInterval) {
222-
logger.println(String("tx/rx: ") + txCount + "/" + rxCount);
223-
const auto end = micros();
224-
const unsigned long interval = end - start;
225-
const long txCps = txCount * (1000000.0 / interval);
226-
const long rxCps = rxCount * (1000000.0 / interval);
227-
const long errorCps = rxErrors * (1000000.0 / interval);
228-
logger.println(effTxTxt + 10 * txCps + "bps, "
229-
+ effRxTxt + 10 * rxCps + "bps, "
230-
+ errorCps + "cps errors (" + 100.0 * rxErrors / rxCount + "%)");
231-
txCount = 0;
232-
rxCount = 0;
233-
rxErrors = 0;
234-
expected = -1;
235-
// resync
236-
delay(static_cast<uint32_t>(1000 * 10 * BLOCKSIZE / IUTBITRATE * 48));
237-
start = micros();
238-
}
170+
// starting deadline for the first bytes to become readable
171+
deadlineStart = ESP.getCycleCount();
172+
inCnt = 0;
173+
while ((ESP.getCycleCount() - deadlineStart) < (1000000 * 10 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) {
174+
int avail = hwLoopback.available();
175+
if (0 >= avail) {
176+
delay(1);
177+
continue;
178+
}
179+
inCnt += hwLoopback.readBytes(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwLoopback.availableForWrite())));
180+
if (inCnt >= BLOCKSIZE) { break; }
181+
// wait for more outstanding bytes to trickle in
182+
deadlineStart = ESP.getCycleCount();
183+
}
184+
hwLoopback.write(inBuf, inCnt);
185+
#endif
186+
187+
// starting deadline for the first bytes to come in
188+
deadlineStart = ESP.getCycleCount();
189+
inCnt = 0;
190+
while ((ESP.getCycleCount() - deadlineStart) < (1000000 * 10 * BLOCKSIZE) / IUTBITRATE * 2 * ESP.getCpuFreqMHz()) {
191+
int avail;
192+
if (0 >= (avail = serialIUT.available())) {
193+
continue;
194+
}
195+
avail = serialIUT.readBytes(inBuf, min(avail, 2 * BLOCKSIZE));
196+
for (int i = 0; i < avail; ++i) {
197+
unsigned char r = inBuf[i];
198+
if (expected == -1) { expected = r; }
199+
else {
200+
expected = (expected + 1) % 256;
201+
}
202+
if (r != (expected & ((1 << (5 + swSerialConfig % 4)) - 1))) {
203+
++rxErrors;
204+
expected = -1;
205+
}
206+
++rxCount;
207+
++inCnt;
208+
}
209+
if (inCnt >= BLOCKSIZE) { break; }
210+
// wait for more outstanding bytes to trickle in
211+
deadlineStart = ESP.getCycleCount();
212+
}
213+
214+
const uint32_t interval = micros() - start;
215+
if (txCount >= ReportInterval && interval) {
216+
logger.println(String("tx/rx: ") + txCount + "/" + rxCount);
217+
const long txCps = txCount * (1000000.0 / interval);
218+
const long rxCps = rxCount * (1000000.0 / interval);
219+
const long errorCps = rxErrors * (1000000.0 / interval);
220+
logger.println(effTxTxt + 10 * txCps + "bps, "
221+
+ effRxTxt + 10 * rxCps + "bps, "
222+
+ errorCps + "cps errors (" + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + "%)");
223+
txCount = 0;
224+
rxCount = 0;
225+
rxErrors = 0;
226+
expected = -1;
227+
// resync
228+
delay(static_cast<uint32_t>(1000 * 10 * BLOCKSIZE / IUTBITRATE * 16));
229+
start = micros();
230+
}
239231
}

examples/repeater/repeater.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// On ESP32:
1010
// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx).
1111

12-
#if !defined(D5)
12+
#if defined(ESP8266) && !defined(D5)
1313
#define D5 (14)
1414
#define D6 (12)
1515
#define D7 (13)
@@ -132,4 +132,4 @@ void loop() {
132132
seqErrors = 0;
133133
expected = -1;
134134
}
135-
}
135+
}

examples/swsertest/swsertest.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#include <SoftwareSerial.h>
66

7-
#if !defined(D5)
7+
#if defined(ESP8266) && !defined(D5)
88
#define D5 (14)
99
#define D6 (12)
1010
#define D7 (13)

src/SoftwareSerial.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void ICACHE_RAM_ATTR SoftwareSerial::preciseDelay(bool asyn, uint32_t savedPS) {
187187
while ((ESP.getCycleCount() - m_periodStart) < m_periodDuration) { if (asyn) optimistic_yield(10000); }
188188
if (asyn)
189189
{
190-
m_periodStart = ESP.getCycleCount();
190+
resetPeriodStart();
191191
m_periodDuration = 0;
192192
}
193193
if (asyn && !m_intTxEnabled) { savedPS = xt_rsil(15); }
@@ -234,7 +234,7 @@ size_t ICACHE_RAM_ATTR SoftwareSerial::write(const uint8_t * buffer, size_t size
234234
// Disable interrupts in order to get a clean transmit timing
235235
savedPS = xt_rsil(15);
236236
}
237-
m_periodStart = ESP.getCycleCount();
237+
resetPeriodStart();
238238
m_periodDuration = 0;
239239
const uint32_t dataMask = ((1UL << m_dataBits) - 1);
240240
for (size_t cnt = 0; cnt < size; ++cnt, ++buffer) {
@@ -299,7 +299,7 @@ void SoftwareSerial::rxBits() {
299299
if (m_isrOverflow.load()) {
300300
m_overflow = true;
301301
m_isrOverflow.store(false);
302-
}
302+
}
303303
#else
304304
if (m_isrOverflow.exchange(false)) {
305305
m_overflow = true;

src/SoftwareSerial.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ class SoftwareSerial : public Stream {
103103
using Print::write;
104104

105105
private:
106+
void resetPeriodStart()
107+
{
108+
#if defined(ESP8266)
109+
m_periodStart = ESP.getCycleCount() - 64;
110+
#elif defined(ESP32)
111+
m_periodStart = ESP.getCycleCount();
112+
#else
113+
m_periodStart = ESP.getCycleCount();
114+
#endif
115+
}
106116
// If asyn, it's legal to exceed the deadline, for instance,
107117
// by enabling interrupts.
108118
void preciseDelay(bool asyn, uint32_t savedPS);

0 commit comments

Comments
 (0)