Skip to content

Commit 29a3e39

Browse files
petuzkbugadaniJurajSadel
authored
Add light/deep sleep support for H2 (#4587)
* MVP for light/deep sleep with timer wakeup The design is based on C6 implementation of esp-hal, and the logic is based on H2 implementation of esp-idf. It is worth noting that esp-idf implementation writes to a lot of registers which are not documented in H2 Technical Reference Manual -- these writes are not added here, which does not break functionality. Both light sleep and deep sleep seem to work in an example app with BT connection, with wakeup on timer. No other wakeup source is currently supported. Some other limitations: * clock settings are not preserved through light sleep * peripheral clocks are not disabled in light sleep * Add changelog entry * Address lint issues * Update esp-hal/CHANGELOG.md Co-authored-by: Juraj Sadel <[email protected]> * Enable the sleep_timer QA test --------- Co-authored-by: Dániel Buga <[email protected]> Co-authored-by: Juraj Sadel <[email protected]>
1 parent cd8addb commit 29a3e39

File tree

7 files changed

+875
-13
lines changed

7 files changed

+875
-13
lines changed

esp-hal/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- Added blocking `send_break`, `wait_for_break` and `wait_for_break_with_timeout` for sending and detecting software breaks with the UART driver (#4284)
1515
- Added support for `RxBreakDetected` interrupt and `wait_for_break_async` for detecting software breaks asynchronously to the UART driver (#4284)
1616
- Unsafely expose GPIO pins that are only available on certain chip/module variants (#4520)
17+
- ESP32-H2: light sleep and deep sleep support with timer wakeup source (#4587)
1718

1819
### Changed
1920
- RMT: `SingleShotTxTransaction` has been renamed to `TxTransaction`. (#4302)

esp-hal/src/clock/clocks_ll/esp32h2.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,9 @@ pub(crate) fn esp32h2_rtc_update_to_xtal(freq: XtalClock, div: u8) {
124124
crate::rom::ets_update_cpu_frequency_rom(freq.mhz());
125125
// Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0)
126126
// first.
127+
clk_ll_cpu_set_divider(div as u32);
127128
clk_ll_ahb_set_divider(div as u32);
128129

129-
PCR::regs()
130-
.cpu_freq_conf()
131-
.modify(|_, w| unsafe { w.cpu_div_num().bits(div - 1) });
132130
// Switch clock source
133131
PCR::regs()
134132
.sysclk_conf()

esp-hal/src/rtc_cntl/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
pub use self::rtc::SocResetReason;
114114
#[cfg(not(esp32))]
115115
use crate::efuse::Efuse;
116-
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
116+
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2, esp32h2))]
117117
use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers};
118118
use crate::{
119119
clock::{Clock, RtcClock},
@@ -123,7 +123,7 @@ use crate::{
123123
time::Duration,
124124
};
125125
// only include sleep where it's been implemented
126-
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
126+
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2, esp32h2))]
127127
pub mod sleep;
128128

129129
#[cfg_attr(esp32, path = "rtc/esp32.rs")]
@@ -403,23 +403,23 @@ impl<'d> Rtc<'d> {
403403
///
404404
/// You can use the [`#[esp_hal::ram(persistent)]`][procmacros::ram]
405405
/// attribute to persist a variable though deep sleep.
406-
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
406+
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2, esp32h2))]
407407
pub fn sleep_deep(&mut self, wake_sources: &[&dyn WakeSource]) -> ! {
408408
let config = RtcSleepConfig::deep();
409409
self.sleep(&config, wake_sources);
410410
unreachable!();
411411
}
412412

413413
/// Enter light sleep and wake with the provided `wake_sources`.
414-
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
414+
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2, esp32h2))]
415415
pub fn sleep_light(&mut self, wake_sources: &[&dyn WakeSource]) {
416416
let config = RtcSleepConfig::default();
417417
self.sleep(&config, wake_sources);
418418
}
419419

