From 9ad0760b9d3cb615ab01c067c083bb232e05f296 Mon Sep 17 00:00:00 2001 From: Konstantin Kondrashov Date: Fri, 30 Mar 2018 17:14:29 +0500 Subject: [PATCH] esp32/clk:Fix starting rtc oscillator if it is bad If the RTC crystal is bad or has no matched capacitance, then you do not need to start such the crystal. It is necessary to determine this case, output an error (about impossibility to start from the oscillator) and start from the internal RC of the chain. Reduced the default value of the number of bootstrap cycles. Because we can oscillating the oscillator which then stops. (in Kconfig). Changed from 100 to 5. The number of calibration cycles has been increased. It is the main criterion for estimating the launch of an oscillator. A large increase leads to an increase in the load time, as well as the stability of recognition of this case. (in Kconfig). Changed from 1024 to 3000. --- components/esp32/Kconfig | 18 +++- components/esp32/clk.c | 36 +++---- components/soc/esp32/test/test_rtc_clk.c | 115 +++++++++++++++++++++-- 3 files changed, 139 insertions(+), 30 deletions(-) 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