diff options
author | Matthijs Kooijman <matthijs@stdin.nl> | 2014-04-22 20:52:09 +0200 |
---|---|---|
committer | Matthijs Kooijman <matthijs@stdin.nl> | 2015-01-26 17:03:25 +0100 |
commit | c69c0b52aa66714a501544175215fd9723532dc2 (patch) | |
tree | 3abb20dfad43de62d277454f8aae20dace0c109d | |
parent | fe390c3cc8e8b908d8207be80aaa34f804d82c79 (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.
-rw-r--r-- | libraries/SoftwareSerial/SoftwareSerial.cpp | 48 |
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);
|