diff --git a/cores/rp2040/SerialPIO.cpp b/cores/rp2040/SerialPIO.cpp index e65892e4..08e5f258 100644 --- a/cores/rp2040/SerialPIO.cpp +++ b/cores/rp2040/SerialPIO.cpp @@ -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() { diff --git a/cores/rp2040/SerialPIO.h b/cores/rp2040/SerialPIO.h index ab3e9728..cfb4c79d 100644 --- a/cores/rp2040/SerialPIO.h +++ b/cores/rp2040/SerialPIO.h @@ -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; diff --git a/cores/rp2040/SerialUART.cpp b/cores/rp2040/SerialUART.cpp index 7926e244..36fbf578 100644 --- a/cores/rp2040/SerialUART.cpp +++ b/cores/rp2040/SerialUART.cpp @@ -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 diff --git a/cores/rp2040/SerialUART.h b/cores/rp2040/SerialUART.h index e6c2357b..9ddad003 100644 --- a/cores/rp2040/SerialUART.h +++ b/cores/rp2040/SerialUART.h @@ -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; diff --git a/docs/serial.rst b/docs/serial.rst index 7ef5b235..e4117bcf 100644 --- a/docs/serial.rst +++ b/docs/serial.rst @@ -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 `_ . diff --git a/keywords.txt b/keywords.txt index f6f4d83c..8743a812 100644 --- a/keywords.txt +++ b/keywords.txt @@ -35,3 +35,4 @@ PIOProgram KEYWORD2 prepare KEYWORD2 SerialPIO KEYWORD2 setFIFOSize KEYWORD2 +setPollingMode KEYWORD2