Skip to content

esp32_timg

TIMG0 and TIMG1: four 64-bit general-purpose timers.

The kernel’s timer::once/every ride the 1 ms scheduler tick, so nothing sub-millisecond is expressible and every period is quantised to the tick and to scheduler load. These are the hardware underneath a timer that has to be accurate: a 16-bit prescaler off the 80 MHz APB clock, a 64-bit counter, and an alarm that fires an interrupt.

Two groups, two timers each. Each timer is wholly independent — its own divider, counter, alarm and interrupt source.

The scheduler tick is the Xtensa core’s own CCOMPARE, in arch-xtensa, and stays that way. A tick that lived in a peripheral would stop when that peripheral was gated off, and the tick is the one clock the kernel cannot do without. These are for everything else.

Offsets and bit positions from esp-idf soc/timer_group_reg.h. T0 sits at the group base and T1 is 0x24 further on, so one set of offsets serves both.

Register Offset Notes
CONFIG 0x00 EN 31, INCREASE 30, AUTORELOAD 29, DIVIDER [28:13], LEVEL_INT_EN 11, ALARM_EN 10
LO / HI 0x04 / 0x08 the counter, only valid after a latch
UPDATE 0x0c write anything to latch the counter into LO/HI
ALARMLO / ALARMHI 0x10 / 0x14 when to fire
LOADLO / LOADHI 0x18 / 0x1c value a reload installs
LOAD 0x20 write anything to install it

Group-wide, not per timer: INT_ENA 0x98, INT_RAW 0x9c, INT_ST 0xa0, INT_CLR 0xa4, with T0 in bit 0 and T1 in bit 1.

  • lact — The LAC timer — a timer group’s fifth counter, and a programmable alarm.
struct Timg { /* private fields */ }

One general-purpose timer.

enum Group {
Timg0,
Timg1,
}

Which timer group.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

enum Mode {
OneShot,
Periodic,
}

What happens when the alarm fires.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

enum Timer {
T0,
T1,
}

Which timer within a group.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

enum TimerError {
UnsupportedResolution,
UnsupportedPeriod,
}

Why a timer could not be configured.

Implements: Clone, Copy, Debug, Eq, From, PartialEq, StructuralPartialEq

unsafe fn clear_interrupt(group: Group, timer: Timer)

Acknowledge a timer’s alarm without holding a [Timg].

For a top-half, which cannot take a lock and so cannot reach a shared handle. The registers are the state; nothing else is needed to clear one bit.

In IRAM, because a top-half that registered as IRAM-safe has to be able to acknowledge its alarm during a flash operation. One register write, and leaving it in flash would make an otherwise-correct IRAM handler stop the core on its very first instruction that mattered.

Writes the group’s interrupt-clear register.

References: Group, Timer

fn divider_for(resolution_hz: u32) -> Result<u32, TimerError>

Prescaler for a requested counter frequency.

The field is 16 bits and the hardware treats 0 as 65536, so the encodable range is 2..=65536 — esp-idf asserts the same lower bound. A divider of 1 is not “no division”; it is rejected.

References: TimerError

unsafe fn rearm(group: Group, timer: Timer)

Re-arm a timer’s alarm without holding a [Timg].

The companion to [clear_interrupt], and needed in the same place: a periodic alarm’s handler has to put ALARM_EN back every time, and a top-half cannot reach a shared handle.

In IRAM, and for the same reason as [clear_interrupt]: a periodic alarm whose handler runs during a flash operation has to put ALARM_EN back, and it cannot reach out to flash to do it.

Read-modify-writes the timer’s config register.

References: Group, Timer