Getting my external XTAL to work/

On my custom built ESP32 S3 board I have attached a 32K XTAL to Board Pins 8 and 9 of the board (which are in turn wired to pins 21 and 22 of the IC). I seem to be able to set the slow clock to the XTAL but as soon as i attempt to go into light sleep I get a coredump. I don’t have an oscilloscope handy right now, so i have to approximate whether it’s my code or a hardware problem…

The code: i built a test environment to do a bunch of hardware checks. the rest of the code is bloaty, but all turned off. the only part of the code that is actually called is the following.

if (testStruct.rtc == true) {
      rtc_cpu_freq_config_t config;
    rtc_clk_cpu_freq_get_config(&config);
    int mhz = (int)config.freq_mhz;
    Serial.println("Clock Config, Current CPU Freq:"+String(mhz)+" MHz");
    String xtal = config.source == RTC_CPU_FREQ_SRC_XTAL ? "XTAL" : "Other";
    Serial.println("Clock Config, Source: "+xtal);
    rtc_slow_freq_t slow_clk_src = rtc_clk_slow_src_get();
    // Get the RTC slow clock source

    const char* source_name = "";
    float frequency_kHz = 0;

    switch (slow_clk_src) {
        case RTC_SLOW_FREQ_RTC: // Internal 150 kHz RC oscillator
            source_name = "Internal 150 kHz RC oscillator";
            frequency_kHz = 150;
            break;
        case RTC_SLOW_FREQ_32K_XTAL: // External 32.768 kHz crystal
            source_name = "External 32.768 kHz XTAL";
            frequency_kHz = 32.768;
            break;
        case RTC_SLOW_FREQ_8MD256: // Internal 8 MHz RC oscillator, divided by 256
            source_name = "Internal 8 MHz RC oscillator, divided by 256";
            frequency_kHz = 8 * 1000 / 256;
            break;
        default:
            source_name = "Unknown";
            break;
    }
    Serial.println("RTC INFO: RTC Slow Clock Source: "+String(source_name));
    Serial.println("RTC INFO: Frequency: "+String(frequency_kHz)+" kHz  and "+String(slow_clk_src));
    rtc_clk_slow_src_set(RTC_SLOW_FREQ_32K_XTAL);
    slow_clk_src = rtc_clk_slow_src_get();
    if (slow_clk_src == RTC_SLOW_FREQ_32K_XTAL) {
        Serial.println("RTC slow clock source set to external 32.768 kHz XTAL successfully.");
    } else {
        Serial.println("Failed to set RTC slow clock source to external 32.768 kHz XTAL.");
    }
    Serial.println("Sleeping");
    esp_sleep_enable_timer_wakeup((unsigned long)(10*1000000));
    esp_light_sleep_start();
    Serial.println("woke up");
    slow_clk_src = rtc_clk_slow_src_get();
    if (slow_clk_src == RTC_SLOW_FREQ_32K_XTAL) {
        Serial.println("RTC slow clock source set to external 32.768 kHz XTAL .");
    } else {
        Serial.println("RTC CLock Source not set to external 32.768 kHz XTAL.");
    }
 delay(10000);
}

The serial output is this, including the coredump.
I originally used only Arduino as framework, but when using espidf and turning on the XTAL in menuconfig i still get the same coredump

RTC INFO: RTC Slow Clock Source: Internal 150 kHz RC oscillator

RTC INFO: Frequency: 150.00 kHz and 0

RTC slow clock source set to external 32.768 Guru Meditation Error: Core 1 panic'ed (IntegerDivideByZero). Exception was unhandled.

Core 1 register dump:

PC : 0x40056999 PS : 0x00060333 A0 : 0x8037c935 A1 : 0x3fcebcb0

A2 : 0x07d00000 A3 : 0x00000001 A4 : 0x00000000 A5 : 0x00000000

A6 : 0x6001f068 A7 : 0x00000002 A8 : 0x82011e9d A9 : 0x00000000

A10 : 0x00000000 A11 : 0x00000000 A12 : 0x07d00000 A13 : 0x00000000

A14 : 0x00000064 A15 : 0x00060323 SAR : 0x00000009 EXCCAUSE: 0x00000006

EXCVADDR: 0x00000000 LBEG : 0x42011dc9 LEND : 0x42011dd6 LCOUNT : 0x00000000

Backtrace: 0x40056996:0x3fcebcb0 0x4037c932:0x3fcebcd0 0x42012e15:0x3fcebcf0 0x42003184:0x3fcebd30 0x4200b6cc:0x3fcec0b0

#0 0x4037c932 in rtc_time_us_to_slowclk at /COMPONENT_ESP_HW_SUPPORT_DIR/port/esp32s3/rtc_time.c:165

#1 0x42012e15 in esp_light_sleep_start at /COMPONENT_ESP_HW_SUPPORT_DIR/sleep_modes.c:1246

#2 0x42003184 in loop() at src/main.cpp:315

#3 0x4200b6cc in loopTask(void*) at /Users/julian/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:80

Any pointers what i might try?

You will not only receive a core dump but also an error message:

Error: Core 1 panic'ed (IntegerDivideByZero). Exception was unhandled.

So something must be 0 in your sketch.

I hope this helps you with troubleshooting.


As esp_sleep_enable_timer_wakeup expects a uint64_t, you should also pass a uint64_t. Otherwise an overflow will occur somewhere (again).

esp_sleep_enable_timer_wakeup(uint64_t(10 * 1000000));

:wink:

the division by zero is happening somewhere deep in the rtc_time.c part of the esp32 library, in rtc_time.c and sleep_modes.c, that’s why i’m wondering. i’m just calling esp_light_sleep_start() which calls rtc_time_us_to_slowclk with the period param being “s_config.rtc_clk_cal_period”. That, in turn, causes the division by zero.
so, somehow s_config.rtc_clk_cal_period is zero, which in turn i don’t really know how to change.

Now, the only point wher ei see this is being set is in

    {
        s_config.rtc_clk_cal_period = rtc_clk_cal(RTC_CAL_RTC_MUX, RTC_CLK_SRC_CAL_CYCLES);
        esp_clk_slowclk_cal_set(s_config.rtc_clk_cal_period);
    }

(lines 641 and following of sleep_modes.c) which is defined in rtc_time.c here:

uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
{
    assert(slowclk_cycles);
    rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
    uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles);

    if ((cal_clk == RTC_CAL_32K_XTAL) && !rtc_clk_cal_32k_valid(xtal_freq, slowclk_cycles, xtal_cycles)) {
        return 0;
    }

    uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles;
    uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider;
    uint32_t period = (uint32_t)(period_64 & UINT32_MAX);
    return period;
}

so I sort of get where it all falls apart (i have to assume either rtc_clk_xtal_freq_get() returns zero or rtc_clk_cal_internal returns zero, but i honestly don’t know how to resolve that mess.

okay, after a while of digging and doing it now works and i don’t know why. I’m also not entirely sure how to find out whether the external XTAL is actually used?

i think i figured it out. adding more and more debug information just allowed for the xtal to actually spin up, and then it worked.
as soon as i commented out a bunch of debug printouts, the system started to crash again. a delay(3000) and a for (int i = 0;i<100000;i++) {} fixed it. will now implement in a way that does the same without using delay.