Skip to content

Commit f7df532

Browse files
bugfix/rtc_clk_32k_bootstrap: Fix starting 32k RTC
1. External 32kHz crystal is started for too long or it may not start at all. It is often observed at the first start. 2. At the first start, it is possible that the crystal did not start. And the recorded period was recorded as 0. Which led to a division error by zero during the transition to the deep sleep mode (Maybe somewhere else). 3. Added a unit test to test a new method of oscillation an external crystal. 4. Added a new method of oscillating of an external crystal. The legs of the crystal are fed with a 32 kHz frequency. The new method eliminates these errors. Added unit test: `\esp-idf\components\soc\esp32\test\test_rtc_clk.c`: `make TEST_COMPONENTS=soc` - 8 Test starting external RTC crystal. Will pass. `Bootstrap cycles for external 32kHz crystal` - is specified in the file Kconfig by default 100. QA tested a new method of oscillation the crystal on 25 boards. The supply of square waves on the crystal showed a 100% result in contrast to the previous method of launching the crystal. After the tests, the old method was deleted. Closes TW19143
1 parent 7594127 commit f7df532

File tree

7 files changed

+142
-38
lines changed

7 files changed

+142
-38
lines changed

components/bootloader_support/src/bootloader_clock.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void bootloader_clock_configure()
5555
*/
5656
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
5757
if (!rtc_clk_32k_enabled()) {
58-
rtc_clk_32k_bootstrap();
58+
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
5959
}
6060
#endif
6161
}

components/esp32/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,16 @@ config ESP32_RTC_CLK_CAL_CYCLES
700700
- 150000 Hz if internal RC oscillator is used as clock source
701701
- 32768 Hz if the 32k crystal oscillator is used
702702

703+
config ESP32_RTC_XTAL_BOOTSTRAP_CYCLES
704+
int "Bootstrap cycles for external 32kHz crystal"
705+
default 100
706+
range 0 32768
707+
help
708+
To reduce the startup time of an external RTC crystal,
709+
we bootstrap it with a 32kHz square wave for a fixed number of cycles.
710+
Setting 0 will disable bootstrapping (if disabled, the crystal may take
711+
longer to start up or fail to oscillate under some conditions).
712+
703713
config ESP32_DEEP_SLEEP_WAKEUP_DELAY
704714
int "Extra delay in deep sleep wake stub (in us)"
705715
default 2000

components/esp32/clk.c

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -127,44 +127,54 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
127127

128128
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
129129
{
130-
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
131-
/* 32k XTAL oscillator needs to be enabled and running before it can
132-
* be used. Hardware doesn't have a direct way of checking if the
133-
* oscillator is running. Here we use rtc_clk_cal function to count
134-
* the number of main XTAL cycles in the given number of 32k XTAL
135-
* oscillator cycles. If the 32k XTAL has not started up, calibration
136-
* will time out, returning 0.
137-
*/
138-
rtc_clk_32k_enable(true);
139-
uint32_t cal_val = 0;
140-
uint32_t wait = 0;
141-
// increment of 'wait' counter equivalent to 3 seconds
142-
const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * XTAL_32K_DETECT_CYCLES);
143-
ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up")
144-
do {
145-
++wait;
146-
cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES);
147-
if (wait % warning_timeout == 0) {
148-
ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up");
149-
}
150-
} while (cal_val == 0);
151-
ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait);
152-
}
153-
rtc_clk_slow_freq_set(slow_clk);
154-
uint32_t cal_val;
155-
if (SLOW_CLK_CAL_CYCLES > 0) {
156-
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
157-
* Improve calibration routine to wait until the frequency is stable.
158-
*/
159-
cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
160-
} else {
161-
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
162-
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
163-
}
130+
uint32_t cal_val = 0;
131+
do {
132+
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
133+
/* 32k XTAL oscillator needs to be enabled and running before it can
134+
* be used. Hardware doesn't have a direct way of checking if the
135+
* oscillator is running. Here we use rtc_clk_cal function to count
136+
* the number of main XTAL cycles in the given number of 32k XTAL
137+
* oscillator cycles. If the 32k XTAL has not started up, calibration
138+
* will time out, returning 0.
139+
*/
140+
uint32_t wait = 0;
141+
// increment of 'wait' counter equivalent to 3 seconds
142+
const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * XTAL_32K_DETECT_CYCLES);
143+
ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up")
144+
do {
145+
++wait;
146+
rtc_clk_32k_enable(true);
147+
cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES);
148+
if (wait % warning_timeout == 0) {
149+
ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up");
150+
}
151+
if(cal_val == 0){
152+
rtc_clk_32k_enable(false);
153+
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
154+
}
155+
} while (cal_val == 0);
156+
}
157+
rtc_clk_slow_freq_set(slow_clk);
158+
159+
if (SLOW_CLK_CAL_CYCLES > 0) {
160+
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
161+
* Improve calibration routine to wait until the frequency is stable.
162+
*/
163+
cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
164+
} else {
165+
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
166+
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
167+
}
168+
} while (cal_val == 0);
164169
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val);
165170
esp_clk_slowclk_cal_set(cal_val);
166171
}
167172

173+
void rtc_clk_select_rtc_slow_clk()
174+
{
175+
select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
176+
}
177+
168178
/* This function is not exposed as an API at this point.
169179
* All peripheral clocks are default enabled after chip is powered on.
170180
* This function disables some peripheral clocks when cpu starts.

components/esp32/esp_clk_internal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@ void esp_clk_init(void);
3737
* This function disables clock of useless peripherals when cpu starts.
3838
*/
3939
void esp_perip_clk_init(void);
40+
41+
/* Selects an external clock source (32 kHz) for RTC.
42+
* Only internal use in unit test.
43+
*/
44+
void rtc_clk_select_rtc_slow_clk();

