Explorar o código

Merge branch 'bugfix/select_rtc_slow_clk' into 'master'

esp32/clk: Fix starting rtc oscillator if it bad

See merge request idf/esp-idf!2215
Angus Gratton %!s(int64=7) %!d(string=hai) anos
pai
achega
662fe55996
Modificáronse 3 ficheiros con 139 adicións e 30 borrados
  1. 14 4
      components/esp32/Kconfig
  2. 20 16
      components/esp32/clk.c
  3. 105 10
      components/soc/esp32/test/test_rtc_clk.c

+ 14 - 4
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)"

+ 20 - 16
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);

+ 105 - 10
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