Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chunked transfers for a big improvement in performance #97

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
241 changes: 147 additions & 94 deletions Adafruit_SPIDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,55 @@
#if !defined(SPI_INTERFACES_COUNT) || \
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))

//#define DEBUG_SERIAL Serial
// #define DEBUG_SERIAL Serial
// #define DEBUG_VERBOSE

#ifdef DEBUG_SERIAL
static void printChunk(const char *title, const uint8_t *buffer,
const uint8_t size) {
DEBUG_SERIAL.print(F("\t"));
DEBUG_SERIAL.print(title);
DEBUG_SERIAL.print(F(" Chunk, size "));
DEBUG_SERIAL.println(size);
#ifdef DEBUG_VERBOSE
DEBUG_SERIAL.print(F("\t"));

for (uint8_t i = 0; i < size; ++i) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
DEBUG_SERIAL.println();
#endif
}

static void printBuffer(const char *title, const uint8_t *buffer,
const size_t len) {
DEBUG_SERIAL.print(F("\t"));
DEBUG_SERIAL.println(title);
#ifdef DEBUG_VERBOSE
for (size_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
}
#endif

/*!
* @brief returns the smaller of both arguments
* @param a argument 1
* @param b argument 2
* @return the smaller of both arguments
*/
template <typename T> constexpr T minimum(const T a, const T b) {
return (a < b) ? a : b;
}

/*!
* @brief Create an SPI device with the given CS pin and settings
Expand Down Expand Up @@ -160,7 +208,6 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
// Serial.print(send, HEX);
for (uint8_t b = startbit; b != 0;
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) {

if (bitdelay_us) {
delayMicroseconds(bitdelay_us);
}
Expand Down Expand Up @@ -312,6 +359,88 @@ void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
endTransaction();
}

void Adafruit_SPIDevice::transferFilledChunk(
ChunkBuffer &chunkBuffer, ChunkBufferIterator &iteratorToIncrement,
const uint8_t *bufferToSend, const size_t bufferLen) {
auto bytesToTransferLen = bufferLen;
auto bytesToTransferBuffer = bufferToSend;

while (bytesToTransferLen) {
auto bytesToTransferLenThisChunk =
minimum(bytesToTransferLen,
ChunkBufferSize - (iteratorToIncrement - chunkBuffer));

memcpy(iteratorToIncrement, bytesToTransferBuffer,
bytesToTransferLenThisChunk);

bytesToTransferLen -= bytesToTransferLenThisChunk;
bytesToTransferBuffer += bytesToTransferLenThisChunk;

if (bytesToTransferLen) {
transfer(chunkBuffer, ChunkBufferSize);

iteratorToIncrement = chunkBuffer;

#ifdef DEBUG_SERIAL
printChunk("transferFilledChunk()", chunkBuffer, ChunkBufferSize);
#endif
} else {
iteratorToIncrement = iteratorToIncrement + bytesToTransferLenThisChunk;
}
}
}

void Adafruit_SPIDevice::transferPartiallyFilledChunk(
ChunkBuffer &chunkBuffer, const ChunkBufferIterator &chunkBufferIterator) {
if (chunkBufferIterator != chunkBuffer) {
auto bytesToTransferLenThisChunk = chunkBufferIterator - chunkBuffer;

transfer(chunkBuffer, bytesToTransferLenThisChunk);

#ifdef DEBUG_SERIAL
printChunk("transferPartiallyFilledChunk()", chunkBuffer,
bytesToTransferLenThisChunk);
#endif
}
}

void Adafruit_SPIDevice::transferAndReadChunks(
ChunkBuffer &chunkBuffer, ChunkBufferIterator &iteratorToIncrement,
uint8_t *readBuffer, const size_t readLen, const uint8_t sendVal) {
auto bytesToTransferLen = readLen;
auto readFromIterator = iteratorToIncrement;

while (bytesToTransferLen) {
auto bytesToTransferLenThisChunk =
minimum(bytesToTransferLen,
ChunkBufferSize - (iteratorToIncrement - chunkBuffer));

memset(iteratorToIncrement, sendVal, bytesToTransferLenThisChunk);

iteratorToIncrement += bytesToTransferLenThisChunk;
bytesToTransferLen -= bytesToTransferLenThisChunk;

{
auto tranferLen = iteratorToIncrement - chunkBuffer;
#if defined(DEBUG_SERIAL) && defined(DEBUG_VERBOSE)
printChunk("transferAndReadChunks() before transmit", chunkBuffer,
tranferLen);
#endif
transfer(chunkBuffer, tranferLen);
#ifdef DEBUG_SERIAL
printChunk("transferAndReadChunks() after transmit", chunkBuffer,
tranferLen);
#endif
}

memcpy(readBuffer, readFromIterator, bytesToTransferLenThisChunk);

readBuffer += bytesToTransferLenThisChunk;

readFromIterator = iteratorToIncrement = chunkBuffer;
}
}

/*!
* @brief Write a buffer or two to the SPI device, with transaction
* management.
Expand All @@ -326,48 +455,17 @@ void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
const uint8_t *prefix_buffer,
size_t prefix_len) {
ChunkBuffer chunkBuffer;
ChunkBufferIterator chunkBufferIterator = chunkBuffer;

beginTransactionWithAssertingCS();

// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (prefix_len > 0) {
_spi->transferBytes(prefix_buffer, nullptr, prefix_len);
}
if (len > 0) {
_spi->transferBytes(buffer, nullptr, len);
}
} else
#endif
{
for (size_t i = 0; i < prefix_len; i++) {
transfer(prefix_buffer[i]);
}
for (size_t i = 0; i < len; i++) {
transfer(buffer[i]);
}
}
endTransactionWithDeassertingCS();
transferFilledChunk(chunkBuffer, chunkBufferIterator, prefix_buffer,
prefix_len);
transferFilledChunk(chunkBuffer, chunkBufferIterator, buffer, len);
transferPartiallyFilledChunk(chunkBuffer, chunkBufferIterator);

#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
for (uint16_t i = 0; i < prefix_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
}
}
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (i % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
endTransactionWithDeassertingCS();

return true;
}
Expand All @@ -390,16 +488,7 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
endTransactionWithDeassertingCS();

#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
printBuffer("read() buffer", buffer, len);
#endif

return true;
Expand All @@ -421,51 +510,15 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
size_t write_len, uint8_t *read_buffer,
size_t read_len, uint8_t sendvalue) {
beginTransactionWithAssertingCS();
// do the writing
#if defined(ARDUINO_ARCH_ESP32)
if (_spi) {
if (write_len > 0) {
_spi->transferBytes(write_buffer, nullptr, write_len);
}
} else
#endif
{
for (size_t i = 0; i < write_len; i++) {
transfer(write_buffer[i]);
}
}
ChunkBuffer chunkBuffer;
ChunkBufferIterator chunkBufferIterator = chunkBuffer;

#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
for (uint16_t i = 0; i < write_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(write_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (write_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif

// do the reading
for (size_t i = 0; i < read_len; i++) {
read_buffer[i] = transfer(sendvalue);
}
beginTransactionWithAssertingCS();

#ifdef DEBUG_SERIAL
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
for (uint16_t i = 0; i < read_len; i++) {
DEBUG_SERIAL.print(F("0x"));
DEBUG_SERIAL.print(read_buffer[i], HEX);
DEBUG_SERIAL.print(F(", "));
if (read_len % 32 == 31) {
DEBUG_SERIAL.println();
}
}
DEBUG_SERIAL.println();
#endif
transferFilledChunk(chunkBuffer, chunkBufferIterator, write_buffer,
write_len);
transferAndReadChunks(chunkBuffer, chunkBufferIterator, read_buffer, read_len,
sendvalue);

endTransactionWithDeassertingCS();

Expand Down
23 changes: 23 additions & 0 deletions Adafruit_SPIDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,35 @@ class Adafruit_SPIDevice {
void endTransactionWithDeassertingCS();

private:
//! constant for the buffer size for the chunked transfer
static constexpr size_t ChunkBufferSize =
#ifdef __AVR__
32;
#else
64;
#endif

using ChunkBuffer = uint8_t[ChunkBufferSize];
using ChunkBufferIterator = uint8_t *;

SPIClass *_spi;
SPISettings *_spiSetting;
uint32_t _freq;
BusIOBitOrder _dataOrder;
uint8_t _dataMode;

void setChipSelect(int value);
void transferFilledChunk(ChunkBuffer &chunkBuffer,
ChunkBufferIterator &iteratorToIncrement,
const uint8_t *bufferToSend, const size_t bufferLen);
void
transferPartiallyFilledChunk(ChunkBuffer &chunkBuffer,
const ChunkBufferIterator &chunkBufferIterator);

void transferAndReadChunks(ChunkBuffer &chunkBuffer,
ChunkBufferIterator &iteratorToIncrement,
uint8_t *readBuffer, const size_t readLen,
const uint8_t sendVal);

int8_t _cs, _sck, _mosi, _miso;
#ifdef BUSIO_USE_FAST_PINIO
Expand Down