From c1181a6985f5dbca57e376840daea6c01f2baa42 Mon Sep 17 00:00:00 2001 From: Adrien Descamps Date: Thu, 11 Aug 2016 23:13:02 +0200 Subject: [PATCH 1/3] Enable interrupt mode of UART tx Current version of write will never use the interrupt mode transmission, because it fails to check if holding register is empty. This commit fix that, and several (maybe not all) bugs related to interrupt mode serial transmission. --- cores/arduino/UARTClass.cpp | 27 ++++++++++++-------- system/libarc32_arduino101/drivers/ns16550.c | 15 ++++++++++- system/libarc32_arduino101/drivers/uart.h | 1 + 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index 922cf74d..b70ec95e 100644 --- a/cores/arduino/UARTClass.cpp +++ b/cores/arduino/UARTClass.cpp @@ -168,7 +168,7 @@ int UARTClass::read( void ) void UARTClass::flush( void ) { - while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent + while (_tx_buffer->_iHead != *(volatile int*)&(_tx_buffer->_iTail)); //wait for transmit data to be sent // Wait for transmission to complete while(!uart_tx_complete(CONFIG_UART_CONSOLE_INDEX)); } @@ -179,11 +179,11 @@ size_t UARTClass::write( const uint8_t uc_data ) return(0); // Is the hardware currently busy? - if (_tx_buffer->_iTail != _tx_buffer->_iHead) + if (_tx_buffer->_iTail != _tx_buffer->_iHead || !uart_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { // If busy we buffer int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE; - while (_tx_buffer->_iTail == l) + while (*(volatile int*)&(_tx_buffer->_iTail) == l) ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data; @@ -201,17 +201,22 @@ size_t UARTClass::write( const uint8_t uc_data ) void UARTClass::IrqHandler( void ) { - uint8_t uc_data; - int ret; - ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); - - while ( ret != -1 ) { - _rx_buffer->store_char(uc_data); + uart_irq_update(CONFIG_UART_CONSOLE_INDEX); + // if irq is Receiver Data Available + if(uart_irq_rx_ready(CONFIG_UART_CONSOLE_INDEX)) + { + uint8_t uc_data; + int ret; ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); + + while ( ret != -1 ) { + _rx_buffer->store_char(uc_data); + ret = uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); + } } - // Do we need to keep sending data? - if (!uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) + // if irq is Transmitter Holding Register + else if(uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { if (_tx_buffer->_iTail != _tx_buffer->_iHead) { uart_poll_out(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer[_tx_buffer->_iTail]); diff --git a/system/libarc32_arduino101/drivers/ns16550.c b/system/libarc32_arduino101/drivers/ns16550.c index 386c6b22..a7032bba 100644 --- a/system/libarc32_arduino101/drivers/ns16550.c +++ b/system/libarc32_arduino101/drivers/ns16550.c @@ -340,7 +340,7 @@ unsigned char uart_poll_out( ) { /* wait for transmitter to ready to accept a character */ - while ((INBYTE(LSR(which)) & LSR_TEMT) == 0) + while ((INBYTE(LSR(which)) & LSR_THRE) == 0) ; OUTBYTE(THR(which), outChar); @@ -640,6 +640,19 @@ uint8_t uart_tx_complete(int which) return INBYTE(LSR(which)) & LSR_TEMT; } +/******************************************************************************* +* +* uart_tx_complete - check if tx holding register is empty +* +* RETURNS: zero if register is non-empty, +* non-zero if register is empty (ready to receive new data) +*/ + +uint8_t uart_tx_ready(int which) +{ + return INBYTE(LSR(which)) & LSR_THRE; +} + /******************************************************************************* * * uart_loop_enable - enable loopback diff --git a/system/libarc32_arduino101/drivers/uart.h b/system/libarc32_arduino101/drivers/uart.h index ed737780..0f0b5065 100644 --- a/system/libarc32_arduino101/drivers/uart.h +++ b/system/libarc32_arduino101/drivers/uart.h @@ -92,6 +92,7 @@ int uart_break_check(int port); void uart_break_send(int port, int delay); void uart_disable(int port); uint8_t uart_tx_complete(int which); +uint8_t uart_tx_ready(int which); void uart_loop_enable(int which); void uart_loop_disable(int which); From 5450f414d71c175e157830b2ad18c76bd49ea355 Mon Sep 17 00:00:00 2001 From: Adrien Descamps Date: Fri, 12 Aug 2016 23:17:23 +0200 Subject: [PATCH 2/3] Use UART TX FIFO When possible, fill the tx FIFO in interrupt handler, in order to reduce number of interrupts called and overhead. --- cores/arduino/UARTClass.cpp | 9 ++++++--- system/libarc32_arduino101/drivers/ns16550.c | 6 ++++-- system/libarc32_arduino101/drivers/uart.h | 3 +++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index b70ec95e..eaa1a14b 100644 --- a/cores/arduino/UARTClass.cpp +++ b/cores/arduino/UARTClass.cpp @@ -218,9 +218,12 @@ void UARTClass::IrqHandler( void ) // if irq is Transmitter Holding Register else if(uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { - if (_tx_buffer->_iTail != _tx_buffer->_iHead) { - uart_poll_out(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer[_tx_buffer->_iTail]); - _tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + if(_tx_buffer->_iTail != _tx_buffer->_iHead) + { + int end = (_tx_buffer->_iTail < _tx_buffer->_iHead) ? _tx_buffer->_iHead:SERIAL_BUFFER_SIZE; + int l = min(end - _tx_buffer->_iTail, UART_FIFO_SIZE); + uart_fifo_fill(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer+_tx_buffer->_iTail, l); + _tx_buffer->_iTail = (_tx_buffer->_iTail+l)%SERIAL_BUFFER_SIZE; } else { diff --git a/system/libarc32_arduino101/drivers/ns16550.c b/system/libarc32_arduino101/drivers/ns16550.c index a7032bba..f32d5d32 100644 --- a/system/libarc32_arduino101/drivers/ns16550.c +++ b/system/libarc32_arduino101/drivers/ns16550.c @@ -352,6 +352,8 @@ unsigned char uart_poll_out( * * uart_fifo_fill - fill FIFO with data * +* It is up to the caller to make sure that FIFO capcity is not exceeded +* * RETURNS: number of bytes sent */ @@ -362,8 +364,8 @@ int uart_fifo_fill(int which, /* UART on which to send */ { int i; - for (i = 0; i < size && (INBYTE(LSR(which)) & - LSR_BOTH_EMPTY) != 0; i++) { + for (i = 0; i < size ; i++) + { OUTBYTE(THR(which), txData[i]); } return i; diff --git a/system/libarc32_arduino101/drivers/uart.h b/system/libarc32_arduino101/drivers/uart.h index 0f0b5065..456c244b 100644 --- a/system/libarc32_arduino101/drivers/uart.h +++ b/system/libarc32_arduino101/drivers/uart.h @@ -54,6 +54,9 @@ extern "C" { /* options for uart init */ #define UART_OPTION_AFCE 0x01 +/* Size of the FIFO in bytes */ +#define UART_FIFO_SIZE 16 + /* generic UART info structure */ struct uart_init_info { int baud_rate; From 3d4fdca41806916fcc8a48ba7c0ad0db94efcd0e Mon Sep 17 00:00:00 2001 From: Adrien Descamps Date: Mon, 15 Aug 2016 12:51:11 +0200 Subject: [PATCH 3/3] Discard data in case of line break In case of line break, a 0x00 byte is added to the receive fifo. This commit change the irq handler to discard this data. Other LSR interrupt sources (overrun, parity or framing error) are ignored. --- cores/arduino/UARTClass.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index eaa1a14b..cfb21828 100644 --- a/cores/arduino/UARTClass.cpp +++ b/cores/arduino/UARTClass.cpp @@ -93,7 +93,7 @@ void UARTClass::init(const uint32_t dwBaudRate, const uint8_t modeReg) while (uart_irq_rx_ready(CONFIG_UART_CONSOLE_INDEX)) uart_fifo_read(CONFIG_UART_CONSOLE_INDEX, &c, 1); - + uart_irq_err_enable(CONFIG_UART_CONSOLE_INDEX); uart_irq_rx_enable(CONFIG_UART_CONSOLE_INDEX); } @@ -116,6 +116,7 @@ void UARTClass::end( void ) //enable loopback, needed to prevent a short disconnection to be //interpreted as a packet and corrupt receiver state uart_loop_enable(CONFIG_UART_CONSOLE_INDEX); + uart_irq_err_disable(CONFIG_UART_CONSOLE_INDEX); SET_PIN_MODE(17, GPIO_MUX_MODE); // Rdx SOC PIN (Arduino header pin 0) SET_PIN_MODE(16, GPIO_MUX_MODE); // Txd SOC PIN (Arduino header pin 1) @@ -202,8 +203,18 @@ size_t UARTClass::write( const uint8_t uc_data ) void UARTClass::IrqHandler( void ) { uart_irq_update(CONFIG_UART_CONSOLE_INDEX); + // if irq is Receiver Line Status + if(uart_irq_err_detected(CONFIG_UART_CONSOLE_INDEX)) + { + // if it is a break line, we discard the data + if(uart_break_check(CONFIG_UART_CONSOLE_INDEX)) + { + uint8_t uc_data; + uart_poll_in(CONFIG_UART_CONSOLE_INDEX, &uc_data); + } + } // if irq is Receiver Data Available - if(uart_irq_rx_ready(CONFIG_UART_CONSOLE_INDEX)) + else if(uart_irq_rx_ready(CONFIG_UART_CONSOLE_INDEX)) { uint8_t uc_data; int ret;