mirror of
https://github.com/RobTillaart/Arduino.git
synced 2024-10-03 18:09:02 -04:00
0.4.0 FastShiftOut
This commit is contained in:
parent
f458e0690e
commit
6ce71e3145
@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## [0.4.0] - 2024-09-03
|
||||
- fix #15, loop unroll option, improving performance, kudos to nt314p
|
||||
- fixed bug in test program (see #15)
|
||||
- added flag to select LOOP UNROLL (is optional as it gives larger code size)
|
||||
- optimized the not unrolled loop with ideas of the unrolling version.
|
||||
- corrected type lastValue to uint8_t
|
||||
- add FastShiftOut_scope_test.ino
|
||||
- update readme.md
|
||||
- minor edits
|
||||
|
||||
----
|
||||
|
||||
## [0.3.3] - 2024-07-23
|
||||
- Fix #13, add wrapper functions
|
||||
- write16/24/32, write(array, size)
|
||||
|
@ -13,7 +13,9 @@ FastShiftOut FSO(12, 13, LSBFIRST);
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.print("example fastShiftOut: ");
|
||||
Serial.print(__FILE__);
|
||||
|
||||
Serial.print("FASTSHIFTOUT_LIB_VERSION: ");
|
||||
Serial.println(FASTSHIFTOUT_LIB_VERSION);
|
||||
|
||||
Serial.println("\nPerformance - time in us");
|
||||
@ -63,7 +65,7 @@ void setup()
|
||||
|
||||
Serial.println("\nTest print interface");
|
||||
start = micros();
|
||||
for (int i=0; i<100; i++)
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
FSO.println("Hello world");
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
//
|
||||
// FILE: FastShiftOut_scope_test.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PURPOSE: test sketch for scope
|
||||
// URL: https://github.com/RobTillaart/FastShiftOut
|
||||
|
||||
#include "FastShiftOut.h"
|
||||
|
||||
FastShiftOut FSO(12, 13, LSBFIRST);
|
||||
|
||||
uint32_t start, duration1, duration2;
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println(__FILE__);
|
||||
Serial.print("FASTSHIFTOUT_LIB_VERSION: ");
|
||||
Serial.println(FASTSHIFTOUT_LIB_VERSION);
|
||||
Serial.println();
|
||||
|
||||
// test1();
|
||||
pinMode(12, OUTPUT);
|
||||
}
|
||||
|
||||
|
||||
void test1()
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write(0x55);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test2()
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.writeLSBFIRST(0x88);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void test3()
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write32(0x55555555);
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// ~ 1000 Hz test
|
||||
// digitalWrite(12, HIGH);
|
||||
// delayMicroseconds(1000);
|
||||
// digitalWrite(12, LOW);
|
||||
// delayMicroseconds(1000);
|
||||
|
||||
// reference
|
||||
// shiftOut(12, 13, MSBFIRST, 0x55);
|
||||
|
||||
FSO.write(0x55);
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
|
||||
|
||||
// -- END OF FILE --
|
@ -1,10 +1,9 @@
|
||||
//
|
||||
// FILE: FastShiftOut_test.ino
|
||||
// AUTHOR: Rob Tillaart
|
||||
// PURPOSE: test sketch
|
||||
// PURPOSE: performance test sketch
|
||||
// URL: https://github.com/RobTillaart/FastShiftOut
|
||||
|
||||
|
||||
#include "FastShiftOut.h"
|
||||
|
||||
FastShiftOut FSO(12, 13, LSBFIRST);
|
||||
@ -15,7 +14,9 @@ uint32_t start, duration1, duration2;
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.print("example fastShiftOut: ");
|
||||
Serial.println(__FILE__);
|
||||
|
||||
Serial.print("FASTSHIFTOUT_LIB_VERSION: ");
|
||||
Serial.println(FASTSHIFTOUT_LIB_VERSION);
|
||||
|
||||
Serial.println("\nPerformance - time in us");
|
||||
@ -24,6 +25,9 @@ void setup()
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test7();
|
||||
test8();
|
||||
|
||||
Serial.println("\ndone ...\n");
|
||||
}
|
||||
@ -72,8 +76,8 @@ void test2()
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write(0x55);
|
||||
FSO.write(0x55);
|
||||
FSO.writeLSBFIRST(0x55);
|
||||
FSO.writeLSBFIRST(0x55);
|
||||
}
|
||||
duration2 = micros() - start;
|
||||
Serial.print("writeLSBFIRST: ");
|
||||
@ -90,7 +94,7 @@ void test3()
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write(0x55);
|
||||
FSO.writeMSBFIRST(0x55);
|
||||
}
|
||||
duration1 = micros() - start;
|
||||
Serial.print("writeMSBFIRST: ");
|
||||
@ -142,6 +146,90 @@ void test4()
|
||||
|
||||
|
||||
void test5()
|
||||
{
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write16(0x5555);
|
||||
}
|
||||
duration1 = micros() - start;
|
||||
Serial.print(" write16: ");
|
||||
Serial.println(duration1 * 0.001);
|
||||
delay(100);
|
||||
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write16(0x5555);
|
||||
FSO.write16(0x5555);
|
||||
}
|
||||
duration2 = micros() - start;
|
||||
Serial.print(" write16: ");
|
||||
Serial.println(duration2 * 0.001);
|
||||
Serial.print(" Delta: ");
|
||||
Serial.println((duration2 - duration1) * 0.001);
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
||||
void test6()
|
||||
{
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write24(0x555555);
|
||||
}
|
||||
duration1 = micros() - start;
|
||||
Serial.print(" write24: ");
|
||||
Serial.println(duration1 * 0.001);
|
||||
delay(100);
|
||||
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write24(0x555555);
|
||||
FSO.write24(0x555555);
|
||||
}
|
||||
duration2 = micros() - start;
|
||||
Serial.print(" write24: ");
|
||||
Serial.println(duration2 * 0.001);
|
||||
Serial.print(" Delta: ");
|
||||
Serial.println((duration2 - duration1) * 0.001);
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
||||
void test7()
|
||||
{
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write32(0x55555555);
|
||||
}
|
||||
duration1 = micros() - start;
|
||||
Serial.print(" write32: ");
|
||||
Serial.println(duration1 * 0.001);
|
||||
delay(100);
|
||||
|
||||
start = micros();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
FSO.write32(0x55555555);
|
||||
FSO.write32(0x55555555);
|
||||
}
|
||||
duration2 = micros() - start;
|
||||
Serial.print(" write32: ");
|
||||
Serial.println(duration2 * 0.001);
|
||||
Serial.print(" Delta: ");
|
||||
Serial.println((duration2 - duration1) * 0.001);
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
||||
void test8()
|
||||
{
|
||||
Serial.println("\nTest print interface");
|
||||
start = micros();
|
||||
@ -172,6 +260,7 @@ void test5()
|
||||
duration1 = micros() - start;
|
||||
Serial.print("println(3.14159265, 4): \t");
|
||||
Serial.println(duration1 * 0.01);
|
||||
Serial.println();
|
||||
delay(100);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,41 @@
|
||||
IDE: 1.8.19
|
||||
Board: UNO
|
||||
|
||||
FASTSHIFTOUT_LIB_VERSION: 0.3.3
|
||||
|
||||
Performance - time in us
|
||||
write: 23.51
|
||||
write: 45.78
|
||||
Delta: 22.27
|
||||
|
||||
writeLSBFIRST: 22.51
|
||||
writeLSBFIRST: 44.76
|
||||
Delta: 22.25
|
||||
|
||||
writeMSBFIRST: 22.51
|
||||
writeMSBFIRST: 44.77
|
||||
Delta: 22.26
|
||||
|
||||
Standard shiftOut1: 89.85
|
||||
Standard shiftOut2: 179.44
|
||||
Delta: 89.59
|
||||
|
||||
write16: 45.65
|
||||
write16: 91.04
|
||||
Delta: 45.39
|
||||
|
||||
write24: 67.90
|
||||
write24: 135.56
|
||||
Delta: 67.66
|
||||
|
||||
write32: 90.16
|
||||
write32: 180.07
|
||||
Delta: 89.91
|
||||
|
||||
Test print interface
|
||||
println("Hello world"): 328.92
|
||||
println(1357): 311.60
|
||||
println(3.14159265, 4): 716.04
|
||||
|
||||
done ...
|
||||
|
@ -0,0 +1,45 @@
|
||||
IDE: 1.8.19
|
||||
Board: UNO
|
||||
|
||||
loop unrolled version
|
||||
|
||||
FASTSHIFTOUT_LIB_VERSION: 0.4.0
|
||||
|
||||
Performance - time in us
|
||||
write: 12.76
|
||||
write: 24.27
|
||||
Delta: 11.51
|
||||
|
||||
writeLSBFIRST: 11.76
|
||||
writeLSBFIRST: 23.26
|
||||
Delta: 11.50
|
||||
|
||||
writeMSBFIRST: 11.76
|
||||
writeMSBFIRST: 23.27
|
||||
Delta: 11.50
|
||||
|
||||
Standard shiftOut1: 89.85
|
||||
Standard shiftOut2: 179.45
|
||||
Delta: 89.60
|
||||
|
||||
write16: 24.14
|
||||
write16: 48.04
|
||||
Delta: 23.89
|
||||
|
||||
write24: 35.66
|
||||
write24: 71.05
|
||||
Delta: 35.40
|
||||
|
||||
write32: 47.16
|
||||
write32: 94.06
|
||||
Delta: 46.90
|
||||
|
||||
|
||||
Test print interface
|
||||
println("Hello world"): 189.20
|
||||
println(1357): 247.12
|
||||
println(3.14159265, 4): 629.96
|
||||
|
||||
|
||||
done ...
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// FILE: FastShiftOut.cpp
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.3.2
|
||||
// VERSION: 0.4.0
|
||||
// PURPOSE: ShiftOut that implements the Print interface
|
||||
// DATE: 2013-08-22
|
||||
// URL: https://github.com/RobTillaart/FastShiftOut
|
||||
@ -47,7 +47,6 @@ size_t FastShiftOut::write(uint8_t data)
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL 0.3.3
|
||||
size_t FastShiftOut::write16(uint16_t data)
|
||||
{
|
||||
if (_bitOrder == LSBFIRST)
|
||||
@ -64,7 +63,6 @@ size_t FastShiftOut::write16(uint16_t data)
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL 0.3.3
|
||||
size_t FastShiftOut::write24(uint32_t data)
|
||||
{
|
||||
if (_bitOrder == LSBFIRST)
|
||||
@ -85,7 +83,6 @@ size_t FastShiftOut::write24(uint32_t data)
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL 0.3.3
|
||||
size_t FastShiftOut::write32(uint32_t data)
|
||||
{
|
||||
if (_bitOrder == LSBFIRST)
|
||||
@ -109,10 +106,8 @@ size_t FastShiftOut::write32(uint32_t data)
|
||||
}
|
||||
|
||||
|
||||
// EXPERIMENTAL 0.3.3
|
||||
size_t FastShiftOut::write(uint8_t * array, size_t size)
|
||||
{
|
||||
size_t n = 0;
|
||||
if (_bitOrder == LSBFIRST)
|
||||
{
|
||||
for (size_t i = size; i > 0; ) // from end to begin ????
|
||||
@ -155,7 +150,6 @@ uint8_t FastShiftOut::getBitOrder(void)
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t FastShiftOut::writeLSBFIRST(uint8_t data)
|
||||
{
|
||||
uint8_t value = data;
|
||||
@ -163,23 +157,103 @@ size_t FastShiftOut::writeLSBFIRST(uint8_t data)
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
|
||||
|
||||
#if defined(FASTSHIFTOUT_AVR_LOOP_UNROLLED) // AVR SPEED OPTIMIZED
|
||||
|
||||
uint8_t cbmask1 = _clockBit;
|
||||
uint8_t cbmask2 = ~_clockBit;
|
||||
uint8_t outmask1 = _dataOutBit;
|
||||
uint8_t outmask2 = ~_dataOutBit;
|
||||
|
||||
volatile uint8_t* localDataOutRegister = _dataOutRegister;
|
||||
volatile uint8_t* localClockRegister = _clockRegister;
|
||||
|
||||
// disable interrupts (for all bits)
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
|
||||
if ((value & 0x01) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
// *_clockRegister |= cbmask1;
|
||||
// *_clockRegister &= cbmask2;
|
||||
// following code is allowed as interrupts are disabled.
|
||||
// so register can not change
|
||||
uint8_t r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset bit
|
||||
|
||||
if ((value & 0x02) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x04) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x08) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x10) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x20) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x40) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x80) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
// restore interrupt state
|
||||
SREG = oldSREG;
|
||||
|
||||
#else // AVR SIZE OPTIMIZED
|
||||
|
||||
uint8_t cbmask1 = _clockBit;
|
||||
uint8_t outmask1 = _dataOutBit;
|
||||
uint8_t outmask2 = ~_dataOutBit;
|
||||
|
||||
volatile uint8_t* localDataOutRegister = _dataOutRegister;
|
||||
volatile uint8_t* localClockRegister = _clockRegister;
|
||||
|
||||
// disable interrupts (for all bits)
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
|
||||
for (uint8_t m = 1; m > 0; m <<= 1)
|
||||
{
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
if ((value & m) == 0) *_dataOutRegister &= outmask2;
|
||||
else *_dataOutRegister |= outmask1;
|
||||
*_clockRegister |= cbmask1;
|
||||
*_clockRegister &= cbmask2;
|
||||
SREG = oldSREG;
|
||||
// process one bit
|
||||
if ((value & m) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
uint8_t r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
}
|
||||
|
||||
#else
|
||||
// restore interrupt state
|
||||
SREG = oldSREG;
|
||||
|
||||
#endif // if (AVR)
|
||||
|
||||
#else // other platforms reference shiftOut()
|
||||
|
||||
shiftOut(_dataPinOut, _clockPin, LSBFIRST, value);
|
||||
|
||||
@ -196,23 +270,104 @@ size_t FastShiftOut::writeMSBFIRST(uint8_t data)
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
|
||||
|
||||
#if defined(FASTSHIFTOUT_AVR_LOOP_UNROLLED) // AVR SPEED OPTIMIZED
|
||||
|
||||
uint8_t cbmask1 = _clockBit;
|
||||
uint8_t cbmask2 = ~_clockBit;
|
||||
// uint8_t cbmask2 = ~_clockBit;
|
||||
uint8_t outmask1 = _dataOutBit;
|
||||
uint8_t outmask2 = ~_dataOutBit;
|
||||
|
||||
volatile uint8_t* localDataOutRegister = _dataOutRegister;
|
||||
volatile uint8_t* localClockRegister = _clockRegister;
|
||||
|
||||
// disable interrupts (for all bits)
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
|
||||
if ((value & 0x80) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
// *localClockRegister |= cbmask1;
|
||||
// *localClockRegister &= cbmask2;
|
||||
// following code is allowed as interrupts are disabled.
|
||||
// so register can not change
|
||||
uint8_t r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x40) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x20) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x10) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x08) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x04) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x02) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
if ((value & 0x01) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
|
||||
// restore interrupt state
|
||||
SREG = oldSREG;
|
||||
|
||||
#else // AVR SIZE OPTIMIZED
|
||||
|
||||
uint8_t cbmask1 = _clockBit;
|
||||
uint8_t outmask1 = _dataOutBit;
|
||||
uint8_t outmask2 = ~_dataOutBit;
|
||||
|
||||
volatile uint8_t* localDataOutRegister = _dataOutRegister;
|
||||
volatile uint8_t* localClockRegister = _clockRegister;
|
||||
|
||||
// disable interrupts (for all bits)
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
|
||||
for (uint8_t m = 0x80; m > 0; m >>= 1)
|
||||
{
|
||||
uint8_t oldSREG = SREG;
|
||||
noInterrupts();
|
||||
if ((value & m) == 0) *_dataOutRegister &= outmask2;
|
||||
else *_dataOutRegister |= outmask1;
|
||||
*_clockRegister |= cbmask1;
|
||||
*_clockRegister &= cbmask2;
|
||||
SREG = oldSREG;
|
||||
// process one bit
|
||||
if ((value & m) == 0) *localDataOutRegister &= outmask2;
|
||||
else *localDataOutRegister |= outmask1;
|
||||
uint8_t r = *localClockRegister;
|
||||
*localClockRegister = r | cbmask1; // set one bit
|
||||
*localClockRegister = r; // reset it
|
||||
}
|
||||
|
||||
#else
|
||||
// restore interrupt state
|
||||
SREG = oldSREG;
|
||||
|
||||
#endif // if (AVR)
|
||||
|
||||
#else // other platforms reference shiftOut()
|
||||
|
||||
shiftOut(_dataPinOut, _clockPin, MSBFIRST, value);
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// FILE: FastShiftOut.h
|
||||
// AUTHOR: Rob Tillaart
|
||||
// VERSION: 0.3.3
|
||||
// VERSION: 0.4.0
|
||||
// PURPOSE: shiftOut class that implements the Print interface
|
||||
// DATE: 2013-08-22
|
||||
// URL: https://github.com/RobTillaart/FastShiftOut
|
||||
@ -11,7 +11,10 @@
|
||||
#include "Arduino.h"
|
||||
#include "Print.h"
|
||||
|
||||
#define FASTSHIFTOUT_LIB_VERSION (F("0.3.3"))
|
||||
#define FASTSHIFTOUT_LIB_VERSION (F("0.4.0"))
|
||||
|
||||
// uncomment next line to get SPEED OPTIMIZED CODE
|
||||
#define FASTSHIFTOUT_AVR_LOOP_UNROLLED 1
|
||||
|
||||
|
||||
class FastShiftOut : public Print
|
||||
@ -39,7 +42,7 @@ public:
|
||||
|
||||
private:
|
||||
uint8_t _bitOrder;
|
||||
int _lastValue;
|
||||
uint8_t _lastValue;
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
|
||||
@ -59,4 +62,4 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// -- END OF FILE --
|
||||
// -- END OF FILE --
|
||||
|
@ -16,8 +16,8 @@ Arduino library for **AVR** optimized shiftOut - e.g. 74HC595.
|
||||
|
||||
## Description
|
||||
|
||||
FastShiftOut is a class that has optimized code (AVR only) to shift out data faster
|
||||
than the normal **shiftOut()** function.
|
||||
FastShiftOut is a class that has optimized code (**AVR** only) to shift out data faster
|
||||
than the default provided **shiftOut()** function.
|
||||
It speeds up the shift using low level ports and masks. These are predetermined
|
||||
in the constructor of the FastShiftOut object.
|
||||
|
||||
@ -33,27 +33,48 @@ These are write16(), write24(), write32() and write(array, size).
|
||||
The latter is used to shift out any size object.
|
||||
|
||||
|
||||
### 0.4.0 breaking changes
|
||||
|
||||
The 0.4.0 version has a flag to unroll the inner loop in **writeLSBFIRST()**
|
||||
and **writeMSBFIRST()**. The AVR optimized code blocks the interrupts per byte.
|
||||
|
||||
Note: this optimization is new and thus experimental.
|
||||
Feedback, including improvements, is welcome.
|
||||
|
||||
|
||||
### Performance
|
||||
|
||||
The performance of **write()** is substantially faster for **AVR** than the default
|
||||
Arduino **shiftOut()**, but not as fast as HW SPI.
|
||||
Exact how large the performance gain is can be seen with the example sketch.
|
||||
It does a comparison and shows how the class is to be used.
|
||||
|
||||
Time in microseconds, Arduino UNO
|
||||
|
||||
| function | 0.2.4 | 0.3.1 | 0.3.3 |
|
||||
|:-------------------------|--------:|---------:|---------:|
|
||||
| write() | 21.66 | 22.48 | 22.37 |
|
||||
| writeLSBFIRST() | 22.94 | 23.37 | 23.26 |
|
||||
| writeMSBFIRST() | 20.30 | 21.86 | 21.75 |
|
||||
| reference shiftOut() | 89.74 | 89.74 | 89.60 |
|
||||
| println("Hello world") | | 328.92 | 328.92 |
|
||||
| println(1357) | | 313.56 | 311.68 |
|
||||
| println(3.14159265, 4) | | 717.36 | 716.04 |
|
||||
Arduino **shiftOut()**, but not as fast as HW SPI (need test?).
|
||||
Exact how large the performance gain is can be seen with the example **FastShiftOut_test.ino**.
|
||||
It does a measurement of different functions and shows how the class is to be used.
|
||||
See also table below.
|
||||
|
||||
|
||||
Note: 0.3.3 has improved the measurement, not the code sec.
|
||||
#### Measurements
|
||||
|
||||
Numbers may vary depending on bit-order flag.
|
||||
|
||||
Indicative time in microseconds, Arduino UNO, IDE 1.8.19, measured over 1000 calls.
|
||||
(delta between 2 calls and 1 call to eliminate overhead)
|
||||
|
||||
| function | 0.2.4 | 0.3.1 | 0.3.3 | 0.4.0 | 0.4.0L |
|
||||
|:-------------------------|--------:|---------:|---------:|---------:|---------:|
|
||||
| write() | 21.66 | 22.48 | 22.27 | 14.10 | 11.51 |
|
||||
| writeLSBFIRST() | 22.94 | 23.37 | 22.25 | 14.09 | 11.50 |
|
||||
| writeMSBFIRST() | 20.30 | 21.86 | 22.26 | 14.08 | 11.50 |
|
||||
| reference shiftOut() | 89.74 | 89.74 | 89.59 | 89.60 | 89.60 |
|
||||
| write16() | na | na | 45.39 | 29.06 | 23.89 |
|
||||
| write24() | na | na | 67.66 | 43.12 | 35.40 |
|
||||
| write32() | na | na | 89.91 | 57.22 | 46.90 |
|
||||
| println("Hello world") | na | 328.92 | 328.92 | 222.68 | 189.20 |
|
||||
| println(1357) | na | 313.56 | 311.60 | 262.60 | 247.12 |
|
||||
| println(3.14159265, 4) | na | 717.36 | 716.04 | 650.68 | 629.96 |
|
||||
|
||||
- Note: 0.3.3 has improved the measurement, not the code sec.
|
||||
- Note: 0.3.3 numbers fixed when implementing 0.4.0. (error in test sketch).
|
||||
- Note: 0.4.0 measured with loop unroll flag disabled.
|
||||
- Note: 0.4.0L measured with loop unrolled flag enabled.
|
||||
|
||||
|
||||
### Related
|
||||
@ -73,33 +94,42 @@ Note: 0.3.3 has improved the measurement, not the code sec.
|
||||
|
||||
### Constructor
|
||||
|
||||
bitOrder = { LSBFIRST, MSBFIRST };
|
||||
|
||||
- **FastShiftOut(uint8_t dataOut, uint8_t clockPin, uint8_t bitOrder = LSBFIRST)** Constructor.
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
- **size_t write(uint8_t data)** send a byte, also the workhorse of the **Print** interface.
|
||||
- **size_t write16(uint16_t data)** send 2 bytes.
|
||||
- **size_t write24(uint32_t data)** send 3 bytes.
|
||||
- **size_t write32(uint32_t data)** send 4 bytes.
|
||||
- **size_t write(uint8_t \*array, size_t size)** send size bytes.
|
||||
- **size_t write16(uint16_t data)** send 2 bytes. Wrapper around 8 bit calls.
|
||||
- **size_t write24(uint32_t data)** send 3 bytes. Wrapper around 8 bit calls.
|
||||
- **size_t write32(uint32_t data)** send 4 bytes. Wrapper around 8 bit calls.
|
||||
- **size_t write(uint8_t \*array, size_t size)** send array of size bytes.
|
||||
- **uint8_t lastWritten()** returns last byte written.
|
||||
|
||||
### Meta
|
||||
|
||||
- **bool setBitOrder(uint8_t bitOrder)** set LSBFIRST or MSBFIRST. Returns false for other values.
|
||||
- **uint8_t getBitOrder(void)** returns LSBFIRST or MSBFIRST.
|
||||
- **size_t writeLSBFIRST(uint8_t data)** most optimized.
|
||||
- **size_t writeMSBFIRST(uint8_t data)** most optimized.
|
||||
- **size_t writeLSBFIRST(uint8_t data)** lowest level function, optimized for LSB.
|
||||
- **size_t writeMSBFIRST(uint8_t data)** lowest level function, optimized for MSB.
|
||||
|
||||
|
||||
As a FastShiftOut object implements the Print interface, one can also call
|
||||
### BitOrder
|
||||
|
||||
- **FSO.print(any type)** or
|
||||
- **FSO.println(any type)**
|
||||
- **bool setBitOrder(uint8_t bitOrder)** set LSBFIRST or MSBFIRST.
|
||||
Returns false for other values ==> no change.
|
||||
- **uint8_t getBitOrder(void)** returns LSBFIRST or MSBFIRST as set in constructor
|
||||
or latest set from **setBitOrder()**.
|
||||
|
||||
|
||||
### Print interface
|
||||
|
||||
As the FastShiftOut library implements the Print interface, one can also call
|
||||
|
||||
- **size_t FSO.print(any type)** or
|
||||
- **size_t FSO.println(any type)**
|
||||
|
||||
to send e.g. a float with 4 digits over the line, or some text string.
|
||||
|
||||
Note: **FSO.print()** returns the number of characters printed, including an optional \\r or \\n.
|
||||
Note: **FSO.print()** and **FSO.println()** return the number of characters printed,
|
||||
including an optional \\r and \\n.
|
||||
|
||||
|
||||
### Byte order
|
||||
@ -117,6 +147,8 @@ If the BIT-order is not the BYTE-order, the user has two options
|
||||
- call **write()** multiple times and merge the bytes in the order needed.
|
||||
- call **write32()** (a.o) and reorder the bytes in a separate function.
|
||||
|
||||
The library will not support such functionality.
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@ -138,15 +170,19 @@ pull up resistors, especially if wires are exceeding 10 cm (4").
|
||||
|
||||
#### Could
|
||||
|
||||
- investigate separate **BYTE**-order,
|
||||
- only MSBFirst and LSBFirst
|
||||
- **void setByteOrder()** + **uint8_t getByteOrder()**
|
||||
- investigate ESP32 optimization readLSBFIRST readMSBFIRST
|
||||
- performance ESP32
|
||||
- example schema
|
||||
- add invert flag?
|
||||
- if ((value & m) == 0) ==> if ((value & m) == invertFlag...
|
||||
- derived class?
|
||||
|
||||
#### Wont
|
||||
|
||||
- investigate separate **BYTE**-order,
|
||||
- only MSBFirst and LSBFirst
|
||||
- **void setByteOrder()** + **uint8_t getByteOrder()**
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobTillaart/FastShiftOut.git"
|
||||
},
|
||||
"version": "0.3.3",
|
||||
"version": "0.4.0",
|
||||
"license": "MIT",
|
||||
"frameworks": "*",
|
||||
"platforms": "*",
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=FastShiftOut
|
||||
version=0.3.3
|
||||
version=0.4.0
|
||||
author=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
|
||||
sentence=Arduino library for (AVR) optimized shiftOut - e.g. 74HC595
|
||||
|
Loading…
Reference in New Issue
Block a user