diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index c820ba7028..b12c4ae5c4 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -685,7 +685,8 @@ endchoice config ESP32_RTC_CLK_CAL_CYCLES int "Number of cycles for RTC_SLOW_CLK calibration" - default 1024 + default 3000 if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + default 1024 if ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC range 0 125000 help When the startup code initializes RTC_SLOW_CLK, it can perform @@ -698,18 +699,27 @@ config ESP32_RTC_CLK_CAL_CYCLES When this option is set to 0, clock calibration will not be performed at startup, and approximate clock frequencies will be assumed: - - 150000 Hz if internal RC oscillator is used as clock source - - 32768 Hz if the 32k crystal oscillator is used + - 150000 Hz if internal RC oscillator is used as clock source. For this use value 1024. + - 32768 Hz if the 32k crystal oscillator is used. For this use value 3000 or more. + In case more value will help improve the definition of the launch of the crystal. + If the crystal could not start, it will be switched to internal RC. config ESP32_RTC_XTAL_BOOTSTRAP_CYCLES int "Bootstrap cycles for external 32kHz crystal" - default 100 + depends on ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + default 5 range 0 32768 help To reduce the startup time of an external RTC crystal, we bootstrap it with a 32kHz square wave for a fixed number of cycles. Setting 0 will disable bootstrapping (if disabled, the crystal may take longer to start up or fail to oscillate under some conditions). + + If this value is too high, a faulty crystal may initially start and then fail. + If this value is too low, an otherwise good crystal may not start. + + To accurately determine if the crystal has started, + set a larger "Number of cycles for RTC_SLOW_CLK calibration" (about 3000). config ESP32_DEEP_SLEEP_WAKEUP_DELAY int "Extra delay in deep sleep wake stub (in us)" diff --git a/components/esp32/clk.c b/components/esp32/clk.c index ec8d73c9bb..49cba58109 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -36,7 +36,6 @@ * Larger values increase startup delay. Smaller values may cause false positive * detection (i.e. oscillator runs for a few cycles and then stops). */ -#define XTAL_32K_DETECT_CYCLES 32 #define SLOW_CLK_CAL_CYCLES CONFIG_ESP32_RTC_CLK_CAL_CYCLES #define MHZ (1000000) @@ -128,6 +127,9 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) { uint32_t cal_val = 0; + uint32_t wait = 0; + const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * SLOW_CLK_CAL_CYCLES); + bool changing_clock_to_150k = false; do { if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { /* 32k XTAL oscillator needs to be enabled and running before it can @@ -137,24 +139,23 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) * oscillator cycles. If the 32k XTAL has not started up, calibration * will time out, returning 0. */ - uint32_t wait = 0; - // increment of 'wait' counter equivalent to 3 seconds - const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * XTAL_32K_DETECT_CYCLES); ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); - do { - ++wait; - rtc_clk_32k_enable(true); - cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES); - if (wait % warning_timeout == 0) { - ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up"); - } - if(cal_val == 0){ - rtc_clk_32k_enable(false); - rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES); - } - } while (cal_val == 0); + rtc_clk_32k_enable(true); + 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; + } } 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); + } if (SLOW_CLK_CAL_CYCLES > 0) { /* TODO: 32k XTAL oscillator has some frequency drift at startup. @@ -165,6 +166,9 @@ 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/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index ee076e04d5..9efad4a1e4 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -13,6 +13,7 @@ #include "freertos/task.h" #include "freertos/semphr.h" #include "../esp_clk_internal.h" +#include "esp_clk.h" @@ -134,7 +135,7 @@ TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]") } #define COUNT_TEST 10 -#define TIMEOUT_TEST_MS 50 +#define TIMEOUT_TEST_MS (5 + CONFIG_ESP32_RTC_CLK_CAL_CYCLES / 16) void stop_rtc_external_quartz(){ const uint8_t pin_32 = 32; @@ -151,6 +152,68 @@ void stop_rtc_external_quartz(){ gpio_output_set_high(0, 0, 0, mask_32 | mask_33); // disable pins } +static void start_freq(rtc_slow_freq_t required_src_freq, uint32_t start_delay_ms) +{ + int i = 0, fail = 0; + uint32_t start_time; + uint32_t end_time; + rtc_slow_freq_t selected_src_freq; + stop_rtc_external_quartz(); +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + uint32_t bootstrap_cycles = CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES; + printf("Test is started. Kconfig settings:\n External 32K crystal is selected,\n Oscillation cycles = %d,\n Calibration cycles = %d.\n", + bootstrap_cycles, + CONFIG_ESP32_RTC_CLK_CAL_CYCLES); +#else + uint32_t bootstrap_cycles = 5; + printf("Test is started. Kconfig settings:\n Internal RC is selected,\n Oscillation cycles = %d,\n Calibration cycles = %d.\n", + bootstrap_cycles, + CONFIG_ESP32_RTC_CLK_CAL_CYCLES); +#endif + if (start_delay_ms == 0 && CONFIG_ESP32_RTC_CLK_CAL_CYCLES < 1500){ + start_delay_ms = 50; + printf("Recommended increase Number of cycles for RTC_SLOW_CLK calibration to 3000!\n"); + } + while(i < COUNT_TEST){ + start_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); + i++; + printf("attempt #%d/%d...", i, COUNT_TEST); + rtc_clk_32k_bootstrap(bootstrap_cycles); + ets_delay_us(start_delay_ms * 1000); + rtc_clk_select_rtc_slow_clk(); + selected_src_freq = rtc_clk_slow_freq_get(); + end_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); + printf(" [time=%d] ", (end_time - start_time) - start_delay_ms); + if(selected_src_freq != required_src_freq){ + printf("FAIL. Time measurement..."); + fail = 1; + } else { + printf("PASS. Time measurement..."); + } + uint64_t clk_rtc_time; + uint32_t fail_measure = 0; + for (int j = 0; j < 3; ++j) { + clk_rtc_time = esp_clk_rtc_time(); + ets_delay_us(1000000); + uint64_t delta = esp_clk_rtc_time() - clk_rtc_time; + if (delta < 900000LL || delta > 1100000){ + printf("FAIL"); + fail = 1; + fail_measure = 1; + break; + } + } + if(fail_measure == 0) { + printf("PASS"); + } + printf(" [calibration val = %d] \n", esp_clk_slowclk_cal_get()); + stop_rtc_external_quartz(); + ets_delay_us(500000); + } + TEST_ASSERT_MESSAGE(fail == 0, "Test failed"); + printf("Test passed successfully\n"); +} + #ifdef CONFIG_SPIRAM_SUPPORT // PSRAM tests run on ESP-WROVER-KIT boards, which have the 32k XTAL installed. // Other tests may run on DevKitC boards, which don't have a 32k XTAL. @@ -159,16 +222,29 @@ TEST_CASE("Test starting external RTC quartz", "[rtc_clk]") int i = 0, fail = 0; uint32_t start_time; uint32_t end_time; - stop_rtc_external_quartz(); - printf("Start test. Number of oscillation cycles = %d\n", CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES); +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + uint32_t bootstrap_cycles = CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES; + printf("Test is started. Kconfig settings:\n External 32K crystal is selected,\n Oscillation cycles = %d,\n Calibration cycles = %d.\n", + bootstrap_cycles, + CONFIG_ESP32_RTC_CLK_CAL_CYCLES); +#else + uint32_t bootstrap_cycles = 5; + printf("Test is started. Kconfig settings:\n Internal RC is selected,\n Oscillation cycles = %d,\n Calibration cycles = %d.\n", + bootstrap_cycles, + CONFIG_ESP32_RTC_CLK_CAL_CYCLES); +#endif + if (CONFIG_ESP32_RTC_CLK_CAL_CYCLES < 1500){ + printf("Recommended increase Number of cycles for RTC_SLOW_CLK calibration to 3000!\n"); + } while(i < COUNT_TEST){ start_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); i++; printf("attempt #%d/%d...", i, COUNT_TEST); - rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES); + rtc_clk_32k_bootstrap(bootstrap_cycles); rtc_clk_select_rtc_slow_clk(); end_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); + printf(" [time=%d] ", end_time - start_time); if((end_time - start_time) > TIMEOUT_TEST_MS){ printf("FAIL\n"); fail = 1; @@ -178,11 +254,30 @@ TEST_CASE("Test starting external RTC quartz", "[rtc_clk]") stop_rtc_external_quartz(); ets_delay_us(100000); } - if (fail == 1){ - printf("Test failed\n"); - TEST_ASSERT(false); - } else { - printf("Test passed successfully\n"); - } + TEST_ASSERT_MESSAGE(fail == 0, "Test failed"); + printf("Test passed successfully\n"); } + +TEST_CASE("Test starting 'External 32kHz XTAL' on the board with it.", "[rtc_clk]") +{ + start_freq(RTC_SLOW_FREQ_32K_XTAL, 200); + start_freq(RTC_SLOW_FREQ_32K_XTAL, 0); +} + +#else + +TEST_CASE("Test starting 'External 32kHz XTAL' on the board without it.", "[rtc_clk]") +{ + printf("Tries to start the 'External 32kHz XTAL' on the board without it. " + "Clock switching to 'Internal 150 kHz RC oscillator'.\n"); + + printf("This test will be successful for boards without an external crystal or non-working crystal. " + "First, there will be an attempt to start from the external crystal after a failure " + "will switch to the internal RC circuit. If the switch to the internal RC circuit " + "was successful then the test succeeded.\n"); + + start_freq(RTC_SLOW_FREQ_RTC, 200); + start_freq(RTC_SLOW_FREQ_RTC, 0); +} + #endif // CONFIG_SPIRAM_SUPPORT