0

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()
{
    Serial.begin(115200);
    SPI.begin();
    SPI.beginTransaction(SPISettings(40000000,MSBFIRST,SPI_MODE0));
}

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};

    while(micros()-tstart<1000000)
    {
        for(byte i=0;i<9;i++)
          SPI.transfer16(0b0101010110101010);
        count+=9;
    }

    Serial.println(count);
}

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 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 at 7:24
  • What DAC are you trying to drive? – Majenko Aug 5 at 10:25
  • @Majenko DAC108S085 manufactured by TI – Wjx Aug 5 at 10:41
  • 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 at 11:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.