diff --git a/hardware/arduino/avr/cores/arduino/Arduino.h b/hardware/arduino/avr/cores/arduino/Arduino.h index de1886d5dde..cf0555a564d 100644 --- a/hardware/arduino/avr/cores/arduino/Arduino.h +++ b/hardware/arduino/avr/cores/arduino/Arduino.h @@ -87,6 +87,10 @@ void yield(void); #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) +// avr-libc defines _NOP() since 1.6.2 +#ifndef _NOP +#define _NOP() do { __asm__ volatile ("nop"); } while (0) +#endif typedef unsigned int word; @@ -195,6 +199,10 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[]; #include "WCharacter.h" #include "WString.h" #include "HardwareSerial.h" +#include "USBAPI.h" +#if defined(HAVE_HWSERIAL0) && defined(HAVE_CDCSERIAL) +#error "Targets with both UART0 and CDC serial not supported" +#endif uint16_t makeWord(uint16_t w); uint16_t makeWord(byte h, byte l); diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial.cpp b/hardware/arduino/avr/cores/arduino/HardwareSerial.cpp index 9a86fa069d0..c611492a1db 100644 --- a/hardware/arduino/avr/cores/arduino/HardwareSerial.cpp +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial.cpp @@ -25,244 +25,158 @@ #include #include #include +#include +#include #include "Arduino.h" #include "wiring_private.h" +#include "HardwareSerial.h" + // this next line disables the entire HardwareSerial.cpp, // this is so I can support Attiny series and any other chip without a uart -#if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H) - -#include "HardwareSerial.h" +#if defined(HAVE_HWSERIAL0) || defined(HAVE_HWSERIAL1) || defined(HAVE_HWSERIAL2) || defined(HAVE_HWSERIAL3) -/* - * on ATmega8, the uart and its bits are not numbered, so there is no "TXC0" - * definition. - */ +// Ensure that the various bit positions we use are available with a 0 +// postfix, so we can always use the values for UART0 for all UARTs. The +// alternative, passing the various values for each UART to the +// HardwareSerial constructor also works, but makes the code bigger and +// slower. #if !defined(TXC0) #if defined(TXC) +// On ATmega8, the uart and its bits are not numbered, so there is no TXC0 etc. #define TXC0 TXC +#define RXEN0 RXEN +#define TXEN0 TXEN +#define RXCIE0 RXCIE +#define UDRIE0 UDRIE +#define U2X0 U2X +#define UPE0 UPE +#define UDRE0 UDRE #elif defined(TXC1) // Some devices have uart1 but no uart0 #define TXC0 TXC1 +#define RXEN0 RXEN1 +#define TXEN0 TXEN1 +#define RXCIE0 RXCIE1 +#define UDRIE0 UDRIE1 +#define U2X0 U2X1 +#define UPE0 UPE1 +#define UDRE0 UDRE1 #else -#error TXC0 not definable in HardwareSerial.h +#error No UART found in HardwareSerial.cpp #endif +#endif // !defined TXC0 + +// Check at compiletime that it is really ok to use the bit positions of +// UART0 for the other UARTs as well, in case these values ever get +// changed for future hardware. +#if defined(TXC1) && (TXC1 != TXC0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || \ + UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || \ + UDRE1 != UDRE0) +#error "Not all bit positions for UART1 are the same as for UART0" #endif - -inline void store_char(unsigned char c, HardwareSerial *s) -{ - int i = (unsigned int)(s->_rx_buffer_head + 1) % SERIAL_BUFFER_SIZE; - - // if we should be storing the received character into the location - // just before the tail (meaning that the head would advance to the - // current location of the tail), we're about to overflow the buffer - // and so we don't write the character or advance the head. - if (i != s->_rx_buffer_tail) { - s->_rx_buffer[s->_rx_buffer_head] = c; - s->_rx_buffer_head = i; - } -} - -#if !defined(USART0_RX_vect) && defined(USART1_RX_vect) -// do nothing - on the 32u4 the first USART is USART1 -#else -#if !defined(USART_RX_vect) && !defined(USART0_RX_vect) && \ - !defined(USART_RXC_vect) - #error "Don't know what the Data Received vector is called for the first UART" -#else - void serialEvent() __attribute__((weak)); - void serialEvent() {} - #define serialEvent_implemented -#if defined(USART_RX_vect) - ISR(USART_RX_vect) -#elif defined(USART0_RX_vect) - ISR(USART0_RX_vect) -#elif defined(USART_RXC_vect) - ISR(USART_RXC_vect) // ATmega8 +#if defined(TXC2) && (TXC2 != TXC0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || \ + UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || \ + UDRE2 != UDRE0) +#error "Not all bit positions for UART2 are the same as for UART0" #endif - { - #if defined(UDR0) - if (bit_is_clear(UCSR0A, UPE0)) { - unsigned char c = UDR0; - store_char(c, &Serial); - } else { - unsigned char c = UDR0; - }; - #elif defined(UDR) - if (bit_is_clear(UCSRA, PE)) { - unsigned char c = UDR; - store_char(c, &Serial); - } else { - unsigned char c = UDR; - }; - #else - #error UDR not defined - #endif - } +#if defined(TXC3) && (TXC3 != TXC0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || \ + UDRIE3 != UDRIE0 || U3X3 != U3X0 || UPE3 != UPE0 || \ + UDRE3 != UDRE0) +#error "Not all bit positions for UART3 are the same as for UART0" #endif + +// SerialEvent functions are weak, so when the user doesn't define them, +// the linker just sets their address to 0 (which is checked below). +// The Serialx_available is just a wrapper around Serialx.available(), +// but we can refer to it weakly so we don't pull in the entire +// HardwareSerial instance if the user doesn't also refer to it. +#if defined(HAVE_HWSERIAL0) + void serialEvent() __attribute__((weak)); + bool Serial0_available() __attribute__((weak)); #endif -#if defined(USART1_RX_vect) +#if defined(HAVE_HWSERIAL1) void serialEvent1() __attribute__((weak)); - void serialEvent1() {} - #define serialEvent1_implemented - ISR(USART1_RX_vect) - { - if (bit_is_clear(UCSR1A, UPE1)) { - unsigned char c = UDR1; - store_char(c, &Serial1); - } else { - unsigned char c = UDR1; - }; - } + bool Serial1_available() __attribute__((weak)); #endif -#if defined(USART2_RX_vect) && defined(UDR2) +#if defined(HAVE_HWSERIAL2) void serialEvent2() __attribute__((weak)); - void serialEvent2() {} - #define serialEvent2_implemented - ISR(USART2_RX_vect) - { - if (bit_is_clear(UCSR2A, UPE2)) { - unsigned char c = UDR2; - store_char(c, &Serial2); - } else { - unsigned char c = UDR2; - }; - } + bool Serial2_available() __attribute__((weak)); #endif -#if defined(USART3_RX_vect) && defined(UDR3) +#if defined(HAVE_HWSERIAL3) void serialEvent3() __attribute__((weak)); - void serialEvent3() {} - #define serialEvent3_implemented - ISR(USART3_RX_vect) - { - if (bit_is_clear(UCSR3A, UPE3)) { - unsigned char c = UDR3; - store_char(c, &Serial3); - } else { - unsigned char c = UDR3; - }; - } + bool Serial3_available() __attribute__((weak)); #endif void serialEventRun(void) { -#ifdef serialEvent_implemented - if (Serial.available()) serialEvent(); +#if defined(HAVE_HWSERIAL0) + if (Serial0_available && serialEvent && Serial0_available()) serialEvent(); #endif -#ifdef serialEvent1_implemented - if (Serial1.available()) serialEvent1(); +#if defined(HAVE_HWSERIAL1) + if (Serial1_available && serialEvent1 && Serial1_available()) serialEvent1(); #endif -#ifdef serialEvent2_implemented - if (Serial2.available()) serialEvent2(); +#if defined(HAVE_HWSERIAL2) + if (Serial2_available && serialEvent2 && Serial2_available()) serialEvent2(); #endif -#ifdef serialEvent3_implemented - if (Serial3.available()) serialEvent3(); +#if defined(HAVE_HWSERIAL3) + if (Serial3_available && serialEvent2 && Serial3_available()) serialEvent3(); #endif } +// Actual interrupt handlers ////////////////////////////////////////////////////////////// -#if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect) -// do nothing - on the 32u4 the first USART is USART1 -#else -#if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect) - #error "Don't know what the Data Register Empty vector is called for the first UART" -#else -#if defined(UART0_UDRE_vect) -ISR(UART0_UDRE_vect) -#elif defined(UART_UDRE_vect) -ISR(UART_UDRE_vect) -#elif defined(USART0_UDRE_vect) -ISR(USART0_UDRE_vect) -#elif defined(USART_UDRE_vect) -ISR(USART_UDRE_vect) -#endif +void HardwareSerial::_rx_complete_irq(void) { - if (Serial._tx_buffer_head == Serial._tx_buffer_tail) { - // Buffer empty, so disable interrupts -#if defined(UCSR0B) - cbi(UCSR0B, UDRIE0); -#else - cbi(UCSRB, UDRIE); -#endif - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = Serial._tx_buffer[Serial._tx_buffer_tail]; - Serial._tx_buffer_tail = (Serial._tx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; - - #if defined(UDR0) - UDR0 = c; - #elif defined(UDR) - UDR = c; - #else - #error UDR not defined - #endif - } + if (bit_is_clear(*_ucsra, UPE0)) { + // No Parity error, read byte and store it in the buffer if there is + // room + unsigned char c = *_udr; + int i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != _rx_buffer_tail) { + _rx_buffer[_rx_buffer_head] = c; + _rx_buffer_head = i; + } + } else { + // Parity error, read byte but discard it + unsigned char c = *_udr; + }; } -#endif -#endif -#ifdef USART1_UDRE_vect -ISR(USART1_UDRE_vect) +void HardwareSerial::_tx_udr_empty_irq(void) { - if (Serial1._tx_buffer_head == Serial1._tx_buffer_tail) { - // Buffer empty, so disable interrupts - cbi(UCSR1B, UDRIE1); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = Serial1._tx_buffer[Serial1._tx_buffer_tail]; - Serial1._tx_buffer_tail = (Serial1._tx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; - - UDR1 = c; - } -} -#endif + // If interrupts are enabled, there must be more data in the output + // buffer. Send the next byte + unsigned char c = _tx_buffer[_tx_buffer_tail]; + _tx_buffer_tail = (_tx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; -#ifdef USART2_UDRE_vect -ISR(USART2_UDRE_vect) -{ - if (Serial2._tx_buffer_head == Serial2._tx_buffer_tail) { - // Buffer empty, so disable interrupts - cbi(UCSR2B, UDRIE2); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = Serial2._tx_buffer[Serial2._tx_buffer_tail]; - Serial2._tx_buffer_tail = (Serial2._tx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; - - UDR2 = c; - } -} -#endif + *_udr = c; -#ifdef USART3_UDRE_vect -ISR(USART3_UDRE_vect) -{ - if (Serial3._tx_buffer_head == Serial3._tx_buffer_tail) { - // Buffer empty, so disable interrupts - cbi(UCSR3B, UDRIE3); - } - else { - // There is more data in the output buffer. Send the next byte - unsigned char c = Serial3._tx_buffer[Serial3._tx_buffer_tail]; - Serial3._tx_buffer_tail = (Serial3._tx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; - - UDR3 = c; + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + sbi(*_ucsra, TXC0); + + if (_tx_buffer_head == _tx_buffer_tail) { + // Buffer empty, so disable interrupts + cbi(*_ucsrb, UDRIE0); } } -#endif // Constructors //////////////////////////////////////////////////////////////// HardwareSerial::HardwareSerial( volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, volatile uint8_t *ucsra, volatile uint8_t *ucsrb, - volatile uint8_t *ucsrc, volatile uint8_t *udr, - uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x) + volatile uint8_t *ucsrc, volatile uint8_t *udr) { _tx_buffer_head = _tx_buffer_tail = 0; _rx_buffer_head = _rx_buffer_tail = 0; @@ -272,102 +186,48 @@ HardwareSerial::HardwareSerial( _ucsrb = ucsrb; _ucsrc = ucsrc; _udr = udr; - _rxen = rxen; - _txen = txen; - _rxcie = rxcie; - _udrie = udrie; - _u2x = u2x; } // Public Methods ////////////////////////////////////////////////////////////// void HardwareSerial::begin(unsigned long baud) { - uint16_t baud_setting; - bool use_u2x = true; - -#if F_CPU == 16000000UL - // hardcoded exception for compatibility with the bootloader shipped - // with the Duemilanove and previous boards and the firmware on the 8U2 - // on the Uno and Mega 2560. - if (baud == 57600) { - use_u2x = false; - } -#endif - -try_again: - - if (use_u2x) { - *_ucsra = 1 << _u2x; - baud_setting = (F_CPU / 4 / baud - 1) / 2; - } else { - *_ucsra = 0; - baud_setting = (F_CPU / 8 / baud - 1) / 2; - } - - if ((baud_setting > 4095) && use_u2x) - { - use_u2x = false; - goto try_again; - } - - // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) - *_ubrrh = baud_setting >> 8; - *_ubrrl = baud_setting; - - transmitting = false; - - sbi(*_ucsrb, _rxen); - sbi(*_ucsrb, _txen); - sbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); + begin(baud, SERIAL_8N1); } void HardwareSerial::begin(unsigned long baud, byte config) { - uint16_t baud_setting; - uint8_t current_config; - bool use_u2x = true; - -#if F_CPU == 16000000UL - // hardcoded exception for compatibility with the bootloader shipped - // with the Duemilanove and previous boards and the firmware on the 8U2 - // on the Uno and Mega 2560. - if (baud == 57600) { - use_u2x = false; - } -#endif - -try_again: - - if (use_u2x) { - *_ucsra = 1 << _u2x; - baud_setting = (F_CPU / 4 / baud - 1) / 2; - } else { + // Try u2x mode first + uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2; + *_ucsra = 1 << U2X0; + + // hardcoded exception for 57600 for compatibility with the bootloader + // shipped with the Duemilanove and previous boards and the firmware + // on the 8U2 on the Uno and Mega 2560. Also, The baud_setting cannot + // be > 4095, so switch back to non-u2x mode if the baud rate is too + // low. + if (((F_CPU == 16000000UL) && (baud == 57600)) || (baud_setting >4095)) + { *_ucsra = 0; baud_setting = (F_CPU / 8 / baud - 1) / 2; } - - if ((baud_setting > 4095) && use_u2x) - { - use_u2x = false; - goto try_again; - } // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) *_ubrrh = baud_setting >> 8; *_ubrrl = baud_setting; + _written = false; + //set the data bits, parity, and stop bits #if defined(__AVR_ATmega8__) config |= 0x80; // select UCSRC register (shared with UBRRH) #endif *_ucsrc = config; - sbi(*_ucsrb, _rxen); - sbi(*_ucsrb, _txen); - sbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); + sbi(*_ucsrb, RXEN0); + sbi(*_ucsrb, TXEN0); + sbi(*_ucsrb, RXCIE0); + cbi(*_ucsrb, UDRIE0); } void HardwareSerial::end() @@ -376,10 +236,10 @@ void HardwareSerial::end() while (_tx_buffer_head != _tx_buffer_tail) ; - cbi(*_ucsrb, _rxen); - cbi(*_ucsrb, _txen); - cbi(*_ucsrb, _rxcie); - cbi(*_ucsrb, _udrie); + cbi(*_ucsrb, RXEN0); + cbi(*_ucsrb, TXEN0); + cbi(*_ucsrb, RXCIE0); + cbi(*_ucsrb, UDRIE0); // clear any received data _rx_buffer_head = _rx_buffer_tail; @@ -387,83 +247,109 @@ void HardwareSerial::end() int HardwareSerial::available(void) { - return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_BUFFER_SIZE; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail) % SERIAL_BUFFER_SIZE; + } } int HardwareSerial::peek(void) { - if (_rx_buffer_head == _rx_buffer_tail) { - return -1; - } else { - return _rx_buffer[_rx_buffer_tail]; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + if (_rx_buffer_head == _rx_buffer_tail) { + return -1; + } else { + return _rx_buffer[_rx_buffer_tail]; + } } } int HardwareSerial::read(void) { - // if the head isn't ahead of the tail, we don't have any characters - if (_rx_buffer_head == _rx_buffer_tail) { - return -1; - } else { - unsigned char c = _rx_buffer[_rx_buffer_tail]; - _rx_buffer_tail = (unsigned int)(_rx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; - return c; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // if the head isn't ahead of the tail, we don't have any characters + if (_rx_buffer_head == _rx_buffer_tail) { + return -1; + } else { + unsigned char c = _rx_buffer[_rx_buffer_tail]; + _rx_buffer_tail = (unsigned int)(_rx_buffer_tail + 1) % SERIAL_BUFFER_SIZE; + return c; + } } } void HardwareSerial::flush() { - // UDR is kept full while the buffer is not empty, so TXC triggers when EMPTY && SENT - while (transmitting && ! (*_ucsra & _BV(TXC0))); - transmitting = false; + // If we have never written a byte, no need to flush. This special + // case is needed since there is no way to force the TXC (transmit + // complete) bit to 1 during initialization + if (!_written) + return; + + while (bit_is_set(*_ucsrb, UDRIE0) || bit_is_clear(*_ucsra, TXC0)) { + if (bit_is_clear(SREG, SREG_I) && bit_is_set(*_ucsrb, UDRIE0)) + // Interrupts are globally disabled, but the DR empty + // interrupt should be enabled, so poll the DR empty flag to + // prevent deadlock + if (bit_is_set(*_ucsra, UDRE0)) + _tx_udr_empty_irq(); + } + // If we get here, nothing is queued anymore (DRIE is disabled) and + // the hardware finished tranmission (TXC is set). } size_t HardwareSerial::write(uint8_t c) { - int i = (_tx_buffer_head + 1) % SERIAL_BUFFER_SIZE; - - // If the output buffer is full, there's nothing for it other than to - // wait for the interrupt handler to empty it a bit - // ???: return 0 here instead? - while (i == _tx_buffer_tail) - ; - - _tx_buffer[_tx_buffer_head] = c; - _tx_buffer_head = i; - - sbi(*_ucsrb, _udrie); - // clear the TXC bit -- "can be cleared by writing a one to its bit location" - transmitting = true; - sbi(*_ucsra, TXC0); - - return 1; + bool interrupts_enabled = bit_is_set(SREG, SREG_I); + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // If the buffer and the data register is empty, just write the byte + // to the data register and be done. This shortcut helps + // significantly improve the effective datarate at high (> + // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. + if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { + *_udr = c; + sbi(*_ucsra, TXC0); + return 1; + } + int i = (_tx_buffer_head + 1) % SERIAL_BUFFER_SIZE; + + // If the output buffer is full, there's nothing for it other than to + // wait for the interrupt handler to empty it a bit + while (i == _tx_buffer_tail) { + if (!interrupts_enabled) { + // Interrupts were disabled at the start of this function, so we + // can't just enable them (that would allow other interrupts to + // trigger as well!). We'll have to poll the data register empty + // flag ourselves. If it is set, pretend an interrupt has happened + // and call the handler to free up space for us. + if(bit_is_set(*_ucsra, UDRE0)) + _tx_udr_empty_irq(); + } else { + // Interrupts were enabled, so we can temporarily enable them so + // our tx interrupt can fire and free up some space. Note that we + // can't just use the UDRE polling approach here instead of + // enabling interrupts, since that would prevent other + // interrupts from triggering while we wait for room. + NONATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + _NOP(); // Any pending interrupts will trigger here + } + // Recalculate i, since other interrupt handlers might have + // written to our buffer + i = (_tx_buffer_head + 1) % SERIAL_BUFFER_SIZE; + } + } + + _tx_buffer[_tx_buffer_head] = c; + _tx_buffer_head = i; + + sbi(*_ucsrb, UDRIE0); + _written = true; + + return 1; + } } HardwareSerial::operator bool() { return true; } -// Preinstantiate Objects ////////////////////////////////////////////////////// - -#if defined(UBRRH) && defined(UBRRL) - HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X); -#elif defined(UBRR0H) && defined(UBRR0L) - HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0); -#elif defined(USBCON) - // do nothing - Serial object and buffers are initialized in CDC code -#else - #error no serial port defined (port 0) -#endif - -#if defined(UBRR1H) - HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1); -#endif -#if defined(UBRR2H) - HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2); -#endif -#if defined(UBRR3H) - HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3); -#endif - #endif // whole file - diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial.h b/hardware/arduino/avr/cores/arduino/HardwareSerial.h index 0f622625320..8bf7744a2b7 100644 --- a/hardware/arduino/avr/cores/arduino/HardwareSerial.h +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial.h @@ -46,14 +46,9 @@ class HardwareSerial : public Stream volatile uint8_t *_ucsrb; volatile uint8_t *_ucsrc; volatile uint8_t *_udr; - uint8_t _rxen; - uint8_t _txen; - uint8_t _rxcie; - uint8_t _udrie; - uint8_t _u2x; - bool transmitting; + // Has any byte been written to the UART since begin() + bool _written; - public: volatile uint8_t _rx_buffer_head; volatile uint8_t _rx_buffer_tail; volatile uint8_t _tx_buffer_head; @@ -65,11 +60,11 @@ class HardwareSerial : public Stream unsigned char _rx_buffer[SERIAL_BUFFER_SIZE]; unsigned char _tx_buffer[SERIAL_BUFFER_SIZE]; + public: HardwareSerial( volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, volatile uint8_t *ucsra, volatile uint8_t *ucsrb, - volatile uint8_t *ucsrc, volatile uint8_t *udr, - uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x); + volatile uint8_t *ucsrc, volatile uint8_t *udr); void begin(unsigned long); void begin(unsigned long, uint8_t); void end(); @@ -84,6 +79,10 @@ class HardwareSerial : public Stream inline size_t write(int n) { return write((uint8_t)n); } using Print::write; // pull in write(str) and write(buf, size) from Print operator bool(); + + // Interrupt handlers - Not intended to be called externally + void _rx_complete_irq(void); + void _tx_udr_empty_irq(void); }; // Define config for Serial.begin(baud, config); @@ -114,18 +113,19 @@ class HardwareSerial : public Stream #if defined(UBRRH) || defined(UBRR0H) extern HardwareSerial Serial; -#elif defined(USBCON) - #include "USBAPI.h" -// extern HardwareSerial Serial_; + #define HAVE_HWSERIAL0 #endif #if defined(UBRR1H) extern HardwareSerial Serial1; + #define HAVE_HWSERIAL1 #endif #if defined(UBRR2H) extern HardwareSerial Serial2; + #define HAVE_HWSERIAL2 #endif #if defined(UBRR3H) extern HardwareSerial Serial3; + #define HAVE_HWSERIAL3 #endif extern void serialEventRun(void) __attribute__((weak)); diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial0.cpp b/hardware/arduino/avr/cores/arduino/HardwareSerial0.cpp new file mode 100644 index 00000000000..dd4d806c756 --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial0.cpp @@ -0,0 +1,54 @@ +#include "Arduino.h" +#include "HardwareSerial.h" + +// Each HardwareSerial is defined in its own file, sine the linker pulls +// in the entire file when any element inside is used. --gc-sections can +// additionally cause unused symbols to be dropped, but ISRs have the +// "used" attribute so are never dropped and they keep the +// HardwareSerial instance in as well. Putting each instance in its own +// file prevents the linker from pulling in any unused instances in the +// first place. + +#if defined(HAVE_HWSERIAL0) + +#if defined(USART_RX_vect) + ISR(USART_RX_vect) +#elif defined(USART0_RX_vect) + ISR(USART0_RX_vect) +#elif defined(USART_RXC_vect) + ISR(USART_RXC_vect) // ATmega8 +#else + #error "Don't know what the Data Received vector is called for the first UART" +#endif + { + Serial._rx_complete_irq(); + } + +#if defined(UART0_UDRE_vect) +ISR(UART0_UDRE_vect) +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) +#elif defined(USART0_UDRE_vect) +ISR(USART0_UDRE_vect) +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) +#else + #error "Don't know what the Data Register Empty vector is called for the first UART" +#endif +{ + Serial._tx_udr_empty_irq(); +} + +#if defined(UBRRH) && defined(UBRRL) + HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); +#else + HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0); +#endif + +// Function that can be weakly referenced by serialEventRun to prevent +// pulling in this file if it's not otherwise used. +bool Serial0_available() { + return Serial.available(); +} + +#endif // HAVE_HWSERIAL0 diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial1.cpp b/hardware/arduino/avr/cores/arduino/HardwareSerial1.cpp new file mode 100644 index 00000000000..c04263e52c4 --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial1.cpp @@ -0,0 +1,54 @@ +#include "Arduino.h" +#include "HardwareSerial.h" + +// Each HardwareSerial is defined in its own file, sine the linker pulls +// in the entire file when any element inside is used. --gc-sections can +// additionally cause unused symbols to be dropped, but ISRs have the +// "used" attribute so are never dropped and they keep the +// HardwareSerial instance in as well. Putting each instance in its own +// file prevents the linker from pulling in any unused instances in the +// first place. + +#if defined(HAVE_HWSERIAL1) + +#if defined(USART_RX_vect) + ISR(USART_RX_vect) +#elif defined(USART1_RX_vect) + ISR(USART1_RX_vect) +#elif defined(USART_RXC_vect) + ISR(USART_RXC_vect) // ATmega8 +#else + #error "Don't know what the Data Received vector is called for the first UART" +#endif + { + Serial1._rx_complete_irq(); + } + +#if defined(UART1_UDRE_vect) +ISR(UART1_UDRE_vect) +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) +#elif defined(USART1_UDRE_vect) +ISR(USART1_UDRE_vect) +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) +#else + #error "Don't know what the Data Register Empty vector is called for the first UART" +#endif +{ + Serial1._tx_udr_empty_irq(); +} + +#if defined(UBRRH) && defined(UBRRL) + HardwareSerial Serial1(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); +#else + HardwareSerial Serial1(&UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UCSR1C, &UDR1); +#endif + +// Function that can be weakly referenced by serialEventRun to prevent +// pulling in this file if it's not otherwise used. +bool Serial1_available() { + return Serial1.available(); +} + +#endif // HAVE_HWSERIAL1 diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial2.cpp b/hardware/arduino/avr/cores/arduino/HardwareSerial2.cpp new file mode 100644 index 00000000000..2c448f9372b --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial2.cpp @@ -0,0 +1,54 @@ +#include "Arduino.h" +#include "HardwareSerial.h" + +// Each HardwareSerial is defined in its own file, sine the linker pulls +// in the entire file when any element inside is used. --gc-sections can +// additionally cause unused symbols to be dropped, but ISRs have the +// "used" attribute so are never dropped and they keep the +// HardwareSerial instance in as well. Putting each instance in its own +// file prevents the linker from pulling in any unused instances in the +// first place. + +#if defined(HAVE_HWSERIAL2) + +#if defined(USART_RX_vect) + ISR(USART_RX_vect) +#elif defined(USART2_RX_vect) + ISR(USART2_RX_vect) +#elif defined(USART_RXC_vect) + ISR(USART_RXC_vect) // ATmega8 +#else + #error "Don't know what the Data Received vector is called for the first UART" +#endif + { + Serial2._rx_complete_irq(); + } + +#if defined(UART2_UDRE_vect) +ISR(UART2_UDRE_vect) +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) +#elif defined(USART2_UDRE_vect) +ISR(USART2_UDRE_vect) +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) +#else + #error "Don't know what the Data Register Empty vector is called for the first UART" +#endif +{ + Serial2._tx_udr_empty_irq(); +} + +#if defined(UBRRH) && defined(UBRRL) + HardwareSerial Serial2(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); +#else + HardwareSerial Serial2(&UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UCSR2C, &UDR2); +#endif + +// Function that can be weakly referenced by serialEventRun to prevent +// pulling in this file if it's not otherwise used. +bool Serial2_available() { + return Serial2.available(); +} + +#endif // HAVE_HWSERIAL2 diff --git a/hardware/arduino/avr/cores/arduino/HardwareSerial3.cpp b/hardware/arduino/avr/cores/arduino/HardwareSerial3.cpp new file mode 100644 index 00000000000..012839df8a6 --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/HardwareSerial3.cpp @@ -0,0 +1,54 @@ +#include "Arduino.h" +#include "HardwareSerial.h" + +// Each HardwareSerial is defined in its own file, sine the linker pulls +// in the entire file when any element inside is used. --gc-sections can +// additionally cause unused symbols to be dropped, but ISRs have the +// "used" attribute so are never dropped and they keep the +// HardwareSerial instance in as well. Putting each instance in its own +// file prevents the linker from pulling in any unused instances in the +// first place. + +#if defined(HAVE_HWSERIAL3) + +#if defined(USART_RX_vect) + ISR(USART_RX_vect) +#elif defined(USART3_RX_vect) + ISR(USART3_RX_vect) +#elif defined(USART_RXC_vect) + ISR(USART_RXC_vect) // ATmega8 +#else + #error "Don't know what the Data Received vector is called for the first UART" +#endif + { + Serial3._rx_complete_irq(); + } + +#if defined(UART3_UDRE_vect) +ISR(UART3_UDRE_vect) +#elif defined(UART_UDRE_vect) +ISR(UART_UDRE_vect) +#elif defined(USART3_UDRE_vect) +ISR(USART3_UDRE_vect) +#elif defined(USART_UDRE_vect) +ISR(USART_UDRE_vect) +#else + #error "Don't know what the Data Register Empty vector is called for the first UART" +#endif +{ + Serial3._tx_udr_empty_irq(); +} + +#if defined(UBRRH) && defined(UBRRL) + HardwareSerial Serial3(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR); +#else + HardwareSerial Serial3(&UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UCSR3C, &UDR3); +#endif + +// Function that can be weakly referenced by serialEventRun to prevent +// pulling in this file if it's not otherwise used. +bool Serial3_available() { + return Serial3.available(); +} + +#endif // HAVE_HWSERIAL3 diff --git a/hardware/arduino/avr/cores/arduino/USBAPI.h b/hardware/arduino/avr/cores/arduino/USBAPI.h index da2e86961cc..5a330024cab 100644 --- a/hardware/arduino/avr/cores/arduino/USBAPI.h +++ b/hardware/arduino/avr/cores/arduino/USBAPI.h @@ -55,6 +55,8 @@ class Serial_ : public Stream }; extern Serial_ Serial; +#define HAVE_CDCSERIAL + //================================================================================ //================================================================================ // Mouse