Merge branch 'feature/32k_xtal' into 'master'

add support for 32k XTAL as RTC_SLOW_CLK source

- RTC_CNTL_SLOWCLK_FREQ define is removed; rtc_clk_slow_freq_get_hz
  function can be used instead to get an approximate RTC_SLOW_CLK
  frequency

- Clock calibration is performed at startup. The value is saved and used
  for timekeeping and when entering deep sleep.

- When using the 32k XTAL, startup code will wait for the oscillator to
  start up. This can be possibly optimized by starting a separate task
  to wait for oscillator startup, and performing clock switch in that
  task.

- Fix a bug that 32k XTAL would be disabled in rtc_clk_init.

- Fix a rounding error in rtc_clk_cal, which caused systematic frequency
  error.

- Fix an overflow bug which caused rtc_clk_cal to timeout early if the
  slow_clk_cycles argument would exceed certain value

- Improve 32k XTAL oscillator startup time by introducing bootstrapping
  code, which uses internal pullup/pulldown resistors on 32K_N/32K_P
  pins to set better initial conditions for the oscillator.

Ref TW11683.

Ref https://esp32.com/viewtopic.php?f=13&t=1570

Fixes https://github.com/espressif/esp-idf/issues/337.

See merge request !696
This commit is contained in:
Ivan Grokhotkov 2017-04-26 14:01:59 +08:00
commit 0fe765a977
16 changed files with 330 additions and 136 deletions

View file

@ -60,8 +60,6 @@ We arrive here after the bootloader finished loading the program from flash. The
flash cache is down and the app CPU is in reset. We do have a stack, so we can do the initialization in C.
*/
// TODO: make a nice header file for ROM functions instead of adding externs all over the place
extern void Cache_Flush(int);
void bootloader_main();
static void unpack_load_app(const esp_partition_pos_t *app_node);
@ -74,6 +72,7 @@ static void set_cache_and_start_app(uint32_t drom_addr,
uint32_t irom_size,
uint32_t entry_addr);
static void update_flash_config(const esp_image_header_t* pfhdr);
static void clock_configure(void);
static void uart_console_configure(void);
static void wdt_reset_check(void);
@ -238,15 +237,7 @@ static bool ota_select_valid(const esp_ota_select_entry_t *s)
void bootloader_main()
{
/* Set CPU to 80MHz. Keep other clocks unmodified. */
uart_tx_wait_idle(0);
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
clk_cfg.cpu_freq = RTC_CPU_FREQ_80M;
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
rtc_clk_init(clk_cfg);
clock_configure();
uart_console_configure();
wdt_reset_check();
ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER);
@ -704,6 +695,29 @@ void print_flash_info(const esp_image_header_t* phdr)
#endif
}
static void clock_configure(void)
{
/* Set CPU to 80MHz. Keep other clocks unmodified. */
uart_tx_wait_idle(0);
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
clk_cfg.cpu_freq = RTC_CPU_FREQ_80M;
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
rtc_clk_init(clk_cfg);
/* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable
* it here. Usually it needs some time to start up, so we amortize at least
* part of the start up time by enabling 32k XTAL early.
* App startup code will wait until the oscillator has started up.
*/
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
if (!rtc_clk_32k_enabled()) {
rtc_clk_32k_bootstrap();
}
#endif
}
static void uart_console_configure(void)
{
#if CONFIG_CONSOLE_UART_NONE

View file

@ -542,6 +542,23 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
bool "External 32kHz crystal"
endchoice
config ESP32_RTC_CLK_CAL_CYCLES
int "Number of cycles for RTC_SLOW_CLK calibration"
default 1024
range 0 125000
help
When the startup code initializes RTC_SLOW_CLK, it can perform
calibration by comparing the RTC_SLOW_CLK frequency with main XTAL
frequency. This option sets the number of RTC_SLOW_CLK cycles measured
by the calibration routine. Higher numbers increase calibration
precision, which may be important for applications which spend a lot of
time in deep sleep. Lower numbers reduce startup time.
When this option is set to 0, clock calibration will not be performed at
startup, and approximate clock frequencies will be assumed:
- 150000 Hz if internal RC oscillator is used as clock source
- 32768 Hz if the 32k crystal oscillator is used
config ESP32_DEEP_SLEEP_WAKEUP_DELAY
int "Extra delay in deep sleep wake stub (in us)"
default 0

131
components/esp32/clk.c Normal file
View file

@ -0,0 +1,131 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
/* Number of cycles to wait from the 32k XTAL oscillator to consider it running.
* Larger values increase startup delay. Smaller values may cause false positive
* detection (i.e. oscillator runs for a few cycles and then stops).
*/
#define XTAL_32K_DETECT_CYCLES 32
#define SLOW_CLK_CAL_CYCLES CONFIG_ESP32_RTC_CLK_CAL_CYCLES
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk);
static const char* TAG = "clk";
/*
* This function is not exposed as an API at this point,
* because FreeRTOS doesn't yet support dynamic changing of
* CPU frequency. Also we need to implement hooks for
* components which want to be notified of CPU frequency
* changes.
*/
void esp_clk_init(void)
{
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
rtc_init(cfg);
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
#else
select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
#endif
uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M;
switch(freq_mhz) {
case 240:
freq = RTC_CPU_FREQ_240M;
break;
case 160:
freq = RTC_CPU_FREQ_160M;
break;
default:
freq_mhz = 80;
/* no break */
case 80:
freq = RTC_CPU_FREQ_80M;
break;
}
// Wait for UART TX to finish, otherwise some UART output will be lost
// when switching APB frequency
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
rtc_clk_cpu_freq_set(freq);
}
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
{
extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU
extern uint32_t g_ticks_per_us_app; // same defined for APP CPU
g_ticks_per_us_pro = ticks_per_us;
g_ticks_per_us_app = ticks_per_us;
}
/* This is a cached value of RTC slow clock period; it is updated by
* the select_rtc_slow_clk function at start up. This cached value is used in
* other places, like time syscalls and deep sleep.
*/
static uint32_t s_rtc_slow_clk_cal = 0;
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);
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.
*/
s_rtc_slow_clk_cal = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
} else {
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
s_rtc_slow_clk_cal = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
}
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", s_rtc_slow_clk_cal);
}
uint32_t esp_clk_slowclk_cal_get()
{
return s_rtc_slow_clk_cal;
}

