Unverified Commit 44cd697b authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

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/
parent d562b00c
......@@ -114,15 +114,13 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
}
}
if ((_writer + 1) % _fifosize != _reader) {
auto next_writer = _writer + 1;
if (next_writer == _fifoSize) {
next_writer = 0;
}
if (next_writer != _reader) {
_queue[_writer] = val & ((1 << _bits) - 1);
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
// Avoid using division or mod because the HW divider could be in use
auto next_writer = _writer + 1;
if (next_writer == _fifosize) {
next_writer = 0;
}
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
_writer = next_writer;
} else {
// TODO: Overflow
......@@ -130,11 +128,11 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
}
}
SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifosize) {
SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize) {
_tx = tx;
_rx = rx;
_fifosize = fifosize + 1; // Always one unused entry
_queue = new uint8_t[_fifosize];
_fifoSize = fifoSize + 1; // Always one unused entry
_queue = new uint8_t[_fifoSize];
mutex_init(&_mutex);
}
......@@ -278,7 +276,7 @@ int SerialPIO::read() {
if (_writer != _reader) {
auto ret = _queue[_reader];
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
auto next_reader = (_reader + 1) % _fifosize;
auto next_reader = (_reader + 1) % _fifoSize;
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
_reader = next_reader;
return ret;
......@@ -291,7 +289,7 @@ int SerialPIO::available() {
if (!_running || !m || (_rx == NOPIN)) {
return 0;
}
return (_writer - _reader) % _fifosize;
return (_writer - _reader) % _fifoSize;
}
int SerialPIO::availableForWrite() {
......
......@@ -32,7 +32,7 @@ extern "C" typedef struct uart_inst uart_inst_t;
class SerialPIO : public HardwareSerial {
public:
static const pin_size_t NOPIN = 0xff; // Use in constructor to disable RX or TX unit
SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifosize = 32);
SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize = 32);
~SerialPIO();
void begin(unsigned long baud = 115200) override {
......@@ -73,7 +73,7 @@ private:
int _rxBits;
// Lockless, IRQ-handled circular queue
size_t _fifosize;
size_t _fifoSize;
uint32_t _writer;
uint32_t _reader;
uint8_t *_queue;
......
......@@ -65,6 +65,14 @@ bool SerialUART::setTX(pin_size_t pin) {
return false;
}
bool SerialUART::setPollingMode(bool mode) {
if (_running) {
return false;
}
_polling = mode;
return true;
}
bool SerialUART::setFIFOSize(size_t size) {
if (!size || _running) {
return false;
......@@ -128,15 +136,19 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
_writer = 0;
_reader = 0;
if (_uart == uart0) {
irq_set_exclusive_handler(UART0_IRQ, _uart0IRQ);
irq_set_enabled(UART0_IRQ, true);
if (!_polling) {
if (_uart == uart0) {
irq_set_exclusive_handler(UART0_IRQ, _uart0IRQ);
irq_set_enabled(UART0_IRQ, true);
} else {
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
irq_set_enabled(UART1_IRQ, true);
}
// Set the IRQ enables and FIFO level to minimum
uart_set_irq_enables(_uart, true, false);
} else {
irq_set_exclusive_handler(UART1_IRQ, _uart1IRQ);
irq_set_enabled(UART1_IRQ, true);
// Polling mode has no IRQs used
}
// Set the IRQ enables and FIFO level to minimum
uart_set_irq_enables(_uart, true, false);
_running = true;
}
......@@ -144,10 +156,12 @@ void SerialUART::end() {
if (!_running) {
return;
}
if (_uart == uart0) {
irq_set_enabled(UART0_IRQ, false);
} else {
irq_set_enabled(UART1_IRQ, false);
if (!_polling) {
if (_uart == uart0) {
irq_set_enabled(UART0_IRQ, false);
} else {
irq_set_enabled(UART1_IRQ, false);
}
}
uart_deinit(_uart);
delete[] _queue;
......@@ -159,6 +173,9 @@ int SerialUART::peek() {
if (!_running || !m) {
return -1;
}
if (_polling) {
_handleIRQ();
}
if (_writer != _reader) {
return _queue[_reader];
}
......@@ -170,6 +187,9 @@ int SerialUART::read() {
if (!_running || !m) {
return -1;
}
if (_polling) {
_handleIRQ();
}
if (_writer != _reader) {
auto ret = _queue[_reader];
asm volatile("" ::: "memory"); // Ensure the value is read before advancing
......@@ -186,6 +206,9 @@ int SerialUART::available() {
if (!_running || !m) {
return 0;
}
if (_polling) {
_handleIRQ();
}
return (_writer - _reader) % _fifoSize;
}
......@@ -194,6 +217,9 @@ int SerialUART::availableForWrite() {
if (!_running || !m) {
return 0;
}
if (_polling) {
_handleIRQ();
}
return (uart_is_writable(_uart)) ? 1 : 0;
}
......@@ -202,6 +228,9 @@ void SerialUART::flush() {
if (!_running || !m) {
return;
}
if (_polling) {
_handleIRQ();
}
uart_tx_wait_blocking(_uart);
}
......@@ -210,6 +239,9 @@ size_t SerialUART::write(uint8_t c) {
if (!_running || !m) {
return 0;
}
if (_polling) {
_handleIRQ();
}
uart_putc_raw(_uart, c);
return 1;
}
......@@ -219,6 +251,9 @@ size_t SerialUART::write(const uint8_t *p, size_t len) {
if (!_running || !m) {
return 0;
}
if (_polling) {
_handleIRQ();
}
size_t cnt = len;
while (cnt) {
uart_putc_raw(_uart, *p);
......@@ -247,21 +282,20 @@ void arduino::serialEvent2Run(void) {
}
}
// IRQ handler, called when FIFO > 1/4 full or when it had held unread data for >32 bit times
// IRQ handler, called when FIFO > 1/8 full or when it had held unread data for >32 bit times
void __not_in_flash_func(SerialUART::_handleIRQ)() {
// ICR is write-to-clear
uart_get_hw(_uart)->icr = UART_UARTICR_RTIC_BITS | UART_UARTICR_RXIC_BITS;
while (uart_is_readable(_uart)) {
auto val = uart_getc(_uart);
if ((_writer + 1) % _fifoSize != _reader) {
auto next_writer = _writer + 1;
if (next_writer == _fifoSize) {
next_writer = 0;
}
if (next_writer != _reader) {
_queue[_writer] = val;
asm volatile("" ::: "memory"); // Ensure the queue is written before the written count advances
// Avoid using division or mod because the HW divider could be in use
auto next_writer = _writer + 1;
if (next_writer == _fifoSize) {
next_writer = 0;
}
asm volatile("" ::: "memory"); // Ensure the reader value is only written once, correctly
_writer = next_writer;
} else {
// TODO: Overflow
......
......@@ -41,6 +41,7 @@ public:
return ret;
}
bool setFIFOSize(size_t size);
bool setPollingMode(bool mode = true);
void begin(unsigned long baud = 115200) override {
begin(baud, SERIAL_8N1);
......@@ -67,6 +68,7 @@ private:
pin_size_t _tx, _rx;
int _baud;
mutex_t _mutex;
bool _polling = false;
// Lockless, IRQ-handled circular queue
uint32_t _writer;
......
......@@ -31,5 +31,16 @@ using the ``setFIFOSize`` call prior to calling ``begin()``
Serial1.setFIFOSize(128);
Serial1.begin(baud);
The FIFO is normally handled via an interrupt, which reduced CPU load and
makes it less likely to lose characters. However, the FIFO introduces up
to 32 bit-times of delay before serial data is available to the application.
For applications where this is an issue (i.e. very low baud), use
``setPollingMode(true)`` before calling ``begin()``
.. code:: cpp
Serial1.setPollingMode(true);
Serial1.begin(110)
For detailed information about the Serial ports, see the
Arduino `Serial Reference <https://www.arduino.cc/reference/en/language/functions/communication/serial/>`_ .
......@@ -35,3 +35,4 @@ PIOProgram KEYWORD2
prepare KEYWORD2
SerialPIO KEYWORD2
setFIFOSize KEYWORD2
setPollingMode KEYWORD2
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment