Skip to content

Commit 4c8d8f1

Browse files
d-a-vdevyte
authored andcommitted
uart: BW improvements (#4620)
* uart fixes and BW improvements * uart: read_char straightly use hw buffer * +attributes for functions called by ISR * uart: BW improvements read_char straightly use hw buffer (+ ~10%bw) read by block (+ ~190%bw) (instead of generic Stream::readBytes) attributes for functions called by ISR remove overrun message remove some ISR flags which were not honoured * fix merge * fix buffer overflow * serial stress test sketch * astyle * serial stress example: interactive keyboard, stop reading, overrun * serial device test: bandwidth & overrun * update + HardwareSerial::hasError() * interactive overrun in example * astyle * Test using @plerup's SoftwareSerial as submodule (tag 3.4.1) * update upstream ref (fix warning) * host mock uart/read(buf,size) * reset style changes in submodules before style diff * update build_boards_manager_package.sh for submodules * trigger CI (removing space) * cannot reproduce locally the CI issue, setting bash -x option to get live trace * remove previously added (in this PR) 'set -e' in package builder (passes local tests, not real CI) script-comment new recipe.hooks.core.prebuild.3 (along with already commented .1 and .2) moved CI package test to be first on the test list remove 'set -x', wish me luck
1 parent 8a88488 commit 4c8d8f1

File tree

11 files changed

+545
-44
lines changed

11 files changed

+545
-44
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "tools/sdk/ssl/bearssl"]
55
path = tools/sdk/ssl/bearssl
66
url = https://github.com./earlephilhower/bearssl-esp8266
7+
[submodule "libraries/SoftwareSerial"]
8+
path = libraries/SoftwareSerial
9+
url = https://github.com./plerup/espsoftwareserial.git

Diff for: .travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ cache:
88

99
matrix:
1010
include:
11+
- env:
12+
- BUILD_TYPE=package
1113
- env:
1214
- BUILD_TYPE=build_even
1315
- env:
@@ -22,8 +24,6 @@ matrix:
2224
- BUILD_TYPE=platformio_odd
2325
- env:
2426
- BUILD_TYPE=docs
25-
- env:
26-
- BUILD_TYPE=package
2727
- env:
2828
- BUILD_TYPE=host_tests
2929
- env:

Diff for: cores/esp8266/HardwareSerial.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ class HardwareSerial: public Stream
8888
void end();
8989

9090
size_t setRxBufferSize(size_t size);
91+
size_t getRxBufferSize()
92+
{
93+
return uart_get_rx_buffer_size(_uart);
94+
}
9195

9296
void swap()
9397
{
@@ -120,14 +124,22 @@ class HardwareSerial: public Stream
120124

121125
int peek(void) override
122126
{
123-
// this may return -1, but that's okay
127+
// return -1 when data is unvailable (arduino api)
124128
return uart_peek_char(_uart);
125129
}
126130
int read(void) override
127131
{
128-
// this may return -1, but that's okay
132+
// return -1 when data is unvailable (arduino api)
129133
return uart_read_char(_uart);
130134
}
135+
size_t readBytes(char* buffer, size_t size) override
136+
{
137+
return uart_read(_uart, buffer, size);
138+
}
139+
size_t readBytes(uint8_t* buffer, size_t size) override
140+
{
141+
return uart_read(_uart, (char*)buffer, size);
142+
}
131143
int availableForWrite(void)
132144
{
133145
return static_cast<int>(uart_tx_free(_uart));
@@ -184,6 +196,11 @@ class HardwareSerial: public Stream
184196
return uart_has_overrun(_uart);
185197
}
186198

199+
bool hasRxError(void)
200+
{
201+
return uart_has_rx_error(_uart);
202+
}
203+
187204
void startDetectBaudrate();
188205

189206
unsigned long testBaudrate();

Diff for: cores/esp8266/uart.c

+111-31
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@
4747
#include "user_interface.h"
4848
#include "uart_register.h"
4949

50-
const char overrun_str [] PROGMEM STORE_ATTR = "uart input full!\r\n";
50+
//const char overrun_str [] PROGMEM STORE_ATTR = "uart input full!\r\n";
5151
static int s_uart_debug_nr = UART0;
5252

5353

54-
struct uart_rx_buffer_
54+
struct uart_rx_buffer_
5555
{
5656
size_t size;
5757
size_t rpos;
@@ -65,7 +65,8 @@ struct uart_
6565
int baud_rate;
6666
bool rx_enabled;
6767
bool tx_enabled;
68-
bool overrun;
68+
bool rx_overrun;
69+
bool rx_error;
6970
uint8_t rx_pin;
7071
uint8_t tx_pin;
7172
struct uart_rx_buffer_ * rx_buffer;
@@ -85,7 +86,8 @@ struct uart_
8586

8687

8788

88-
inline size_t
89+
// called by ISR
90+
inline size_t ICACHE_RAM_ATTR
8991
uart_rx_fifo_available(const int uart_nr)
9092
{
9193
return (USS(uart_nr) >> USRXC) & 0xFF;
@@ -110,11 +112,11 @@ uart_rx_available_unsafe(uart_t* uart)
110112
return uart_rx_buffer_available_unsafe(uart->rx_buffer) + uart_rx_fifo_available(uart->uart_nr);
111113
}
112114

113-
114115
//#define UART_DISCARD_NEWEST
115116

116117
// Copy all the rx fifo bytes that fit into the rx buffer
117-
inline void
118+
// called by ISR
119+
inline void ICACHE_RAM_ATTR
118120
uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart)
119121
{
120122
struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer;
@@ -124,11 +126,10 @@ uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart)
124126
size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size;
125127
if(nextPos == rx_buffer->rpos)
126128
{
127-
128-
if (!uart->overrun)
129+
if (!uart->rx_overrun)
129130
{
130-
uart->overrun = true;
131-
os_printf_plus(overrun_str);
131+
uart->rx_overrun = true;
132+
//os_printf_plus(overrun_str);
132133
}
133134

134135
// a choice has to be made here,
@@ -158,25 +159,27 @@ uart_peek_char_unsafe(uart_t* uart)
158159

159160
//without the following if statement and body, there is a good chance of a fifo overrun
160161
if (uart_rx_buffer_available_unsafe(uart->rx_buffer) == 0)
162+
// hw fifo can't be peeked, data need to be copied to sw
161163
uart_rx_copy_fifo_to_buffer_unsafe(uart);
162164

163165
return uart->rx_buffer->buffer[uart->rx_buffer->rpos];
164166
}
165167

166-
inline int
168+
// taking data straight from hw fifo: loopback-test BW jumps by 19%
169+
inline int
167170
uart_read_char_unsafe(uart_t* uart)
168171
{
169-
int data = uart_peek_char_unsafe(uart);
170-
if(data != -1)
172+
if (uart_rx_buffer_available_unsafe(uart->rx_buffer))
173+
{
174+
// take oldest sw data
175+
int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos];
171176
uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size;
172-
return data;
177+
return ret;
178+
}
179+
// unavailable
180+
return -1;
173181
}
174182