420420
/// Enter sleep with the provided `config` and wake with the provided
421421
/// `wake_sources`.
422-
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))]
422+
#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2, esp32h2))]
423423
pub fn sleep(&mut self, config: &RtcSleepConfig, wake_sources: &[&dyn WakeSource]) {
424424
let mut config = *config;
425425
let mut wakeup_triggers = WakeTriggers::default();

esp-hal/src/rtc_cntl/rtc/esp32h2.rs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
use strum::FromRepr;
22

33
use crate::{
4-
clock::{RtcClock, RtcFastClock, RtcSlowClock, clocks_ll::regi2c_write_mask},
4+
clock::{
5+
RtcClock,
6+
RtcFastClock,
7+
RtcSlowClock,
8+
XtalClock,
9+
clocks_ll::{esp32h2_rtc_update_to_xtal, regi2c_write_mask},
10+
},
511
peripherals::{LP_AON, PMU},
612
rtc_cntl::RtcCalSel,
713
};
@@ -37,6 +43,13 @@ const I2C_PMU_OR_XPD_TRX: u8 = 15;
3743
const I2C_PMU_OR_XPD_TRX_MSB: u8 = 2;
3844
const I2C_PMU_OR_XPD_TRX_LSB: u8 = 2;
3945

46+
const I2C_BIAS: u8 = 0x6a;
47+
const I2C_BIAS_HOSTID: u8 = 0;
48+
49+
const I2C_BIAS_DREG_0P8: u8 = 0;
50+
const I2C_BIAS_DREG_0P8_MSB: u8 = 7;
51+
const I2C_BIAS_DREG_0P8_LSB: u8 = 4;
52+
4053
pub(crate) fn init() {
4154
// * No peripheral reg i2c power up required on the target */
4255
regi2c_write_mask(
@@ -95,6 +108,14 @@ pub(crate) fn init() {
95108
I2C_PMU_OR_XPD_TRX_LSB,
96109
0,
97110
);
111+
regi2c_write_mask(
112+
I2C_BIAS,
113+
I2C_BIAS_HOSTID,
114+
I2C_BIAS_DREG_0P8,
115+
I2C_BIAS_DREG_0P8_MSB,
116+
I2C_BIAS_DREG_0P8_LSB,
117+
8,
118+
);
98119

99120
let pmu = PMU::regs();
100121
unsafe {
@@ -110,6 +131,43 @@ pub(crate) fn init() {
110131
pmu.hp_sleep_lp_regulator0()
111132
.modify(|_, w| w.hp_sleep_lp_regulator_dbias().bits(26));
112133

134+
pmu.hp_sleep_dig_power().modify(|_, w| {
135+
w.hp_sleep_vdd_spi_pd_en()
136+
.set_bit()
137+
.hp_sleep_pd_hp_wifi_pd_en()
138+
.set_bit()
139+
.hp_sleep_pd_hp_cpu_pd_en()
140+
.set_bit()
141+
.hp_sleep_pd_top_pd_en()
142+
.set_bit()
143+
});
144+
145+
pmu.hp_active_hp_ck_power().modify(|_, w| {
146+
w.hp_active_xpd_bbpll()
147+
.set_bit()
148+
.hp_active_xpd_bb_i2c()
149+
.set_bit()
150+
.hp_active_xpd_bbpll_i2c()
151+
.set_bit()
152+
});
153+
154+
pmu.hp_active_sysclk().modify(|_, w| {
155+
w.hp_active_icg_sys_clock_en()
156+
.set_bit()
157+
.hp_active_sys_clk_slp_sel()
158+
.clear_bit()
159+
.hp_active_icg_slp_sel()
160+
.clear_bit()
161+
});
162+
pmu.hp_sleep_sysclk().modify(|_, w| {
163+
w.hp_sleep_icg_sys_clock_en()
164+
.clear_bit()
165+
.hp_sleep_sys_clk_slp_sel()
166+
.set_bit()
167+
.hp_sleep_icg_slp_sel()
168+
.set_bit()
169+
});
170+
113171
pmu.slp_wakeup_cntl5()
114172
.modify(|_, w| w.lp_ana_wait_target().bits(15));
115173
pmu.slp_wakeup_cntl7()
@@ -185,3 +243,85 @@ pub enum SocResetReason {
185243
/// Glitch on power resets the digital core
186244
CorePwrGlitch = 0x17,
187245
}
246+
247+
bitfield::bitfield! {
248+
/// Representation of `PMU_HP_{ACTIVE,SLEEP}_DIG_POWER_REG` registers.
249+
#[derive(Clone, Copy, Default)]
250+
pub struct HpDigPower(u32);
251+
252+
pub bool, vdd_spi_pd_en, set_vdd_spi_pd_en: 21;
253+
pub bool, mem_dslp , set_mem_dslp : 22;
254+
pub bool, modem_pd_en , set_modem_pd_en : 27;
255+
pub bool, cpu_pd_en , set_cpu_pd_en : 29;
256+
pub bool, top_pd_en , set_top_pd_en : 31;
257+
}
258+
259+
bitfield::bitfield! {
260+
/// Representation of `PMU_HP_{ACTIVE,SLEEP}_HP_CK_POWER_REG` registers.
261+
#[derive(Clone, Copy, Default)]
262+
pub struct HpClkPower(u32);
263+
264+
pub bool, xpd_bbpll , set_xpd_bbpll : 30;
265+
}
266+
267+
bitfield::bitfield! {
268+
/// Representation of `PMU_{HP_ACTIVE,HP_SLEEP,LP_SLEEP}_XTAL_REG` register.
269+
#[derive(Clone, Copy, Default)]
270+
pub struct XtalPower(u32);
271+
272+
pub bool, xpd_xtal , set_xpd_xtal : 31;
273+
}
274+
275+
/// Combined HP system power settings.
276+
#[derive(Clone, Copy, Default)]
277+
pub struct HpSysPower {
278+
pub dig_power: HpDigPower,
279+
pub clk: HpClkPower,
280+
pub xtal: XtalPower,
281+
}
282+
283+
bitfield::bitfield! {
284+
/// Representation of `PMU_{HP,LP}_SLEEP_LP_DIG_POWER_REG`.
285+
#[derive(Clone, Copy, Default)]
286+
pub struct LpDigPower(u32);
287+
288+
pub bool, bod_source_sel, set_bod_source_sel : 27;
289+
pub u32, vddbat_mode, set_vddbat_mode : 29, 28;
290+
pub u32, mem_dslp , set_mem_dslp : 30;
291+
292+
}
293+
294+
bitfield::bitfield! {
295+
/// Representation of `PMU_{HP,LP}_SLEEP_LP_CK_POWER_REG`.
296+
#[derive(Clone, Copy, Default)]
297+
pub struct LpClkPower(u32);
298+
299+
pub u32, xpd_xtal32k, set_xpd_xtal32k: 28;
300+
pub u32, xpd_fosc , set_xpd_fosc : 30;
301+
}
302+
303+
/// Combined LP system power settings.
304+
#[derive(Clone, Copy, Default)]
305+
pub struct LpSysPower {
306+
pub dig_power: LpDigPower,
307+
pub clk_power: LpClkPower,
308+
pub xtal: XtalPower,
309+
}
310+
311+
bitfield::bitfield! {
312+
/// Representation of `PMU_HP_{ACTIVE,SLEEP}_HP_SYS_CNTL_REG` register.
313+
#[derive(Clone, Copy, Default)]
314+
pub struct HpSysCntlReg(u32);
315+
316+
pub bool, uart_wakeup_en , set_uart_wakeup_en : 24;
317+
pub bool, lp_pad_hold_all, set_lp_pad_hold_all: 25;
318+
pub bool, hp_pad_hold_all, set_hp_pad_hold_all: 26;
319+
pub bool, dig_pad_slp_sel, set_dig_pad_slp_sel: 27;
320+
pub bool, dig_pause_wdt , set_dig_pause_wdt : 28;
321+
pub bool, dig_cpu_stall , set_dig_cpu_stall : 29;
322+
}
323+
324+
pub(crate) fn rtc_clk_cpu_freq_set_xtal() {
325+
// rtc_clk_cpu_set_to_default_config
326+
esp32h2_rtc_update_to_xtal(XtalClock::_32M, 1);
327+
}

0 commit comments

Comments
 (0)