diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index fd0d45413a9..e5137010b71 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- ESP32-C6 / ESP32-H2: Implement `ETM` for general purpose timers (#1274) + ### Fixed - Reserve `esp32` ROM stacks to prevent the trashing of dram2 section (#1289) diff --git a/esp-hal/src/timer.rs b/esp-hal/src/timer.rs index ee8a10e9cae..ad97998c5c9 100644 --- a/esp-hal/src/timer.rs +++ b/esp-hal/src/timer.rs @@ -79,9 +79,13 @@ pub trait TimerGroupInstance { 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 @@ -132,6 +136,9 @@ impl TimerGroupInstance for TIMG0 { #[cfg(timg1)] impl TimerGroupInstance for TIMG1 { + fn id() -> u8 { + 1 + } #[inline(always)] fn register_block() -> *const RegisterBlock { crate::peripherals::TIMG1::PTR @@ -878,3 +885,91 @@ where self.feed(); } } + +#[cfg(soc_etm)] +pub mod etm { + use super::*; + use crate::{ + etm::{EtmEvent, EtmTask}, + private::Sealed, + }; + + pub struct TimerEtmEvent { + id: u8, + } + + pub struct TimerEtmTask { + id: u8, + } + + impl EtmEvent for TimerEtmEvent { + fn id(&self) -> u8 { + self.id + } + } + + impl Sealed for TimerEtmEvent {} + + impl EtmTask for TimerEtmTask { + fn id(&self) -> u8 { + self.id + } + } + + impl Sealed for TimerEtmTask {} + + /// General purpose timer ETM events + pub trait TimerEtmEvents { + fn on_alarm(&self) -> TimerEtmEvent; + } + + /// General purpose timer ETM tasks + pub trait TimerEtmTasks { + fn cnt_start(&self) -> TimerEtmTask; + fn cnt_stop(&self) -> TimerEtmTask; + fn cnt_reload(&self) -> TimerEtmTask; + fn cnt_cap(&self) -> TimerEtmTask; + fn alarm_start(&self) -> TimerEtmTask; + } + + impl TimerEtmEvents for Timer0 + where + TG: TimerGroupInstance, + { + /// ETM event triggered on alarm + fn on_alarm(&self) -> TimerEtmEvent { + TimerEtmEvent { id: 48 + TG::id() } + } + } + + impl TimerEtmTasks for Timer0 + 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/examples/src/bin/etm_timer.rs b/examples/src/bin/etm_timer.rs new file mode 100644 index 00000000000..391e08942c3 --- /dev/null +++ b/examples/src/bin/etm_timer.rs @@ -0,0 +1,90 @@ +//! This shows how to use the general purpose timers ETM tasks and events +//! Notice you need to import the traits esp_hal::timer::etm::{TimerEtmEvents, TimerEtmTasks} + +//% CHIPS: esp32c6 esp32h2 + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + delay::Delay, + etm::Etm, + interrupt::{self, Priority}, + peripherals::{Interrupt, Peripherals, TIMG0}, + prelude::*, + timer::{ + etm::{TimerEtmEvents, TimerEtmTasks}, + Timer, + Timer0, + TimerGroup, + }, +}; + +static TIMER0: Mutex>>>> = Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer0 = timg0.timer0; + + // Configure ETM to stop timer0 when alarm is triggered + let event = timer0.on_alarm(); + let task = timer0.cnt_stop(); + + let etm = Etm::new(peripherals.SOC_ETM); + + let channel0 = etm.channel0; + + let _configured_channel = channel0.setup(&event, &task); + + // Setup alarm at 100ms + // 80 / 2 (default divider) timer clock cycles == 1 us + timer0.load_alarm_value(100 * 1_000 * 40); + timer0.set_alarm_active(true); + + // Enable interrupt that will be triggered by the alarm + interrupt::enable(Interrupt::TG0_T0_LEVEL, Priority::Priority1).unwrap(); + + timer0.listen(); + timer0.set_counter_active(true); + + critical_section::with(|cs| { + TIMER0.borrow_ref_mut(cs).replace(timer0); + }); + + let delay = Delay::new(&clocks); + + loop { + delay.delay_millis(500u32); + + critical_section::with(|cs| { + let mut timer0 = TIMER0.borrow_ref_mut(cs); + let timer0 = timer0.as_mut().unwrap(); + // Counter value should be the same than in interrupt + esp_println::println!("counter in main: {}", timer0.now()); + }); + } +} + +#[interrupt] +fn TG0_T0_LEVEL() { + critical_section::with(|cs| { + let mut timer0 = TIMER0.borrow_ref_mut(cs); + let timer0 = timer0.as_mut().unwrap(); + + timer0.clear_interrupt(); + + // Counter value should be a very small number as the alarm triggered a counter reload to 0 + // and ETM stopped the counter quickly after + esp_println::println!("counter in interrupt: {}", timer0.now()); + }); +}