diff options
Diffstat (limited to 'cores/arduino/HardwareSerial.cpp')
-rw-r--r-- | cores/arduino/HardwareSerial.cpp | 250 |
1 files changed, 170 insertions, 80 deletions
diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 4397efb..db6b149 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -24,7 +24,7 @@ #include <stdio.h> #include <string.h> #include <inttypes.h> -#include "wiring.h" +#include "Arduino.h" #include "wiring_private.h" // this next line disables the entire HardwareSerial.cpp, @@ -34,149 +34,223 @@ #include "HardwareSerial.h" // Define constants and variables for buffering incoming serial data. We're -// using a ring buffer (I think), in which rx_buffer_head is the index of the -// location to which to write the next incoming character and rx_buffer_tail -// is the index of the location from which to read. +// 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. #if (RAMEND < 1000) - #define RX_BUFFER_SIZE 32 + #define SERIAL_BUFFER_SIZE 16 #else - #define RX_BUFFER_SIZE 128 + #define SERIAL_BUFFER_SIZE 64 #endif struct ring_buffer { - unsigned char buffer[RX_BUFFER_SIZE]; - int head; - int tail; + unsigned char buffer[SERIAL_BUFFER_SIZE]; + volatile int head; + volatile int tail; }; #if defined(UBRRH) || defined(UBRR0H) ring_buffer rx_buffer = { { 0 }, 0, 0 }; + ring_buffer tx_buffer = { { 0 }, 0, 0 }; #endif #if defined(UBRR1H) ring_buffer rx_buffer1 = { { 0 }, 0, 0 }; + ring_buffer tx_buffer1 = { { 0 }, 0, 0 }; #endif #if defined(UBRR2H) ring_buffer rx_buffer2 = { { 0 }, 0, 0 }; + ring_buffer tx_buffer2 = { { 0 }, 0, 0 }; #endif #if defined(UBRR3H) ring_buffer rx_buffer3 = { { 0 }, 0, 0 }; + ring_buffer tx_buffer3 = { { 0 }, 0, 0 }; #endif -inline void store_char(unsigned char c, ring_buffer *rx_buffer) +inline void store_char(unsigned char c, ring_buffer *buffer) { - int i = (unsigned int)(rx_buffer->head + 1) % RX_BUFFER_SIZE; + int i = (unsigned int)(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->buffer[rx_buffer->head] = c; - rx_buffer->head = i; + if (i != buffer->tail) { + buffer->buffer[buffer->head] = c; + buffer->head = i; } } +#if !defined(USART_RX_vect) && !defined(SIG_USART0_RECV) && \ + !defined(SIG_UART0_RECV) && !defined(USART0_RX_vect) && \ + !defined(SIG_UART_RECV) + #error Don't know what the Data Received vector is called for the first UART +#else + void serialEvent() __attribute__((weak)); + void serialEvent() {} #if defined(USART_RX_vect) SIGNAL(USART_RX_vect) - { - #if defined(UDR0) - unsigned char c = UDR0; - #elif defined(UDR) - unsigned char c = UDR; // atmega8535 - #else - #error UDR not defined - #endif - store_char(c, &rx_buffer); - } -#elif defined(SIG_USART0_RECV) && defined(UDR0) +#elif defined(SIG_USART0_RECV) SIGNAL(SIG_USART0_RECV) - { - unsigned char c = UDR0; - store_char(c, &rx_buffer); - } -#elif defined(SIG_UART0_RECV) && defined(UDR0) +#elif defined(SIG_UART0_RECV) SIGNAL(SIG_UART0_RECV) - { - unsigned char c = UDR0; - store_char(c, &rx_buffer); - } -//#elif defined(SIG_USART_RECV) #elif defined(USART0_RX_vect) - // fixed by Mark Sproul this is on the 644/644p - //SIGNAL(SIG_USART_RECV) SIGNAL(USART0_RX_vect) +#elif defined(SIG_UART_RECV) + SIGNAL(SIG_UART_RECV) +#endif { #if defined(UDR0) unsigned char c = UDR0; #elif defined(UDR) - unsigned char c = UDR; // atmega8, atmega32 + unsigned char c = UDR; #else #error UDR not defined #endif store_char(c, &rx_buffer); + serialEvent(); } -#elif defined(SIG_UART_RECV) - // this is for atmega8 - SIGNAL(SIG_UART_RECV) - { - #if defined(UDR0) - unsigned char c = UDR0; // atmega645 - #elif defined(UDR) - unsigned char c = UDR; // atmega8 - #endif - store_char(c, &rx_buffer); - } -#elif defined(USBCON) - #warning No interrupt handler for usart 0 - #warning Serial(0) is on USB interface -#else - #error No interrupt handler for usart 0 #endif -//#if defined(SIG_USART1_RECV) #if defined(USART1_RX_vect) - //SIGNAL(SIG_USART1_RECV) + void serialEvent1() __attribute__((weak)); + void serialEvent1() {} SIGNAL(USART1_RX_vect) { unsigned char c = UDR1; store_char(c, &rx_buffer1); + serialEvent1(); } #elif defined(SIG_USART1_RECV) #error SIG_USART1_RECV #endif #if defined(USART2_RX_vect) && defined(UDR2) + void serialEvent2() __attribute__((weak)); + void serialEvent2() {} SIGNAL(USART2_RX_vect) { unsigned char c = UDR2; store_char(c, &rx_buffer2); + serialEvent2(); } #elif defined(SIG_USART2_RECV) #error SIG_USART2_RECV #endif #if defined(USART3_RX_vect) && defined(UDR3) + void serialEvent3() __attribute__((weak)); + void serialEvent3() {} SIGNAL(USART3_RX_vect) { unsigned char c = UDR3; store_char(c, &rx_buffer3); + serialEvent3(); } #elif defined(SIG_USART3_RECV) #error SIG_USART3_RECV #endif +#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 +{ + if (tx_buffer.head == 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 = tx_buffer.buffer[tx_buffer.tail]; + tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE; + + #if defined(UDR0) + UDR0 = c; + #elif defined(UDR) + UDR = c; + #else + #error UDR not defined + #endif + } +} +#endif + +#ifdef USART1_UDRE_vect +ISR(USART1_UDRE_vect) +{ + if (tx_buffer1.head == tx_buffer1.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 = tx_buffer1.buffer[tx_buffer1.tail]; + tx_buffer1.tail = (tx_buffer1.tail + 1) % SERIAL_BUFFER_SIZE; + + UDR1 = c; + } +} +#endif + +#ifdef USART2_UDRE_vect +ISR(USART2_UDRE_vect) +{ + if (tx_buffer2.head == tx_buffer2.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 = tx_buffer2.buffer[tx_buffer2.tail]; + tx_buffer2.tail = (tx_buffer2.tail + 1) % SERIAL_BUFFER_SIZE; + + UDR2 = c; + } +} +#endif + +#ifdef USART3_UDRE_vect +ISR(USART3_UDRE_vect) +{ + if (tx_buffer3.head == tx_buffer3.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 = tx_buffer3.buffer[tx_buffer3.tail]; + tx_buffer3.tail = (tx_buffer3.tail + 1) % SERIAL_BUFFER_SIZE; + + UDR3 = c; + } +} +#endif + // Constructors //////////////////////////////////////////////////////////////// -HardwareSerial::HardwareSerial(ring_buffer *rx_buffer, +HardwareSerial::HardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer, volatile uint8_t *ubrrh, volatile uint8_t *ubrrl, volatile uint8_t *ucsra, volatile uint8_t *ucsrb, volatile uint8_t *udr, - uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udre, uint8_t u2x) + uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x) { _rx_buffer = rx_buffer; + _tx_buffer = tx_buffer; _ubrrh = ubrrh; _ubrrl = ubrrl; _ucsra = ucsra; @@ -185,13 +259,13 @@ HardwareSerial::HardwareSerial(ring_buffer *rx_buffer, _rxen = rxen; _txen = txen; _rxcie = rxcie; - _udre = udre; + _udrie = udrie; _u2x = u2x; } // Public Methods ////////////////////////////////////////////////////////////// -void HardwareSerial::begin(long baud) +void HardwareSerial::begin(unsigned long baud) { uint16_t baud_setting; bool use_u2x = true; @@ -204,6 +278,8 @@ void HardwareSerial::begin(long baud) use_u2x = false; } #endif + +try_again: if (use_u2x) { *_ucsra = 1 << _u2x; @@ -212,6 +288,12 @@ void HardwareSerial::begin(long baud) *_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; @@ -220,18 +302,27 @@ void HardwareSerial::begin(long baud) sbi(*_ucsrb, _rxen); sbi(*_ucsrb, _txen); sbi(*_ucsrb, _rxcie); + cbi(*_ucsrb, _udrie); } void HardwareSerial::end() { + // wait for transmission of outgoing data + while (_tx_buffer->head != _tx_buffer->tail) + ; + cbi(*_ucsrb, _rxen); cbi(*_ucsrb, _txen); cbi(*_ucsrb, _rxcie); + cbi(*_ucsrb, _udrie); + + // clear any received data + _rx_buffer->head = _rx_buffer->tail; } int HardwareSerial::available(void) { - return (unsigned int)(RX_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % RX_BUFFER_SIZE; + return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE; } int HardwareSerial::peek(void) @@ -250,39 +341,38 @@ int HardwareSerial::read(void) return -1; } else { unsigned char c = _rx_buffer->buffer[_rx_buffer->tail]; - _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % RX_BUFFER_SIZE; + _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE; return c; } } void HardwareSerial::flush() { - // don't reverse this or there may be problems if the RX interrupt - // occurs after reading the value of rx_buffer_head but before writing - // the value to rx_buffer_tail; the previous value of rx_buffer_head - // may be written to rx_buffer_tail, making it appear as if the buffer - // don't reverse this or there may be problems if the RX interrupt - // occurs after reading the value of rx_buffer_head but before writing - // the value to rx_buffer_tail; the previous value of rx_buffer_head - // may be written to rx_buffer_tail, making it appear as if the buffer - // were full, not empty. - _rx_buffer->head = _rx_buffer->tail; + while (_tx_buffer->head != _tx_buffer->tail) + ; } void HardwareSerial::write(uint8_t c) { - while (!((*_ucsra) & (1 << _udre))) + 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) ; - - *_udr = c; + + _tx_buffer->buffer[_tx_buffer->head] = c; + _tx_buffer->head = i; + + sbi(*_ucsrb, _udrie); } // Preinstantiate Objects ////////////////////////////////////////////////////// #if defined(UBRRH) && defined(UBRRL) - HardwareSerial Serial(&rx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRE, U2X); + HardwareSerial Serial(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X); #elif defined(UBRR0H) && defined(UBRR0L) - HardwareSerial Serial(&rx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRE0, U2X0); + HardwareSerial Serial(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0); #elif defined(USBCON) #warning no serial port defined (port 0) #else @@ -290,13 +380,13 @@ void HardwareSerial::write(uint8_t c) #endif #if defined(UBRR1H) - HardwareSerial Serial1(&rx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UDR1, RXEN1, TXEN1, RXCIE1, UDRE1, U2X1); + HardwareSerial Serial1(&rx_buffer1, &tx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1); #endif #if defined(UBRR2H) - HardwareSerial Serial2(&rx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UDR2, RXEN2, TXEN2, RXCIE2, UDRE2, U2X2); + HardwareSerial Serial2(&rx_buffer2, &tx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2); #endif #if defined(UBRR3H) - HardwareSerial Serial3(&rx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UDR3, RXEN3, TXEN3, RXCIE3, UDRE3, U2X3); + HardwareSerial Serial3(&rx_buffer3, &tx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3); #endif #endif // whole file |