Skip to content

Commit 059c87e

Browse files
committed
chunked transfers for a big improvement in performance
If the code doesn't run on an 8bit-AVR, the data is prepared by transmitting the reads and writes in chunks instead of byte for byte. A constant is used for the chunk size. This is a tradeof between RAM/CPU and bus speed: on the 'bigger' arduino platforms the CPU is a lot faster than the SPI and there is a lot of RAM avaible, so using more RAM/CPU cycles and then letting the DMA do its work is the way to go. The chunked transfers also combine the reads and writes, so the dead time in between is removed, which is especially important for register reads of SPI-attached chips. Chunked transfers give an improvement of about 40% over bytewise ones, additionally +5% in the case of small reads/writes as used in BusIO_Register by removing the dead time between writing and reading. This is for all supported platforms except 8bit AVRs, which are specifically #if'd out. The special case for the ESP32 is therefore removed and ARM M0/M4, ESP8266, teensy etc should profit of this improvement too, without special casing each platform by using non standard arduino core extensions.
1 parent 515f63f commit 059c87e

File tree

1 file changed

+194
-72
lines changed

1 file changed

+194
-72
lines changed

Adafruit_SPIDevice.cpp

+194-72
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,55 @@
11
#include "Adafruit_SPIDevice.h"
22

3+
#if !defined(__AVR__)
4+
#include <array>
5+
#endif
6+
37
#if !defined(SPI_INTERFACES_COUNT) || \
48
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
59

10+
//! constant for the buffer size for the chunked transfer
11+
constexpr size_t maxBufferSizeForChunkedTransfer = 64;
12+
613
//#define DEBUG_SERIAL Serial
714