View file

@ -1,67 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "esp_attr.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
/*
* This function is not exposed as an API at this point,
* because FreeRTOS doesn't yet support dynamic changing of
* CPU frequency. Also we need to implement hooks for
* components which want to be notified of CPU frequency
* changes.
*/
void esp_set_cpu_freq(void)
{
uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M;
switch(freq_mhz) {
case 240:
freq = RTC_CPU_FREQ_240M;
break;
case 160:
freq = RTC_CPU_FREQ_160M;
break;
default:
freq_mhz = 80;
/* no break */
case 80:
freq = RTC_CPU_FREQ_80M;
break;
}
// Wait for UART TX to finish, otherwise some UART output will be lost
// when switching APB frequency
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
rtc_config_t cfg = RTC_CONFIG_DEFAULT();
rtc_init(cfg);
rtc_clk_cpu_freq_set(freq);
#if ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
#endif
}
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
{
extern uint32_t g_ticks_per_us_pro; // g_ticks_us defined in ROM for PRO CPU
extern uint32_t g_ticks_per_us_app; // same defined for APP CPU
g_ticks_per_us_pro = ticks_per_us;
g_ticks_per_us_app = ticks_per_us;
}

View file

@ -1,9 +1,9 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <string.h>
@ -60,6 +61,7 @@
#include "esp_panic.h"
#include "esp_core_dump.h"
#include "esp_app_trace.h"
#include "esp_clk.h"
#include "trax.h"
#define STRINGIFY(s) STRINGIFY2(s)
@ -202,7 +204,7 @@ void start_cpu0_default(void)
#endif
trax_start_trace(TRAX_DOWNCOUNT_WORDS);
#endif
esp_set_cpu_freq(); // set CPU frequency configured in menuconfig
esp_clk_init();
#ifndef CONFIG_CONSOLE_UART_NONE
uart_div_modify(CONFIG_CONSOLE_UART_NUM, (rtc_clk_apb_freq_get() << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
#endif

View file

@ -17,6 +17,7 @@
#include "esp_attr.h"
#include "esp_deep_sleep.h"
#include "esp_log.h"
#include "esp_clk.h"
#include "rom/cache.h"
#include "rom/rtc.h"
#include "rom/uart.h"
@ -183,13 +184,7 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us)
static void timer_wakeup_prepare()
{
// Do calibration if not using 32k XTAL
uint32_t period;
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
period = rtc_clk_cal(RTC_CAL_RTC_MUX, 128);
} else {
period = (uint32_t) ((1000000ULL /* us*Hz */ << RTC_CLK_CAL_FRACT) / 32768 /* Hz */);
}
uint32_t period = esp_clk_slowclk_cal_get();
uint64_t rtc_count_delta = rtc_time_us_to_slowclk(s_config.sleep_duration, period);
uint64_t cur_rtc_count = rtc_time_get();
rtc_sleep_set_wakeup_time(cur_rtc_count + rtc_count_delta);

View file

@ -0,0 +1,44 @@
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
/**
* @file esp_clk.h
*
* This file contains declarations of clock related functions.
* These functions are used in ESP-IDF components, but should not be considered
* to be part of public API.
*/
/**
* @brief Initialize clock-related settings
*
* Called from cpu_start.c, not intended to be called from other places.
* This function configures the CPU clock, RTC slow and fast clocks, and
* performs RTC slow clock calibration.
*/
void esp_clk_init(void);
/**
* @brief Get the cached calibration value of RTC slow clock
*
* The value is in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
*
* @return the calibration value obtained using rtc_clk_cal, at startup time
*/
uint32_t esp_clk_slowclk_cal_get();

View file

@ -317,7 +317,7 @@ static void esp_panic_wdt_start()
REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_RESET_SYSTEM);
// 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
// @ 115200 UART speed it will take more than 6 sec to print them out.
WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, RTC_CNTL_SLOWCLK_FREQ*7);
WRITE_PERI_REG(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 7);
REG_SET_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN);
WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0);
}

