Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ check-configs = [
{ features = ["unstable", "rt"], env = { ESP_HAL_CONFIG_PLACE_ANON_IN_RAM = "true", ESP_HAL_CONFIG_PLACE_RMT_DRIVER_IN_RAM = "true", ESP_HAL_CONFIG_PLACE_SPI_MASTER_DRIVER_IN_RAM = "true", ESP_HAL_CONFIG_CLEAR_CRYPTO_SECRETS = "false" } },
{ features = ["unstable", "rt"], env = { ESP_HAL_CONFIG_ENABLE_PMP = "false" }, if = 'riscv && !esp32c2 && !esp32c3' },
{ features = ["unstable", "rt"], env = { ESP_HAL_CONFIG_INIT_STACK_PTR_RANGE_CHECK = "false" } },
{ features = ["unstable", "rt"], env = { ESP_HAL_CONFIG_USE_XTAL32K = "true" }, if = 'soc_has_xtal32k_pads' },
{ features = ["unstable", "rt", "__usb_otg"], if = 'usb_otg_driver_supported' },
{ features = ["unstable", "rt", "__bluetooth"], if = 'bt_driver_supported' },
{ features = ["unstable", "rt", "__sdmmc"], if = 'sdmmc_driver_supported' },
Expand Down
10 changes: 10 additions & 0 deletions esp-hal/esp_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ options:
default:
- value: true

- name: use-xtal32k
description: "Enable the external 32 kHz crystal (XTAL32K). When enabled, LP/RTC slow clock
defaults to the crystal, XTAL32K is powered at init, and the dedicated crystal
GPIO pins are removed from `Peripherals`. Do not use those pads for GPIO, ADC, LP,
or any other function (including via `steal` / `AnyPin`) while the crystal is active.
When disabled (default), XTAL32K stays off so those pins can be used as hi-Z GPIO."
default:
- value: false

@bugadani bugadani Aug 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this goes against "esp-config does not change public API" so this will need @MabezDev 's approval. This could turn into a cargo feature if the API change is a problem.

active: "soc_has_xtal32k_pads"

- name: enable-pmp
description: "Protects additional memory regions via Physical Memory Protection (PMP). (Only RISC-V other than ESP32-C2/C3)"
default:
Expand Down
6 changes: 4 additions & 2 deletions esp-hal/src/peripherals/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ for_each_peripheral! {
};

// Define the Peripherals struct
(singletons $( ($name:ident $(($unstable:ident))?) ),*) => {
(singletons $( ( $(#[$cfg:meta])* $name:ident $(($unstable:ident))?) ),*) => {
// We need a way to ignore the "unstable" marker, but macros can't generate attributes or struct fields.
// The solution is printing an empty doc comment.
macro_rules! ignore { ($any:tt) => {""} }
Expand Down Expand Up @@ -217,6 +217,7 @@ for_each_peripheral! {
// #[cfg(not(feature = "unstable"))]
// pub(crate) PERI: PERI<'static>,
// ```
$(#[$cfg])*
#[doc = concat!("The ", stringify!($name), " peripheral.")]
$(
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
Expand All @@ -229,7 +230,7 @@ for_each_peripheral! {
pub $name: $name<'static>,

$(
#[doc = concat!("The ", stringify!($unstable_name), " peripheral.")]
#[doc = concat!("The ", stringify!($name), " peripheral.")]
#[doc = "**This API is marked as unstable** and is only available when the `unstable`
crate feature is enabled. This comes with no stability guarantees, and could be changed
or removed at any time."]
Expand Down Expand Up @@ -268,6 +269,7 @@ for_each_peripheral! {
unsafe {
Self {
$(
$(#[$cfg])*
$name: $name::steal(),
)*
}
Expand Down
3 changes: 3 additions & 0 deletions esp-hal/src/rtc_cntl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ pub mod sleep;
#[cfg_attr(esp32s31, path = "rtc/esp32s31.rs")]
pub(crate) mod rtc;

#[cfg(soc_has_xtal32k_pads)]
pub(crate) mod xtal32k;

cfg_select! {
esp32s31 => {
use crate::peripherals::{LP_SYS as LP_AON, LP_WDT};
Expand Down
3 changes: 2 additions & 1 deletion esp-hal/src/rtc_cntl/rtc/esp32c5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use strum::FromRepr;

use crate::{
peripherals::{LP_CLKRST, MODEM_LPCON, MODEM_SYSCON, PCR, PMU},
rtc_cntl::xtal32k,
soc::{
clocks::{ClockConfig, LpSlowClkConfig},
regi2c,
Expand Down Expand Up @@ -900,7 +901,7 @@ impl LpSystemInit {
dig_power.set_mem_dslp(false);

let mut clk_power = LpClkPower::default();
clk_power.set_xpd_xtal32k(true);
clk_power.set_xpd_xtal32k(xtal32k::use_xtal32k());
// ESP32-C5 disables RC32K in LP active by default.
clk_power.set_xpd_rc32k(false);
clk_power.set_xpd_fosc(true);
Expand Down
3 changes: 2 additions & 1 deletion esp-hal/src/rtc_cntl/rtc/esp32c6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use strum::FromRepr;
use crate::{
clock::ClockConfig,
peripherals::{LP_CLKRST, MODEM_LPCON, MODEM_SYSCON, PCR, PMU},
rtc_cntl::xtal32k,
soc::{clocks::LpSlowClkConfig, regi2c},
};

Expand Down Expand Up @@ -894,7 +895,7 @@ impl LpSystemInit {
dig_power.set_mem_dslp(false);

let mut clk_power = LpClkPower::default();
clk_power.set_xpd_xtal32k(true);
clk_power.set_xpd_xtal32k(xtal32k::use_xtal32k());
clk_power.set_xpd_rc32k(true);
clk_power.set_xpd_fosc(true);

Expand Down
3 changes: 2 additions & 1 deletion esp-hal/src/rtc_cntl/rtc/esp32c61.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use strum::FromRepr;

use crate::{
peripherals::{LP_CLKRST, MODEM_LPCON, MODEM_SYSCON, PCR, PMU},
rtc_cntl::xtal32k,
soc::{
clocks::{ClockConfig, LpSlowClkConfig},
regi2c,
Expand Down Expand Up @@ -900,7 +901,7 @@ impl LpSystemInit {
dig_power.set_mem_dslp(false);

let mut clk_power = LpClkPower::default();
clk_power.set_xpd_xtal32k(true);
clk_power.set_xpd_xtal32k(xtal32k::use_xtal32k());
// ESP32-C61 disables RC32K in LP active by default.
clk_power.set_xpd_rc32k(false);
clk_power.set_xpd_fosc(true);
Expand Down
5 changes: 4 additions & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
Rtc,
rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower},
sleep::{Ext1WakeupSource, WakeTriggers, pmu_common::SleepTimeConfig},
xtal32k,
},
soc::clocks::{self, ClockTree, HpRootClkConfig, LpSlowClkConfig},
};
Expand Down Expand Up @@ -234,7 +235,9 @@ impl PowerSleepConfig {

self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());

self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
self.lp_sys_active
.clk_power
.set_xpd_xtal32k(xtal32k::use_xtal32k());
self.lp_sys_active.clk_power.set_xpd_rc32k(true);
self.lp_sys_active.clk_power.set_xpd_fosc(true);

Expand Down
5 changes: 4 additions & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
Rtc,
rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower},
sleep::{Ext1WakeupSource, WakeTriggers, pmu_common::SleepTimeConfig},
xtal32k,
},
soc::clocks::{self, ClockTree, LpSlowClkConfig, SocRootClkConfig},
};
Expand Down Expand Up @@ -248,7 +249,9 @@ impl PowerSleepConfig {

self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());

self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
self.lp_sys_active
.clk_power
.set_xpd_xtal32k(xtal32k::use_xtal32k());
self.lp_sys_active.clk_power.set_xpd_rc32k(true);
self.lp_sys_active.clk_power.set_xpd_fosc(true);

Expand Down
5 changes: 4 additions & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c61.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
Rtc,
rtc::{HpAnalog, HpSysCntlReg, HpSysPower, LpAnalog, LpSysPower},
sleep::{Ext1WakeupSource, WakeTriggers, pmu_common::SleepTimeConfig},
xtal32k,
},
soc::clocks::{self, ClockTree, HpRootClkConfig, LpSlowClkConfig},
};
Expand Down Expand Up @@ -236,7 +237,9 @@ impl PowerSleepConfig {

self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());

self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
self.lp_sys_active
.clk_power
.set_xpd_xtal32k(xtal32k::use_xtal32k());
self.lp_sys_active.clk_power.set_xpd_rc32k(true);
self.lp_sys_active.clk_power.set_xpd_fosc(true);

Expand Down
5 changes: 4 additions & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32h2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
Rtc,
rtc::{HpSysCntlReg, HpSysPower, LpSysPower},
sleep::{Ext1WakeupSource, WakeTriggers, pmu_common::SleepTimeConfig},
xtal32k,
},
soc::clocks::{self, ClockTree, CpuClkConfig, HpRootClkConfig, LpSlowClkConfig},
};
Expand Down Expand Up @@ -138,7 +139,9 @@ impl PowerSleepConfig {

self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not());

self.lp_sys_active.clk_power.set_xpd_xtal32k(true);
self.lp_sys_active
.clk_power
.set_xpd_xtal32k(xtal32k::use_xtal32k());
self.lp_sys_active.clk_power.set_xpd_fosc(true);

self.lp_sys_sleep.dig_power.set_mem_dslp(true);
Expand Down
31 changes: 31 additions & 0 deletions esp-hal/src/rtc_cntl/xtal32k.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Build-time configuration of the external 32 kHz crystal (XTAL32K).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very arbitrary folder for this... maybe move to soc

//!
//! The crystal is kept powered down unless `ESP_HAL_CONFIG_USE_XTAL32K` is set. Powering it up
//! drives the dedicated crystal pads, which stops those pins from reaching a true high-impedance
//! state when an application uses them as GPIO.

/// Whether esp-hal should power and use the external 32 kHz crystal.
#[inline(always)]
pub(crate) const fn use_xtal32k() -> bool {
cfg!(use_xtal32k)
}

/// The LP slow clock source to select by default.
#[cfg(soc_has_clock_node_lp_slow_clk)]
pub(crate) const fn default_lp_slow_clk() -> crate::soc::clocks::LpSlowClkConfig {
if use_xtal32k() {
crate::soc::clocks::LpSlowClkConfig::Xtal32k
} else {
crate::soc::clocks::LpSlowClkConfig::RcSlow
}
}

/// The RTC slow clock source to select by default.
#[cfg(soc_has_clock_node_rtc_slow_clk)]
pub(crate) const fn default_rtc_slow_clk() -> crate::soc::clocks::RtcSlowClkConfig {
if use_xtal32k() {
crate::soc::clocks::RtcSlowClkConfig::Xtal32k
} else {
crate::soc::clocks::RtcSlowClkConfig::RcSlow
}
}
8 changes: 4 additions & 4 deletions esp-hal/src/soc/esp32/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
clock::RtcClock,
efuse::VOL_LEVEL_HP_INV,
peripherals::{APB_CTRL, LPWR, RMT, RTC_IO, SYSTEM, TIMG0},
rtc_cntl::Rtc,
rtc_cntl::{Rtc, xtal32k},
soc::regi2c,
time::Rate,
};
Expand Down Expand Up @@ -56,7 +56,7 @@ impl CpuClock {
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_4)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
Expand All @@ -67,7 +67,7 @@ impl CpuClock {
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_2)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
Expand All @@ -78,7 +78,7 @@ impl CpuClock {
cpu_pll_div: Some(CpuPllDivConfig::new(CpuPllDivDivisor::_2)),
syscon_pre_div: None,
cpu_clk: Some(CpuClkConfig::Pll),
rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow),
rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()),
rtc_fast_clk: Some(RtcFastClkConfig::Rc),
timg_calibration_clock: None,
};
Expand Down
7 changes: 4 additions & 3 deletions esp-hal/src/soc/esp32c5/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

use crate::{
peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU},
rtc_cntl::xtal32k,
soc::regi2c,
};

Expand Down Expand Up @@ -50,7 +51,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(3)), // 40MHz - cannot exceed XTAL_CLK
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFast),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
crypto_clk: Some(CryptoClkConfig::PllF480m),
timg_calibration_clock: None,
};
Expand All @@ -61,7 +62,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(3)), // 40MHz - cannot exceed XTAL_CLK
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFast),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
crypto_clk: Some(CryptoClkConfig::PllF480m),
timg_calibration_clock: None,
};
Expand All @@ -72,7 +73,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(5)), // 40MHz - cannot exceed XTAL_CLK
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFast),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
crypto_clk: Some(CryptoClkConfig::PllF480m),
timg_calibration_clock: None,
};
Expand Down
5 changes: 3 additions & 2 deletions esp-hal/src/soc/esp32c6/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use crate::{
peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU, TIMG0},
rtc_cntl::xtal32k,
soc::regi2c,
};

