Skip to content

Commit

Permalink
Fix #13, add wrapper functions (#14)
Browse files Browse the repository at this point in the history
- Fix #13, add wrapper functions
  - write16/24/32, write(array, size)
- improved timing measurement in FastShiftOut_test.ino
- update readme.md
- minor edits
  • Loading branch information
RobTillaart authored Jul 23, 2024
1 parent 8b73e15 commit 9ac57a7
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 67 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).


## [0.3.2] - 2023-10-31
## [0.3.3] - 2024-07-23
- Fix #13, add wrapper functions
- write16/24/32, write(array, size)
- improved timing measurement in FastShiftOut_test.ino
- update readme.md
- minor edits

## [0.3.2] - 2023-10-31
- update readme.md

## [0.3.1] - 2023-02-20
- update readme.md
Expand Down
120 changes: 91 additions & 29 deletions FastShiftOut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,113 @@ size_t FastShiftOut::write(uint8_t data)
}


/* experimental
size_t write(const uint8_t \*buffer, size_t size)
// EXPERIMENTAL 0.3.3
size_t FastShiftOut::write16(uint16_t data)
{
if (_bitOrder == LSBFIRST)
{
writeLSBFIRST(data & 0xFF);
writeLSBFIRST(data >> 8);
}
else
{
writeMSBFIRST(data >> 8);
writeMSBFIRST(data & 0xFF);
}
return 2;
}


// EXPERIMENTAL 0.3.3
size_t FastShiftOut::write24(uint32_t data)
{
if (_bitOrder == LSBFIRST)
{
writeLSBFIRST(data & 0xFF);
data >>= 8;
writeLSBFIRST(data & 0xFF);
data >>= 8;
writeLSBFIRST(data & 0xFF);
}
else
{
writeMSBFIRST((data >> 16) & 0xFF);
writeMSBFIRST((data >> 8) & 0xFF);
writeMSBFIRST(data & 0xFF);
}
return 3;
}


// EXPERIMENTAL 0.3.3
size_t FastShiftOut::write32(uint32_t data)
{
if (_bitOrder == LSBFIRST)
{
writeLSBFIRST(data & 0xFF);
data >>= 8;
writeLSBFIRST(data & 0xFF);
data >>= 8;
writeLSBFIRST(data & 0xFF);
data >>= 8;
writeLSBFIRST(data & 0xFF);
}
else
{
writeMSBFIRST((data >> 24) & 0xFF);
writeMSBFIRST((data >> 16) & 0xFF);
writeMSBFIRST((data >> 8) & 0xFF);
writeMSBFIRST(data & 0xFF);
}
return 4;
}


// 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 ????
{
i--;
n += writeLSBFIRST(buffer[i]);
writeLSBFIRST(array[i]);
}
}
else
{
for (size_t i = 0; i < size; i++) // from begin to end..
{
n += writeMSBFIRST(buffer[i]);
writeMSBFIRST(array[i]);
}
}
return n;
return size;
}
*/


uint8_t FastShiftOut::lastWritten(void)
{
return _lastValue;
}


bool FastShiftOut::setBitOrder(const uint8_t bitOrder)
{
if ((bitOrder == LSBFIRST) || (bitOrder == MSBFIRST))
{
_bitOrder = bitOrder;
return true;
};
return false;
}


uint8_t FastShiftOut::getBitOrder(void)
{
return _bitOrder;
}



size_t FastShiftOut::writeLSBFIRST(uint8_t data)
Expand Down Expand Up @@ -137,28 +222,5 @@ size_t FastShiftOut::writeMSBFIRST(uint8_t data)
}


uint8_t FastShiftOut::lastWritten(void)
{
return _lastValue;
}


bool FastShiftOut::setBitOrder(const uint8_t bitOrder)
{
if ((bitOrder == LSBFIRST) || (bitOrder == MSBFIRST))
{
_bitOrder = bitOrder;
return true;
};
return false;
}


uint8_t FastShiftOut::getBitOrder(void)
{
return _bitOrder;
}


// -- END OF FILE --

15 changes: 10 additions & 5 deletions FastShiftOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// FILE: FastShiftOut.h
// AUTHOR: Rob Tillaart
// VERSION: 0.3.2
// VERSION: 0.3.3
// PURPOSE: shiftOut class that implements the Print interface
// DATE: 2013-08-22
// URL: https://github.com/RobTillaart/FastShiftOut
Expand All @@ -11,7 +11,7 @@
#include "Arduino.h"
#include "Print.h"

#define FASTSHIFTOUT_LIB_VERSION (F("0.3.2"))
#define FASTSHIFTOUT_LIB_VERSION (F("0.3.3"))


class FastShiftOut : public Print
Expand All @@ -21,14 +21,19 @@ class FastShiftOut : public Print
FastShiftOut(uint8_t dataOut, uint8_t clockPin, uint8_t bitOrder = LSBFIRST);