View file

@ -1,4 +1,4 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,7 +24,9 @@
#include <rom/rtc.h>
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_clk.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/frc_timer_reg.h"
#include "rom/ets_sys.h"
@ -44,15 +46,8 @@
#ifdef WITH_RTC
static uint64_t get_rtc_time_us()
{
SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) {
;
}
CLEAR_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M);
uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
uint64_t ticks = (high << 32) | low;
return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000); // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow
uint64_t ticks = rtc_time_get();
return (uint32_t) ((ticks * esp_clk_slowclk_cal_get()) >> RTC_CLK_CAL_FRACT);
}
#endif // WITH_RTC

View file

@ -73,15 +73,6 @@ static inline void cpu_configure_region_protection()
cpu_write_itlb(0x20000000, 0);
}
/**
* @brief Set CPU frequency to the value defined in menuconfig
*
* Called from cpu_start.c, not intended to be called from other places.
* This is a temporary function which will be replaced once dynamic
* CPU frequency changing is implemented.
*/
void esp_set_cpu_freq(void);
/**
* @brief Stall CPU using RTC controller
* @param cpu_id ID of the CPU to stall (0 = PRO, 1 = APP)

View file

@ -168,6 +168,15 @@ void rtc_clk_32k_enable(bool en);
*/
bool rtc_clk_32k_enabled();
/**
* @brief Enable 32k oscillator, configuring it for fast startup time.
* Note: to achieve higher frequency stability, rtc_clk_32k_enable function
* 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.
*/
void rtc_clk_32k_bootstrap();
/**
* @brief Enable or disable 8 MHz internal oscillator
*
@ -229,6 +238,20 @@ void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq);
*/
rtc_slow_freq_t rtc_clk_slow_freq_get();
/**
* @brief Get the approximate frequency of RTC_SLOW_CLK, in Hz
*
* - if RTC_SLOW_FREQ_RTC is selected, returns ~150000
* - if RTC_SLOW_FREQ_32K_XTAL is selected, returns 32768
* - if RTC_SLOW_FREQ_8MD256 is selected, returns ~33000
*
* rtc_clk_cal function can be used to get more precise value by comparing
* RTC_SLOW_CLK frequency to the frequency of main XTAL.
*
* @return RTC_SLOW_CLK frequency, in Hz
*/
uint32_t rtc_clk_slow_freq_get_hz();
/**
* @brief Select source for RTC_FAST_CLK
* @param fast_freq clock source (one of rtc_fast_freq_t values)

View file

@ -239,9 +239,6 @@
#define RTC_CNTL_TIME_VALID_V 0x1
#define RTC_CNTL_TIME_VALID_S 30
/* frequency of RTC slow clock, Hz */
#define RTC_CNTL_SLOWCLK_FREQ 150000
#define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10)
/* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */
/*description: RTC timer low 32 bits*/

View file

