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
This commit is contained in:
commit
887b6e2925
7 changed files with 142 additions and 38 deletions
|
@ -55,7 +55,7 @@ void bootloader_clock_configure()
|
|||
*/
|
||||
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
|
||||
if (!rtc_clk_32k_enabled()) {
|
||||
rtc_clk_32k_bootstrap();
|
||||
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -700,6 +700,16 @@ config ESP32_RTC_CLK_CAL_CYCLES
|
|||
- 150000 Hz if internal RC oscillator is used as clock source
|
||||
- 32768 Hz if the 32k crystal oscillator is used
|
||||
|
||||
config ESP32_RTC_XTAL_BOOTSTRAP_CYCLES
|
||||
int "Bootstrap cycles for external 32kHz crystal"
|
||||
default 100
|
||||
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).
|
||||
|
||||
config ESP32_DEEP_SLEEP_WAKEUP_DELAY
|
||||
int "Extra delay in deep sleep wake stub (in us)"
|
||||
default 2000
|
||||
|
|
|
@ -127,44 +127,54 @@ void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
|
|||
|
||||
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
|
||||
{
|
||||
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
|
||||
/* 32k XTAL oscillator needs to be enabled and running before it can
|
||||
* be used. Hardware doesn't have a direct way of checking if the
|
||||
* oscillator is running. Here we use rtc_clk_cal function to count
|
||||
* the number of main XTAL cycles in the given number of 32k XTAL
|
||||
* oscillator cycles. If the 32k XTAL has not started up, calibration
|
||||
* will time out, returning 0.
|
||||
*/
|
||||
rtc_clk_32k_enable(true);
|
||||
uint32_t cal_val = 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;
|
||||
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");
|
||||
}
|
||||
} while (cal_val == 0);
|
||||
ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait);
|
||||
}
|
||||
rtc_clk_slow_freq_set(slow_clk);
|
||||
uint32_t cal_val;
|
||||
if (SLOW_CLK_CAL_CYCLES > 0) {
|
||||
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
|
||||
* Improve calibration routine to wait until the frequency is stable.
|
||||
*/
|
||||
cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
|
||||
} else {
|
||||
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
|
||||
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
|
||||
}
|
||||
uint32_t cal_val = 0;
|
||||
do {
|
||||
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
|
||||
/* 32k XTAL oscillator needs to be enabled and running before it can
|
||||
* be used. Hardware doesn't have a direct way of checking if the
|
||||
* oscillator is running. Here we use rtc_clk_cal function to count
|
||||
* the number of main XTAL cycles in the given number of 32k XTAL
|
||||
* 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_slow_freq_set(slow_clk);
|
||||
|
||||
if (SLOW_CLK_CAL_CYCLES > 0) {
|
||||
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
|
||||
* Improve calibration routine to wait until the frequency is stable.
|
||||
*/
|
||||
cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
|
||||
} else {
|
||||
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
|
||||
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
|
||||
}
|
||||
} while (cal_val == 0);
|
||||
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val);
|
||||
esp_clk_slowclk_cal_set(cal_val);
|
||||
}
|
||||
|
||||
void rtc_clk_select_rtc_slow_clk()
|
||||
{
|
||||
select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
|
||||
}
|
||||
|
||||
/* This function is not exposed as an API at this point.
|
||||
* All peripheral clocks are default enabled after chip is powered on.
|
||||
* This function disables some peripheral clocks when cpu starts.
|
||||
|
|
|
@ -37,3 +37,8 @@ void esp_clk_init(void);
|
|||
* This function disables clock of useless peripherals when cpu starts.
|
||||
*/
|
||||
void esp_perip_clk_init(void);
|
||||
|
||||
/* Selects an external clock source (32 kHz) for RTC.
|
||||
* Only internal use in unit test.
|
||||
*/
|
||||
void rtc_clk_select_rtc_slow_clk();
|
||||
|
|
|
@ -185,8 +185,11 @@ bool rtc_clk_32k_enabled();
|
|||
* must be called one the 32k XTAL oscillator has started up. This function
|
||||
* will initially disable the 32k XTAL oscillator, so it should not be called
|
||||
* when the system is using 32k XTAL as RTC_SLOW_CLK.
|
||||
*
|
||||
* @param cycle Number of 32kHz cycles to bootstrap external crystal.
|
||||
* If 0, no square wave will be used to bootstrap crystal oscillation.
|
||||
*/
|
||||
void rtc_clk_32k_bootstrap();
|
||||
void rtc_clk_32k_bootstrap(uint32_t cycle);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable 8 MHz internal oscillator
|
||||
|
@ -604,7 +607,6 @@ rtc_vddsdio_config_t rtc_vddsdio_get_config();
|
|||
*/
|
||||
void rtc_vddsdio_set_config(rtc_vddsdio_config_t config);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "rom/ets_sys.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "rom/uart.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
|
@ -122,11 +123,37 @@ void rtc_clk_32k_enable(bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
void rtc_clk_32k_bootstrap()
|
||||
/* Helping external 32kHz crystal to start up.
|
||||
* External crystal connected to outputs GPIO32 GPIO33.
|
||||
* Forms N pulses with a frequency of about 32KHz on the outputs of the crystal.
|
||||
*/
|
||||
void rtc_clk_32k_bootstrap(uint32_t cycle)
|
||||
{
|
||||
if (cycle){
|
||||
const uint32_t pin_32 = 32;
|
||||
const uint32_t pin_33 = 33;
|
||||
const uint32_t mask_32 = (1 << (pin_32 - 32));
|
||||
const uint32_t mask_33 = (1 << (pin_33 - 32));
|
||||
|
||||
gpio_pad_select_gpio(pin_32);
|
||||
gpio_pad_select_gpio(pin_33);
|
||||
gpio_output_set_high(mask_32, mask_33, mask_32 | mask_33, 0);
|
||||
|
||||
const uint32_t delay_us = (1000000 / RTC_SLOW_CLK_FREQ_32K / 2);
|
||||
while(cycle){
|
||||
gpio_output_set_high(mask_32, mask_33, mask_32 | mask_33, 0);
|
||||
ets_delay_us(delay_us);
|
||||
gpio_output_set_high(mask_33, mask_32, mask_32 | mask_33, 0);
|
||||
ets_delay_us(delay_us);
|
||||
cycle--;
|
||||
}
|
||||
gpio_output_set_high(0, 0, 0, mask_32 | mask_33); // disable pins
|
||||
}
|
||||
|
||||
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
|
||||
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE);
|
||||
ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US);
|
||||
|
||||
rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL,
|
||||
XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "../esp_clk_internal.h"
|
||||
|
||||
|
||||
|
||||
#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]")
|
|||
test_clock_switching(rtc_clk_cpu_freq_set_fast);
|
||||
}
|
||||
|
||||
#define COUNT_TEST 10
|
||||
#define TIMEOUT_TEST_MS 50
|
||||
|
||||
void stop_rtc_external_quartz(){
|
||||
const uint8_t pin_32 = 32;
|
||||
const uint8_t pin_33 = 33;
|
||||
const uint8_t mask_32 = (1 << (pin_32 - 32));
|
||||
const uint8_t mask_33 = (1 << (pin_33 - 32));
|
||||
|
||||
rtc_clk_32k_enable(false);
|
||||
|
||||
gpio_pad_select_gpio(pin_32);
|
||||
gpio_pad_select_gpio(pin_33);
|
||||
gpio_output_set_high(0, mask_32 | mask_33, mask_32 | mask_33, 0);
|
||||
ets_delay_us(500000);
|
||||
gpio_output_set_high(0, 0, 0, mask_32 | mask_33); // disable pins
|
||||
}
|
||||
|
||||
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);
|
||||
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_select_rtc_slow_clk();
|
||||
end_time = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ);
|
||||
if((end_time - start_time) > TIMEOUT_TEST_MS){
|
||||
printf("FAIL\n");
|
||||
fail = 1;
|
||||
} else {
|
||||
printf("PASS\n");
|
||||
}
|
||||
stop_rtc_external_quartz();
|
||||
ets_delay_us(100000);
|
||||
}
|
||||
if (fail == 1){
|
||||
printf("Test failed\n");
|
||||
TEST_ASSERT(false);
|
||||
} else {
|
||||
printf("Test passed successfully\n");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue