Skip to content

Commit a670b04

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 bb7c77a commit a670b04

File tree

1 file changed

+187
-72
lines changed

1 file changed

+187
-72
lines changed

Adafruit_SPIDevice.cpp

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

3+
#include <array>
4+
35
#if !defined(SPI_INTERFACES_COUNT) || \
46
(defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0))
57

8+
//! constant for the buffer size for the chunked transfer
9+
constexpr size_t maxBufferSizeForChunkedTransfer = 64;
10+
611
//#define DEBUG_SERIAL Serial
712

13+
#ifdef DEBUG_SERIAL
14+
static printChunk(char *title, uint8_t buffer, uint8_t size,
15+
uint16_t chunkNumber) {
16+
DEBUG_SERIAL.print(F("\t"));
17+
DEBUG_SERIAL.print(title);
18+
DEBUG_SERIAL.print(F(" Chunk #"));
19+
DEBUG_SERIAL.print(chunkNumber);
20+
DEBUG_SERIAL.print(F(", size "));
21+
DEBUG_SERIAL.println(size);
22+
23+
for (uint8_t i = 0; i < size; ++i) {
24+
DEBUG_SERIAL.print(F("0x"));
25+
DEBUG_SERIAL.print(buffer[i], HEX);
26+
DEBUG_SERIAL.print(F(", "));
27+
}
28+
DEBUG_SERIAL.println();
29+
}
30+
31+
static printBuffer(char *title, uint8_t buffer, size_t len) {
32+
DEBUG_SERIAL.print(F("\t"));
33+
DEBUG_SERIAL.println(title);
34+
for (size_t i = 0; i < len; i++) {
35+
DEBUG_SERIAL.print(F("0x"));
36+
DEBUG_SERIAL.print(buffer[i], HEX);
37+
DEBUG_SERIAL.print(F(", "));
38+
if (read_len % 32 == 31) {
39+
DEBUG_SERIAL.println();
40+
}
41+
}
42+
DEBUG_SERIAL.println();
43+
}
44+
#endif
45+
846
/*!
947
* @brief Create an SPI device with the given CS pin and settings
1048
* @param cspin The arduino pin number to use for chip select
@@ -160,7 +198,6 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
160198
// Serial.print(send, HEX);
161199
for (uint8_t b = startbit; b != 0;
162200
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) {
163-
164201
if (bitdelay_us) {
165202
delayMicroseconds(bitdelay_us);
166203
}
@@ -326,49 +363,77 @@ void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
326363
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
327364
const uint8_t *prefix_buffer,
328365
size_t prefix_len) {
366+
#if !defined(__AVR__)
367+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
368+
369+
auto chunkBufferIterator = chunkBuffer.begin();
370+
371+
#ifdef DEBUG_SERIAL
372+
uint8_t chunkNumber = 1;
373+
#endif
374+
329375
beginTransactionWithAssertingCS();
330376

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
377+
for (size_t i = 0; i < prefix_len; ++i) {
378+
*chunkBufferIterator++ = prefix_buffer[i];
379+
380+
if (chunkBufferIterator == chunkBuffer.end()) {
381+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
382+
chunkBufferIterator = chunkBuffer.begin();
383+
384+
#ifdef DEBUG_SERIAL
385+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
386+
chunkNumber++);
341387
#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]);
348388
}
349389
}
350-
endTransactionWithDeassertingCS();
390+
391+
for (size_t i = 0; i < len; ++i) {
392+
*chunkBufferIterator++ = buffer[i];
393+
394+
if (chunkBufferIterator == chunkBuffer.end()) {
395+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
396+
chunkBufferIterator = chunkBuffer.begin();
351397

352398
#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(", "));
399+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
400+
chunkNumber++);
401+
#endif
359402
}
360403
}
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-
}
404+
405+
if (chunkBufferIterator != chunkBuffer.begin()) {
406+
auto numberByteToTransfer = chunkBufferIterator - chunkBuffer.begin();
407+
transfer(chunkBuffer.data(), numberByteToTransfer);
408+
409+
#ifdef DEBUG_SERIAL
410+
printChunk("write() Wrote remaining", mpBuffer, numberByteToTransfer,
411+
chunkNumber++);
412+
#endif
368413
}
369-
DEBUG_SERIAL.println();
414+
415+
endTransactionWithDeassertingCS();
416+
417+
#else // !defined(__AVR__)
418+
419+
beginTransactionWithAssertingCS();
420+
421+
for (size_t i = 0; i < prefix_len; i++) {
422+
transfer(prefix_buffer[i]);
423+
}
424+
for (size_t i = 0; i < len; i++) {
425+
transfer(buffer[i]);
426+
}
427+
428+
endTransactionWithDeassertingCS();
429+
430+
#ifdef DEBUG_SERIAL
431+
printBuffer("write() prefix_buffer", prefix_buffer, prefix_len);
432+
printBuffer("write() buffer", buffer, len);
370433
#endif
371434

435+
#endif // !defined(__AVR__)
436+
372437
return true;
373438
}
374439

@@ -390,16 +455,7 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
390455
endTransactionWithDeassertingCS();
391456

392457
#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();
458+
printBuffer("read() buffer", buffer, len);
403459
#endif
404460

405461
return true;
@@ -421,53 +477,112 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
421477
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
422478
size_t write_len, uint8_t *read_buffer,
423479
size_t read_len, uint8_t sendvalue) {
480+
#if !defined(__AVR__)
481+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
482+
483+
auto chunkBufferIterator = chunkBuffer.begin();
484+
485+
#ifdef DEBUG_SERIAL
486+
uint8_t chunkNumber = 1;
487+
#endif
488+
424489
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
490+
491+
for (size_t i = 0; i < write_len; ++i) {
492+
*chunkBufferIterator++ = write_buffer[i];
493+
494+
if (chunkBufferIterator == chunkBuffer.end()) {
495+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
496+
chunkBufferIterator = chunkBuffer.begin();
497+
498+
#ifdef DEBUG_SERIAL
499+
printChunk("write_then_read() Wrote", chunkBuffer,
500+
maxBufferSizeForChunkedTransfer, chunkNumber++);
432501
#endif
433-
{
434-
for (size_t i = 0; i < write_len; i++) {
435-
transfer(write_buffer[i]);
436502
}
437503
}
438504

505+
auto readBufferIterator = read_buffer;
506+
auto readFromIterator = chunkBufferIterator;
507+
size_t readBufferLen = read_len;
508+
509+
for (size_t i = 0; i < read_len; ++i) {
510+
*chunkBufferIterator++ = sendvalue;
511+
512+
if (chunkBufferIterator == chunkBuffer.end()) {
439513
#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();
514+
printChunk("write_then_read() before transmit", chunkBuffer,
515+
maxBufferSizeForChunkedTransfer, chunkNumber);
516+
#endif
517+
518+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
519+
520+
while (readBufferLen) {
521+
if (readFromIterator != chunkBuffer.end()) {
522+
*readBufferIterator++ = *readFromIterator++;
523+
--readBufferLen;
524+
} else {
525+
break;
526+
}
527+
}
528+
529+
#ifdef DEBUG_SERIAL
530+
printChunk("write_then_read() after transmit", chunkBuffer,
531+
maxBufferSizeForChunkedTransfer, chunkNumber++);
532+
#endif
533+
534+
chunkBufferIterator = chunkBuffer.begin();
535+
readFromIterator = chunkBuffer.begin();
447536
}
448537
}
449-
DEBUG_SERIAL.println();
538+
539+
if (chunkBufferIterator != chunkBuffer.begin()) {
540+
auto numberByteToTransfer = chunkBufferIterator - chunkBuffer.begin();
541+
542+
#ifdef DEBUG_SERIAL
543+
printChunk("write_then_read() before transmit remaining", chunkBuffer,
544+
maxBufferSizeForChunkedTransfer, chunkNumber);
545+
#endif
546+
547+
transfer(chunkBuffer.data(), numberByteToTransfer);
548+
549+
#ifdef DEBUG_SERIAL
550+
printChunk("write_then_read() after transmit remaining", chunkBuffer,
551+
numberByteToTransfer, chunkNumber);
450552
#endif
451553

452-
// do the reading
554+
while (readBufferLen) {
555+
if (readFromIterator != chunkBuffer.end()) {
556+
*readBufferIterator++ = *readFromIterator++;
557+
--readBufferLen;
558+
} else {
559+
break;
560+
}
561+
}
562+
}
563+
564+
endTransactionWithDeassertingCS();
565+
566+
#else // !defined(__AVR__)
567+
568+
beginTransactionWithAssertingCS();
569+
570+
for (size_t i = 0; i < write_len; i++) {
571+
transfer(write_buffer[i]);
572+
}
573+
453574
for (size_t i = 0; i < read_len; i++) {
454575
read_buffer[i] = transfer(sendvalue);
455576
}
456577

578+
endTransactionWithDeassertingCS();
579+
457580
#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();
581+
printBuffer("write_then_read() write_buffer", write_buffer, write_len);
582+
printBuffer("write_then_read() read_buffer", read_buffer, read_len);
468583
#endif
469584

470-
endTransactionWithDeassertingCS();
585+
#endif // !defined(__AVR__)
471586

472587
return true;
473588
}

0 commit comments

Comments
 (0)