Expand Down Expand Up @@ -54,7 +55,7 @@ impl CpuClock {
apb_clk: Some(ApbClkConfig::new(ApbClkDivisor::_0)),
ledc_sclk: Some(LedcSclkConfig::PllF80m),
lp_fast_clk: Some(LpFastClkConfig::RcFastClk),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
timg_calibration_clock: None,
};
const PRESET_160: ClockConfig = ClockConfig {
Expand All @@ -70,7 +71,7 @@ impl CpuClock {
apb_clk: Some(ApbClkConfig::new(ApbClkDivisor::_0)),
ledc_sclk: Some(LedcSclkConfig::PllF80m),
lp_fast_clk: Some(LpFastClkConfig::RcFastClk),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
timg_calibration_clock: None,
};
}
Expand Down
5 changes: 3 additions & 2 deletions esp-hal/src/soc/esp32c61/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use crate::{
peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU},
rtc_cntl::xtal32k,
soc::regi2c,
};

Expand Down Expand Up @@ -45,7 +46,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(3)),
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFast),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
timg_calibration_clock: None,
};
const PRESET_160: ClockConfig = ClockConfig {
Expand All @@ -55,7 +56,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(3)),
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFast),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
timg_calibration_clock: None,
};
}
Expand Down
3 changes: 2 additions & 1 deletion esp-hal/src/soc/esp32h2/clocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use crate::{
peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU, TIMG0},
rtc_cntl::xtal32k,
soc::regi2c,
};

Expand Down Expand Up @@ -43,7 +44,7 @@ impl CpuClock {
ahb_clk: Some(AhbClkConfig::new(0)),
apb_clk: Some(ApbClkConfig::new(0)),
lp_fast_clk: Some(LpFastClkConfig::RcFastClk),
lp_slow_clk: Some(LpSlowClkConfig::RcSlow),
lp_slow_clk: Some(xtal32k::default_lp_slow_clk()),
timg_calibration_clock: None,
};
}
Expand Down
Loading
Loading