175-
176-
/**********************************************************/
177-
178-
179-
180183
size_t
181184
uart_rx_available(uart_t* uart)
182185
{
@@ -204,14 +207,47 @@ uart_peek_char(uart_t* uart)
204207

205208
int
206209
uart_read_char(uart_t* uart)
210+
{
211+
uint8_t ret;
212+
return uart_read(uart, (char*)&ret, 1)? ret: -1;
213+
}
214+
215+
// loopback-test BW jumps by 190%
216+
size_t
217+
uart_read(uart_t* uart, char* userbuffer, size_t usersize)
207218
{
208219
if(uart == NULL || !uart->rx_enabled)
209-
return -1;
210-
220+
return 0;
221+
222+
size_t ret = 0;
211223
ETS_UART_INTR_DISABLE();
212-
int data = uart_read_char_unsafe(uart);
224+
225+
while (ret < usersize && uart_rx_available_unsafe(uart))
226+
{
227+
if (!uart_rx_buffer_available_unsafe(uart->rx_buffer))
228+
{
229+
// no more data in sw buffer, take them from hw fifo
230+
while (ret < usersize && uart_rx_fifo_available(uart->uart_nr))
231+
userbuffer[ret++] = USF(uart->uart_nr);
232+
233+
// no more sw/hw data available
234+
break;
235+
}
236+
237+
// pour sw buffer to user's buffer
238+
// get largest linear length from sw buffer
239+
size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos?
240+
uart->rx_buffer->wpos - uart->rx_buffer->rpos:
241+
uart->rx_buffer->size - uart->rx_buffer->rpos;
242+
if (ret + chunk > usersize)
243+
chunk = usersize - ret;
244+
memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk);
245+
uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size;
246+
ret += chunk;
247+
}
248+
213249
ETS_UART_INTR_ENABLE();
214-
return data;
250+
return ret;
215251
}
216252

217253
size_t
@@ -231,6 +267,8 @@ uart_resize_rx_buffer(uart_t* uart, size_t new_size)
231267
ETS_UART_INTR_DISABLE();
232268
while(uart_rx_available_unsafe(uart) && new_wpos < new_size)
233269
new_buf[new_wpos++] = uart_read_char_unsafe(uart); //if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1
270+
if (new_wpos == new_size)
271+
new_wpos = 0;
234272

235273
uint8_t * old_buf = uart->rx_buffer->buffer;
236274
uart->rx_buffer->rpos = 0;
@@ -242,22 +280,39 @@ uart_resize_rx_buffer(uart_t* uart, size_t new_size)
242280
return uart->rx_buffer->size;
243281
}
244282

283+
size_t
284+
uart_get_rx_buffer_size(uart_t* uart)
285+
{
286+
return uart && uart->rx_enabled? uart->rx_buffer->size: 0;
287+
}
245288

