Skip to content

Commit 44cd697

Browse files
Add SerialUART::setPollingMode() (#473)
Fixes #472 Instead of using interrupts, explicitly call the IRQ handler dueing Serial read/peek/available calls. Add to keywords.txt for syntax hilighting. Add poll calls in the SerialUART::write-like calls (write, flush, etc.) Really remove division from IRQ routines/
1 parent d562b00 commit 44cd697

File tree

6 files changed

+78
-32
lines changed

6 files changed

+78
-32
lines changed

cores/rp2040/SerialPIO.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,25 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
114114
}
115115
}
116116

117-
if ((_writer + 1) % _fifosize != _reader) {
117+
auto next_writer = _writer + 1;
118+
if (next_writer == _fifoSize) {
119+
next_writer = 0;
120+
}
121+
if (next_writer != _reader) {
118122
_queue[_writer] = val & ((1 << _bits) - 1);
119123
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
120-
// Avoid using division or mod because the HW divider could be in use
121-
auto next_writer = _writer + 1;
122-
if (next_writer == _fifosize) {
123-
next_writer = 0;
124-
}
125-
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
126124
_writer = next_writer;
127125
} else {
128126
// TODO: Overflow
129127
}
130128
}
131129
}
132130

133-
SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifosize) {
131+
SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize) {
134132
_tx = tx;
135133
_rx = rx;
136-
_fifosize = fifosize + 1; // Always one unused entry
137-
_queue = new uint8_t[_fifosize];
134+
_fifoSize = fifoSize + 1; // Always one unused entry
135+
_queue = new uint8_t[_fifoSize];
138136
mutex_init(&_mutex);
139137
}
140138

