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:
Ivan Grokhotkov 2018-03-21 18:38:49 +08:00
commit 887b6e2925
7 changed files with 142 additions and 38 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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.

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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");
}
}