#![doc = crate::before_snippet!()]
#![doc = crate::before_snippet!()]
use core::marker::PhantomData;
use super::Error;
#[cfg(timg1)]
use crate::peripherals::TIMG1;
#[cfg(any(esp32c6, esp32h2))]
use crate::soc::constants::TIMG_DEFAULT_CLK_SRC;
use crate::{
asynch::AtomicWaker,
clock::Clocks,
interrupt::{self, InterruptConfigurable, InterruptHandler},
pac::timg0::RegisterBlock,
peripheral::Peripheral,
peripherals::{Interrupt, TIMG0},
private::Sealed,
system::PeripheralClockControl,
time::{Duration, Instant, Rate},
};
const NUM_TIMG: usize = 1 + cfg!(timg1) as usize;
cfg_if::cfg_if! {
if #[cfg(all(timg_timer1, not(any(esp32, esp32s2))))] {
use crate::sync::{lock, RawMutex};
static INT_ENA_LOCK: [RawMutex; NUM_TIMG] = [const { RawMutex::new() }; NUM_TIMG];
}
}
#[cfg_attr(not(timg_timer1), doc = "a general purpose timer")]
#[cfg_attr(timg_timer1, doc = "2 timers")]
pub struct TimerGroup<T>
where
T: TimerGroupInstance,
{
_timer_group: PhantomData<T>,
pub timer0: Timer,
#[cfg(timg_timer1)]
pub timer1: Timer,
pub wdt: Wdt<T>,
}
#[doc(hidden)]
pub trait TimerGroupInstance {
fn id() -> u8;
fn register_block() -> *const RegisterBlock;
fn configure_src_clk();
fn enable_peripheral();
fn reset_peripheral();
fn configure_wdt_src_clk();
fn wdt_interrupt() -> Interrupt;
}
impl TimerGroupInstance for TIMG0 {
fn id() -> u8 {
0
}
#[inline(always)]
fn register_block() -> *const RegisterBlock {
Self::regs()
}
fn configure_src_clk() {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
} else if #[cfg(any(esp32c2, esp32c3, esp32s2, esp32s3))] {
unsafe {
(*<Self as TimerGroupInstance>::register_block())
.t(0)
.config()
.modify(|_, w| w.use_xtal().clear_bit());
}
} else if #[cfg(any(esp32c6, esp32h2))] {
crate::peripherals::PCR::regs()
.timergroup0_timer_clk_conf()
.modify(|_, w| unsafe { w.tg0_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
}
}
}
fn enable_peripheral() {
PeripheralClockControl::enable(crate::system::Peripheral::Timg0);
}
fn reset_peripheral() {
}
fn configure_wdt_src_clk() {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3))] {
} else if #[cfg(any(esp32c2, esp32c3))] {
unsafe {
(*<Self as TimerGroupInstance>::register_block())
.wdtconfig0()
.modify(|_, w| w.wdt_use_xtal().clear_bit());
}
} else if #[cfg(any(esp32c6, esp32h2))] {
crate::peripherals::PCR::regs()
.timergroup0_wdt_clk_conf()
.modify(|_, w| unsafe { w.tg0_wdt_clk_sel().bits(1) });
}
}
}
fn wdt_interrupt() -> Interrupt {
Interrupt::TG0_WDT_LEVEL
}
}
#[cfg(timg1)]
impl TimerGroupInstance for crate::peripherals::TIMG1 {
fn id() -> u8 {
1
}
#[inline(always)]
fn register_block() -> *const RegisterBlock {
Self::regs()
}
fn configure_src_clk() {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32c2, esp32c3))] {
} else if #[cfg(any(esp32c6, esp32h2))] {
crate::peripherals::PCR::regs()
.timergroup1_timer_clk_conf()
.modify(|_, w| unsafe { w.tg1_timer_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
} else if #[cfg(any(esp32s2, esp32s3))] {
unsafe {
(*<Self as TimerGroupInstance>::register_block())
.t(1)
.config()
.modify(|_, w| w.use_xtal().clear_bit());
}
}
}
}
fn enable_peripheral() {
PeripheralClockControl::enable(crate::system::Peripheral::Timg1);
}
fn reset_peripheral() {
PeripheralClockControl::reset(crate::system::Peripheral::Timg1)
}
fn configure_wdt_src_clk() {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2, esp32s3, esp32c2, esp32c3))] {
} else if #[cfg(any(esp32c6, esp32h2))] {
crate::peripherals::PCR::regs()
.timergroup1_wdt_clk_conf()
.modify(|_, w| unsafe { w.tg1_wdt_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) });
}
}
}
fn wdt_interrupt() -> Interrupt {
Interrupt::TG1_WDT_LEVEL
}
}
impl<T> TimerGroup<T>
where
T: TimerGroupInstance,
{
pub fn new(_timer_group: T) -> Self {
T::reset_peripheral();
T::enable_peripheral();
T::configure_src_clk();
Self {
_timer_group: PhantomData,
timer0: Timer {
timer: 0,
tg: T::id(),
register_block: T::register_block(),
},
#[cfg(timg_timer1)]
timer1: Timer {
timer: 1,
tg: T::id(),
register_block: T::register_block(),
},
wdt: Wdt::new(),
}
}
}
impl super::Timer for Timer {
fn start(&self) {
self.set_counter_active(false);
self.set_alarm_active(false);
self.reset_counter();
self.set_counter_decrementing(false);
self.set_counter_active(true);
self.set_alarm_active(true);
}
fn stop(&self) {
self.set_counter_active(false);
}
fn reset(&self) {
self.reset_counter()
}
fn is_running(&self) -> bool {
self.is_counter_active()
}
fn now(&self) -> Instant {
self.now()
}
fn load_value(&self, value: Duration) -> Result<(), Error> {
self.load_value(value)
}
fn enable_auto_reload(&self, auto_reload: bool) {
self.set_auto_reload(auto_reload)
}
fn enable_interrupt(&self, state: bool) {
self.set_interrupt_enabled(state);
}
fn clear_interrupt(&self) {
self.clear_interrupt()
}
fn is_interrupt_set(&self) -> bool {
self.is_interrupt_set()
}
fn async_interrupt_handler(&self) -> InterruptHandler {
match (self.timer_group(), self.timer_number()) {
(0, 0) => asynch::timg0_timer0_handler,
#[cfg(timg_timer1)]
(0, 1) => asynch::timg0_timer1_handler,
#[cfg(timg1)]
(1, 0) => asynch::timg1_timer0_handler,
#[cfg(all(timg_timer1, timg1))]
(1, 1) => asynch::timg1_timer1_handler,
_ => unreachable!(),
}
}
fn peripheral_interrupt(&self) -> Interrupt {
match (self.timer_group(), self.timer_number()) {
(0, 0) => Interrupt::TG0_T0_LEVEL,
#[cfg(timg_timer1)]
(0, 1) => Interrupt::TG0_T1_LEVEL,
#[cfg(timg1)]
(1, 0) => Interrupt::TG1_T0_LEVEL,
#[cfg(all(timg_timer1, timg1))]
(1, 1) => Interrupt::TG1_T1_LEVEL,
_ => unreachable!(),
}
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.set_interrupt_handler(handler)
}
fn waker(&self) -> &AtomicWaker {
asynch::waker(self)
}
}
impl Peripheral for Timer {
type P = Self;
#[inline]
unsafe fn clone_unchecked(&self) -> Self::P {
core::ptr::read(self as *const _)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Timer {
register_block: *const RegisterBlock,
timer: u8,
tg: u8,
}
impl Sealed for Timer {}
unsafe impl Send for Timer {}
impl Timer {
pub(crate) fn set_interrupt_handler(&self, handler: InterruptHandler) {
let interrupt = match (self.timer_group(), self.timer_number()) {
(0, 0) => Interrupt::TG0_T0_LEVEL,
#[cfg(timg_timer1)]
(0, 1) => Interrupt::TG0_T1_LEVEL,
#[cfg(timg1)]
(1, 0) => Interrupt::TG1_T0_LEVEL,
#[cfg(all(timg_timer1, timg1))]
(1, 1) => Interrupt::TG1_T1_LEVEL,
_ => unreachable!(),
};
for core in crate::system::Cpu::other() {
crate::interrupt::disable(core, interrupt);
}
unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) };
unwrap!(interrupt::enable(interrupt, handler.priority()));
}
fn register_block(&self) -> &RegisterBlock {
unsafe { &*self.register_block }
}
fn timer_group(&self) -> u8 {
self.tg
}
fn timer_number(&self) -> u8 {
self.timer
}
fn t(&self) -> &crate::pac::timg0::T {
self.register_block().t(self.timer_number().into())
}
fn reset_counter(&self) {
let t = 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) });
}
fn set_counter_active(&self, state: bool) {
self.t().config().modify(|_, w| w.en().bit(state));
}
fn is_counter_active(&self) -> bool {
self.t().config().read().en().bit_is_set()
}
fn set_counter_decrementing(&self, decrementing: bool) {
self.t()
.config()
.modify(|_, w| w.increase().bit(!decrementing));
}
fn set_auto_reload(&self, auto_reload: bool) {
self.t()
.config()
.modify(|_, w| w.autoreload().bit(auto_reload));
}
fn set_alarm_active(&self, state: bool) {
self.t().config().modify(|_, w| w.alarm_en().bit(state));
}
fn load_value(&self, value: Duration) -> Result<(), Error> {
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
let clk_src = Clocks::get().pll_48m_clock;
} else {
let clk_src = Clocks::get().apb_clock;
}
}
let ticks = timeout_to_ticks(value, clk_src, self.divider());
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.t();
t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) });
t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) });
Ok(())
}
fn clear_interrupt(&self) {
self.register_block()
.int_clr()
.write(|w| w.t(self.timer).clear_bit_by_one());
let periodic = self.t().config().read().autoreload().bit_is_set();
self.set_alarm_active(periodic);
}
fn now(&self) -> Instant {
let t = self.t();
t.update().write(|w| w.update().set_bit());
while t.update().read().update().bit_is_set() {
}
let value_lo = t.lo().read().bits() as u64;
let value_hi = t.hi().read().bits() as u64;
let ticks = (value_hi << 32) | value_lo;
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
let clk_src = Clocks::get().pll_48m_clock;
} else {
let clk_src = Clocks::get().apb_clock;
}
}
let micros = ticks_to_timeout(ticks, clk_src, self.divider());
Instant::from_ticks(micros)
}
fn divider(&self) -> u32 {
let t = self.t();
match t.config().read().divider().bits() {
0 => 65536,
1 | 2 => 2,
n => n as u32,
}
}
fn is_interrupt_set(&self) -> bool {
self.register_block()
.int_raw()
.read()
.t(self.timer)
.bit_is_set()
}
fn set_interrupt_enabled(&self, state: bool) {
cfg_if::cfg_if! {
if #[cfg(any(esp32, esp32s2))] {
self.register_block()
.t(self.timer as usize)
.config()
.modify(|_, w| w.level_int_en().bit(state));
} else if #[cfg(timg_timer1)] {
lock(&INT_ENA_LOCK[self.timer_group() as usize], || {
self.register_block()
.int_ena()
.modify(|_, w| w.t(self.timer_number()).bit(state));
});
} else {
self.register_block()
.int_ena()
.modify(|_, w| w.t(0).bit(state));
}
}
}
}
fn ticks_to_timeout(ticks: u64, clock: Rate, divider: u32) -> u64 {
let period: u64 = 1_000_000 * 1_000_000 / (clock.as_hz() as u64 / divider as u64);
ticks * period / 1_000_000
}
fn timeout_to_ticks(timeout: Duration, clock: Rate, divider: u32) -> u64 {
let micros = timeout.as_micros();
let period: u64 = 1_000_000 * 1_000_000 / ((clock.as_hz() / divider) as u64);
(1_000_000 * micros) / period
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub enum MwdtStageAction {
Off = 0,
Interrupt = 1,
ResetCpu = 2,
ResetSystem = 3,
}
#[derive(Debug, Clone, Copy)]
pub enum MwdtStage {
Stage0,
Stage1,
Stage2,
Stage3,
}
pub struct Wdt<TG> {
phantom: PhantomData<TG>,
}
impl<TG> Wdt<TG>
where
TG: TimerGroupInstance,
{
pub fn new() -> Self {
TG::configure_wdt_src_clk();
Self {
phantom: PhantomData,
}
}
pub fn enable(&mut self) {
unsafe { self.set_wdt_enabled(true) };
}
pub fn disable(&mut self) {
unsafe { self.set_wdt_enabled(false) };
}
pub unsafe fn set_wdt_enabled(&mut self, enabled: bool) {
let reg_block = unsafe { &*TG::register_block() };
self.set_write_protection(false);
if !enabled {
reg_block.wdtconfig0().write(|w| unsafe { w.bits(0) });
} else {
reg_block.wdtconfig0().write(|w| w.wdt_en().bit(true));
reg_block
.wdtconfig0()
.write(|w| w.wdt_flashboot_mod_en().bit(false));
#[cfg_attr(esp32, allow(unused_unsafe))]
reg_block.wdtconfig0().write(|w| unsafe {
w.wdt_en()
.bit(true)
.wdt_stg0()
.bits(MwdtStageAction::ResetSystem as u8)
.wdt_cpu_reset_length()
.bits(7)
.wdt_sys_reset_length()
.bits(7)
.wdt_stg1()
.bits(MwdtStageAction::Off as u8)
.wdt_stg2()
.bits(MwdtStageAction::Off as u8)
.wdt_stg3()
.bits(MwdtStageAction::Off as u8)
});
#[cfg(any(esp32c2, esp32c3, esp32c6))]
reg_block
.wdtconfig0()
.modify(|_, w| w.wdt_conf_update_en().set_bit());
}
self.set_write_protection(true);
}
pub fn feed(&mut self) {
let reg_block = unsafe { &*TG::register_block() };
self.set_write_protection(false);
reg_block.wdtfeed().write(|w| unsafe { w.bits(1) });
self.set_write_protection(true);
}
fn set_write_protection(&mut self, enable: bool) {
let reg_block = unsafe { &*TG::register_block() };
let wkey = if enable { 0u32 } else { 0x50D8_3AA1u32 };
reg_block
.wdtwprotect()
.write(|w| unsafe { w.wdt_wkey().bits(wkey) });
}
pub fn set_timeout(&mut self, stage: MwdtStage, timeout: Duration) {
let timeout_raw = (timeout.as_micros() * 10_000 / 125) as u32;
let reg_block = unsafe { &*TG::register_block() };
self.set_write_protection(false);
reg_block
.wdtconfig1()
.write(|w| unsafe { w.wdt_clk_prescale().bits(1) });
unsafe {
match stage {
MwdtStage::Stage0 => reg_block
.wdtconfig2()
.write(|w| w.wdt_stg0_hold().bits(timeout_raw)),
MwdtStage::Stage1 => reg_block
.wdtconfig3()
.write(|w| w.wdt_stg1_hold().bits(timeout_raw)),
MwdtStage::Stage2 => reg_block
.wdtconfig4()
.write(|w| w.wdt_stg2_hold().bits(timeout_raw)),
MwdtStage::Stage3 => reg_block
.wdtconfig5()
.write(|w| w.wdt_stg3_hold().bits(timeout_raw)),
};
}
#[cfg(any(esp32c2, esp32c3, esp32c6))]
reg_block
.wdtconfig0()
.modify(|_, w| w.wdt_conf_update_en().set_bit());
self.set_write_protection(true);
}
pub fn set_stage_action(&mut self, stage: MwdtStage, action: MwdtStageAction) {
let reg_block = unsafe { &*TG::register_block() };
self.set_write_protection(false);
match stage {
MwdtStage::Stage0 => {
reg_block
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) });
}
MwdtStage::Stage1 => {
reg_block
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) });
}
MwdtStage::Stage2 => {
reg_block
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) });
}
MwdtStage::Stage3 => {
reg_block
.wdtconfig0()
.modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) });
}
}
self.set_write_protection(true);
}
}
impl<TG> crate::private::Sealed for Wdt<TG> where TG: TimerGroupInstance {}
impl<TG> InterruptConfigurable for Wdt<TG>
where
TG: TimerGroupInstance,
{
fn set_interrupt_handler(&mut self, handler: interrupt::InterruptHandler) {
let interrupt = TG::wdt_interrupt();
unsafe {
interrupt::bind_interrupt(interrupt, handler.handler());
interrupt::enable(interrupt, handler.priority()).unwrap();
}
}
}
impl<TG> Default for Wdt<TG>
where
TG: TimerGroupInstance,
{
fn default() -> Self {
Self::new()
}
}
mod asynch {
use procmacros::handler;
use super::*;
use crate::asynch::AtomicWaker;
const NUM_WAKERS: usize = {
let timer_per_group = 1 + cfg!(timg_timer1) as usize;
NUM_TIMG * timer_per_group
};
static WAKERS: [AtomicWaker; NUM_WAKERS] = [const { AtomicWaker::new() }; NUM_WAKERS];
pub(super) fn waker(timer: &Timer) -> &AtomicWaker {
let index = (timer.timer_number() << 1) | timer.timer_group();
&WAKERS[index as usize]
}
#[inline]
fn handle_irq(timer: Timer) {
timer.set_interrupt_enabled(false);
waker(&timer).wake();
}
#[handler]
pub(crate) fn timg0_timer0_handler() {
handle_irq(Timer {
register_block: TIMG0::regs(),
timer: 0,
tg: 0,
});
}
#[cfg(timg1)]
#[handler]
pub(crate) fn timg1_timer0_handler() {
handle_irq(Timer {
register_block: TIMG1::regs(),
timer: 0,
tg: 1,
});
}
#[cfg(timg_timer1)]
#[handler]
pub(crate) fn timg0_timer1_handler() {
handle_irq(Timer {
register_block: TIMG0::regs(),
timer: 1,
tg: 0,
});
}
#[cfg(all(timg1, timg_timer1))]
#[handler]
pub(crate) fn timg1_timer1_handler() {
handle_irq(Timer {
register_block: TIMG1::regs(),
timer: 1,
tg: 1,
});
}
}
#[cfg(soc_etm)]
pub mod etm {
use super::*;
use crate::etm::{EtmEvent, EtmTask};
pub struct Event {
id: u8,
}
pub struct Task {
id: u8,
}
impl EtmEvent for Event {
fn id(&self) -> u8 {
self.id
}
}
impl Sealed for Event {}
impl EtmTask for Task {
fn id(&self) -> u8 {
self.id
}
}
impl Sealed for Task {}
pub trait Events {
fn on_alarm(&self) -> Event;
}
pub trait Tasks {
fn cnt_start(&self) -> Task;
fn cnt_stop(&self) -> Task;
fn cnt_reload(&self) -> Task;
fn cnt_cap(&self) -> Task;
fn alarm_start(&self) -> Task;
}
impl Events for Timer {
fn on_alarm(&self) -> Event {
Event {
id: 48 + self.timer_group(),
}
}
}
impl Tasks for Timer {
fn cnt_start(&self) -> Task {
Task {
id: 88 + self.timer_group(),
}
}
fn alarm_start(&self) -> Task {
Task {
id: 90 + self.timer_group(),
}
}
fn cnt_stop(&self) -> Task {
Task {
id: 92 + self.timer_group(),
}
}
fn cnt_reload(&self) -> Task {
Task {
id: 94 + self.timer_group(),
}
}
fn cnt_cap(&self) -> Task {
Task {
id: 96 + self.timer_group(),
}
}
}
}