diff --git a/CHANGELOG.md b/CHANGELOG.md index 97342b40583..a06e88be42f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `embassy_serial` and `embassy_wait` examples for ESP32-H2 (#569) - Fix Async GPIO not disabling interupts on chips with multiple banks (#572) - Add unified field-based efuse access +- Add `timer_interrupt` example in ESP32-H2 and refactor `clk_src` configuration (#576) ### Changed diff --git a/esp-hal-common/src/soc/esp32c6/mod.rs b/esp-hal-common/src/soc/esp32c6/mod.rs index 310b8d7141c..b94159f5c74 100644 --- a/esp-hal-common/src/soc/esp32c6/mod.rs +++ b/esp-hal-common/src/soc/esp32c6/mod.rs @@ -6,3 +6,7 @@ pub mod radio_clocks; pub(crate) mod registers { pub const INTERRUPT_MAP_BASE: u32 = 0x60010000; } + +pub(crate) mod constants { + pub const TIMG_DEFAULT_CLK_SRC: u8 = 1; +} diff --git a/esp-hal-common/src/soc/esp32h2/mod.rs b/esp-hal-common/src/soc/esp32h2/mod.rs index 310b8d7141c..4cfdb3d97d9 100644 --- a/esp-hal-common/src/soc/esp32h2/mod.rs +++ b/esp-hal-common/src/soc/esp32h2/mod.rs @@ -6,3 +6,7 @@ pub mod radio_clocks; pub(crate) mod registers { pub const INTERRUPT_MAP_BASE: u32 = 0x60010000; } + +pub(crate) mod constants { + pub const TIMG_DEFAULT_CLK_SRC: u8 = 2; +} diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 62f7b164597..a929e37f4ee 100755 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -455,37 +455,22 @@ impl PeripheralClockControl { Peripheral::Timg0 => { system .timergroup0_timer_clk_conf - .write(|w| w.tg0_timer_clk_en().set_bit()); - let bits = if cfg!(esp32c6) { 1 } else { 2 }; - system - .timergroup0_timer_clk_conf - .write(|w| unsafe { w.tg0_timer_clk_sel().bits(bits) }); + .modify(|_, w| w.tg0_timer_clk_en().set_bit()); } #[cfg(timg1)] Peripheral::Timg1 => { system .timergroup1_timer_clk_conf - .write(|w| w.tg1_timer_clk_en().set_bit()); - let bits = if cfg!(esp32c6) { 1 } else { 2 }; - system - .timergroup1_timer_clk_conf - .write(|w| unsafe { w.tg1_timer_clk_sel().bits(bits) }); + .modify(|_, w| w.tg1_timer_clk_en().set_bit()); } #[cfg(lp_wdt)] Peripheral::Wdt => { system .timergroup0_wdt_clk_conf - .write(|w| w.tg0_wdt_clk_en().set_bit()); - system - .timergroup0_wdt_clk_conf - .write(|w| unsafe { w.tg0_wdt_clk_sel().bits(1) }); - - system - .timergroup1_timer_clk_conf - .write(|w| w.tg1_timer_clk_en().set_bit()); + .modify(|_, w| w.tg0_wdt_clk_en().set_bit()); system .timergroup1_timer_clk_conf - .write(|w| unsafe { w.tg1_timer_clk_sel().bits(1) }); + .modify(|_, w| w.tg1_timer_clk_en().set_bit()); } #[cfg(sha)] Peripheral::Sha => { diff --git a/esp-hal-common/src/timer.rs b/esp-hal-common/src/timer.rs index 9af8edb1d4a..dab28155459 100644 --- a/esp-hal-common/src/timer.rs +++ b/esp-hal-common/src/timer.rs @@ -14,6 +14,8 @@ use void::Void; #[cfg(timg1)] use crate::peripherals::TIMG1; +#[cfg(any(esp32c6, esp32h2))] +use crate::soc::constants::TIMG_DEFAULT_CLK_SRC; use crate::{ clock::Clocks, peripheral::{Peripheral, PeripheralRef}, @@ -44,6 +46,8 @@ where pub trait TimerGroupInstance { fn register_block() -> *const RegisterBlock; + fn configure_src_clk(); + fn configure_wdt_src_clk(); } impl TimerGroupInstance for TIMG0 { @@ -51,6 +55,48 @@ impl TimerGroupInstance for TIMG0 { fn register_block() -> *const RegisterBlock { crate::peripherals::TIMG0::PTR } + #[inline(always)] + #[cfg(any(esp32c6, esp32h2))] + fn configure_src_clk() { + unsafe { &*crate::peripherals::PCR::PTR } + .timergroup0_timer_clk_conf + .modify(|_, w| unsafe { w.tg0_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) }); + } + #[inline(always)] + #[cfg(any(esp32c2, esp32c3, esp32s2, esp32s3))] + fn configure_src_clk() { + unsafe { + (*Self::register_block()) + .t0config + .modify(|_, w| w.use_xtal().clear_bit()) + }; + } + #[inline(always)] + #[cfg(esp32)] + fn configure_src_clk() { + // ESP32 has only APB clock source, do nothing + } + #[inline(always)] + #[cfg(any(esp32c2, esp32c3))] + fn configure_wdt_src_clk() { + unsafe { + (*Self::register_block()) + .wdtconfig0 + .modify(|_, w| w.wdt_use_xtal().clear_bit()) + }; + } + #[inline(always)] + #[cfg(any(esp32c6, esp32h2))] + fn configure_wdt_src_clk() { + unsafe { &*crate::peripherals::PCR::PTR } + .timergroup0_wdt_clk_conf + .modify(|_, w| unsafe { w.tg0_wdt_clk_sel().bits(1) }); + } + #[inline(always)] + #[cfg(any(esp32, esp32s2, esp32s3))] + fn configure_wdt_src_clk() { + // ESP32, ESP32-S2, and ESP32-S3 use only ABP, do nothing + } } #[cfg(timg1)] @@ -59,6 +105,41 @@ impl TimerGroupInstance for TIMG1 { fn register_block() -> *const RegisterBlock { crate::peripherals::TIMG1::PTR } + #[inline(always)] + #[cfg(any(esp32c6, esp32h2))] + fn configure_src_clk() { + unsafe { &*crate::peripherals::PCR::PTR } + .timergroup1_timer_clk_conf + .modify(|_, w| unsafe { w.tg1_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) }); + } + #[inline(always)] + #[cfg(any(esp32s2, esp32s3))] + fn configure_src_clk() { + unsafe { + (*Self::register_block()) + .t1config + .modify(|_, w| w.use_xtal().clear_bit()) + }; + } + #[inline(always)] + #[cfg(any(esp32, esp32c2, esp32c3))] + fn configure_src_clk() { + // ESP32 has only APB clock source, do nothing + // ESP32-C2 and ESP32-C3 don't have t1config only t0config, do nothing + } + #[inline(always)] + #[cfg(any(esp32c6, esp32h2))] + fn configure_wdt_src_clk() { + unsafe { &*crate::peripherals::PCR::PTR } + .timergroup1_wdt_clk_conf + .modify(|_, w| unsafe { w.tg1_wdt_clk_sel().bits(1) }); + } + #[inline(always)] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3))] + fn configure_wdt_src_clk() { + // ESP32-C2 and ESP32-C3 don't have t1config only t0config, do nothing + // ESP32, ESP32-S2, and ESP32-S3 use only ABP, do nothing + } } impl<'d, T> TimerGroup<'d, T> @@ -72,11 +153,17 @@ where ) -> Self { crate::into_ref!(timer_group); + T::configure_src_clk(); + + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK let timer0 = Timer::new( Timer0 { phantom: PhantomData::default(), }, + #[cfg(not(esp32h2))] clocks.apb_clock, + #[cfg(esp32h2)] + clocks.pll_48m_clock, peripheral_clock_control, ); @@ -120,6 +207,7 @@ where ) -> Self { // TODO: this currently assumes APB_CLK is being used, as we don't yet have a // way to select the XTAL_CLK. + timg.enable_peripheral(peripheral_clock_control); Self { timg, apb_clk_freq } } @@ -193,6 +281,7 @@ impl Timer0 where TG: TimerGroupInstance, { + #[cfg(feature = "embassy-time-timg0")] pub(crate) unsafe fn steal() -> Self { Self { phantom: PhantomData, @@ -353,6 +442,7 @@ impl Timer1 where TG: TimerGroupInstance, { + #[cfg(feature = "embassy-time-timg1")] pub(crate) unsafe fn steal() -> Self { Self { phantom: PhantomData, @@ -597,6 +687,7 @@ where pub fn new(_peripheral_clock_control: &mut PeripheralClockControl) -> Self { #[cfg(lp_wdt)] _peripheral_clock_control.enable(crate::system::Peripheral::Wdt); + TG::configure_wdt_src_clk(); Self { phantom: PhantomData::default(), } diff --git a/esp32h2-hal/examples/timer_interrupt.rs b/esp32h2-hal/examples/timer_interrupt.rs new file mode 100644 index 00000000000..0a2d4da7a35 --- /dev/null +++ b/esp32h2-hal/examples/timer_interrupt.rs @@ -0,0 +1,110 @@ +//! This shows how to use the TIMG peripheral interrupts. +//! There is TIMG0 and TIMG1 each of them containing a general purpose timer and +//! a watchdog timer. + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp32h2_hal::{ + clock::ClockControl, + interrupt, + peripherals::{self, Peripherals, TIMG0, TIMG1}, + prelude::*, + riscv, + timer::{Timer, Timer0, TimerGroup}, + Rtc, +}; +use esp_backtrace as _; + +static TIMER0: Mutex>>>> = Mutex::new(RefCell::new(None)); +static TIMER1: Mutex>>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut timer1 = timer_group1.timer0; + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + interrupt::enable( + peripherals::Interrupt::TG0_T0_LEVEL, + interrupt::Priority::Priority2, + ) + .unwrap(); + + timer0.start(500u64.millis()); + + timer0.listen(); + + interrupt::enable( + peripherals::Interrupt::TG1_T0_LEVEL, + interrupt::Priority::Priority2, + ) + .unwrap(); + + timer1.start(1u64.secs()); + timer1.listen(); + + critical_section::with(|cs| { + TIMER0.borrow_ref_mut(cs).replace(timer0); + TIMER1.borrow_ref_mut(cs).replace(timer1); + }); + + unsafe { + riscv::interrupt::enable(); + } + + loop {} +} + +#[interrupt] +fn TG0_T0_LEVEL() { + critical_section::with(|cs| { + esp_println::println!("Interrupt 1"); + + let mut timer0 = TIMER0.borrow_ref_mut(cs); + let timer0 = timer0.as_mut().unwrap(); + + timer0.clear_interrupt(); + timer0.start(500u64.millis()); + }); +} + +#[interrupt] +fn TG1_T0_LEVEL() { + critical_section::with(|cs| { + esp_println::println!("Interrupt 11"); + + let mut timer1 = TIMER1.borrow_ref_mut(cs); + let timer1 = timer1.as_mut().unwrap(); + + timer1.clear_interrupt(); + timer1.start(1u64.secs()); + }); +}