size_t write(uint8_t data);
// experimental
// size_t write(const uint8_t \*buffer, size_t size);
uint8_t lastWritten(void);

// EXPERIMENTAL (wrappers)
size_t write16(uint16_t data);
size_t write24(uint32_t data);
size_t write32(uint32_t data);
size_t write(uint8_t * array, size_t size);

// META
bool setBitOrder(uint8_t bitOrder);
uint8_t getBitOrder(void);

// overrule bitOrder (most optimized).
// overrule bitOrder, most optimized.
size_t writeLSBFIRST(uint8_t data);
size_t writeMSBFIRST(uint8_t data);

Expand Down
100 changes: 72 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,56 @@

Arduino library for **AVR** optimized shiftOut - e.g. 74HC595.

Related libraries
- https://github.com/RobTillaart/FastShiftIn
- https://github.com/RobTillaart/FastShiftInOut
- https://github.com/RobTillaart/ShiftInSlow
- https://github.com/RobTillaart/ShiftOutSlow


## Description

FastShiftOut is a class that has optimized code for AVR to shift out data faster
FastShiftOut is a class that has optimized code (AVR only) to shift out data faster
than the normal **shiftOut()** function.
It speeds up the shift using low level ports and masks. These are predetermined
in the constructor of the FastShiftOut object.

If not an **ARDUINO_ARCH_AVR** or **ARDUINO_ARCH_MEGAAVR** the class falls back
to the default shiftOut() implementation.

The library allows to set (and get) the bitOrder and apply this to multiple write()
calls. It also provide access to **writeLSBFIRST()** and **writeMSBFIRST()** which
are the low level workers and most optimized code (so far).

The library provides wrapper functions to write multi-byte variables.
These are write16(), write24(), write32() and write(array, size).
The latter is used to shift out any size object.


## Performance
### Performance

The performance of **write()** is substantially faster than the default Arduino
**shiftOut()**, but not as fast as HW SPI.
Exact how big the performance gain is can be seen with the example sketch.
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 |
|:-------------------------|--------:|---------:|
| write() | 21.66 | 22.48 |
| writeLSBFIRST() | 22.94 | 23.37 |
| writeMSBFIRST() | 20.30 | 21.86 |
| reference shiftOut() | 89.74 | 89.74 |
| println("Hello world") | | 328.92 |
| println(1357) | | 313.56 |
| println(3.14159265, 4) | | 717.36 |
| 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 |


Note: 0.3.3 has improved the measurement, not the code sec.


### Related

- https://github.com/RobTillaart/FastShiftIn
- https://github.com/RobTillaart/FastShiftInOut
- https://github.com/RobTillaart/FastShiftOut
- https://github.com/RobTillaart/ShiftInSlow
- https://github.com/RobTillaart/ShiftOutSlow


## Interface
Expand All @@ -57,27 +71,53 @@ Time in microseconds, Arduino UNO
#include "FastShiftOut.h"
```

#### Functions
### Constructor

- **FastShiftOut(uint8_t dataOut, uint8_t clockPin, uint8_t bitOrder = LSBFIRST)** Constructor.
- **size_t write(const uint8_t data)** send a byte, also the workhorse of the **Print** interface.

### 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.
- **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(const uint8_t data);** most optimized.
- **size_t writeMSBFIRST(const uint8_t data);** most optimized.
- **size_t writeLSBFIRST(uint8_t data)** most optimized.
- **size_t writeMSBFIRST(uint8_t data)** most optimized.


As a FastShiftOut object implements the Print interface, one can also call

- **FSO.print(any type);** or
- **FSO.println(any type);**
- **FSO.print(any type)** or
- **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.


### Byte order

The functions **write16()**, **write24()** and **write32()** of this library assume
that the BIT-order is also the BYTE-order.
This is not always the case as an n-byte element can have n! == factorial(n)
distinct byte orders.

So **write16()** can have two, **write24()** can have six and **write32()** can even have
(in theory) 24 distinct byte orders. Although LSB and MSB are the most common,
other byte orders exist, and sometimes one explicitly wants to reorder the bytes.

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.


## Notes

- The optimizations are AVR only for now, other platforms may follow.
Expand All @@ -90,15 +130,19 @@ pull up resistors, especially if wires are exceeding 10 cm (4").

#### Must

- keep in sync with FastShiftIn()

#### Should

- extend unit tests

#### Could

- investigate separate **BYTE**-order,
- only MSBFirst and LSBFirst
- **void setByteOrder()** + **uint8_t getByteOrder()**
- investigate ESP32 optimization readLSBFIRST readMSBFIRST
- performance ESP32
- check optimized ESP32
- add **size_t write(const uint8_t \*buffer, size_t size)**
- example schema

#### Wont
Expand Down
Loading

0 comments on commit 9ac57a7

Please sign in to comment.