diff --git a/build/shared/revisions.txt b/build/shared/revisions.txt index 5944bfa18..512a88e85 100644 --- a/build/shared/revisions.txt +++ b/build/shared/revisions.txt @@ -6,6 +6,9 @@ The following changes are included also in the Arduino IDE 1.0.7: [ide] * Mitigated Serial Monitor resource exhaustion when the connected device sends a lot of data (Paul Stoffregen) +[core] +* sam: HardwareSerial now performs ISR based buffered transmission (Collin Kidder) + ARDUINO 1.6.0rc1 * IDE internals have been refactored and sorted out. (Claudio Indellicati) diff --git a/hardware/arduino/sam/cores/arduino/RingBuffer.cpp b/hardware/arduino/sam/cores/arduino/RingBuffer.cpp index f0b3ed1df..d9931c977 100644 --- a/hardware/arduino/sam/cores/arduino/RingBuffer.cpp +++ b/hardware/arduino/sam/cores/arduino/RingBuffer.cpp @@ -21,7 +21,7 @@ RingBuffer::RingBuffer( void ) { - memset( _aucBuffer, 0, SERIAL_BUFFER_SIZE ) ; + memset( (void *)_aucBuffer, 0, SERIAL_BUFFER_SIZE ) ; _iHead=0 ; _iTail=0 ; } diff --git a/hardware/arduino/sam/cores/arduino/RingBuffer.h b/hardware/arduino/sam/cores/arduino/RingBuffer.h index 28309df45..1a5861b0b 100644 --- a/hardware/arduino/sam/cores/arduino/RingBuffer.h +++ b/hardware/arduino/sam/cores/arduino/RingBuffer.h @@ -25,14 +25,14 @@ // using a ring buffer (I think), in which head is the index of the location // to which to write the next incoming character and tail is the index of the // location from which to read. -#define SERIAL_BUFFER_SIZE 64 +#define SERIAL_BUFFER_SIZE 128 class RingBuffer { public: - uint8_t _aucBuffer[SERIAL_BUFFER_SIZE] ; - int _iHead ; - int _iTail ; + volatile uint8_t _aucBuffer[SERIAL_BUFFER_SIZE] ; + volatile int _iHead ; + volatile int _iTail ; public: RingBuffer( void ) ; diff --git a/hardware/arduino/sam/cores/arduino/UARTClass.cpp b/hardware/arduino/sam/cores/arduino/UARTClass.cpp index 16188b128..22ec20abb 100644 --- a/hardware/arduino/sam/cores/arduino/UARTClass.cpp +++ b/hardware/arduino/sam/cores/arduino/UARTClass.cpp @@ -23,33 +23,39 @@ // Constructors //////////////////////////////////////////////////////////////// -UARTClass::UARTClass( Uart* pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer ) +UARTClass::UARTClass( Uart *pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer *pRx_buffer, RingBuffer *pTx_buffer ) { - _rx_buffer = pRx_buffer ; + _rx_buffer = pRx_buffer; + _tx_buffer = pTx_buffer; - _pUart=pUart ; - _dwIrq=dwIrq ; - _dwId=dwId ; + _pUart=pUart; + _dwIrq=dwIrq; + _dwId=dwId; } // Public Methods ////////////////////////////////////////////////////////////// void UARTClass::begin( const uint32_t dwBaudRate ) +{ + begin( dwBaudRate, UART_MR_PAR_NO | UART_MR_CHMODE_NORMAL ); +} + +void UARTClass::begin( const uint32_t dwBaudRate, const uint32_t config ) { // Configure PMC - pmc_enable_periph_clk( _dwId ) ; + pmc_enable_periph_clk( _dwId ); // Disable PDC channel - _pUart->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS ; + _pUart->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS; // Reset and disable receiver and transmitter - _pUart->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS ; + _pUart->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS; // Configure mode - _pUart->UART_MR = UART_MR_PAR_NO | UART_MR_CHMODE_NORMAL ; + _pUart->UART_MR = config; // Configure baudrate (asynchronous, no oversampling) - _pUart->UART_BRGR = (SystemCoreClock / dwBaudRate) >> 4 ; + _pUart->UART_BRGR = (SystemCoreClock / dwBaudRate) >> 4; // Configure interrupts _pUart->UART_IDR = 0xFFFFFFFF; @@ -58,63 +64,99 @@ void UARTClass::begin( const uint32_t dwBaudRate ) // Enable UART interrupt in NVIC NVIC_EnableIRQ(_dwIrq); + // Make sure both ring buffers are initialized back to empty. + _rx_buffer->_iHead = _rx_buffer->_iTail = 0; + _tx_buffer->_iHead = _tx_buffer->_iTail = 0; + // Enable receiver and transmitter - _pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN ; + _pUart->UART_CR = UART_CR_RXEN | UART_CR_TXEN; } void UARTClass::end( void ) { - // clear any received data - _rx_buffer->_iHead = _rx_buffer->_iTail ; - - // Disable UART interrupt in NVIC - NVIC_DisableIRQ( _dwIrq ) ; + // Clear any received data + _rx_buffer->_iHead = _rx_buffer->_iTail; // Wait for any outstanding data to be sent flush(); - pmc_disable_periph_clk( _dwId ) ; + // Disable UART interrupt in NVIC + NVIC_DisableIRQ( _dwIrq ); + + pmc_disable_periph_clk( _dwId ); +} + +void UARTClass::setInterruptPriority(uint32_t priority) +{ + NVIC_SetPriority(_dwIrq, priority & 0x0F); +} + +uint32_t UARTClass::getInterruptPriority() +{ + return NVIC_GetPriority(_dwIrq); } int UARTClass::available( void ) { - return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ; + return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE; +} + +int UARTClass::availableForWrite(void) +{ + int head = _tx_buffer->_iHead; + int tail = _tx_buffer->_iTail; + if (head >= tail) return SERIAL_BUFFER_SIZE - 1 - head + tail; + return tail - head - 1; } int UARTClass::peek( void ) { if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) - return -1 ; + return -1; - return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ; + return _rx_buffer->_aucBuffer[_rx_buffer->_iTail]; } int UARTClass::read( void ) { // if the head isn't ahead of the tail, we don't have any characters if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) - return -1 ; + return -1; - uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ; - _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ; - return uc ; + uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail]; + _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + return uc; } void UARTClass::flush( void ) { + while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent // Wait for transmission to complete while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) - ; + ; } size_t UARTClass::write( const uint8_t uc_data ) { - // Check if the transmitter is ready - while ((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) - ; + // Is the hardware currently busy? + if (((_pUart->UART_SR & UART_SR_TXRDY) != UART_SR_TXRDY) | + (_tx_buffer->_iTail != _tx_buffer->_iHead)) + { + // If busy we buffer + unsigned int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE; + while (_tx_buffer->_iTail == l) + ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent - // Send character - _pUart->UART_THR = uc_data; + _tx_buffer->_aucBuffer[_tx_buffer->_iHead] = uc_data; + _tx_buffer->_iHead = l; + // Make sure TX interrupt is enabled + _pUart->UART_IER = UART_IER_TXRDY; + } + else + { + // Bypass buffering and send character directly + _pUart->UART_THR = uc_data; + } return 1; } @@ -122,15 +164,28 @@ void UARTClass::IrqHandler( void ) { uint32_t status = _pUart->UART_SR; - // Did we receive data ? + // Did we receive data? if ((status & UART_SR_RXRDY) == UART_SR_RXRDY) _rx_buffer->store_char(_pUart->UART_RHR); - // Acknowledge errors - if ((status & UART_SR_OVRE) == UART_SR_OVRE || - (status & UART_SR_FRAME) == UART_SR_FRAME) + // Do we need to keep sending data? + if ((status & UART_SR_TXRDY) == UART_SR_TXRDY) { - // TODO: error reporting outside ISR + if (_tx_buffer->_iTail != _tx_buffer->_iHead) { + _pUart->UART_THR = _tx_buffer->_aucBuffer[_tx_buffer->_iTail]; + _tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + } + else + { + // Mask off transmit interrupt so we don't get it anymore + _pUart->UART_IDR = UART_IDR_TXRDY; + } + } + + // Acknowledge errors + if ((status & UART_SR_OVRE) == UART_SR_OVRE || (status & UART_SR_FRAME) == UART_SR_FRAME) + { + // TODO: error reporting outside ISR _pUart->UART_CR |= UART_CR_RSTSTA; } } diff --git a/hardware/arduino/sam/cores/arduino/UARTClass.h b/hardware/arduino/sam/cores/arduino/UARTClass.h index 5836f2e62..1bc223d80 100644 --- a/hardware/arduino/sam/cores/arduino/UARTClass.h +++ b/hardware/arduino/sam/cores/arduino/UARTClass.h @@ -28,32 +28,32 @@ class UARTClass : public HardwareSerial { protected: - RingBuffer *_rx_buffer ; + RingBuffer *_rx_buffer; + RingBuffer *_tx_buffer; protected: - Uart* _pUart ; - IRQn_Type _dwIrq ; - uint32_t _dwId ; + Uart* _pUart; + IRQn_Type _dwIrq; + uint32_t _dwId; public: - UARTClass( Uart* pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer ) ; + UARTClass(Uart* pUart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer, RingBuffer* pTx_buffer); - void begin( const uint32_t dwBaudRate ) ; - void end( void ) ; - int available( void ) ; - int peek( void ) ; - int read( void ) ; - void flush( void ) ; - size_t write( const uint8_t c ) ; + void begin(const uint32_t dwBaudRate); + void begin(const uint32_t dwBaudRate, const uint32_t config); + void end(void); + int available(void); + int availableForWrite(void); + int peek(void); + int read(void); + void flush(void); + size_t write(const uint8_t c); + using Print::write; // pull in write(str) and write(buf, size) from Print - void IrqHandler( void ) ; + void setInterruptPriority(uint32_t priority); + uint32_t getInterruptPriority(); -#if defined __GNUC__ /* GCC CS3 */ - using Print::write ; // pull in write(str) and write(buf, size) from Print -#elif defined __ICCARM__ /* IAR Ewarm 5.41+ */ -// virtual void write( const char *str ) ; -// virtual void write( const uint8_t *buffer, size_t size ) ; -#endif + void IrqHandler(void); operator bool() { return true; }; // UART always active }; diff --git a/hardware/arduino/sam/cores/arduino/USARTClass.cpp b/hardware/arduino/sam/cores/arduino/USARTClass.cpp index d950c50c9..9fcfa30a6 100644 --- a/hardware/arduino/sam/cores/arduino/USARTClass.cpp +++ b/hardware/arduino/sam/cores/arduino/USARTClass.cpp @@ -23,121 +23,17 @@ // Constructors //////////////////////////////////////////////////////////////// -USARTClass::USARTClass( Usart* pUsart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer ) +USARTClass::USARTClass( Usart* pUsart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer, RingBuffer* pTx_buffer ) + : UARTClass((Uart*)pUsart, dwIrq, dwId, pRx_buffer, pTx_buffer) { - _rx_buffer = pRx_buffer ; - - _pUsart=pUsart ; - _dwIrq=dwIrq ; - _dwId=dwId ; + // In case anyone needs USART specific functionality in the future + _pUsart=pUsart; } // Public Methods ////////////////////////////////////////////////////////////// -void USARTClass::begin( const uint32_t dwBaudRate ) -{ - begin( dwBaudRate, SERIAL_8N1 ); -} - void USARTClass::begin( const uint32_t dwBaudRate, const uint32_t config ) { - // Configure PMC - pmc_enable_periph_clk( _dwId ) ; - - // Disable PDC channel - _pUsart->US_PTCR = US_PTCR_RXTDIS | US_PTCR_TXTDIS ; - - // Reset and disable receiver and transmitter - _pUsart->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS ; - - // Configure mode - _pUsart->US_MR = config; - - - // Configure baudrate, asynchronous no oversampling - _pUsart->US_BRGR = (SystemCoreClock / dwBaudRate) / 16 ; - - // Configure interrupts - _pUsart->US_IDR = 0xFFFFFFFF; - _pUsart->US_IER = US_IER_RXRDY | US_IER_OVRE | US_IER_FRAME; - - // Enable UART interrupt in NVIC - NVIC_EnableIRQ( _dwIrq ) ; - - // Enable receiver and transmitter - _pUsart->US_CR = US_CR_RXEN | US_CR_TXEN ; -} - -void USARTClass::end( void ) -{ - // clear any received data - _rx_buffer->_iHead = _rx_buffer->_iTail ; - - // Disable UART interrupt in NVIC - NVIC_DisableIRQ( _dwIrq ) ; - - // Wait for any outstanding data to be sent - flush(); - - pmc_disable_periph_clk( _dwId ) ; -} - -int USARTClass::available( void ) -{ - return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE ; -} - -int USARTClass::peek( void ) -{ - if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) - return -1 ; - - return _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ; -} - -int USARTClass::read( void ) -{ - // if the head isn't ahead of the tail, we don't have any characters - if ( _rx_buffer->_iHead == _rx_buffer->_iTail ) - return -1 ; - - uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail] ; - _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE ; - return uc ; -} - -void USARTClass::flush( void ) -{ - // Wait for transmission to complete - while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY) - ; -} - -size_t USARTClass::write( const uint8_t uc_data ) -{ - // Check if the transmitter is ready - while ((_pUsart->US_CSR & US_CSR_TXRDY) != US_CSR_TXRDY) - ; - - // Send character - _pUsart->US_THR = uc_data ; - return 1; -} - -void USARTClass::IrqHandler( void ) -{ - uint32_t status = _pUsart->US_CSR; - - // Did we receive data ? - if ((status & US_CSR_RXRDY) == US_CSR_RXRDY) - _rx_buffer->store_char( _pUsart->US_RHR ) ; - - // Acknowledge errors - if ((status & US_CSR_OVRE) == US_CSR_OVRE || - (status & US_CSR_FRAME) == US_CSR_FRAME) - { - // TODO: error reporting outside ISR - _pUsart->US_CR |= US_CR_RSTSTA; - } + UARTClass::begin(dwBaudRate, config); } diff --git a/hardware/arduino/sam/cores/arduino/USARTClass.h b/hardware/arduino/sam/cores/arduino/USARTClass.h index 9082cc6c2..235f54910 100644 --- a/hardware/arduino/sam/cores/arduino/USARTClass.h +++ b/hardware/arduino/sam/cores/arduino/USARTClass.h @@ -19,7 +19,7 @@ #ifndef _USART_CLASS_ #define _USART_CLASS_ -#include "HardwareSerial.h" +#include "UARTClass.h" #include "RingBuffer.h" // Includes Atmel CMSIS @@ -56,38 +56,16 @@ #define SERIAL_7O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_7_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL) #define SERIAL_8O2 (US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK | US_MR_CHRL_8_BIT | US_MR_PAR_ODD | US_MR_NBSTOP_2_BIT | US_MR_CHMODE_NORMAL) -class USARTClass : public HardwareSerial +class USARTClass : public UARTClass { protected: - RingBuffer *_rx_buffer ; - - protected: - Usart* _pUsart ; - IRQn_Type _dwIrq ; - uint32_t _dwId ; + Usart* _pUsart; public: - USARTClass( Usart* pUsart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer ) ; + USARTClass( Usart* pUsart, IRQn_Type dwIrq, uint32_t dwId, RingBuffer* pRx_buffer, RingBuffer* pTx_buffer ); - void begin( const uint32_t dwBaudRate ) ; - void begin( const uint32_t dwBaudRate , const uint32_t config ) ; - void end( void ) ; - int available( void ) ; - int peek( void ) ; - int read( void ) ; - void flush( void ) ; - size_t write( const uint8_t c ) ; - - void IrqHandler( void ) ; - -#if defined __GNUC__ /* GCC CS3 */ - using Print::write ; // pull in write(str) and write(buf, size) from Print -#elif defined __ICCARM__ /* IAR Ewarm 5.41+ */ -// virtual void write( const char *str ) ; -// virtual void write( const uint8_t *buffer, size_t size ) ; -#endif - - operator bool() { return true; }; // USART always active + void begin( const uint32_t dwBaudRate , const uint32_t config ); + using UARTClass::begin; // Needed only for polymorphic methods }; #endif // _USART_CLASS_ diff --git a/hardware/arduino/sam/variants/arduino_due_x/variant.cpp b/hardware/arduino/sam/variants/arduino_due_x/variant.cpp index ac066898d..4cbe0df80 100644 --- a/hardware/arduino/sam/variants/arduino_due_x/variant.cpp +++ b/hardware/arduino/sam/variants/arduino_due_x/variant.cpp @@ -299,8 +299,9 @@ extern const PinDescription g_APinDescription[]= * UART objects */ RingBuffer rx_buffer1; +RingBuffer tx_buffer1; -UARTClass Serial(UART, UART_IRQn, ID_UART, &rx_buffer1); +UARTClass Serial(UART, UART_IRQn, ID_UART, &rx_buffer1, &tx_buffer1); void serialEvent() __attribute__((weak)); void serialEvent() { } @@ -317,14 +318,17 @@ void UART_Handler(void) RingBuffer rx_buffer2; RingBuffer rx_buffer3; RingBuffer rx_buffer4; +RingBuffer tx_buffer2; +RingBuffer tx_buffer3; +RingBuffer tx_buffer4; -USARTClass Serial1(USART0, USART0_IRQn, ID_USART0, &rx_buffer2); +USARTClass Serial1(USART0, USART0_IRQn, ID_USART0, &rx_buffer2, &tx_buffer2); void serialEvent1() __attribute__((weak)); void serialEvent1() { } -USARTClass Serial2(USART1, USART1_IRQn, ID_USART1, &rx_buffer3); +USARTClass Serial2(USART1, USART1_IRQn, ID_USART1, &rx_buffer3, &tx_buffer3); void serialEvent2() __attribute__((weak)); void serialEvent2() { } -USARTClass Serial3(USART3, USART3_IRQn, ID_USART3, &rx_buffer4); +USARTClass Serial3(USART3, USART3_IRQn, ID_USART3, &rx_buffer4, &tx_buffer4); void serialEvent3() __attribute__((weak)); void serialEvent3() { }