@ -32,6 +32,12 @@
#define MHZ (1000000)
/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */
#define RTC_FAST_CLK_FREQ_8M 8500000
#define RTC_SLOW_CLK_FREQ_150K 150000
#define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256)
#define RTC_SLOW_CLK_FREQ_32K 32768
static const char* TAG = "rtc_clk";
/* Various constants related to the analog internals of the chip.
@ -55,14 +61,21 @@ static const char* TAG = "rtc_clk";
#define XTAL_32K_DRES_VAL 3
#define XTAL_32K_DBIAS_VAL 0
#define XTAL_32K_BOOTSTRAP_DAC_VAL 3
#define XTAL_32K_BOOTSTRAP_DRES_VAL 3
#define XTAL_32K_BOOTSTRAP_DBIAS_VAL 0
#define XTAL_32K_BOOTSTRAP_TIME_US 7
/* Delays for various clock sources to be enabled/switched.
* All values are in microseconds.
* TODO: some of these are excessive, and should be reduced.
*/
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL 80
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K 80
#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K 160
#define DELAY_CPU_FREQ_SWITCH_TO_PLL 10
#define DELAY_PLL_DBIAS_RAISE 3
#define DELAY_PLL_ENABLE 80
#define DELAY_PLL_ENABLE_WITH_150K 80
#define DELAY_PLL_ENABLE_WITH_32K 160
#define DELAY_FAST_CLK_SWITCH 3
#define DELAY_SLOW_CLK_SWITCH 300
#define DELAY_8M_ENABLE 50
@ -73,22 +86,36 @@ static const char* TAG = "rtc_clk";
#define XTAL_FREQ_EST_CYCLES 10
static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
{
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, dac);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, dres);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, dbias);
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
}
void rtc_clk_32k_enable(bool enable)
{
if (enable) {
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, XTAL_32K_DAC_VAL);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, XTAL_32K_DRES_VAL);
REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, XTAL_32K_DBIAS_VAL);
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL);
} else {
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
}
}
void rtc_clk_32k_bootstrap()
{
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);
}
bool rtc_clk_32k_enabled()
{
return GET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K) != 0;
@ -172,6 +199,15 @@ rtc_slow_freq_t rtc_clk_slow_freq_get()
return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL);
}
uint32_t rtc_clk_slow_freq_get_hz()
{
switch(rtc_clk_slow_freq_get()) {
case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_150K;
case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K;
case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256;
}
return 0;
}
void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq)
{
@ -280,7 +316,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref);
I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
ets_delay_us(DELAY_PLL_ENABLE);
uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K;
ets_delay_us(delay_pll_en);
}
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
@ -291,7 +329,9 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
ets_update_cpu_frequency(xtal_freq);
ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_XTAL);
uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K;
ets_delay_us(delay_xtal_switch);
REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
@ -518,7 +558,7 @@ void rtc_clk_init(rtc_clk_config_t cfg)
/* Slow & fast clocks setup */
if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
rtc_clk_32k_enable(false);
rtc_clk_32k_enable(true);
}
if (cfg.fast_freq == RTC_FAST_FREQ_8M) {
bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256;

View file

@ -31,7 +31,14 @@
* once, and TIMG_RTC_CALI_RDY bit is set when counting is done. One-off mode is
* enabled using TIMG_RTC_CALI_START bit.
*/
uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
/**
* @brief Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio
* @param cal_clk which clock to calibrate
* @param slowclk_cycles number of slow clock cycles to count
* @return number of XTAL clock cycles within the given number of slow clock cycles
*/
static uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
{
/* Enable requested clock (150k clock is always on) */
if (cal_clk == RTC_CAL_32K_XTAL) {
@ -56,7 +63,7 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
} else {
expected_freq = 150000; /* 150k internal oscillator */
}
uint32_t us_time_estimate = slowclk_cycles * MHZ / expected_freq;
uint32_t us_time_estimate = (uint32_t) (((uint64_t) slowclk_cycles) * MHZ / expected_freq);
/* Start calibration */
CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
SET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START);
@ -83,8 +90,13 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
return 0;
}
uint64_t xtal_cycles = REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE);
uint64_t ratio_64 = (xtal_cycles << RTC_CLK_CAL_FRACT) / slowclk_cycles;
return REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE);
}
uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
{
uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles);
uint64_t ratio_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT)) / slowclk_cycles;
uint32_t ratio = (uint32_t)(ratio_64 & UINT32_MAX);
return ratio;
}
@ -92,8 +104,10 @@ uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
{
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
uint32_t ratio = rtc_clk_cal_ratio(cal_clk, slowclk_cycles);
uint32_t period = ratio / xtal_freq;
uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles);
uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles;
uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider;
uint32_t period = (uint32_t)(period_64 & UINT32_MAX);
return period;
}

View file

@ -27,6 +27,7 @@
#include "esp32/ulp.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "driver/rtc_io.h"
@ -321,15 +322,11 @@ TEST_CASE("ulp timer setting", "[ulp]")
uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff;
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
// compare the actual and expected numbers of iterations of ULP program
float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f;
float expected_period = (cycles_to_test[i] + 16) / (float) rtc_clk_slow_freq_get_hz() + 5 / 8e6f;
float error = 1.0f - counter * expected_period;
printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error);
// Should be within 15%
TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100);
// Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it
// Precisely by running calibration similar to the one done in deep sleep.
// This may cause the test to fail on some chips which have the slow clock frequency
// way off.
}
}

View file

@ -22,6 +22,7 @@
#include "driver/rtc_io.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc.h"
static RTC_DATA_ATTR struct timeval sleep_enter_time;
@ -286,7 +287,7 @@ static void start_ulp_temperature_monitoring()
assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
// Set ULP wakeup period
const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec;
const uint32_t sleep_cycles = rtc_clk_slow_freq_get_hz() / measurements_per_sec;
REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles);
// Start ULP