diff --git a/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.cpp b/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.cpp index d1f6c9256a6..527f3f9f03f 100644 --- a/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -42,92 +42,7 @@ The latest version of this library can always be found at #include #include #include -// -// Lookup table -// -typedef struct _DELAY_TABLE -{ - long baud; - unsigned short rx_delay_centering; - unsigned short rx_delay_intrabit; - unsigned short rx_delay_stopbit; - unsigned short tx_delay; -} DELAY_TABLE; - -#if F_CPU == 16000000 - -static const DELAY_TABLE PROGMEM table[] = -{ - // baud rxcenter rxintra rxstop tx - { 115200, 1, 17, 17, 12, }, - { 57600, 10, 37, 37, 33, }, - { 38400, 25, 57, 57, 54, }, - { 31250, 31, 70, 70, 68, }, - { 28800, 34, 77, 77, 74, }, - { 19200, 54, 117, 117, 114, }, - { 14400, 74, 156, 156, 153, }, - { 9600, 114, 236, 236, 233, }, - { 4800, 233, 474, 474, 471, }, - { 2400, 471, 950, 950, 947, }, - { 1200, 947, 1902, 1902, 1899, }, - { 600, 1902, 3804, 3804, 3800, }, - { 300, 3804, 7617, 7617, 7614, }, -}; - -const int XMIT_START_ADJUSTMENT = 5; - -#elif F_CPU == 8000000 - -static const DELAY_TABLE table[] PROGMEM = -{ - // baud rxcenter rxintra rxstop tx - { 115200, 1, 5, 5, 3, }, - { 57600, 1, 15, 15, 13, }, - { 38400, 2, 25, 26, 23, }, - { 31250, 7, 32, 33, 29, }, - { 28800, 11, 35, 35, 32, }, - { 19200, 20, 55, 55, 52, }, - { 14400, 30, 75, 75, 72, }, - { 9600, 50, 114, 114, 112, }, - { 4800, 110, 233, 233, 230, }, - { 2400, 229, 472, 472, 469, }, - { 1200, 467, 948, 948, 945, }, - { 600, 948, 1895, 1895, 1890, }, - { 300, 1895, 3805, 3805, 3802, }, -}; - -const int XMIT_START_ADJUSTMENT = 4; - -#elif F_CPU == 20000000 - -// 20MHz support courtesy of the good people at macegr.com. -// Thanks, Garrett! - -static const DELAY_TABLE PROGMEM table[] = -{ - // baud rxcenter rxintra rxstop tx - { 115200, 3, 21, 21, 18, }, - { 57600, 20, 43, 43, 41, }, - { 38400, 37, 73, 73, 70, }, - { 31250, 45, 89, 89, 88, }, - { 28800, 46, 98, 98, 95, }, - { 19200, 71, 148, 148, 145, }, - { 14400, 96, 197, 197, 194, }, - { 9600, 146, 297, 297, 294, }, - { 4800, 296, 595, 595, 592, }, - { 2400, 592, 1189, 1189, 1186, }, - { 1200, 1187, 2379, 2379, 2376, }, - { 600, 2379, 4759, 4759, 4755, }, - { 300, 4759, 9523, 9523, 9520, }, -}; - -const int XMIT_START_ADJUSTMENT = 6; - -#else - -#error This version of SoftwareSerial supports only 20, 16 and 8MHz processors - -#endif +#include // // Statics @@ -162,36 +77,44 @@ inline void DebugPulse(uint8_t pin, uint8_t count) /* static */ inline void SoftwareSerial::tunedDelay(uint16_t delay) { - uint8_t tmp=0; - - asm volatile("sbiw %0, 0x01 \n\t" - "ldi %1, 0xFF \n\t" - "cpi %A0, 0xFF \n\t" - "cpc %B0, %1 \n\t" - "brne .-10 \n\t" - : "+r" (delay), "+a" (tmp) - : "0" (delay) - ); + _delay_loop_2(delay); } // This function sets the current object as the "listening" // one and returns true if it replaces another bool SoftwareSerial::listen() { + if (!_rx_delay_stopbit) + return false; + if (active_object != this) { + if (active_object) + active_object->stopListening(); + _buffer_overflow = false; - uint8_t oldSREG = SREG; - cli(); _receive_buffer_head = _receive_buffer_tail = 0; active_object = this; - SREG = oldSREG; + + setRxIntMsk(true); return true; } return false; } +// Stop listening. Returns true if we were actually listening. +bool SoftwareSerial::stopListening() +{ + if (active_object == this) + { + setRxIntMsk(false); + active_object = NULL; + return true; + } + return false; +} + // // The receive routine called by the interrupt handler // @@ -220,43 +143,49 @@ void SoftwareSerial::recv() // so interrupt is probably not for us if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) { + // Disable further interrupts during reception, this prevents + // triggering another interrupt directly after we return, which can + // cause problems at higher baudrates. + setRxIntMsk(false); + // Wait approximately 1/2 of a bit width to "center" the sample tunedDelay(_rx_delay_centering); DebugPulse(_DEBUG_PIN2, 1); // Read each of the 8 bits - for (uint8_t i=0x1; i; i <<= 1) + for (uint8_t i=8; i > 0; --i) { tunedDelay(_rx_delay_intrabit); + d >>= 1; DebugPulse(_DEBUG_PIN2, 1); - uint8_t noti = ~i; if (rx_pin_read()) - d |= i; - else // else clause added to ensure function timing is ~balanced - d &= noti; + d |= 0x80; } - // skip the stop bit - tunedDelay(_rx_delay_stopbit); - DebugPulse(_DEBUG_PIN2, 1); - if (_inverse_logic) d = ~d; // if buffer full, set the overflow flag and return - if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) + uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + if (next != _receive_buffer_head) { // save new data in buffer: tail points to where byte goes _receive_buffer[_receive_buffer_tail] = d; // save new byte - _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + _receive_buffer_tail = next; } else { -#if _DEBUG // for scope: pulse pin as overflow indictator DebugPulse(_DEBUG_PIN1, 1); -#endif _buffer_overflow = true; } + + // skip the stop bit + tunedDelay(_rx_delay_stopbit); + DebugPulse(_DEBUG_PIN1, 1); + + // Re-enable interrupts when we're sure to be inside the stop bit + setRxIntMsk(true); + } #if GCC_VERSION < 40302 @@ -275,14 +204,6 @@ void SoftwareSerial::recv() #endif } -void SoftwareSerial::tx_pin_write(uint8_t pin_state) -{ - if (pin_state == LOW) - *_transmitPortRegister &= ~_transmitBitMask; - else - *_transmitPortRegister |= _transmitBitMask; -} - uint8_t SoftwareSerial::rx_pin_read() { return *_receivePortRegister & _receiveBitMask; @@ -309,24 +230,15 @@ ISR(PCINT0_vect) #endif #if defined(PCINT1_vect) -ISR(PCINT1_vect) -{ - SoftwareSerial::handle_interrupt(); -} +ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect)); #endif #if defined(PCINT2_vect) -ISR(PCINT2_vect) -{ - SoftwareSerial::handle_interrupt(); -} +ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect)); #endif #if defined(PCINT3_vect) -ISR(PCINT3_vect) -{ - SoftwareSerial::handle_interrupt(); -} +ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect)); #endif // @@ -354,8 +266,12 @@ SoftwareSerial::~SoftwareSerial() void SoftwareSerial::setTX(uint8_t tx) { - pinMode(tx, OUTPUT); + // First write, then set output. If we do this the other way around, + // the pin would be output low for a short while before switching to + // output hihg. Now, it is input with pullup for a short while, which + // is fine. With inverse logic, either order is fine. digitalWrite(tx, _inverse_logic ? LOW : HIGH); + pinMode(tx, OUTPUT); _transmitBitMask = digitalPinToBitMask(tx); uint8_t port = digitalPinToPort(tx); _transmitPortRegister = portOutputRegister(port); @@ -372,6 +288,13 @@ void SoftwareSerial::setRX(uint8_t rx) _receivePortRegister = portInputRegister(port); } +uint16_t SoftwareSerial::subtract_cap(uint16_t num, uint16_t sub) { + if (num > sub) + return num - sub; + else + return 1; +} + // // Public methods // @@ -380,27 +303,64 @@ void SoftwareSerial::begin(long speed) { _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; - for (unsigned i=0; i 40800 + // Timings counted from gcc 4.8.2 output. This works up to 115200 on + // 16Mhz and 57600 on 8Mhz. + // + // When the start bit occurs, there are 3 or 4 cycles before the + // interrupt flag is set, 4 cycles before the PC is set to the right + // interrupt vector address and the old PC is pushed on the stack, + // and then 75 cycles of instructions (including the RJMP in the + // ISR vector table) until the first delay. After the delay, there + // are 17 more cycles until the pin value is read (excluding the + // delay in the loop). + // We want to have a total delay of 1.5 bit time. Inside the loop, + // we already wait for 1 bit time - 23 cycles, so here we wait for + // 0.5 bit time - (71 + 18 - 22) cycles. + _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4); + + // There are 23 cycles in each loop iteration (excluding the delay) + _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4); + + // There are 37 cycles from the last bit read to the start of + // stopbit delay and 11 cycles from the delay until the interrupt + // mask is enabled again (which _must_ happen during the stopbit). + // This delay aims at 3/4 of a bit time, meaning the end of the + // delay will be at 1/4th of the stopbit. This allows some extra + // time for ISR cleanup, which makes 115200 baud at 16Mhz work more + // reliably + _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4); + #else // Timings counted from gcc 4.3.2 output + // Note that this code is a _lot_ slower, mostly due to bad register + // allocation choices of gcc. This works up to 57600 on 16Mhz and + // 38400 on 8Mhz. + _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11) / 4); + _rx_delay_intrabit = subtract_cap(bit_delay, 11 / 4); + _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17) / 4); + #endif + + + // Enable the PCINT for the entire port here, but never disable it + // (others might also need it, so we disable the interrupt by using + // the per-pin PCMSK register). + *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); + // Precalculate the pcint mask register and value, so setRxIntMask + // can be used inside the ISR without costing too much time. + _pcint_maskreg = digitalPinToPCMSK(_receivePin); + _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin)); - // Set up RX interrupts, but only if we have a valid RX baud rate - if (_rx_delay_stopbit) - { - if (digitalPinToPCICR(_receivePin)) - { - *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); - *digitalPinToPCMSK(_receivePin) |= _BV(digitalPinToPCMSKbit(_receivePin)); - } tunedDelay(_tx_delay); // if we were low this establishes the end } @@ -412,10 +372,17 @@ void SoftwareSerial::begin(long speed) listen(); } +void SoftwareSerial::setRxIntMsk(bool enable) +{ + if (enable) + *_pcint_maskreg |= _pcint_maskvalue; + else + *_pcint_maskreg &= ~_pcint_maskvalue; +} + void SoftwareSerial::end() { - if (digitalPinToPCMSK(_receivePin)) - *digitalPinToPCMSK(_receivePin) &= ~_BV(digitalPinToPCMSKbit(_receivePin)); + stopListening(); } @@ -450,42 +417,47 @@ size_t SoftwareSerial::write(uint8_t b) return 0; } + // By declaring these as local variables, the compiler will put them + // in registers _before_ disabling interrupts and entering the + // critical timing sections below, which makes it a lot easier to + // verify the cycle timings + volatile uint8_t *reg = _transmitPortRegister; + uint8_t reg_mask = _transmitBitMask; + uint8_t inv_mask = ~_transmitBitMask; uint8_t oldSREG = SREG; + bool inv = _inverse_logic; + uint16_t delay = _tx_delay; + + if (inv) + b = ~b; + cli(); // turn off interrupts for a clean txmit // Write the start bit - tx_pin_write(_inverse_logic ? HIGH : LOW); - tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT); + if (inv) + *reg |= reg_mask; + else + *reg &= inv_mask; + + tunedDelay(delay); // Write each of the 8 bits - if (_inverse_logic) + for (uint8_t i = 8; i > 0; --i) { - for (byte mask = 0x01; mask; mask <<= 1) - { - if (b & mask) // choose bit - tx_pin_write(LOW); // send 1 - else - tx_pin_write(HIGH); // send 0 - - tunedDelay(_tx_delay); - } + if (b & 1) // choose bit + *reg |= reg_mask; // send 1 + else + *reg &= inv_mask; // send 0 - tx_pin_write(LOW); // restore pin to natural state + tunedDelay(delay); + b >>= 1; } - else - { - for (byte mask = 0x01; mask; mask <<= 1) - { - if (b & mask) // choose bit - tx_pin_write(HIGH); // send 1 - else - tx_pin_write(LOW); // send 0 - - tunedDelay(_tx_delay); - } - tx_pin_write(HIGH); // restore pin to natural state - } + // restore pin to natural state + if (inv) + *reg &= inv_mask; + else + *reg |= reg_mask; SREG = oldSREG; // turn interrupts back on tunedDelay(_tx_delay); diff --git a/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.h b/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.h index a6a60b5560f..e28da98e517 100644 --- a/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.h +++ b/hardware/arduino/avr/libraries/SoftwareSerial/SoftwareSerial.h @@ -53,7 +53,10 @@ class SoftwareSerial : public Stream volatile uint8_t *_receivePortRegister; uint8_t _transmitBitMask; volatile uint8_t *_transmitPortRegister; + volatile uint8_t *_pcint_maskreg; + uint8_t _pcint_maskvalue; + // Expressed as 4-cycle delays (must never be 0!) uint16_t _rx_delay_centering; uint16_t _rx_delay_intrabit; uint16_t _rx_delay_stopbit; @@ -69,11 +72,15 @@ class SoftwareSerial : public Stream static SoftwareSerial *active_object; // private methods - void recv(); + void recv() __attribute__((__always_inline__)); uint8_t rx_pin_read(); - void tx_pin_write(uint8_t pin_state); + void tx_pin_write(uint8_t pin_state) __attribute__((__always_inline__)); void setTX(uint8_t transmitPin); void setRX(uint8_t receivePin); + void setRxIntMsk(bool enable) __attribute__((__always_inline__)); + + // Return num - sub, or 1 if the result would be < 1 + static uint16_t subtract_cap(uint16_t num, uint16_t sub); // private static method for timing static inline void tunedDelay(uint16_t delay); @@ -86,7 +93,8 @@ class SoftwareSerial : public Stream bool listen(); void end(); bool isListening() { return this == active_object; } - bool overflow() { bool ret = _buffer_overflow; _buffer_overflow = false; return ret; } + bool stopListening(); + bool overflow() { bool ret = _buffer_overflow; if (ret) _buffer_overflow = false; return ret; } int peek(); virtual size_t write(uint8_t byte); @@ -97,7 +105,7 @@ class SoftwareSerial : public Stream using Print::write; // public only for easy access by interrupt handlers - static inline void handle_interrupt(); + static inline void handle_interrupt() __attribute__((__always_inline__)); }; // Arduino 0012 workaround