@@ -278,7 +276,7 @@ int SerialPIO::read() {
278276
if (_writer != _reader) {
279277
auto ret = _queue[_reader];
280278
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
281-
auto next_reader = (_reader + 1) % _fifosize;
279+
auto next_reader = (_reader + 1) % _fifoSize;
282280
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
283281
_reader = next_reader;
284282
return ret;
@@ -291,7 +289,7 @@ int SerialPIO::available() {
291289
if (!_running || !m || (_rx == NOPIN)) {
292290
return 0;
293291
}
294-
return (_writer - _reader) % _fifosize;
292+
return (_writer - _reader) % _fifoSize;
295293
}
296294

297295
int SerialPIO::availableForWrite() {

cores/rp2040/SerialPIO.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern "C" typedef struct uart_inst uart_inst_t;
3232
class SerialPIO : public HardwareSerial {
3333
public:
3434
static const pin_size_t NOPIN = 0xff; // Use in constructor to disable RX or TX unit
35-
SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifosize = 32);
35+
SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize = 32);
3636
~SerialPIO();
3737

3838
void begin(unsigned long baud = 115200) override {
@@ -73,7 +73,7 @@ class SerialPIO : public HardwareSerial {
7373
int _rxBits;
7474

7575
// Lockless, IRQ-handled circular queue
76-
size_t _fifosize;
76+
size_t _fifoSize;
7777
uint32_t _writer;
7878
uint32_t _reader;
7979
uint8_t *_queue;

cores/rp2040/SerialUART.cpp

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ bool SerialUART::setTX(pin_size_t pin) {
6565
return false;
6666
}
6767

68+
bool SerialUART::setPollingMode(bool mode) {
69+
if (_running) {
70+
return false;
71+
}
72+
_polling = mode;
73+
return true;
74+
}
75+
6876
bool SerialUART::setFIFOSize(size_t size) {
6977
if (!size || _running) {
7078
return false;
@@ -128,26 +136,32 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
128136
_writer = 0;
129137
_reader = 0;
130138

131-
if (_uart == uart0) {
132-
irq_set_exclusive_handler(UART0_IRQ, _uart0IRQ);
133-
irq_set_enabled(UART0_IRQ, true);
139+
if (!_polling) {
140+
if (_uart == uart0) {
141+
irq_set_exclusive_handler(UART0_IRQ, _uart0IRQ);
142+
irq_set_enabled(UART0_IRQ, true);
143+
} else {
144+
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
145+
irq_set_enabled(UART1_IRQ, true);
146+
}
147+
// Set the IRQ enables and FIFO level to minimum
148+
uart_set_irq_enables(_uart, true, false);
134149
} else {
135-
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
136-
irq_set_enabled(UART1_IRQ, true);
150+
// Polling mode has no IRQs used
137151
}
138-
// Set the IRQ enables and FIFO level to minimum
139-
uart_set_irq_enables(_uart, true, false);
140152
_running = true;
141153
}
142154

143155
void SerialUART::end() {
144156
if (!_running) {
145157
return;
146158
}
147-
if (_uart == uart0) {
148-
irq_set_enabled(UART0_IRQ, false);
149-
} else {
150-
irq_set_enabled(UART1_IRQ, false);
159+
if (!_polling) {
160+
if (_uart == uart0) {
161+
irq_set_enabled(UART0_IRQ, false);
162+
} else {
163+
irq_set_enabled(UART1_IRQ, false);
164+
}
151165
}
152166
uart_deinit(_uart);
153167
delete[] _queue;
@@ -159,6 +173,9 @@ int SerialUART::peek() {
159173
if (!_running || !m) {
160174
return -1;
161175
}
176+
if (_polling) {
177+
_handleIRQ();
178+
}
162179
if (_writer != _reader) {
163180
return _queue[_reader];
164181
}
@@ -170,6 +187,9 @@ int SerialUART::read() {
170187
if (!_running || !m) {
171188
return -1;
172189
}
190+
if (_polling) {
191+
_handleIRQ();
192+
}
173193
if (_writer != _reader) {
174194
auto ret = _queue[_reader];
175195
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
@@ -186,6 +206,9 @@ int SerialUART::available() {
186206
if (!_running || !m) {
187207
return 0;
188208
}
209+
if (_polling) {
210+
_handleIRQ();
211+
}
189212
return (_writer - _reader) % _fifoSize;
190213
}
191214

@@ -194,6 +217,9 @@ int SerialUART::availableForWrite() {
194217
if (!_running || !m) {
195218
return 0;
196219
}
220+
if (_polling) {
221+
_handleIRQ();
222+
}
197223
return (uart_is_writable(_uart)) ? 1 : 0;
198224
}
199225

@@ -202,6 +228,9 @@ void SerialUART::flush() {
202228
if (!_running || !m) {
203229
return;
204230
}
231+
if (_polling) {
232+
_handleIRQ();
233+
}
205234
uart_tx_wait_blocking(_uart);
206235
}
207236

@@ -210,6 +239,9 @@ size_t SerialUART::write(uint8_t c) {
210239
if (!_running || !m) {
211240
return 0;
212241
}
242+
if (_polling) {
243+
_handleIRQ();
244+
}
213245
uart_putc_raw(_uart, c);
214246
return 1;
215247
}
@@ -219,6 +251,9 @@ size_t SerialUART::write(const uint8_t *p, size_t len) {
219251
if (!_running || !m) {
220252
return 0;
221253
}
254+
if (_polling) {
255+
_handleIRQ();
256+
}
222257
size_t cnt = len;
223258
while (cnt) {
224259
uart_putc_raw(_uart, *p);
@@ -247,21 +282,20 @@ void arduino::serialEvent2Run(void) {
247282
}
248283
}
249284

250-
// IRQ handler, called when FIFO > 1/4 full or when it had held unread data for >32 bit times
285+
// IRQ handler, called when FIFO > 1/8 full or when it had held unread data for >32 bit times
251286
void __not_in_flash_func(SerialUART::_handleIRQ)() {
252287
// ICR is write-to-clear
253288
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
254289
while (uart_is_readable(_uart)) {
255290
auto val = uart_getc(_uart);
256-
if ((_writer + 1) % _fifoSize != _reader) {
291+
auto next_writer = _writer + 1;
292+
if (next_writer == _fifoSize) {
293+
next_writer = 0;
294+
}
295+
if (next_writer != _reader) {
257296
_queue[_writer] = val;
258297
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
259298
// Avoid using division or mod because the HW divider could be in use
260-
auto next_writer = _writer + 1;
261-
if (next_writer == _fifoSize) {
262-
next_writer = 0;
263-
}
264-
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
265299
_writer = next_writer;
266300
} else {
267301
// TODO: Overflow

cores/rp2040/SerialUART.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class SerialUART : public HardwareSerial {
4141
return ret;
4242
}
4343
bool setFIFOSize(size_t size);
44+
bool setPollingMode(bool mode = true);
4445

4546
void begin(unsigned long baud = 115200) override {
4647
begin(baud, SERIAL_8N1);
@@ -67,6 +68,7 @@ class SerialUART : public HardwareSerial {
6768
pin_size_t _tx, _rx;
6869
int _baud;
6970
mutex_t _mutex;
71+
bool _polling = false;
7072

7173
// Lockless, IRQ-handled circular queue
7274
uint32_t _writer;

docs/serial.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,16 @@ using the ``setFIFOSize`` call prior to calling ``begin()``
3131
Serial1.setFIFOSize(128);
3232
Serial1.begin(baud);
3333
34+
The FIFO is normally handled via an interrupt, which reduced CPU load and
35+
makes it less likely to lose characters. However, the FIFO introduces up
36+
to 32 bit-times of delay before serial data is available to the application.
37+
38+
For applications where this is an issue (i.e. very low baud), use
39+
``setPollingMode(true)`` before calling ``begin()``
40+
41+
.. code:: cpp
42+
Serial1.setPollingMode(true);
43+
Serial1.begin(110)
44+
3445
For detailed information about the Serial ports, see the
3546
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .

keywords.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ PIOProgram KEYWORD2
3535
prepare KEYWORD2
3636
SerialPIO KEYWORD2
3737
setFIFOSize KEYWORD2
38+
setPollingMode KEYWORD2

0 commit comments

Comments
 (0)