aboutsummaryrefslogtreecommitdiff
path: root/libraries/SoftwareSerial
diff options
context:
space:
mode:
authorMatthijs Kooijman <matthijs@stdin.nl>2014-04-22 20:52:09 +0200
committerMatthijs Kooijman <matthijs@stdin.nl>2015-01-26 17:03:25 +0100
commitc69c0b52aa66714a501544175215fd9723532dc2 (patch)
tree3abb20dfad43de62d277454f8aae20dace0c109d /libraries/SoftwareSerial
parentfe390c3cc8e8b908d8207be80aaa34f804d82c79 (diff)
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.
Diffstat (limited to 'libraries/SoftwareSerial')
-rw-r--r--libraries/SoftwareSerial/SoftwareSerial.cpp48
1 files 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);