246289

247290
void ICACHE_RAM_ATTR
248291
uart_isr(void * arg)
249292
{
250293
uart_t* uart = (uart_t*)arg;
294+
uint32_t usis = USIS(uart->uart_nr);
295+
251296
if(uart == NULL || !uart->rx_enabled)
252297
{
253-
USIC(uart->uart_nr) = USIS(uart->uart_nr);
298+
USIC(uart->uart_nr) = usis;
254299
ETS_UART_INTR_DISABLE();
255300
return;
256301
}
257-
if(USIS(uart->uart_nr) & ((1 << UIFF) | (1 << UITO)))
302+
303+
if(usis & (1 << UIFF))
258304
uart_rx_copy_fifo_to_buffer_unsafe(uart);
305+
306+
if((usis & (1 << UIOF)) && !uart->rx_overrun)
307+
{
308+
uart->rx_overrun = true;
309+
//os_printf_plus(overrun_str);
310+
}
259311

260-
USIC(uart->uart_nr) = USIS(uart->uart_nr);
312+
if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO)))
313+
uart->rx_error = true;
314+
315+
USIC(uart->uart_nr) = usis;
261316
}
262317

263318
static void
@@ -270,9 +325,22 @@ uart_start_isr(uart_t* uart)
270325
// triggers the IRS very often. A value of 127 would not leave much time
271326
// for ISR to clear fifo before the next byte is dropped. So pick a value
272327
// in the middle.
273-
USC1(uart->uart_nr) = (100 << UCFFT) | (0x02 << UCTOT) | (1 <<UCTOE );
328+
// update: loopback test @ 3Mbauds/8n1 (=2343Kibits/s):
329+
// - 4..120 give > 2300Kibits/s
330+
// - 1, 2, 3 are below
331+
// was 100, use 16 to stay away from overrun
332+
#define INTRIGG 16
333+
334+
//was:USC1(uart->uart_nr) = (INTRIGG << UCFFT) | (0x02 << UCTOT) | (1 <<UCTOE);
335+
USC1(uart->uart_nr) = (INTRIGG << UCFFT);
274336
USIC(uart->uart_nr) = 0xffff;
275-
USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO);
337+
//was: USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO);
338+
// UIFF: rx fifo full
339+
// UIOF: rx fifo overflow (=overrun)
340+
// UIFR: frame error
341+
// UIPE: parity error
342+
// UITO: rx fifo timeout
343+
USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIOF) | (1 << UIFR) | (1 << UIPE) | (1 << UITO);
276344
ETS_UART_INTR_ATTACH(uart_isr, (void *)uart);
277345
ETS_UART_INTR_ENABLE();
278346
}
@@ -415,7 +483,8 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
415483
return NULL;
416484

417485
uart->uart_nr = uart_nr;
418-
uart->overrun = false;
486+
uart->rx_overrun = false;
487+
uart->rx_error = false;
419488

420489
switch(uart->uart_nr)
421490
{
@@ -678,11 +747,22 @@ uart_rx_enabled(uart_t* uart)
678747
bool
679748
uart_has_overrun (uart_t* uart)
680749
{
681-
if (uart == NULL || !uart->overrun)
750+
if (uart == NULL || !uart->rx_overrun)
751+
return false;
752+
753+
// clear flag
754+
uart->rx_overrun = false;
755+
return true;
756+
}
757+
758+
bool
759+
uart_has_rx_error (uart_t* uart)
760+
{
761+
if (uart == NULL || !uart->rx_error)
682762
return false;
683763

684764
// clear flag
685-
uart->overrun = false;
765+
uart->rx_error = false;
686766
return true;
687767
}
688768

Diff for: cores/esp8266/uart.h

+3
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,20 @@ void uart_set_baudrate(uart_t* uart, int baud_rate);
126126
int uart_get_baudrate(uart_t* uart);
127127

128128
size_t uart_resize_rx_buffer(uart_t* uart, size_t new_size);
129+
size_t uart_get_rx_buffer_size(uart_t* uart);
129130

130131
size_t uart_write_char(uart_t* uart, char c);
131132
size_t uart_write(uart_t* uart, const char* buf, size_t size);
132133
int uart_read_char(uart_t* uart);
133134
int uart_peek_char(uart_t* uart);
135+
size_t uart_read(uart_t* uart, char* buffer, size_t size);
134136
size_t uart_rx_available(uart_t* uart);
135137
size_t uart_tx_free(uart_t* uart);
136138
void uart_wait_tx_empty(uart_t* uart);
137139
void uart_flush(uart_t* uart);
138140

139141
bool uart_has_overrun (uart_t* uart); // returns then clear overrun flag
142+
bool uart_has_rx_error (uart_t* uart); // returns then clear rxerror flag
140143

141144
void uart_set_debug(int uart_nr);
142145
int uart_get_debug();

Diff for: libraries/SoftwareSerial

Submodule SoftwareSerial added at 23ae000

0 commit comments

Comments
 (0)