1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-01-18 07:52:14 +01:00

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.
This commit is contained in:
Matthijs Kooijman 2014-04-22 20:52:09 +02:00
parent 80ea38b1dc
commit 764f24266e

View File

@ -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;
tunedDelay(delay);
// Write each of the 8 bits
if (_inverse_logic)
b = ~b;
for (byte mask = 0x01; mask; mask <<= 1)
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);