components/soc/esp32/include/soc/rtc.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,11 @@ bool rtc_clk_32k_enabled();
185185
* must be called one the 32k XTAL oscillator has started up. This function
186186
* will initially disable the 32k XTAL oscillator, so it should not be called
187187
* when the system is using 32k XTAL as RTC_SLOW_CLK.
188+
*
189+
* @param cycle Number of 32kHz cycles to bootstrap external crystal.
190+
* If 0, no square wave will be used to bootstrap crystal oscillation.
188191
*/
189-
void rtc_clk_32k_bootstrap();
192+
void rtc_clk_32k_bootstrap(uint32_t cycle);
190193

191194
/**
192195
* @brief Enable or disable 8 MHz internal oscillator
@@ -604,7 +607,6 @@ rtc_vddsdio_config_t rtc_vddsdio_get_config();
604607
*/
605608
void rtc_vddsdio_set_config(rtc_vddsdio_config_t config);
606609

607-
608610
#ifdef __cplusplus
609611
}
610612
#endif

components/soc/esp32/rtc_clk.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "rom/ets_sys.h"
2020
#include "rom/rtc.h"
2121
#include "rom/uart.h"
22+
#include "rom/gpio.h"
2223
#include "soc/rtc.h"
2324
#include "soc/rtc_cntl_reg.h"
2425
#include "soc/rtc_io_reg.h"
@@ -122,11 +123,37 @@ void rtc_clk_32k_enable(bool enable)
122123
}
123124
}
124125

125-
void rtc_clk_32k_bootstrap()
126+
/* Helping external 32kHz crystal to start up.
127+
* External crystal connected to outputs GPIO32 GPIO33.
128+
* Forms N pulses with a frequency of about 32KHz on the outputs of the crystal.
129+
*/
130+
void rtc_clk_32k_bootstrap(uint32_t cycle)
126131
{
132+
if (cycle){
133+
const uint32_t pin_32 = 32;
134+
const uint32_t pin_33 = 33;
135+
const uint32_t mask_32 = (1 << (pin_32 - 32));
136+
const uint32_t mask_33 = (1 << (pin_33 - 32));
137+
138+
gpio_pad_select_gpio(pin_32);
139+
gpio_pad_select_gpio(pin_33);
140+
gpio_output_set_high(mask_32, mask_33, mask_32 | mask_33, 0);
141+
142+
const uint32_t delay_us = (1000000 / RTC_SLOW_CLK_FREQ_32K / 2);
143+
while(cycle){
144+
gpio_output_set_high(mask_32, mask_33, mask_32 | mask_33, 0);
145+
ets_delay_us(delay_us);
146+
gpio_output_set_high(mask_33, mask_32, mask_32 | mask_33, 0);
147+
ets_delay_us(delay_us);
148+
cycle--;
149+
}
150+
gpio_output_set_high(0, 0, 0, mask_32 | mask_33); // disable pins
151+
}
152+
127153
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
128154
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE);
129155
ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US);
156+
130157
rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL,
131158
XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL);
132159
}

components/soc/esp32/test/test_rtc_clk.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "freertos/FreeRTOS.h"
1313
#include "freertos/task.h"
1414
#include "freertos/semphr.h"
15+
#include "../esp_clk_internal.h"
16+
1517

1618

1719
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
@@ -131,4 +133,52 @@ TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]")
131133
test_clock_switching(rtc_clk_cpu_freq_set_fast);
132134
}
133135

136+
#define COUNT_TEST 10
137+
#define TIMEOUT_TEST_MS 50
138+
139+
void stop_rtc_external_quartz(){
140+
const uint8_t pin_32 = 32;
141+
const uint8_t pin_33 = 33;
142+
const uint8_t mask_32 = (1 << (pin_32 - 32));
143+
const uint8_t mask_33 = (1 << (pin_33 - 32));
144+
145+
rtc_clk_32k_enable(false);
146+
147+
gpio_pad_select_gpio(pin_32);
148+
gpio_pad_select_gpio(pin_33);
149+
gpio_output_set_high(0, mask_32 | mask_33, mask_32 | mask_33, 0);
150+
ets_delay_us(500000);
151+
gpio_output_set_high(0, 0, 0, mask_32 | mask_33); // disable pins
152+
}
134153

154+
TEST_CASE("Test starting external RTC quartz", "[rtc_clk]")
155+
{
156+
int i = 0, fail = 0;
157+
uint32_t start_time;
158+
uint32_t end_time;
159+
160+
stop_rtc_external_quartz();
161+
printf("Start test. Number of oscillation cycles = %d\n", CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
162+
while(i < COUNT_TEST){
163+
start_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ);
164+
i++;
165+
printf("attempt #%d/%d...", i, COUNT_TEST);
166+
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
167+
rtc_clk_select_rtc_slow_clk();
168+
end_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ);
169+
if((end_time - start_time) > TIMEOUT_TEST_MS){
170+
printf("FAIL\n");
171+
fail = 1;
172+
} else {
173+
printf("PASS\n");
174+
}
175+
stop_rtc_external_quartz();
176+
ets_delay_us(100000);
177+
}
178+
if (fail == 1){
179+
printf("Test failed\n");
180+
TEST_ASSERT(false);
181+
} else {
182+
printf("Test passed successfully\n");
183+
}
184+
}

0 commit comments

Comments
 (0)