Skip to content

Commit 887b6e2

Browse files
committed
Merge branch 'bugfix/rtc_clk_32k_bootstrap' into 'master'
bugfix/rtc_clk_32k_bootstrap: Fix starting 32k RTC See merge request idf/esp-idf!2085
2 parents 8478823 + f7df532 commit 887b6e2

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)