15+
#ifdef DEBUG_SERIAL
16+
#if !defined(__AVR__)
17+
template <typename T>
18+
static void printChunk(const char *title, const T &buffer, const uint8_t size,
19+
const uint16_t chunkNumber) {
20+
DEBUG_SERIAL.print(F("\t"));
21+
DEBUG_SERIAL.print(title);
22+
DEBUG_SERIAL.print(F(" Chunk #"));
23+
DEBUG_SERIAL.print(chunkNumber);
24+
DEBUG_SERIAL.print(F(", size "));
25+
DEBUG_SERIAL.println(size);
26+
DEBUG_SERIAL.print(F("\t"));
27+
28+
for (uint8_t i = 0; i < size; ++i) {
29+
DEBUG_SERIAL.print(F("0x"));
30+
DEBUG_SERIAL.print(buffer[i], HEX);
31+
DEBUG_SERIAL.print(F(", "));
32+
}
33+
DEBUG_SERIAL.println();
34+
}
35+
#endif
36+
37+
static void printBuffer(const char *title, const uint8_t *buffer,
38+
const size_t len) {
39+
DEBUG_SERIAL.print(F("\t"));
40+
DEBUG_SERIAL.println(title);
41+
for (size_t i = 0; i < len; i++) {
42+
DEBUG_SERIAL.print(F("0x"));
43+
DEBUG_SERIAL.print(buffer[i], HEX);
44+
DEBUG_SERIAL.print(F(", "));
45+
if (i % 32 == 31) {
46+
DEBUG_SERIAL.println();
47+
}
48+
}
49+
DEBUG_SERIAL.println();
50+
}
51+
#endif
52+
853
/*!
954
* @brief Create an SPI device with the given CS pin and settings
1055
* @param cspin The arduino pin number to use for chip select
@@ -160,7 +205,6 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
160205
// Serial.print(send, HEX);
161206
for (uint8_t b = startbit; b != 0;
162207
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) {
163-
164208
if (bitdelay_us) {
165209
delayMicroseconds(bitdelay_us);
166210
}
@@ -326,49 +370,77 @@ void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
326370
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
327371
const uint8_t *prefix_buffer,
328372
size_t prefix_len) {
373+
#if !defined(__AVR__)
374+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
375+
376+
auto chunkBufferIterator = chunkBuffer.begin();
377+
378+
#ifdef DEBUG_SERIAL
379+
uint8_t chunkNumber = 1;
380+
#endif
381+
329382
beginTransactionWithAssertingCS();
330383

331-
// do the writing
332-
#if defined(ARDUINO_ARCH_ESP32)
333-
if (_spi) {
334-
if (prefix_len > 0) {
335-
_spi->transferBytes(prefix_buffer, nullptr, prefix_len);
336-
}
337-
if (len > 0) {
338-
_spi->transferBytes(buffer, nullptr, len);
339-
}
340-
} else
384+
for (size_t i = 0; i < prefix_len; ++i) {
385+
*chunkBufferIterator++ = prefix_buffer[i];
386+
387+
if (chunkBufferIterator == chunkBuffer.end()) {
388+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
389+
chunkBufferIterator = chunkBuffer.begin();
390+
391+
#ifdef DEBUG_SERIAL
392+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
393+
chunkNumber++);
341394
#endif
342-
{
343-
for (size_t i = 0; i < prefix_len; i++) {
344-
transfer(prefix_buffer[i]);
345-
}
346-
for (size_t i = 0; i < len; i++) {
347-
transfer(buffer[i]);
348395
}
349396
}
350-
endTransactionWithDeassertingCS();
397+
398+
for (size_t i = 0; i < len; ++i) {
399+
*chunkBufferIterator++ = buffer[i];
400+
401+
if (chunkBufferIterator == chunkBuffer.end()) {
402+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
403+
chunkBufferIterator = chunkBuffer.begin();
351404

352405
#ifdef DEBUG_SERIAL
353-
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
354-
if ((prefix_len != 0) && (prefix_buffer != nullptr)) {
355-
for (uint16_t i = 0; i < prefix_len; i++) {
356-
DEBUG_SERIAL.print(F("0x"));
357-
DEBUG_SERIAL.print(prefix_buffer[i], HEX);
358-
DEBUG_SERIAL.print(F(", "));
406+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
407+
chunkNumber++);
408+
#endif
359409
}
360410
}
361-
for (uint16_t i = 0; i < len; i++) {
362-
DEBUG_SERIAL.print(F("0x"));
363-
DEBUG_SERIAL.print(buffer[i], HEX);
364-
DEBUG_SERIAL.print(F(", "));
365-
if (i % 32 == 31) {
366-
DEBUG_SERIAL.println();
367-
}
411+
412+
if (chunkBufferIterator != chunkBuffer.begin()) {
413+
auto numberByteToTransfer = chunkBufferIterator - chunkBuffer.begin();
414+
transfer(chunkBuffer.data(), numberByteToTransfer);
415+
416+
#ifdef DEBUG_SERIAL
417+
printChunk("write() Wrote remaining", chunkBuffer, numberByteToTransfer,
418+
chunkNumber++);
419+
#endif
368420
}
369-
DEBUG_SERIAL.println();
421+
422+
endTransactionWithDeassertingCS();
423+
424+
#else // !defined(__AVR__)
425+
426+
beginTransactionWithAssertingCS();
427+
428+
for (size_t i = 0; i < prefix_len; i++) {
429+
transfer(prefix_buffer[i]);
430+
}
431+
for (size_t i = 0; i < len; i++) {
432+
transfer(buffer[i]);
433+
}
434+
435+
endTransactionWithDeassertingCS();
436+
437+
#ifdef DEBUG_SERIAL
438+
printBuffer("write() prefix_buffer", prefix_buffer, prefix_len);
439+
printBuffer("write() buffer", buffer, len);
370440
#endif
371441

442+
#endif // !defined(__AVR__)
443+
372444
return true;
373445
}
374446

@@ -390,16 +462,7 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
390462
endTransactionWithDeassertingCS();
391463

392464
#ifdef DEBUG_SERIAL
393-
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
394-
for (uint16_t i = 0; i < len; i++) {
395-
DEBUG_SERIAL.print(F("0x"));
396-
DEBUG_SERIAL.print(buffer[i], HEX);
397-
DEBUG_SERIAL.print(F(", "));
398-
if (len % 32 == 31) {
399-
DEBUG_SERIAL.println();
400-
}
401-
}
402-
DEBUG_SERIAL.println();
465+
printBuffer("read() buffer", buffer, len);
403466
#endif
404467

405468
return true;
@@ -421,53 +484,112 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
421484
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
422485
size_t write_len, uint8_t *read_buffer,
423486
size_t read_len, uint8_t sendvalue) {
487+
#if !defined(__AVR__)
488+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
489+
490+
auto chunkBufferIterator = chunkBuffer.begin();
491+
492+
#ifdef DEBUG_SERIAL
493+
uint8_t chunkNumber = 1;
494+
#endif
495+
424496
beginTransactionWithAssertingCS();
425-
// do the writing
426-
#if defined(ARDUINO_ARCH_ESP32)
427-
if (_spi) {
428-
if (write_len > 0) {
429-
_spi->transferBytes(write_buffer, nullptr, write_len);
430-
}
431-
} else
497+
498+
for (size_t i = 0; i < write_len; ++i) {
499+
*chunkBufferIterator++ = write_buffer[i];
500+
501+
if (chunkBufferIterator == chunkBuffer.end()) {
502+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
503+
chunkBufferIterator = chunkBuffer.begin();
504+
505+
#ifdef DEBUG_SERIAL
506+
printChunk("write_then_read() Wrote", chunkBuffer,
507+
maxBufferSizeForChunkedTransfer, chunkNumber++);
432508
#endif
433-
{
434-
for (size_t i = 0; i < write_len; i++) {
435-
transfer(write_buffer[i]);
436509
}
437510
}
438511

512+
auto readBufferIterator = read_buffer;
513+
auto readFromIterator = chunkBufferIterator;
514+
size_t readBufferLen = read_len;
515+
516+
for (size_t i = 0; i < read_len; ++i) {
517+
*chunkBufferIterator++ = sendvalue;
518+
519+
if (chunkBufferIterator == chunkBuffer.end()) {
439520
#ifdef DEBUG_SERIAL
440-
DEBUG_SERIAL.print(F("\tSPIDevice Wrote: "));
441-
for (uint16_t i = 0; i < write_len; i++) {
442-
DEBUG_SERIAL.print(F("0x"));
443-
DEBUG_SERIAL.print(write_buffer[i], HEX);
444-
DEBUG_SERIAL.print(F(", "));
445-
if (write_len % 32 == 31) {
446-
DEBUG_SERIAL.println();
521+
printChunk("write_then_read() before transmit", chunkBuffer,
522+
maxBufferSizeForChunkedTransfer, chunkNumber);
523+
#endif
524+
525+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
526+
527+
while (readBufferLen) {
528+
if (readFromIterator != chunkBuffer.end()) {
529+
*readBufferIterator++ = *readFromIterator++;
530+
--readBufferLen;
531+
} else {
532+
break;
533+
}
534+
}
535+
536+
#ifdef DEBUG_SERIAL
537+
printChunk("write_then_read() after transmit", chunkBuffer,
538+
maxBufferSizeForChunkedTransfer, chunkNumber++);
539+
#endif
540+
541+
chunkBufferIterator = chunkBuffer.begin();
542+
readFromIterator = chunkBuffer.begin();
447543
}
448544
}
449-
DEBUG_SERIAL.println();
545+
546+
if (chunkBufferIterator != chunkBuffer.begin()) {
547+
auto numberByteToTransfer = chunkBufferIterator - chunkBuffer.begin();
548+
549+
#ifdef DEBUG_SERIAL
550+
printChunk("write_then_read() before transmit remaining", chunkBuffer,
551+
numberByteToTransfer, chunkNumber);
552+
#endif
553+
554+
transfer(chunkBuffer.data(), numberByteToTransfer);
555+
556+
#ifdef DEBUG_SERIAL
557+
printChunk("write_then_read() after transmit remaining", chunkBuffer,
558+
numberByteToTransfer, chunkNumber);
450559
#endif
451560

452-
// do the reading
561+
while (readBufferLen) {
562+
if (readFromIterator != chunkBuffer.end()) {
563+
*readBufferIterator++ = *readFromIterator++;
564+
--readBufferLen;
565+
} else {
566+
break;
567+
}
568+
}
569+
}
570+
571+
endTransactionWithDeassertingCS();
572+
573+
#else // !defined(__AVR__)
574+
575+
beginTransactionWithAssertingCS();
576+
577+
for (size_t i = 0; i < write_len; i++) {
578+
transfer(write_buffer[i]);
579+
}
580+
453581
for (size_t i = 0; i < read_len; i++) {
454582
read_buffer[i] = transfer(sendvalue);
455583
}
456584

585+
endTransactionWithDeassertingCS();
586+
457587
#ifdef DEBUG_SERIAL
458-
DEBUG_SERIAL.print(F("\tSPIDevice Read: "));
459-
for (uint16_t i = 0; i < read_len; i++) {
460-
DEBUG_SERIAL.print(F("0x"));
461-
DEBUG_SERIAL.print(read_buffer[i], HEX);
462-
DEBUG_SERIAL.print(F(", "));
463-
if (read_len % 32 == 31) {
464-
DEBUG_SERIAL.println();
465-
}
466-
}
467-
DEBUG_SERIAL.println();
588+
printBuffer("write_then_read() write_buffer", write_buffer, write_len);
589+
printBuffer("write_then_read() read_buffer", read_buffer, read_len);
468590
#endif
469591

470-
endTransactionWithDeassertingCS();
592+
#endif // !defined(__AVR__)
471593

472594
return true;
473595
}

0 commit comments

Comments
 (0)