I'm trying to drive an external DAC via SPI interface using ESP32 chip. But I cannot achieve the desired speed (>1M transfers/sec, each transfer consists of 16bits). The reason is not the SPI speed itself, which could be set as high as 40MHz, but the time delay between two consecutive SPI transfers.

A sample code is as follows:

#include <SPI.h>

void setup()

void loop()
    unsigned long tstart=micros(),count=0;

    byte data[18]={0,1,2,1,3,1,0,1,2,1,3,1,0,1,2,1,3,1};

        for(byte i=0;i<9;i++)


The output shows that ESP32 can do approximately 694,800 transfers/sec, still far away from my requirement of 1M transfers/sec. However, theoretically, using a 40M SPI bus, each transfer only takes 16/40M sec, which should enable a >2M transfers/sec speed, so the difference must originate from the time delay between two consecutive SPI transfers.

I know that these delays can possibly be canceled by transferring more data in a single transfer command. However, the interface of DAC requires me to pulse SYNC once (set it high and then low) before each SPI transfer, which is not possible if I use a single SPI transfer command.

I have searched for solutions online and found two related problems:

The one here suggests disabling all MUTEX commands in esp32-hal-spi.c/h. It seems reasonable, but I see no effects, probably because the compiler has already deleted those codes in optimization.

Another here suggests using RMT to replace SPI, but suffer from timing issues.

I think there is also room for improvement as I only need to output data, and I access SPI on a single thread, so the solution can be thread-unsafe.

So I'm here to ask, is there any way to accelerate the SPI transfers? Thanks a lot in advance!

  • How are you accounting for the time needed to call micros, do the subtraction, do the comparison, check the while condition, handle the for loop, and add to count?
    – Delta_G
    Aug 5, 2020 at 4:01
  • @Delta_G, I've checked, checking micros() and adding count takes way less than a microsecond. 95%+ of time is still consumed on SPI.transfer(), which takes 1us+ on each call.
    – Wjx
    Aug 5, 2020 at 7:24
  • What DAC are you trying to drive?
    – Majenko
    Aug 5, 2020 at 10:25
  • 1
    I am not familiar with the SPI peripheral in the ESP32, but it looks like you can 1) confgure it to automatically assert the CS pin (for /SYNC) for each transaction, and 2) user DMA to stream large amounts of data quickly. How you would do all that I have no clue, but you'd be using ESP-IDF functions not Arduino functions to do it. And that's way beyond the scope of this site.
    – Majenko
    Aug 5, 2020 at 11:00
  • 1
    @Majenko thanks for your reply! I'm currently trying to (ab)use I2S interface to emulate SPI. would update if I succeeded. DMA seems not a good solution though, as preparing DMA takes time as well...
    – Wjx
    Aug 9, 2020 at 18:17


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.