diff --git a/esp-hal/src/embassy/time_driver_systimer.rs b/esp-hal/src/embassy/time_driver_systimer.rs index 300198d2090..f53dc819a0c 100644 --- a/esp-hal/src/embassy/time_driver_systimer.rs +++ b/esp-hal/src/embassy/time_driver_systimer.rs @@ -5,7 +5,11 @@ use super::AlarmState; use crate::{ clock::Clocks, peripherals, - timer::systimer::{Alarm, SystemTimer, Target}, + prelude::*, + timer::{ + systimer::{Alarm, SystemTimer, Target}, + Timer as _, + }, }; pub const ALARM_COUNT: usize = 3; @@ -43,9 +47,9 @@ impl EmbassyTimer { pub(super) fn on_alarm_allocated(&self, n: usize) { match n { - 0 => self.alarm0.enable_interrupt_internal(true), - 1 => self.alarm1.enable_interrupt_internal(true), - 2 => self.alarm2.enable_interrupt_internal(true), + 0 => self.alarm0.enable_interrupt(true), + 1 => self.alarm1.enable_interrupt(true), + 2 => self.alarm2.enable_interrupt(true), _ => {} } } @@ -126,18 +130,18 @@ impl EmbassyTimer { fn clear_interrupt(&self, id: usize) { match id { - 0 => self.alarm0.clear_interrupt_internal(), - 1 => self.alarm1.clear_interrupt_internal(), - 2 => self.alarm2.clear_interrupt_internal(), + 0 => self.alarm0.clear_interrupt(), + 1 => self.alarm1.clear_interrupt(), + 2 => self.alarm2.clear_interrupt(), _ => {} } } fn arm(&self, id: usize, timestamp: u64) { match id { - 0 => self.alarm0.set_target_internal(timestamp), - 1 => self.alarm1.set_target_internal(timestamp), - 2 => self.alarm2.set_target_internal(timestamp), + 0 => self.alarm0.load_value(timestamp.micros()).unwrap(), + 1 => self.alarm1.load_value(timestamp.micros()).unwrap(), + 2 => self.alarm2.load_value(timestamp.micros()).unwrap(), _ => {} } } diff --git a/esp-hal/src/embassy/time_driver_timg.rs b/esp-hal/src/embassy/time_driver_timg.rs index c88ad982ca9..3e5f8a6c104 100644 --- a/esp-hal/src/embassy/time_driver_timg.rs +++ b/esp-hal/src/embassy/time_driver_timg.rs @@ -43,14 +43,14 @@ impl EmbassyTimer { pub(super) fn on_alarm_allocated(&self, _n: usize) {} - fn on_interrupt(&self, id: u8, mut timer: Timer) { + fn on_interrupt(&self, id: u8, timer: Timer) { critical_section::with(|cs| { timer.clear_interrupt(); self.trigger_alarm(id as usize, cs); }); } - pub fn init(clocks: &Clocks, mut timer: TimerType) { + pub fn init(clocks: &Clocks, timer: TimerType) { // set divider to get a 1mhz clock. APB (80mhz) / 80 = 1mhz... timer.timer0.set_divider(clocks.apb_clock.to_MHz() as u16); timer.timer0.set_counter_active(true); diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index 1064150a2e9..4362d66d959 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -1,4 +1,34 @@ -//! General-purpose timers. +//! # General-purpose Timers +//! +//! The [OneShotTimer] and [PeriodicTimer] types can be backed by any hardware +//! peripheral which implements the [Timer] trait. +//! +//! ## Usage +//! +//! ### Examples +//! +//! #### One-shot Timer +//! +//! ```no_run +//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks, None); +//! let one_shot = OneShotTimer::new(timg0.timer0); +//! +//! one_shot.delay_millis(500); +//! ``` +//! +//! #### Periodic Timer +//! +//! ```no_run +//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks, None); +//! let periodic = PeriodicTimer::new(timg0.timer0); +//! +//! periodic.start(1.secs()); +//! loop { +//! nb::block!(periodic.wait()); +//! } +//! ``` + +#![deny(missing_docs)] use fugit::{ExtU64, Instant, MicrosDurationU64}; @@ -17,6 +47,8 @@ pub enum Error { TimerInactive, /// The alarm is not currently active. AlarmInactive, + /// The provided timeout is too large. + InvalidTimeout, } /// Functionality provided by any timer peripheral. @@ -37,7 +69,7 @@ pub trait Timer: crate::private::Sealed { fn now(&self) -> Instant; /// Load a target value into the timer. - fn load_value(&self, value: MicrosDurationU64); + fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error>; /// Enable auto reload of the loaded value. fn enable_auto_reload(&self, auto_reload: bool); @@ -51,7 +83,8 @@ pub trait Timer: crate::private::Sealed { /// Has the timer triggered? fn is_interrupt_set(&self) -> bool; - /// FIXME: This is (hopefully?) temporary... + // NOTE: This is an unfortunate implementation detail of `TIMGx` + #[doc(hidden)] fn set_alarm_active(&self, state: bool); } @@ -93,7 +126,7 @@ where self.inner.reset(); self.inner.enable_auto_reload(false); - self.inner.load_value(us); + self.inner.load_value(us).unwrap(); self.inner.start(); while !self.inner.is_interrupt_set() { @@ -132,9 +165,8 @@ impl embedded_hal::delay::DelayNs for OneShotTimer where T: Timer, { - #[allow(clippy::useless_conversion)] fn delay_ns(&mut self, ns: u32) { - self.delay_nanos(ns.into()); + self.delay_nanos(ns); } } @@ -153,7 +185,7 @@ where } /// Start a new count down. - pub fn start(&mut self, timeout: MicrosDurationU64) { + pub fn start(&mut self, timeout: MicrosDurationU64) -> Result<(), Error> { if self.inner.is_running() { self.inner.stop(); } @@ -162,15 +194,17 @@ where self.inner.reset(); self.inner.enable_auto_reload(true); - self.inner.load_value(timeout); + self.inner.load_value(timeout)?; self.inner.start(); + + Ok(()) } /// "Wait" until the count down finishes without blocking. pub fn wait(&mut self) -> nb::Result<(), void::Void> { if self.inner.is_interrupt_set() { self.inner.clear_interrupt(); - self.inner.set_alarm_active(true); // FIXME: Remove if/when able + self.inner.set_alarm_active(true); Ok(()) } else { @@ -201,7 +235,7 @@ where where Time: Into, { - self.start(timeout.into()); + self.start(timeout.into()).unwrap(); } fn wait(&mut self) -> nb::Result<(), void::Void> { diff --git a/esp-hal/src/timer/systimer.rs b/esp-hal/src/timer/systimer.rs index 77ca269ed6f..252dc6b6b34 100644 --- a/esp-hal/src/timer/systimer.rs +++ b/esp-hal/src/timer/systimer.rs @@ -1,33 +1,42 @@ -//! # System Timer peripheral driver +//! # System Timer (SYSTIMER) //! //! The System Timer is a -#![cfg_attr(esp32s2, doc = "`64-bit`")] -#![cfg_attr(not(esp32s2), doc = "`54-bit`")] -//! timer with three comparators capable of raising an alarm interupt on each. +#![cfg_attr(esp32s2, doc = "64-bit")] +#![cfg_attr(not(esp32s2), doc = "52-bit")] +//! timer which can be used, for example, to generate tick interrupts for an +//! operating system, or simply as a general-purpose timer. //! -//! To obtain the current timer value, call [`SystemTimer::now`]. +//! The timer consists of two counters, `UNIT0` and `UNIT1`. The counter values +//! can be monitored by 3 comparators, `COMP0`, `COMP1`, and `COMP2`. //! -//! [`Alarm`]'s can be configured into two different modes: +//! [Alarm]s can be configured in two modes: [Target] (one-shot) and [Periodic]. //! -//! - [`Target`], for one-shot timer behaviour. -//! - [`Periodic`], for alarm triggers at a repeated interval. +//! ## Usage //! -//! ## Example -//! ```no_run -//! let peripherals = Peripherals::take(); +//! ### Examples +//! +//! #### General-purpose Timer //! +//! ```no_run //! let systimer = SystemTimer::new(peripherals.SYSTIMER); -//! println!("SYSTIMER Current value = {}", SystemTimer::now()); //! -//! let mut alarm0 = systimer.alarm0; -//! // Block for a second -//! alarm0.wait_until(SystemTimer::now().wrapping_add(SystemTimer::TICKS_PER_SECOND)); +//! // Get the current timestamp, in microseconds: +//! let now = systimer.now(); +//! +//! // Wait for timeout: +//! systimer.load_value(1.secs()); +//! systimer.start(); +//! +//! while !systimer.is_interrupt_set() { +//! // Wait +//! } //! ``` use core::marker::PhantomData; use fugit::{Instant, MicrosDurationU32, MicrosDurationU64}; +use super::{Error, Timer as _}; use crate::{ interrupt::{self, InterruptHandler}, peripheral::Peripheral, @@ -41,18 +50,18 @@ use crate::{ Mode, }; -/// The SystemTimer +/// System Timer driver. pub struct SystemTimer<'d, DM> where DM: Mode, { + _phantom: PhantomData<&'d ()>, /// Alarm 0. pub alarm0: Alarm, /// Alarm 1. pub alarm1: Alarm, /// Alarm 2. pub alarm2: Alarm, - _phantom: PhantomData<&'d ()>, } impl<'d> SystemTimer<'d, Blocking> { @@ -62,11 +71,15 @@ impl<'d> SystemTimer<'d, Blocking> { pub const BIT_MASK: u64 = u64::MAX; /// The ticks per second the underlying peripheral uses. pub const TICKS_PER_SECOND: u64 = 80_000_000; + // Bitmask to be applied to the raw period register value. + const PERIOD_MASK: u64 = 0x1FFF_FFFF; } else { /// Bitmask to be applied to the raw register value. pub const BIT_MASK: u64 = 0xF_FFFF_FFFF_FFFF; /// The ticks per second the underlying peripheral uses. pub const TICKS_PER_SECOND: u64 = 16_000_000; + // Bitmask to be applied to the raw period register value. + const PERIOD_MASK: u64 = 0x3FF_FFFF; } } @@ -76,14 +89,14 @@ impl<'d> SystemTimer<'d, Blocking> { etm::enable_etm(); Self { + _phantom: PhantomData, alarm0: Alarm::new(), alarm1: Alarm::new(), alarm2: Alarm::new(), - _phantom: PhantomData, } } - /// Get the current count of unit 0 in the system timer. + /// Get the current count of Unit 0 in the System Timer. pub fn now() -> u64 { // This should be safe to access from multiple contexts // worst case scenario the second accessor ends up reading @@ -136,7 +149,6 @@ impl Alarm where DM: Mode, { - // private constructor fn new() -> Self { Self { _pd: PhantomData } } @@ -178,36 +190,6 @@ where tconf.modify(|_r, w| w.work_en().set_bit()); } } - - pub(crate) fn enable_interrupt_internal(&self, val: bool) { - let systimer = unsafe { &*SYSTIMER::ptr() }; - systimer.int_ena().modify(|_, w| w.target(CHANNEL).bit(val)); - } - - pub(crate) fn clear_interrupt_internal(&self) { - let systimer = unsafe { &*SYSTIMER::ptr() }; - systimer - .int_clr() - .write(|w| w.target(CHANNEL).clear_bit_by_one()); - } - - pub(crate) fn set_target_internal(&self, timestamp: u64) { - self.configure(|tconf, target| unsafe { - tconf.write(|w| w.period_mode().clear_bit()); // target mode - target.hi().write(|w| w.hi().bits((timestamp >> 32) as u32)); - target - .lo() - .write(|w| w.lo().set((timestamp & 0xFFFF_FFFF) as u32)); - }) - } - - pub(crate) fn set_period_internal(&self, ticks: u32) { - self.configure(|tconf, target| { - tconf.write(|w| unsafe { w.period_mode().set_bit().period().bits(ticks) }); - target.hi().write(|w| w.hi().set(0)); - target.lo().write(|w| w.lo().set(0)); - }); - } } impl Alarm { @@ -238,16 +220,6 @@ impl Alarm { _ => unreachable!(), } } - - /// Enable the interrupt for this alarm. - pub fn enable_interrupt(&mut self, val: bool) { - self.enable_interrupt_internal(val); - } - - /// Enable the interrupt pending status for this alarm. - pub fn clear_interrupt(&mut self) { - self.clear_interrupt_internal(); - } } impl Alarm @@ -256,13 +228,20 @@ where { /// Set the target value of this [Alarm] pub fn set_target(&mut self, timestamp: u64) { - self.set_target_internal(timestamp); + self.configure(|tconf, target| unsafe { + tconf.write(|w| w.period_mode().clear_bit()); // target mode + target.hi().write(|w| w.hi().bits((timestamp >> 32) as u32)); + target + .lo() + .write(|w| w.lo().set((timestamp & 0xFFFF_FFFF) as u32)); + }); } /// Block waiting until the timer reaches the `timestamp` pub fn wait_until(&mut self, timestamp: u64) { - self.clear_interrupt_internal(); + self.clear_interrupt(); self.set_target(timestamp); + let r = unsafe { &*crate::peripherals::SYSTIMER::PTR }.int_raw(); loop { if r.read().target(CHANNEL).bit_is_set() { @@ -286,7 +265,11 @@ where let us = period.ticks(); let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000) as u32; - self.set_period_internal(ticks); + self.configure(|tconf, target| { + tconf.write(|w| unsafe { w.period_mode().set_bit().period().bits(ticks) }); + target.hi().write(|w| w.hi().set(0)); + target.lo().write(|w| w.lo().set(0)); + }); } /// Converts this [Alarm] into [Target] mode @@ -350,12 +333,9 @@ where let systimer = unsafe { &*SYSTIMER::PTR }; #[cfg(esp32s2)] - match CHANNEL { - 0 => systimer.target0_conf().modify(|_, w| w.work_en().set_bit()), - 1 => systimer.target1_conf().modify(|_, w| w.work_en().set_bit()), - 2 => systimer.target2_conf().modify(|_, w| w.work_en().set_bit()), - _ => unreachable!(), - } + systimer + .target_conf(CHANNEL as usize) + .modify(|_, w| w.work_en().set_bit()); #[cfg(not(esp32s2))] systimer.conf().modify(|_, w| match CHANNEL { @@ -370,18 +350,9 @@ where let systimer = unsafe { &*SYSTIMER::PTR }; #[cfg(esp32s2)] - match CHANNEL { - 0 => systimer - .target0_conf() - .modify(|_, w| w.work_en().clear_bit()), - 1 => systimer - .target1_conf() - .modify(|_, w| w.work_en().clear_bit()), - 2 => systimer - .target2_conf() - .modify(|_, w| w.work_en().clear_bit()), - _ => unreachable!(), - } + systimer + .target_conf(CHANNEL as usize) + .modify(|_, w| w.work_en().clear_bit()); #[cfg(not(esp32s2))] systimer.conf().modify(|_, w| match CHANNEL { @@ -416,11 +387,12 @@ where let systimer = unsafe { &*SYSTIMER::PTR }; #[cfg(esp32s2)] - match CHANNEL { - 0 => systimer.target0_conf().read().work_en().bit_is_set(), - 1 => systimer.target1_conf().read().work_en().bit_is_set(), - 2 => systimer.target2_conf().read().work_en().bit_is_set(), - _ => unreachable!(), + { + systimer + .target_conf(CHANNEL as usize) + .read() + .work_en() + .bit_is_set() } #[cfg(not(esp32s2))] @@ -452,8 +424,7 @@ where Instant::::from_ticks(us) } - #[allow(clippy::unnecessary_cast)] - fn load_value(&self, value: MicrosDurationU64) { + fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { let systimer = unsafe { &*SYSTIMER::PTR }; let auto_reload = systimer @@ -463,11 +434,18 @@ where .bit_is_set(); let us = value.ticks(); - let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000) as u64; + let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000); if auto_reload { // Period mode + // The `SYSTIMER_TARGETx_PERIOD` field is 26-bits wide (or + // 29-bits on the ESP32-S2), so we must ensure that the provided + // value is not too wide: + if (ticks & !SystemTimer::PERIOD_MASK) != 0 { + return Err(Error::InvalidTimeout); + } + systimer .target_conf(CHANNEL as usize) .modify(|_, w| unsafe { w.period().bits(ticks as u32) }); @@ -493,6 +471,14 @@ where // Wait for value registers to update } + // The counters/comparators are 52-bits wide (except on ESP32-S2, + // which is 64-bits), so we must ensure that the provided value + // is not too wide: + #[cfg(not(esp32s2))] + if (ticks & !SystemTimer::BIT_MASK) != 0 { + return Err(Error::InvalidTimeout); + } + let hi = systimer.unit0_value().hi().read().bits(); let lo = systimer.unit0_value().lo().read().bits(); @@ -513,6 +499,8 @@ where .comp_load(CHANNEL as usize) .write(|w| w.load().set_bit()); } + + Ok(()) } fn enable_auto_reload(&self, auto_reload: bool) { @@ -571,7 +559,7 @@ mod asynch { impl<'a, const N: u8> AlarmFuture<'a, N> { pub(crate) fn new(alarm: &'a Alarm) -> Self { - alarm.clear_interrupt_internal(); + alarm.clear_interrupt(); let (interrupt, handler) = match N { 0 => (Interrupt::SYSTIMER_TARGET0, target0_handler), @@ -584,7 +572,7 @@ mod asynch { interrupt::enable(interrupt, handler.priority()).unwrap(); } - alarm.enable_interrupt_internal(true); + alarm.enable_interrupt(true); Self { phantom: PhantomData, diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index 8a03894bdd1..f1805260055 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -1,39 +1,53 @@ -//! # General-purpose timers +//! # Timer Group (TIMG) //! -//! ## Overview -//! The `general-purpose timer` peripheral consists of a timer group, which can -//! have up to two timers (depending on the chip) and a watchdog timer. The -//! timer group allows for the management of multiple timers and synchronization -//! between them. +//! The Timer Group (TIMG) peripherals contain one or more general-purpose +//! timers, plus one or more watchdog timers. //! -//! This peripheral can be used to perform a variety of -//! tasks, such as triggering an interrupt after a particular interval -//! (periodically and aperiodically), precisely time an interval, act as a -//! hardware clock and so on. +//! The general-purpose timers are based on a 16-bit pre-scaler and a 54-bit +//! auto-reload-capable up-down counter. The timers have configurable alarms, +//! which are triggered when the internal counter of the timers reaches a +//! specific target value. The timers are clocked using the APB clock source. //! -//! Each timer group consists of two general purpose timers and one Main System -//! Watchdog Timer(MSWDT). All general purpose timers are based on 16-bit -//! prescalers and 54-bit auto-reload-capable up-down counters. +//! Typically, a general-purpose timer can be used in scenarios such as: //! -//! The driver uses APB as it's clock source. +//! - Generate period alarms; trigger events periodically +//! - Generate one-shot alarms; trigger events once +//! - Free-running; fetching a high-resolution timestamp on demand //! -//! ## Example +//! ## Usage +//! +//! ### Examples +//! +//! #### General-purpose Timer +//! +//! ```no_run +//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! let timer0 = timg0.timer0; +//! +//! // Get the current timestamp, in microseconds: +//! let now = timer0.now(); +//! +//! // Wait for timeout: +//! timer0.load_value(1.secs()); +//! timer0.start(); +//! +//! while !timer0.is_interrupt_set() { +//! // Wait +//! } +//! ``` +//! +//! #### Watchdog Timer //! //! ```no_run -//! let mut rtc = Rtc::new(peripherals.LPWR, None); +//! let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); +//! let mut wdt = timg0.wdt; //! -//! // Create timer groups -//! let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); -//! // Get watchdog timers of timer groups -//! let mut wdt0 = timer_group0.wdt; -//! let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); -//! let mut wdt1 = timer_group1.wdt; +//! wdt.set_timeout(5_000.millis()); +//! wdt.enable(); //! -//! // Disable watchdog timers -//! rtc.swd.disable(); -//! rtc.rwdt.disable(); -//! wdt0.disable(); -//! wdt1.disable(); +//! loop { +//! wdt.feed(); +//! } //! ``` use core::{ @@ -43,64 +57,78 @@ use core::{ use fugit::{HertzU32, Instant, MicrosDurationU64}; +use super::Error; #[cfg(timg1)] use crate::peripherals::TIMG1; #[cfg(any(esp32c6, esp32h2))] use crate::soc::constants::TIMG_DEFAULT_CLK_SRC; use crate::{ clock::Clocks, - interrupt::InterruptHandler, + interrupt::{self, InterruptHandler}, peripheral::{Peripheral, PeripheralRef}, - peripherals::{timg0::RegisterBlock, TIMG0}, + peripherals::{timg0::RegisterBlock, Interrupt, TIMG0}, + private::Sealed, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, Async, Blocking, Mode, }; -/// Interrupts which can be registered in [crate::Blocking] mode +/// Interrupts which can be registered in [Blocking] mode #[derive(Debug, Default)] pub struct TimerInterrupts { + /// T0 Interrupt for [`Timer0`] pub timer0_t0: Option, + /// T1 Interrupt for [`Timer0`] pub timer0_t1: Option, + /// WDT Interrupt for [`Timer0`] pub timer0_wdt: Option, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// T0 Interrupt for [`Timer1`] + #[cfg(timg_timer1)] pub timer1_t0: Option, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// T1 Interrupt for [`Timer1`] + #[cfg(timg_timer1)] pub timer1_t1: Option, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// WDT Interrupt for [`Timer1`] + #[cfg(timg_timer1)] pub timer1_wdt: Option, } -// A timergroup consisting of up to 2 timers (chip dependent) and a watchdog -// timer +/// A timer group consisting of up to 2 timers (chip dependent) and a watchdog +/// timer. pub struct TimerGroup<'d, T, DM> where T: TimerGroupInstance, DM: Mode, { _timer_group: PeripheralRef<'d, T>, + /// Timer 0 pub timer0: Timer, DM>, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + /// Timer 1 + #[cfg(timg_timer1)] pub timer1: Timer, DM>, + /// Watchdog timer pub wdt: Wdt, } +#[doc(hidden)] pub trait TimerGroupInstance { + fn id() -> u8; fn register_block() -> *const RegisterBlock; fn configure_src_clk(); fn configure_wdt_src_clk(); - fn id() -> u8; } impl TimerGroupInstance for TIMG0 { fn id() -> u8 { 0 } + #[inline(always)] fn register_block() -> *const RegisterBlock { - crate::peripherals::TIMG0::PTR + TIMG0::PTR } + #[inline(always)] #[cfg(any(esp32c6, esp32h2))] fn configure_src_clk() { @@ -108,6 +136,7 @@ impl TimerGroupInstance for TIMG0 { .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() { @@ -118,11 +147,13 @@ impl TimerGroupInstance for TIMG0 { .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() { @@ -132,6 +163,7 @@ impl TimerGroupInstance for TIMG0 { .modify(|_, w| w.wdt_use_xtal().clear_bit()) }; } + #[inline(always)] #[cfg(any(esp32c6, esp32h2))] fn configure_wdt_src_clk() { @@ -139,6 +171,7 @@ impl TimerGroupInstance for TIMG0 { .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() { @@ -151,10 +184,12 @@ impl TimerGroupInstance for TIMG1 { fn id() -> u8 { 1 } + #[inline(always)] fn register_block() -> *const RegisterBlock { - crate::peripherals::TIMG1::PTR + TIMG1::PTR } + #[inline(always)] #[cfg(any(esp32c6, esp32h2))] fn configure_src_clk() { @@ -162,6 +197,7 @@ impl TimerGroupInstance for TIMG1 { .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() { @@ -172,12 +208,14 @@ impl TimerGroupInstance for TIMG1 { .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() { @@ -185,6 +223,7 @@ impl TimerGroupInstance for TIMG1 { .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() { @@ -197,12 +236,13 @@ impl<'d, T> TimerGroup<'d, T, Blocking> where T: TimerGroupInstance, { + /// Construct a new instance of [`TimerGroup`] in blocking mode pub fn new( - timer_group: impl Peripheral

+ 'd, + _timer_group: impl Peripheral

+ 'd, clocks: &Clocks, isr: Option, ) -> Self { - crate::into_ref!(timer_group); + crate::into_ref!(_timer_group); T::configure_src_clk(); @@ -217,7 +257,7 @@ where clocks.pll_48m_clock, ); - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + #[cfg(timg_timer1)] let timer1 = Timer::new( Timer1 { phantom: PhantomData, @@ -228,98 +268,56 @@ where if let Some(isr) = isr { if let Some(handler) = isr.timer0_t0 { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG0_T0_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG0_T0_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG0_T0_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG0_T0_LEVEL, handler.priority()).unwrap(); } } #[cfg(any(esp32, esp32s2, esp32s3))] if let Some(handler) = isr.timer0_t1 { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG0_T1_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG0_T1_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG0_T1_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG0_T1_LEVEL, handler.priority()).unwrap(); } } if let Some(handler) = isr.timer0_wdt { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG0_WDT_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG0_WDT_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG0_WDT_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG0_WDT_LEVEL, handler.priority()).unwrap(); } } - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + #[cfg(timg_timer1)] { if let Some(handler) = isr.timer1_t0 { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG1_T0_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG1_T0_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG1_T0_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG1_T0_LEVEL, handler.priority()).unwrap(); } } #[cfg(any(esp32, esp32s2, esp32s3))] if let Some(handler) = isr.timer1_t1 { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG1_T1_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG1_T1_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG1_T1_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG1_T1_LEVEL, handler.priority()).unwrap(); } } if let Some(handler) = isr.timer1_wdt { unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::TG1_WDT_LEVEL, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::TG1_WDT_LEVEL, - handler.priority(), - ) - .unwrap(); + interrupt::bind_interrupt(Interrupt::TG1_WDT_LEVEL, handler.handler()); + interrupt::enable(Interrupt::TG1_WDT_LEVEL, handler.priority()).unwrap(); } } } } Self { - _timer_group: timer_group, + _timer_group, timer0, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + #[cfg(timg_timer1)] timer1, wdt: Wdt::new(), } @@ -330,8 +328,9 @@ impl<'d, T> TimerGroup<'d, T, Async> where T: TimerGroupInstance, { - pub fn new_async(timer_group: impl Peripheral

+ 'd, clocks: &Clocks) -> Self { - crate::into_ref!(timer_group); + /// Construct a new instance of [`TimerGroup`] in asynchronous mode + pub fn new_async(_timer_group: impl Peripheral

+ 'd, clocks: &Clocks) -> Self { + crate::into_ref!(_timer_group); T::configure_src_clk(); @@ -346,7 +345,7 @@ where clocks.pll_48m_clock, ); - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + #[cfg(timg_timer1)] let timer1 = Timer::new( Timer1 { phantom: PhantomData, @@ -355,16 +354,16 @@ where ); Self { - _timer_group: timer_group, + _timer_group, timer0, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] + #[cfg(timg_timer1)] timer1, wdt: Wdt::new(), } } } -/// General-purpose Timer driver +/// General-purpose timer. pub struct Timer where DM: Mode, @@ -379,7 +378,7 @@ where T: Instance, DM: Mode, { - /// Create a new timer instance. + /// Construct a new instance of [`Timer`] pub fn new(timg: T, apb_clk_freq: HertzU32) -> Self { timg.enable_peripheral(); timg.set_counter_active(true); @@ -391,23 +390,6 @@ where } } - /// Start the timer with the given time period. - pub fn start(&mut self, timeout: MicrosDurationU64) { - self.timg.set_counter_active(false); - self.timg.set_alarm_active(false); - - self.timg.reset_counter(); - - // TODO: can we cache the divider (only get it on initialization)? - let ticks = timeout_to_ticks(timeout, self.apb_clk_freq, self.timg.divider()); - self.timg.load_alarm_value(ticks); - - self.timg.set_counter_decrementing(false); - self.timg.set_auto_reload(true); - self.timg.set_counter_active(true); - self.timg.set_alarm_active(true); - } - /// Check if the timer has elapsed pub fn has_elapsed(&mut self) -> bool { if !self.timg.is_counter_active() { @@ -452,7 +434,7 @@ where } } -impl crate::private::Sealed for Timer +impl Sealed for Timer where T: Instance, DM: Mode, @@ -514,17 +496,24 @@ where Instant::::from_ticks(micros) } - fn load_value(&self, value: MicrosDurationU64) { + fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { let ticks = timeout_to_ticks(value, self.apb_clk_freq, self.timg.divider()); - let value = ticks & 0x3F_FFFF_FFFF_FFFF; - let high = (value >> 32) as u32; - let low = (value & 0xFFFF_FFFF) as u32; + // The counter is 54-bits wide, so we must ensure that the provided + // value is not too wide: + if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 { + return Err(Error::InvalidTimeout); + } + + let high = (ticks >> 32) as u32; + let low = (ticks & 0xFFFF_FFFF) as u32; let t = self.register_block().t(self.timer_number().into()); t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) }); t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) }); + + Ok(()) } fn enable_auto_reload(&self, auto_reload: bool) { @@ -562,8 +551,8 @@ where } } -/// Timer peripheral instance. -pub trait Instance: crate::private::Sealed + Enable { +#[doc(hidden)] +pub trait Instance: Sealed + Enable { fn register_block(&self) -> &RegisterBlock; fn timer_number(&self) -> u8; @@ -599,20 +588,24 @@ pub trait Instance: crate::private::Sealed + Enable { fn is_interrupt_set(&self) -> bool; } -pub trait Enable: crate::private::Sealed { +#[doc(hidden)] +pub trait Enable: Sealed { fn enable_peripheral(&self); } +/// A timer within a Timer Group. pub struct TimerX { phantom: PhantomData, } +/// Timer 0 in the Timer Group. pub type Timer0 = TimerX; -#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] +/// Timer 1 in the Timer Group. +#[cfg(timg_timer1)] pub type Timer1 = TimerX; -impl crate::private::Sealed for TimerX {} +impl Sealed for TimerX {} impl TimerX where @@ -648,7 +641,6 @@ where let t = unsafe { Self::t() }; t.loadlo().write(|w| unsafe { w.load_lo().bits(0) }); - t.loadhi().write(|w| unsafe { w.load_hi().bits(0) }); t.load().write(|w| unsafe { w.load().bits(1) }); @@ -774,7 +766,7 @@ where } } -#[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] +#[cfg(timg_timer1)] impl Enable for Timer1 where TG: TimerGroupInstance, @@ -815,7 +807,7 @@ where #[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::CountDown for Timer where - T: Instance, + T: Instance + super::Timer, DM: Mode, { type Time = MicrosDurationU64; @@ -824,7 +816,8 @@ where where Time: Into, { - (*self).start(timeout.into()) + self.timg.load_value(timeout.into()).unwrap(); + self.timg.start(); } fn wait(&mut self) -> nb::Result<(), void::Void> { @@ -839,7 +832,7 @@ where #[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::Cancel for Timer where - T: Instance, + T: Instance + super::Timer, DM: Mode, { type Error = super::Error; @@ -860,7 +853,7 @@ where #[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::timer::Periodic for Timer where - T: Instance, + T: Instance + super::Timer, DM: Mode, { } @@ -876,10 +869,10 @@ where TG: TimerGroupInstance, DM: Mode, { - /// Create a new watchdog timer instance + /// Construct a new instance of [`Wdt`] pub fn new() -> Self { #[cfg(lp_wdt)] - PeripheralClockControl::enable(crate::system::Peripheral::Wdt); + PeripheralClockControl::enable(PeripheralEnable::Wdt); TG::configure_wdt_src_clk(); @@ -927,6 +920,7 @@ where .write(|w| unsafe { w.wdt_wkey().bits(0u32) }); } + /// Feed the watchdog timer pub fn feed(&mut self) { let reg_block = unsafe { &*TG::register_block() }; @@ -941,6 +935,7 @@ where .write(|w| unsafe { w.wdt_wkey().bits(0u32) }); } + /// Set the timeout, in microseconds, of the watchdog timer pub fn set_timeout(&mut self, timeout: MicrosDurationU64) { let timeout_raw = (timeout.to_nanos() * 10 / 125) as u32; @@ -1036,18 +1031,18 @@ where } } +/// Event Task Matrix #[cfg(soc_etm)] pub mod etm { use super::*; - use crate::{ - etm::{EtmEvent, EtmTask}, - private::Sealed, - }; + use crate::etm::{EtmEvent, EtmTask}; + /// Event Task Matrix event for a timer. pub struct TimerEtmEvent { id: u8, } + /// Event Task Matrix task for a timer. pub struct TimerEtmTask { id: u8, } @@ -1068,17 +1063,28 @@ pub mod etm { impl Sealed for TimerEtmTask {} - /// General purpose timer ETM events + /// General purpose timer ETM events. pub trait TimerEtmEvents { + /// ETM event triggered on alarm fn on_alarm(&self) -> TimerEtmEvent; } /// General purpose timer ETM tasks pub trait TimerEtmTasks { + /// ETM task to start the counter fn cnt_start(&self) -> TimerEtmTask; + + /// ETM task to start the alarm fn cnt_stop(&self) -> TimerEtmTask; + + /// ETM task to stop the counter fn cnt_reload(&self) -> TimerEtmTask; + + /// ETM task to reload the counter fn cnt_cap(&self) -> TimerEtmTask; + + /// ETM task to load the counter with the value stored when the last + /// `now()` was called fn alarm_start(&self) -> TimerEtmTask; } @@ -1086,7 +1092,6 @@ pub mod etm { where TG: TimerGroupInstance, { - /// ETM event triggered on alarm fn on_alarm(&self) -> TimerEtmEvent { TimerEtmEvent { id: 48 + TG::id() } } @@ -1096,28 +1101,22 @@ pub mod etm { where TG: TimerGroupInstance, { - /// ETM task to start the counter fn cnt_start(&self) -> TimerEtmTask { TimerEtmTask { id: 88 + TG::id() } } - /// ETM task to start the alarm fn alarm_start(&self) -> TimerEtmTask { TimerEtmTask { id: 90 + TG::id() } } - /// ETM task to stop the counter fn cnt_stop(&self) -> TimerEtmTask { TimerEtmTask { id: 92 + TG::id() } } - /// ETM task to reload the counter fn cnt_reload(&self) -> TimerEtmTask { TimerEtmTask { id: 94 + TG::id() } } - /// ETM task to load the counter with the value stored when the last - /// `now()` was called fn cnt_cap(&self) -> TimerEtmTask { TimerEtmTask { id: 96 + TG::id() } } diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 8782e24d0be..8e02cb7cbc8 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -59,6 +59,7 @@ symbols = [ "bt", "wifi", "psram", + "timg_timer1", # ROM capabilities "rom_crc_le", diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index 666623ff443..2d11f3e9203 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -57,6 +57,7 @@ symbols = [ "wifi", "psram", "ulp_riscv_core", + "timg_timer1", # ROM capabilities "rom_crc_le", diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 3e337b9dbcb..0377f118e7f 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -69,6 +69,7 @@ symbols = [ "wifi", "psram", "ulp_riscv_core", + "timg_timer1", # ROM capabilities "rom_crc_le", diff --git a/esp-wifi/src/timer/riscv.rs b/esp-wifi/src/timer/riscv.rs index 9433fcb4e00..ce9534ae2f2 100644 --- a/esp-wifi/src/timer/riscv.rs +++ b/esp-wifi/src/timer/riscv.rs @@ -10,6 +10,7 @@ use crate::{ hal::{ interrupt::{self, TrapFrame}, peripherals::{self, Interrupt}, + prelude::*, riscv, timer::systimer::{Alarm, Periodic, Target}, }, diff --git a/esp-wifi/src/timer/xtensa.rs b/esp-wifi/src/timer/xtensa.rs index 6f36488096f..b00a4f05715 100644 --- a/esp-wifi/src/timer/xtensa.rs +++ b/esp-wifi/src/timer/xtensa.rs @@ -67,7 +67,10 @@ fn do_task_switch(context: &mut TrapFrame) { let mut timer = TIMER1.borrow_ref_mut(cs); let timer = unwrap!(timer.as_mut()); timer.clear_interrupt(); - timer.start(TIMESLICE_FREQUENCY.into_duration()); + timer + .load_value(TIMESLICE_FREQUENCY.into_duration()) + .unwrap(); + timer.start(); }); task_switch(context); diff --git a/examples/src/bin/timer_interrupt.rs b/examples/src/bin/timer_interrupt.rs index 430a11ac441..1c78624df12 100644 --- a/examples/src/bin/timer_interrupt.rs +++ b/examples/src/bin/timer_interrupt.rs @@ -40,7 +40,7 @@ fn main() -> ! { let timer0 = timg0.timer0; interrupt::enable(Interrupt::TG0_T0_LEVEL, Priority::Priority1).unwrap(); - timer0.load_value(500u64.millis()); + timer0.load_value(500u64.millis()).unwrap(); timer0.start(); timer0.listen(); @@ -65,6 +65,7 @@ fn tg0_t0_level() { let timer0 = timer0.as_mut().unwrap(); timer0.clear_interrupt(); - timer0.start(500u64.millis()); + timer0.load_value(500u64.millis()).unwrap(); + timer0.start(); }); }