From c69c0b52aa66714a501544175215fd9723532dc2 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 22 Apr 2014 20:52:09 +0200 Subject: Further optimize SoftwareSerial::write This change restructures the loop, to help the compiler generate shorter code (because now only the LSB of the data byte is checked and subsequent bytes are shifted down one by one, it can use th "skip if bit set" instruction). Furthermore, it puts most attributes in local variables, which causes the compiler to put them into registers. This makes the timing-critical part of the code smaller, making it easier to provide accurate timings. On an Arduino uno using gcc 4.3, this saves 58 bytes. On gcc 4.8, this saves 14 bytes. --- libraries/SoftwareSerial/SoftwareSerial.cpp | 48 +++++++++++++++++------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/libraries/SoftwareSerial/SoftwareSerial.cpp b/libraries/SoftwareSerial/SoftwareSerial.cpp index da9af30..621a2af 100644 --- a/libraries/SoftwareSerial/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/SoftwareSerial.cpp @@ -292,14 +292,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; @@ -477,29 +469,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; - // Write each of the 8 bits - if (_inverse_logic) - b = ~b; + tunedDelay(delay); - for (byte mask = 0x01; mask; mask <<= 1) + // Write each of the 8 bits + for (uint8_t i = 8; i > 0; --i) { - if (b & mask) // choose bit - tx_pin_write(HIGH); // send 1 + if (b & 1) // choose bit + *reg |= reg_mask; // send 1 else - tx_pin_write(LOW); // send 0 + *reg &= inv_mask; // send 0 - tunedDelay(_tx_delay); + tunedDelay(delay); + b >>= 1; } // restore pin to natural state - tx_pin_write(_inverse_logic ? LOW : HIGH); + if (inv) + *reg &= inv_mask; + else + *reg |= reg_mask; SREG = oldSREG; // turn interrupts back on tunedDelay(_tx_delay); -- cgit v1.2.3-18-g5258