ESP32 External 32.768kHZ Crystal for RTC

dovov97815
Posts: 22
Joined: Mon Aug 17, 2020 10:24 am

ESP32 External 32.768kHZ Crystal for RTC

Postby dovov97815 » Tue Jan 03, 2023 5:06 am

Due to inaccuracy of internal RTC, I want to implement external 32.768kHZ Crystal.
So, how to implement this in Arduino code.
From Searching I found I need to recompile some libraries, but these posts are old, so, Is there any update to do this.

Thank You

bidrohini
Posts: 202
Joined: Thu Oct 27, 2022 12:55 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby bidrohini » Tue Jan 03, 2023 11:34 am

In the following links, there were some discussions regarding using an external crystal:
https://github.com/espressif/arduino-esp32/issues/1225
viewtopic.php?t=1175&start=10

dovov97815
Posts: 22
Joined: Mon Aug 17, 2020 10:24 am

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby dovov97815 » Wed Jan 04, 2023 12:06 pm

This post is of 2018, thereafter many updates has been taken place, so, if have any new news regarding this

DOugL-
Posts: 21
Joined: Sun Dec 18, 2022 6:00 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby DOugL- » Wed Jan 04, 2023 8:13 pm

I'm looking to do the same thing since my ESP32 boards have GIO32/GIO33 broken out and I have a bunch of 32.7KHz crystals to try.
But I've not found anything current helping with the building process of getting the Arduino 2.0 IDE updated and if this will effect all ESP32 projects.

I have the esp32arduino v2.0.6 version installed in the Arduino IDE and found this but have not worked out the details of what exactly needs to be done. I've tried <./build.sh -t esp32 -b idf_libs qio 40m> but did not see any updates to the Arduino 2.0 platform and the directory stated does not exist - ‘$HOME/Arduino/hardware/espressif/esp32’
This directory looks more accurate - '$HOME/.arduino15/packages/esp32/hardware/esp32/2.0.6/libraries' and I found no updates there.

https://espressif-docs.readthedocs-host ... ilder.html

dovov97815
Posts: 22
Joined: Mon Aug 17, 2020 10:24 am

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby dovov97815 » Thu Jan 05, 2023 5:04 am

Thanks for sharing your experience, Have you found any ready solution, or build esp32 image for external crystal?

DOugL-
Posts: 21
Joined: Sun Dec 18, 2022 6:00 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby DOugL- » Thu Jan 05, 2023 6:16 am

I'm still working on it. I'm running the Arduino IDE v2.0.3 but more importantly I'm figuring out what steps are needed to get the Arduino-esp32 core and library built, modifying the RTC clock sourse with idf.py menuconfig and then getting it integrated back into the Arduino IDE.

I'll post my steps ounce I get it figured out. Currently my process is roughly, install the v4.4 of the esp-idf, setup my env paths, then using the esp32 Arduino Lib Builder build the library. I'm getting hung up on this last process but I think it was because I had v4.4.2 of the esp-idf instead of v4.4 but I've corrected that and the long build process is happening again.

DOugL-
Posts: 21
Joined: Sun Dec 18, 2022 6:00 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby DOugL- » Thu Jan 05, 2023 5:27 pm

So far I have it built as tar files and need to verify the "-c" option works. Adding idf.py menuconfig in there and changing the RTC clock source should provide the libraries needed. Now I just need to get them into the Arduino IDE v2 paths.

from: https://docs.espressif.com/projects/esp ... setup.html
Install v4.4.x of the esp-idf in directory ~/Projects/esp/esp-idf-4.4
note: delete or move previous ~/.espressif directory or set env IDF_TOOLS_PATH=~/.espressif4.4
mkdir ~/Projects/esp
cd ~/Projects/esp
git clone -b v4.4 --recursive https://github.com/espressif/esp-idf.git esp-idf-4_4
cd esp-idf-4_4/
./install.sh
note: created alias for get_idf4.4 to load export.sh to setup environment variables.
note: changing branches: from: https://docs.espressif.com/projects/esp ... sions.html
cd $IDF_PATH
git fetch
git checkout release/v4.4
git pull
git submodule update --init --recursive

from: https://espressif-docs.readthedocs-host ... ilder.html
or without Arduino info: from: https://docs.espressif.com/projects/ard ... onent.html
Install Arduino lib-builder
cd ~/Project/esp/
git clone https://github.com/espressif/esp32-arduino-lib-builder
cd esp32-arduino-lib-builder
note: maybe: idf.py menuconfig
./build.sh

Set the Arduino-ESP32 Destination Folder¶
Set the arduino-esp32 folder to copy the result to. ex. ‘$HOME/Arduino/hardware/espressif/esp32’
./build.sh -c $HOME/.arduino15/packages/esp32/hardware/esp32/2.0.6/ esp32

