Skip to content

Commit fc5c987

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 fc5c987

File tree

1 file changed

+189
-72
lines changed

1 file changed

+189
-72
lines changed

Adafruit_SPIDevice.cpp

+189-72
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,50 @@
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+
static printChunk(char *title, uint8_t buffer, uint8_t size,
17+
uint16_t chunkNumber) {
18+
DEBUG_SERIAL.print(F("\t"));
19+
DEBUG_SERIAL.print(title);
20+
DEBUG_SERIAL.print(F(" Chunk #"));
21+
DEBUG_SERIAL.print(chunkNumber);
22+
DEBUG_SERIAL.print(F(", size "));
23+
DEBUG_SERIAL.println(size);
24+
25+
for (uint8_t i = 0; i < size; ++i) {
26+
DEBUG_SERIAL.print(F("0x"));
27+
DEBUG_SERIAL.print(buffer[i], HEX);
28+
DEBUG_SERIAL.print(F(", "));
29+
}
30+
DEBUG_SERIAL.println();
31+
}
32+
33+
static printBuffer(char *title, uint8_t buffer, size_t len) {
34+
DEBUG_SERIAL.print(F("\t"));
35+
DEBUG_SERIAL.println(title);
36+
for (size_t i = 0; i < len; i++) {
37+
DEBUG_SERIAL.print(F("0x"));
38+
DEBUG_SERIAL.print(buffer[i], HEX);
39+
DEBUG_SERIAL.print(F(", "));
40+
if (read_len % 32 == 31) {
41+
DEBUG_SERIAL.println();
42+
}
43+
}
44+
DEBUG_SERIAL.println();
45+
}
46+
#endif
47+
848
/*!
949
* @brief Create an SPI device with the given CS pin and settings
1050
* @param cspin The arduino pin number to use for chip select
@@ -160,7 +200,6 @@ void Adafruit_SPIDevice::transfer(uint8_t *buffer, size_t len) {
160200
// Serial.print(send, HEX);
161201
for (uint8_t b = startbit; b != 0;
162202
b = (_dataOrder == SPI_BITORDER_LSBFIRST) ? b << 1 : b >> 1) {
163-
164203
if (bitdelay_us) {
165204
delayMicroseconds(bitdelay_us);
166205
}
@@ -326,49 +365,77 @@ void Adafruit_SPIDevice::endTransactionWithDeassertingCS() {
326365
bool Adafruit_SPIDevice::write(const uint8_t *buffer, size_t len,
327366
const uint8_t *prefix_buffer,
328367
size_t prefix_len) {
368+
#if !defined(__AVR__)
369+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
370+
371+
auto chunkBufferIterator = chunkBuffer.begin();
372+
373+
#ifdef DEBUG_SERIAL
374+
uint8_t chunkNumber = 1;
375+
#endif
376+
329377
beginTransactionWithAssertingCS();
330378

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
379+
for (size_t i = 0; i < prefix_len; ++i) {
380+
*chunkBufferIterator++ = prefix_buffer[i];
381+
382+
if (chunkBufferIterator == chunkBuffer.end()) {
383+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
384+
chunkBufferIterator = chunkBuffer.begin();
385+
386+
#ifdef DEBUG_SERIAL
387+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
388+
chunkNumber++);
341389
#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]);
348390
}
349391
}
350-
endTransactionWithDeassertingCS();
392+
393+
for (size_t i = 0; i < len; ++i) {
394+
*chunkBufferIterator++ = buffer[i];
395+
396+
if (chunkBufferIterator == chunkBuffer.end()) {
397+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
398+
chunkBufferIterator = chunkBuffer.begin();
351399

352400
#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(", "));
401+
printChunk("write() Wrote", chunkBuffer, maxBufferSizeForChunkedTransfer,
402+
chunkNumber++);
403+
#endif
359404
}
360405
}
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-
}
406+
407+
if (chunkBufferIterator != chunkBuffer.begin()) {
408+
auto numberByteToTransfer = chunkBufferIterator - chunkBuffer.begin();
409+
transfer(chunkBuffer.data(), numberByteToTransfer);
410+
411+
#ifdef DEBUG_SERIAL
412+
printChunk("write() Wrote remaining", mpBuffer, numberByteToTransfer,
413+
chunkNumber++);
414+
#endif
368415
}
369-
DEBUG_SERIAL.println();
416+
417+
endTransactionWithDeassertingCS();
418+
419+
#else // !defined(__AVR__)
420+
421+
beginTransactionWithAssertingCS();
422+
423+
for (size_t i = 0; i < prefix_len; i++) {
424+
transfer(prefix_buffer[i]);
425+
}
426+
for (size_t i = 0; i < len; i++) {
427+
transfer(buffer[i]);
428+
}
429+
430+
endTransactionWithDeassertingCS();
431+
432+
#ifdef DEBUG_SERIAL
433+
printBuffer("write() prefix_buffer", prefix_buffer, prefix_len);
434+
printBuffer("write() buffer", buffer, len);
370435
#endif
371436

437+
#endif // !defined(__AVR__)
438+
372439
return true;
373440
}
374441

@@ -390,16 +457,7 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
390457
endTransactionWithDeassertingCS();
391458

392459
#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();
460+
printBuffer("read() buffer", buffer, len);
403461
#endif
404462

405463
return true;
@@ -421,53 +479,112 @@ bool Adafruit_SPIDevice::read(uint8_t *buffer, size_t len, uint8_t sendvalue) {
421479
bool Adafruit_SPIDevice::write_then_read(const uint8_t *write_buffer,
422480
size_t write_len, uint8_t *read_buffer,
423481
size_t read_len, uint8_t sendvalue) {
482+
#if !defined(__AVR__)
483+
std::array<uint8_t, maxBufferSizeForChunkedTransfer> chunkBuffer;
484+
485+
auto chunkBufferIterator = chunkBuffer.begin();
486+
487+
#ifdef DEBUG_SERIAL
488+
uint8_t chunkNumber = 1;
489+
#endif
490+
424491
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
492+
493+
for (size_t i = 0; i < write_len; ++i) {
494+
*chunkBufferIterator++ = write_buffer[i];
495+
496+
if (chunkBufferIterator == chunkBuffer.end()) {
497+
transfer(chunkBuffer.data(), maxBufferSizeForChunkedTransfer);
498+
chunkBufferIterator = chunkBuffer.begin();
499+
500+
#ifdef DEBUG_SERIAL
501+
printChunk("write_then_read() Wrote", chunkBuffer,
502+
maxBufferSizeForChunkedTransfer, chunkNumber++);
432503
#endif
433-
{
434-
for (size_t i = 0; i < write_len; i++) {
435-
transfer(write_buffer[i]);
436504
}
437505
}
438506

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

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

580+
endTransactionWithDeassertingCS();
581+
457582
#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();
583+
printBuffer("write_then_read() write_buffer", write_buffer, write_len);
584+
printBuffer("write_then_read() read_buffer", read_buffer, read_len);
468585
#endif
469586

470-
endTransactionWithDeassertingCS();
587+
#endif // !defined(__AVR__)
471588

472589
return true;
473590
}

0 commit comments

Comments
 (0)