ESP32 SPI Clock Frequency, Arduino Framework

Hello,

I have the following setup.

[env:upesy_wroom]
platform = espressif32
board = upesy_wroom
framework = arduino

When I set SPI.setFrequency(xx000000);
It doesn’t seem to matter what value I use for the xx parameter. I always get either a 40MHz or 80MHz clock. What can I do to achieve around 60MHz, or even better, a 62.5MHz clock?

Thank you.

The ESP32’s SPI peripheral can only produce SPI frequencies by dividing its APB bus frequency with an integer divider. So, not all SPI clock values are possible.

At that high of a frequency, it has troubles finding an integer divider that divides the APB bus frequency (80MHz nominally), into the SPI freuqency you want (e.g., 60MHz). Essentially it picks “1” or “2” here as its best choices, yielding 80 or 40MHz, whichever frequency is closer.

See datasheet and code.

This gets more complicated: To achieve a clean 62.5 MHz SPI frequency, your APB bus frequency would have to be an integer multiple of that, e.g., 62.5 MHz, 125 MHz, 187.5 MHz, etc.

The APB bus per this is can have different frequencies


If the PLL_CLK source is selected (which it is by default in the Arduino IDE), APB will be 80MHz. That’s bad since it cannot be divided cleanly into 60MHz.

With XTAL_CLK, the frequency of the quartz crystal is taken directly. Usually on ESP32 boards, that is, 24, 26 or 40 MHz. All of which are too slow. I think I remember that the ESP32 hardware or bootrom cannot handle a different XTAL value. And in any case, you would need to resolder that tiny tiny crystal.

RTC8M_CLK would give you a 8MHz clock.

So the only possibility I see is that the CPU_CLK source must be from the Audio-PLL (APLL) with a frequency of at 4 times the wanted SPI frequency (so e.g. 250MHz). This is because the CPU_CLK is half the APLL_CLK at best, and APB clock is half of the CPU_CLK.

I.e., the APLL must be started and configured to produce 250MHz, the CPU_CLK source must be switched over to APLL_CLK (div 2, so that it’s 125MHz), such that APB_CLK will follow with 62.5MHz, and after that, SPI.setFrequency(62500000) should work, as it can now can find a divider of exactly 1 to produce the wanted frequency.

Since the Arduino-ESP32 core does not natively support such a clocking mechanism, it might heavily intefere with other parts of the system to have APB be 62.5MHz instead of the usual 80MHz. Some timers depending on 80MHz APB might thus run slower.

There is some existing code here and here that might be useful for it.

2 Likes

Actually, I don’t think this is possible with the APLL.

If the biggest value in the numerator can be 500 MHz and the smallest possible divisor can be 4 (setting odiv = 0), then the max output frequency of the APLL is only 500 / 4 = 125 MHz.

This would be divided by 2 to get CPU CLK, again divided by 2 to get APB clock, which would also be the max SPI clock, and that turns then into only 31.25 MHz.

That’s worse than the regular PLL that could do 40 MHz or 80MHz.

As such, 40MHz is probably the closest you can get, if you don’t do crazy assembly level, single cycle I/O bit-banging of the SPI line.

3 Likes

Thank you very much for this detailed response, very helpful.
It would also be interesting to know if this applies to all ESP32 variants… but I think that’s another topic…

1 Like

As far as I could see, the same constraints are in place for ESP32-C3 and ESP32-S3. APB_CLK fixed to 80MHz. (And they don’t even have an APLL).

ok, and 31.25MHz is also not feasible, right?

Not directly with the SPI peripheral and the available clock speeds and divisors.

Some very highly optimized assembly and a high CPU speed (e.g. 240MHz) might be able to achieve a high enough SCLK by manipulating the GPIO registers directly, e.g., in the style of https://github.com/BlackBrix/bitBangedSPIfast. That has to be really cycle optimized assembly though.