Skip to content

Commit c69c0b5

Browse files
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.
1 parent fe390c3 commit c69c0b5

File tree

1 file changed

+29
-19
lines changed

1 file changed

+29
-19
lines changed

libraries/SoftwareSerial/SoftwareSerial.cpp

+29-19
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,6 @@ void SoftwareSerial::recv()
292292
#endif
293293
}
294294

295-
void SoftwareSerial::tx_pin_write(uint8_t pin_state)
296-
{
297-
if (pin_state == LOW)
298-
*_transmitPortRegister &= ~_transmitBitMask;
299-
else
300-
*_transmitPortRegister |= _transmitBitMask;
301-
}
302-
303295
uint8_t SoftwareSerial::rx_pin_read()
304296
{
305297
return *_receivePortRegister & _receiveBitMask;
@@ -477,29 +469,47 @@ size_t SoftwareSerial::write(uint8_t b)
477469
return 0;
478470
}
479471

472+
// By declaring these as local variables, the compiler will put them
473+
// in registers _before_ disabling interrupts and entering the
474+
// critical timing sections below, which makes it a lot easier to
475+
// verify the cycle timings
476+
volatile uint8_t *reg = _transmitPortRegister;
477+
uint8_t reg_mask = _transmitBitMask;
478+
uint8_t inv_mask = ~_transmitBitMask;
480479
uint8_t oldSREG = SREG;
480+
bool inv = _inverse_logic;
481+
uint16_t delay = _tx_delay;
482+
483+
if (inv)
484+
b = ~b;
485+
481486
cli(); // turn off interrupts for a clean txmit
482487

483488
// Write the start bit
484-
tx_pin_write(_inverse_logic ? HIGH : LOW);
485-
tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
489+
if (inv)
490+
*reg |= reg_mask;
491+
else
492+
*reg &= inv_mask;
486493

487-
// Write each of the 8 bits
488-
if (_inverse_logic)
489-
b = ~b;
494+
tunedDelay(delay);
490495

491-
for (byte mask = 0x01; mask; mask <<= 1)
496+
// Write each of the 8 bits
497+
for (uint8_t i = 8; i > 0; --i)
492498
{
493-
if (b & mask) // choose bit
494-
tx_pin_write(HIGH); // send 1
499+
if (b & 1) // choose bit
500+
*reg |= reg_mask; // send 1
495501
else
496-
tx_pin_write(LOW); // send 0
502+
*reg &= inv_mask; // send 0
497503

498-
tunedDelay(_tx_delay);
504+
tunedDelay(delay);
505+
b >>= 1;
499506
}
500507

501508
// restore pin to natural state
502-
tx_pin_write(_inverse_logic ? LOW : HIGH);
509+
if (inv)
510+
*reg &= inv_mask;
511+
else
512+
*reg |= reg_mask;
503513

504514
SREG = oldSREG; // turn interrupts back on
505515
tunedDelay(_tx_delay);

0 commit comments

Comments
 (0)