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
This commit is contained in:
Konstantin Kondrashov 2018-03-19 13:05:32 +05:00
parent 7594127ca3
commit f7df532ec0
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");
}
}