DOugL-
Posts: 21
Joined: Sun Dec 18, 2022 6:00 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby DOugL- » Fri Jan 06, 2023 1:02 am

using the -c option does put the libraries in place but I'm having a problem getting the libraries built with the RTC clock source change staying. In the esp32 arduino lib builder I run idf.py menuconfig and make the change but after the libraries are built, it is reverted back to using the 150KHz RC source.

Something in the new builds is requiring esptool and openocd-esp32 in the tools directory(tools/esptool/.. and tools/openocd-esp32/..).
I've opted to keep the standard esp32 setup installed (via the IDE) and then I tar the newly built libs over that installation. I've also copied the esptool/ and openocd-esp32/ directories from elsewhere into the hardware/esp32/2.0.6/ directory.
cp -a $HOME/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32 tools/
cp -a $HOME/Projects/esp/esp-idf-4_4/components/esptool_py/esptool tools/
and then backed those up for later use while in the 2.0.6 directory:
tar -cvzf ../othertools.tgz tools/openocd-esp32 tools/esptool

Because sdkconfig/menuconfig gets reset on every build I've found this works:
-backup default installation's sdkconfig for esp32
cp components/arduino/tools/sdk/esp32/sdkconfig components/arduino/tools/sdk/esp32/sdkconfigORG
./build.sh -t esp32 -b menuconfig
cp sdkconfig components/arduino/tools/sdk/esp32/sdkconfig
./build.sh -t esp32

the updated libraries will be in the dist/ directory
I tried soldering a 32.768kHz xtal on pins GIO32/GIO33 but the clock wouldn't start. I don't have any 12.5pF caps to try so I have to go to an external RTC. From what I hear, even with the xtal the RTC will lose many minutes per year while the DS3231 is spec'ed for +/-2 min/yr and has battery backup.

dovov97815
Posts: 22
Joined: Mon Aug 17, 2020 10:24 am

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby dovov97815 » Thu Jan 12, 2023 7:25 am

Code: Select all

#include "Arduino.h"
#include "soc/rtc.h"

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */


RTC_DATA_ATTR int bootCount = 0;

#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "esp_deep_sleep.h"
#include "rom/rtc.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

void debug_xtal_out_dac1() {
    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE | RTC_IO_X32N_RDE);
    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
    SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, 1, RTC_IO_DAC_XTAL_32K_S);
    SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, 3, RTC_IO_DRES_XTAL_32K_S);
    SET_PERI_REG_BITS(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, 0, RTC_IO_DBIAS_XTAL_32K_S);
    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
    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);
    REG_SET_FIELD(SENS_SAR_DAC_CTRL1_REG, SENS_DEBUG_BIT_SEL, 0);
    const uint8_t sel = 4; /* sel = 4 : 32k XTAL; sel = 5 : internal 150k RC */
    REG_SET_FIELD(RTC_IO_RTC_DEBUG_SEL_REG, RTC_IO_DEBUG_SEL0, sel);
}

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
    case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
  }
}

const float factor = (1 << 19) * 1000.0f;

#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)

static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name)
{

    const uint32_t cal_count = 1000;
    uint32_t cali_val;
    printf("%s:\n", name);
    for (int i = 0; i < 5; ++i)
    {
        printf("calibrate (%d): ", i);
        cali_val = rtc_clk_cal(cal_clk, cal_count);
        printf("%.3f kHz\n", factor / (float)cali_val);
    }
    return cali_val;
}

void print_slow_clock_source() {
  rtc_slow_freq_t slow_clk = rtc_clk_slow_freq_get();
  Serial.print("Slow clk source: ");
  switch(slow_clk) {
    case RTC_SLOW_FREQ_RTC: Serial.println("Internal 150kHz"); break;
    case RTC_SLOW_FREQ_32K_XTAL: Serial.println("External 32kHz"); break;
    case RTC_SLOW_FREQ_8MD256: Serial.println("Internal 8MHz");
  }
}

void print_fast_clk_math() {
  const uint32_t cal_count = 1000;
  uint32_t cali_val;
  uint64_t freq = (uint64_t)rtc_clk_xtal_freq_get();
  printf("Fast Clk: %lluMHz\n", freq);
}

float crystal_frequency() {
  const uint32_t cal_count = 100;
  uint32_t cali_val;
  cali_val = rtc_clk_cal(RTC_CAL_32K_XTAL, cal_count);
  float freq_32k = factor / (float)cali_val;
  return freq_32k;
}

