From f8a8a2a3595751c39ec8604ab21fdfbd7daccad6 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Fri, 8 Jul 2016 05:11:14 +0300 Subject: [PATCH] Implement Serial RX Buffer (#2239) In connection with: https://github.com/esp8266/Arduino/issues/2237 https://github.com/esp8266/Arduino/issues/2037 https://github.com/esp8266/Arduino/issues/1683 --- cores/esp8266/HardwareSerial.cpp | 52 +++------ cores/esp8266/HardwareSerial.h | 4 +- cores/esp8266/uart.c | 189 +++++++++++++++++++++++++++---- cores/esp8266/uart.h | 5 +- 4 files changed, 191 insertions(+), 59 deletions(-) diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index b69729ae4..979b0c0da 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -32,21 +32,13 @@ HardwareSerial::HardwareSerial(int uart_nr) - : _uart_nr(uart_nr) + : _uart_nr(uart_nr), _rx_size(256) {} void HardwareSerial::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin) { - if(uart_get_debug() == _uart_nr) { - uart_set_debug(UART_NO); - } - - if (_uart) { - free(_uart); - } - - _uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin); - _peek_char = -1; + end(); + _uart = uart_init(_uart_nr, baud, (int) config, (int) mode, tx_pin, _rx_size); } void HardwareSerial::end() @@ -55,8 +47,19 @@ void HardwareSerial::end() uart_set_debug(UART_NO); } - uart_uninit(_uart); - _uart = NULL; + if (_uart) { + uart_uninit(_uart); + _uart = NULL; + } +} + +size_t HardwareSerial::setRxBufferSize(size_t size){ + if(_uart) { + _rx_size = uart_resize_rx_buffer(_uart, size); + } else { + _rx_size = size; + } + return _rx_size; } void HardwareSerial::swap(uint8_t tx_pin) @@ -114,14 +117,7 @@ bool HardwareSerial::isRxEnabled(void) int HardwareSerial::available(void) { - if(!_uart || !uart_rx_enabled(_uart)) { - return 0; - } - int result = static_cast(uart_rx_available(_uart)); - if (_peek_char != -1) { - result += 1; - } if (!result) { optimistic_yield(10000); } @@ -130,25 +126,13 @@ int HardwareSerial::available(void) int HardwareSerial::peek(void) { - if (_peek_char != -1) { - return _peek_char; - } // this may return -1, but that's okay - _peek_char = uart_read_char(_uart); - return _peek_char; + return uart_peek_char(_uart); } int HardwareSerial::read(void) { - if(!_uart || !uart_rx_enabled(_uart)) { - return -1; - } - - if (_peek_char != -1) { - auto tmp = _peek_char; - _peek_char = -1; - return tmp; - } + // this may return -1, but that's okay return uart_read_char(_uart); } diff --git a/cores/esp8266/HardwareSerial.h b/cores/esp8266/HardwareSerial.h index a05cb5d30..4b384fee5 100644 --- a/cores/esp8266/HardwareSerial.h +++ b/cores/esp8266/HardwareSerial.h @@ -87,6 +87,8 @@ public: void end(); + size_t setRxBufferSize(size_t size); + void swap() { swap(1); @@ -138,7 +140,7 @@ public: protected: int _uart_nr; uart_t* _uart = nullptr; - int _peek_char = -1; + size_t _rx_size; }; extern HardwareSerial Serial; diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 123261aef..9f0f0eb1b 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -47,6 +47,13 @@ static int s_uart_debug_nr = UART0; +struct uart_rx_buffer_ { + size_t size; + size_t rpos; + size_t wpos; + uint8_t * buffer; +}; + struct uart_ { int uart_nr; int baud_rate; @@ -54,8 +61,132 @@ struct uart_ { bool tx_enabled; uint8_t rx_pin; uint8_t tx_pin; + struct uart_rx_buffer_ * rx_buffer; }; +size_t uart_resize_rx_buffer(uart_t* uart, size_t new_size) +{ + if(uart == NULL || !uart->rx_enabled) { + return 0; + } + if(uart->rx_buffer->size == new_size) { + return uart->rx_buffer->size; + } + uint8_t * new_buf = (uint8_t*)malloc(new_size); + if(!new_buf) { + return uart->rx_buffer->size; + } + size_t new_wpos = 0; + ETS_UART_INTR_DISABLE(); + while(uart_rx_available(uart) && new_wpos < new_size) { + new_buf[new_wpos++] = uart_read_char(uart); + } + uint8_t * old_buf = uart->rx_buffer->buffer; + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = new_wpos; + uart->rx_buffer->size = new_size; + uart->rx_buffer->buffer = new_buf; + free(old_buf); + ETS_UART_INTR_ENABLE(); + return uart->rx_buffer->size; +} + +int uart_peek_char(uart_t* uart) +{ + if(uart == NULL || !uart->rx_enabled) { + return -1; + } + if (!uart_rx_available(uart)) { + return -1; + } + return uart->rx_buffer->buffer[uart->rx_buffer->rpos]; +} + +int uart_read_char(uart_t* uart) +{ + int data = uart_peek_char(uart); + if(data != -1) { + uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size; + } + return data; +} + +size_t uart_rx_available(uart_t* uart) +{ + if(uart == NULL || !uart->rx_enabled) { + return 0; + } + if(uart->rx_buffer->wpos < uart->rx_buffer->rpos) { + return (uart->rx_buffer->wpos + uart->rx_buffer->size) - uart->rx_buffer->rpos; + } + return uart->rx_buffer->wpos - uart->rx_buffer->rpos; +} + + +void ICACHE_RAM_ATTR uart_isr(void * arg) +{ + uart_t* uart = (uart_t*)arg; + if(uart == NULL || !uart->rx_enabled) { + USIC(uart->uart_nr) = 0xffff; + ETS_UART_INTR_DISABLE(); + return; + } + + uint32_t int_status = USIS(uart->uart_nr); + + if(int_status & (1 << UIFR)) { + USIC(uart->uart_nr) = (1 << UIFR);//clear any frame error + } + + if(int_status & (1 << UIFF) || int_status & (1 << UITO)){ + ETS_UART_INTR_DISABLE(); + while(((USS(uart->uart_nr) >> USRXC) & 0x7F) != 0){ + uint8_t data = USF(uart->uart_nr); + size_t nextPos = (uart->rx_buffer->wpos + 1) % uart->rx_buffer->size; + if(nextPos != uart->rx_buffer->rpos) { + uart->rx_buffer->buffer[uart->rx_buffer->wpos] = data; + uart->rx_buffer->wpos = nextPos; + } else { + //rx buffer OverFlow + //maybe stop the loop and try later? + } + } + int_status = USIS(uart->uart_nr); + if(int_status & (1 << UIFF)) { + USIC(uart->uart_nr) = (1 << UIFF);//clear any FIFO FULL error + } + if(int_status & (1 << UITO)) { + USIC(uart->uart_nr) = (1 << UITO);//clear any TimeOut error + } + ETS_UART_INTR_ENABLE(); + } +} + +void uart_start_isr(uart_t* uart) +{ + if(uart == NULL || !uart->rx_enabled) { + return; + } + USC1(uart->uart_nr) = (127 << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = 0xffff; + USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO); + ETS_UART_INTR_ATTACH(uart_isr, (void *)uart); + ETS_UART_INTR_ENABLE(); +} + +void uart_stop_isr(uart_t* uart) +{ + if(uart == NULL || !uart->rx_enabled) { + return; + } + ETS_UART_INTR_DISABLE(); + USC1(uart->uart_nr) = 0; + USIC(uart->uart_nr) = 0xffff; + USIE(uart->uart_nr) = 0; + ETS_UART_INTR_ATTACH(NULL, NULL); +} + + void uart_write_char(uart_t* uart, char c) { if(uart == NULL || !uart->tx_enabled) { @@ -75,25 +206,6 @@ void uart_write(uart_t* uart, const char* buf, size_t size) } } -int uart_read_char(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) { - return -1; - } - if (!uart_rx_available(uart)) { - return -1; - } - return USF(uart->uart_nr) & 0xff; -} - -size_t uart_rx_available(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) { - return -1; - } - return (USS(uart->uart_nr) >> USRXC) & 0xff; -} - size_t uart_tx_free(uart_t* uart) { if(uart == NULL || !uart->tx_enabled) { @@ -121,6 +233,10 @@ void uart_flush(uart_t* uart) uint32_t tmp = 0x00000000; if(uart->rx_enabled) { tmp |= (1 << UCRXRST); + ETS_UART_INTR_DISABLE(); + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = 0; + ETS_UART_INTR_ENABLE(); } if(uart->tx_enabled) { @@ -148,7 +264,7 @@ int uart_get_baudrate(uart_t* uart) return uart->baud_rate; } -uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin) +uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size) { uart_t* uart = (uart_t*) malloc(sizeof(uart_t)); if(uart == NULL) { @@ -159,9 +275,29 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin) switch(uart->uart_nr) { case UART0: + ETS_UART_INTR_DISABLE(); + ETS_UART_INTR_ATTACH(NULL, NULL); uart->rx_enabled = (mode != UART_TX_ONLY); uart->tx_enabled = (mode != UART_RX_ONLY); uart->rx_pin = (uart->rx_enabled)?3:255; + if(uart->rx_enabled) { + struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_)); + if(rx_buffer == NULL) { + free(uart); + return NULL; + } + rx_buffer->size = rx_size;//var this + rx_buffer->rpos = 0; + rx_buffer->wpos = 0; + rx_buffer->buffer = (uint8_t *)malloc(rx_buffer->size); + if(rx_buffer->buffer == NULL) { + free(rx_buffer); + free(uart); + return NULL; + } + uart->rx_buffer = rx_buffer; + pinMode(uart->rx_pin, SPECIAL); + } if(uart->tx_enabled) { if (tx_pin == 2) { uart->tx_pin = 2; @@ -173,9 +309,6 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin) } else { uart->tx_pin = 255; } - if(uart->rx_enabled) { - pinMode(uart->rx_pin, SPECIAL); - } IOSWAP &= ~(1 << IOSWAPU0); break; case UART1: @@ -199,6 +332,11 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin) USC0(uart->uart_nr) = config; uart_flush(uart); USC1(uart->uart_nr) = 0; + USIC(uart->uart_nr) = 0xffff; + USIE(uart->uart_nr) = 0; + if(uart->uart_nr == UART0 && uart->rx_enabled) { + uart_start_isr(uart); + } return uart; } @@ -230,6 +368,11 @@ void uart_uninit(uart_t* uart) break; } + if(uart->rx_enabled){ + free(uart->rx_buffer->buffer); + free(uart->rx_buffer); + //uart_stop_isr(uart); + } free(uart); } diff --git a/cores/esp8266/uart.h b/cores/esp8266/uart.h index e5480f222..b79745adf 100644 --- a/cores/esp8266/uart.h +++ b/cores/esp8266/uart.h @@ -113,7 +113,7 @@ extern "C" { struct uart_; typedef struct uart_ uart_t; -uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin); +uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size); void uart_uninit(uart_t* uart); void uart_swap(uart_t* uart, int tx_pin); @@ -125,9 +125,12 @@ bool uart_rx_enabled(uart_t* uart); void uart_set_baudrate(uart_t* uart, int baud_rate); int uart_get_baudrate(uart_t* uart); +size_t uart_resize_rx_buffer(uart_t* uart, size_t new_size); + void uart_write_char(uart_t* uart, char c); void uart_write(uart_t* uart, const char* buf, size_t size); int uart_read_char(uart_t* uart); +int uart_peek_char(uart_t* uart); size_t uart_rx_available(uart_t* uart); size_t uart_tx_free(uart_t* uart); void uart_wait_tx_empty(uart_t* uart);