diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index dc897dff2f..2f7d4c868e 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -687,18 +687,39 @@ choice ESP32_RTC_CLOCK_SOURCE default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC help Choose which clock is used as RTC clock source. + + - "Internal 150kHz oscillator" option provides lowest deep sleep current + consumption, and does not require extra external components. However + frequency stability with respect to temperature is poor, so time may + drift in deep/light sleep modes. + - "External 32kHz crystal" provides better frequency stability, at the + expense of slightly higher (1uA) deep sleep current consumption. + - "External 32kHz oscillator" allows using 32kHz clock generated by an + external circuit. In this case, external clock signal must be connected + to 32K_XP pin. Amplitude should be <1.2V in case of sine wave signal, + and <1V in case of square wave signal. Common mode voltage should be + 0.1 < Vcm < 0.5Vamp, where Vamp is the signal amplitude. + Additionally, 1nF capacitor must be connected between 32K_XN pin and + ground. 32K_XN pin can not be used as a GPIO in this case. + - "Internal 8.5MHz oscillator divided by 256" option results in higher + deep sleep current (by 5uA) but has better frequency stability than + the internal 150kHz oscillator. It does not require external components. config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC bool "Internal 150kHz RC oscillator" config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL bool "External 32kHz crystal" +config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC + bool "External 32kHz oscillator at 32K_XP pin" +config ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 + bool "Internal 8.5MHz oscillator, divided by 256 (~33kHz)" endchoice config ESP32_RTC_CLK_CAL_CYCLES int "Number of cycles for RTC_SLOW_CLK calibration" default 3000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL default 1024 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC - range 0 27000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + range 0 27000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL || ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC || ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 range 0 32766 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC help When the startup code initializes RTC_SLOW_CLK, it can perform diff --git a/components/esp32/clk.c b/components/esp32/clk.c index bb348fd672..17afaa1b34 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -40,7 +40,23 @@ #define MHZ (1000000) -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); +/* Indicates that this 32k oscillator gets input from external oscillator, rather + * than a crystal. + */ +#define EXT_OSC_FLAG BIT(3) + +/* This is almost the same as rtc_slow_freq_t, except that we define + * an extra enum member for the external 32k oscillator. + * For convenience, lower 2 bits should correspond to rtc_slow_freq_t values. + */ +typedef enum { + SLOW_CLK_150K = RTC_SLOW_FREQ_RTC, //!< Internal 150 kHz RC oscillator + SLOW_CLK_32K_XTAL = RTC_SLOW_FREQ_32K_XTAL, //!< External 32 kHz XTAL + SLOW_CLK_8MD256 = RTC_SLOW_FREQ_8MD256, //!< Internal 8 MHz RC oscillator, divided by 256 + SLOW_CLK_32K_EXT_OSC = RTC_SLOW_FREQ_32K_XTAL | EXT_OSC_FLAG //!< External 32k oscillator connected to 32K_XP pin +} slow_clk_sel_t; + +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk); // g_ticks_us defined in ROMs for PRO and APP CPU extern uint32_t g_ticks_per_us_pro; @@ -71,8 +87,12 @@ void esp_clk_init(void) rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); -#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL - select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#if defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL) + select_rtc_slow_clk(SLOW_CLK_32K_XTAL); +#elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC) + select_rtc_slow_clk(SLOW_CLK_32K_EXT_OSC); +#elif defined(CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256) + select_rtc_slow_clk(SLOW_CLK_8MD256); #else select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); #endif @@ -117,16 +137,12 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) g_ticks_per_us_app = ticks_per_us; } -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk) { + rtc_slow_freq_t rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V; uint32_t cal_val = 0; - uint32_t wait = 0; - uint32_t freq_hz = ((slow_clk == RTC_SLOW_FREQ_32K_XTAL) ? 32768 : 150000); - uint32_t warning_timeout = 3 /* sec */ * freq_hz /* Hz */ / (SLOW_CLK_CAL_CYCLES + 1); - warning_timeout = ((warning_timeout == 0) ? 3 /* sec */ : warning_timeout); - bool changing_clock_to_150k = false; do { - if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + if (rtc_slow_freq == RTC_SLOW_FREQ_32K_XTAL) { /* 32k XTAL oscillator needs to be enabled and running before it can * be used. Hardware doesn't have a direct way of checking if the * oscillator is running. Here we use rtc_clk_cal function to count @@ -135,25 +151,23 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) * will time out, returning 0. */ ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); - rtc_clk_32k_enable(true); + if (slow_clk == SLOW_CLK_32K_XTAL) { + rtc_clk_32k_enable(true); + } else if (slow_clk == SLOW_CLK_32K_EXT_OSC) { + rtc_clk_32k_enable_external(); + } // When SLOW_CLK_CAL_CYCLES is set to 0, clock calibration will not be performed at startup. if (SLOW_CLK_CAL_CYCLES > 0) { cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES); if (cal_val == 0 || cal_val < 15000000L) { - ESP_EARLY_LOGE(TAG, "RTC: Not found External 32 kHz XTAL. Switching to Internal 150 kHz RC chain"); - slow_clk = RTC_SLOW_FREQ_RTC; - changing_clock_to_150k = true; + ESP_EARLY_LOGW(TAG, "32 kHz XTAL not found, switching to internal 150 kHz oscillator"); + rtc_slow_freq = RTC_SLOW_FREQ_RTC; } } + } else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) { + rtc_clk_8m_enable(true, true); } - rtc_clk_slow_freq_set(slow_clk); - if (changing_clock_to_150k == true && wait > 1){ - // This helps when there are errors when switching the clock from External 32 kHz XTAL to Internal 150 kHz RC chain. - rtc_clk_32k_enable(false); - uint32_t min_bootstrap = 5; // Min bootstrapping for continue switching the clock. - rtc_clk_32k_bootstrap(min_bootstrap); - rtc_clk_32k_enable(true); - } + rtc_clk_slow_freq_set(rtc_slow_freq); if (SLOW_CLK_CAL_CYCLES > 0) { /* TODO: 32k XTAL oscillator has some frequency drift at startup. @@ -164,9 +178,6 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); } - if (++wait % warning_timeout == 0) { - ESP_EARLY_LOGW(TAG, "still waiting for source selection RTC"); - } } while (cal_val == 0); ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val); esp_clk_slowclk_cal_set(cal_val); diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index b02d31eac3..a528bdd15d 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -193,6 +193,11 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq); */ void rtc_clk_32k_enable(bool en); +/** + * @brief Configure 32 kHz XTAL oscillator to accept external clock signal + */ +void rtc_clk_32k_enable_external(); + /** * @brief Get the state of 32k XTAL oscillator * @return true if 32k XTAL oscillator has been enabled diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index 38737fbaa2..30f20e0eb0 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -62,6 +62,10 @@ #define XTAL_32K_BOOTSTRAP_DBIAS_VAL 0 #define XTAL_32K_BOOTSTRAP_TIME_US 7 +#define XTAL_32K_EXT_DAC_VAL 2 +#define XTAL_32K_EXT_DRES_VAL 3 +#define XTAL_32K_EXT_DBIAS_VAL 1 + /* Delays for various clock sources to be enabled/switched. * All values are in microseconds. * TODO: some of these are excessive, and should be reduced. @@ -98,7 +102,7 @@ static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val) // Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled. static int s_cur_pll_freq; -static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) +static void rtc_clk_32k_enable_common(int dac, int dres, int dbias) { 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, @@ -113,12 +117,17 @@ static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) void rtc_clk_32k_enable(bool enable) { if (enable) { - rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); + rtc_clk_32k_enable_common(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); } else { CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); } } +void rtc_clk_32k_enable_external() +{ + rtc_clk_32k_enable_common(XTAL_32K_EXT_DAC_VAL, XTAL_32K_EXT_DRES_VAL, XTAL_32K_EXT_DBIAS_VAL); +} + /* Helping external 32kHz crystal to start up. * External crystal connected to outputs GPIO32 GPIO33. * Forms N pulses with a frequency of about 32KHz on the outputs of the crystal. @@ -150,7 +159,7 @@ void rtc_clk_32k_bootstrap(uint32_t cycle) SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE); ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US); - rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL, + rtc_clk_32k_enable_common(XTAL_32K_BOOTSTRAP_DAC_VAL, XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL); } diff --git a/components/soc/esp32/rtc_sleep.c b/components/soc/esp32/rtc_sleep.c index 041c2d1b6b..6b16aa2889 100644 --- a/components/soc/esp32/rtc_sleep.c +++ b/components/soc/esp32/rtc_sleep.c @@ -197,6 +197,12 @@ void rtc_sleep_init(rtc_sleep_config_t cfg) REG_SET_FIELD(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, cfg.xtal_fpu); + if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL) == RTC_SLOW_FREQ_8MD256) { + REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + } else { + REG_CLR_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + } + /* enable VDDSDIO control by state machine */ REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE); REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en);