I looked into this recently, too, on ESP32.
First I tried to use the slow clock frequency adjustment register (RTC_CNTL_SCK_DCAP) to periodically tune the frequency in deep sleep wakeup stubs. After too long, I noticed that increasing the frequency is detrimental to sleep power consumption, so that was scrapped.
I had more luck using the stub to accumulate actual time by periodically waking up and calculating the actual elapsed time based on the current actual frequency. This led to the revelation that the frequency changes dramatically between RTC sleep and wake states - so much so that in the brief window of time between deep sleep and the wake stub trying to determine the frequency, it may have slowed by more than 5000ppm.
This is worth looking into separately, in my opinion. There is probably scope to simply and significantly improve the ESP-IDF's tracking of time in deep sleep by adjusting for the higher deep sleep slow clock frequency. (Consider how often reports of slow clock inaccuracy describe devices waking up too soon vs the alternative.)
The typical 2nd stage bootloader and app startup is quite sluggish with logging enabled, so by the time RTC calibration rolls around, the slow clock has slowed down even more, and so the obtained value will be even further from the actual deep sleep frequency.
In searching for some examples just now, I found a discussion that reached a similar conclusion as I did citing 0.7% speedup in sleep here:
https://github.com/espressif/esp-idf/issues/6687
Unfortunately this rate of change differs between devices. I had one with as low as 1000ppm, and one above 5000ppm. A few WROOM32Es off the same tape were clustered within ~200ppm of 4200ppm. I couldn't find any correlation between this value and any on-board information (eg. VREF) so unfortunately any calibration would need to be external. I fiddled with all sorts of registers, but nothing had any positive effect without an unacceptable increase in power consumption. If you don't mind >100uA there are many options, but I wasn't interested in anything that added >1uA.
Anyway, once the stub algorithm was in place and the device's specific sleep/wake difference ppm was dialled in, I found it is possible to get within about 100ppm error at the cost of a few hundred nA increase in average deep sleep current. I would have to go back and test again to refresh my memory to be sure. (I was sick to death of it by this point.)
Only very very late into all this did I find that there is the ability to output the slow clock using GPIO25/26. This would have saved a lot of time:
Code: Select all
static void output_rtc_slow_clock(int gpio)
{
assert(gpio == 25 || gpio == 26);
if(gpio == 25)
{
REG_SET_BIT(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M);
REG_CLR_BIT(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M | RTC_IO_PDAC1_RUE_M);
REG_SET_FIELD(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_FUN_SEL, 1);
}
else if(gpio == 26)
{
REG_SET_BIT(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M);
REG_CLR_BIT(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RDE_M | RTC_IO_PDAC2_RUE_M);
REG_SET_FIELD(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_FUN_SEL, 1);
}
REG_SET_FIELD(SENS_SAR_DAC_CTRL1_REG, SENS_DEBUG_BIT_SEL, 0);
REG_SET_FIELD(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_SEL0, RTC_IO_DEBUG_SEL0_150K_OSC);
}
If you're curious, enable that and then alternate between sleep/wake every 1min. If your oscilloscope can keep count of the frequency on that pin, you'll see a bimodal distribution manifest as the slow clock transitions between the faster sleep frequency and the slower wake frequency.
tldr- If you're up for a challenge, there might be some gains to be made here. Otherwise, life's too short: use an external RTC and move on.