void setExternalCrystalAsRTCSource(){
   if (bootCount == 1) {
    Serial.println("First boot, bootstrap and enable 32k XTAL");
    print_fast_clk_math();
    // rtc_clk_32k_bootstrap(10);
    rtc_clk_32k_enable(true);
  } else {
    Serial.println("Wake from deepsleep, not jumping XTAL.");
    printf("rtc sleep reg0: %u\n", REG_READ(RTC_CNTL_SLP_TIMER0_REG));
    printf("rtc sleep reg1: %u\n", REG_READ(RTC_CNTL_SLP_TIMER1_REG));
  }

    uint32_t cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL);
    debug_xtal_out_dac1();
    float freq_32k = factor / (float)cal_32k;
    float delta = freq_32k - 32.768;
    if (delta < 0) delta = -delta;
    uint32_t startCal=millis();
    while (delta > 0.002 && millis()-startCal<15000) {
      printf("Waiting for 32kHz clock to be stable: %.3f kHz\n", freq_32k);
      cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL);
      freq_32k = factor / (float)cal_32k;
      delta = freq_32k - 32.768;
      if (delta < 0) delta = -delta;
    }
    if(delta < 0.002){
      rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
      uint32_t rtc_clk_calibration = REG_READ(RTC_SLOW_CLK_CAL_REG);
      printf("Slow clock calibration: %u\n", rtc_clk_calibration);
      printf("32k calibration: %u\n", cal_32k);
      if ((rtc_clk_calibration > (cal_32k + 5)) || (rtc_clk_calibration < (cal_32k - 5))) {
        printf("Miscalibrated, setting calibration register to 32k calibration.\n");
        REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_32k);
        rtc_clk_calibration = REG_READ(RTC_SLOW_CLK_CAL_REG);
        if (rtc_clk_calibration != cal_32k) {
          printf("ERROR Calibration write failure.\n");
        }
      }
  
      if (cal_32k == 0)
      {
          printf("32K XTAL OSC has not started up");
      }
      else
      {
          printf("done\n");
      }
  
      if (rtc_clk_32k_enabled())
      {
          Serial.println("OSC Enabled");
      }
    }
    else{
      Serial.println("OSC Not Enabled, using Internal 150KHz RC");
    }

    print_slow_clock_source();
}


void setup() {
  delay(500);
  Serial.begin(115200);
  

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();
  print_slow_clock_source();
  setExternalCrystalAsRTCSource();
      /*
    First we configure the wake up source
    We set our ESP32 to wake up every 5 seconds
    */
    esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
    Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
    " Seconds");

    // By default the ESP32 powers down all peripherals which are not needed to wake up again.
    // We can change this behaviour with pd_config
    // ESP_PD_DOMAIN_RTC_PERIPH
    // ESP_PD_DOMAIN_RTC_SLOW_MEM
    // ESP_PD_DOMAIN_RTC_FAST_MEM
    esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
    Serial.println("Configured peripheral power");
    delay(50);
    uint64_t ticks = rtc_time_get();
    Serial.printf("Going to sleep now, rtc ticks: %llu\n", ticks);
    Serial.flush();
    delay(50);
    esp_deep_sleep_start();
    Serial.println("This will never be printed");
}

void loop() {
  // put your main code here, to run repeatedly:
  // Left blank because we are just deepsleeping
}
This code is working fine for rtc external crystal selection, without building esp-idf library for arduino.
Could you please confirm that it is working with your hardware setup, Or any mistake in this.
I am going to test this for longer deepsleep time of 24 hours.

xerom32
Posts: 1
Joined: Tue Jan 02, 2024 6:55 pm

Re: ESP32 External 32.768kHZ Crystal for RTC

Postby xerom32 » Tue Jan 02, 2024 7:02 pm

The slow clock reverts to internal after waking from deep sleep. I had to save the clock calibration to RTC memory. Then apply the calibration and enable external clock on wake from deep sleep.

Otherwise it seems to work great.

Code: Select all

RTC_DATA_ATTR uint32_t _cal_32k = 1600000;

void setExternalCrystalAsRTCSource(){
   if (bootcount <= 1) {
    Serial.println("First boot, bootstrap and enable 32k XTAL");
    print_fast_clk_math();
    // rtc_clk_32k_bootstrap(10);
    rtc_clk_32k_enable(true);
  } else {
    Serial.println("Wake from deepsleep, not jumping XTAL.");
    printf("rtc sleep reg0: %u\n", REG_READ(RTC_CNTL_SLP_TIMER0_REG));
    printf("rtc sleep reg1: %u\n", REG_READ(RTC_CNTL_SLP_TIMER1_REG));

    rtc_clk_32k_enable(true);
    rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
    REG_WRITE(RTC_SLOW_CLK_CAL_REG, _cal_32k);

    return;
  }

  // ...  
}

Who is online

Users browsing this forum: Google [Bot] and 30 guests