Step 24 of 56
Timers and pulse counting
A 54-bit counter with an alarm, and four edge counters — the two ways to measure time and events without the CPU watching.
General-purpose timers
Two timer groups, TIMG0 and TIMG1. Each has one 54-bit up/down counter clocked from the APB clock through a 16-bit prescaler, with a programmable alarm. (The per-group watchdogs are separate and this driver does not touch them.)
type Timer_Index is range 0 .. 1; -- 0 = TIMG0, 1 = TIMG1
type Ticks is new Interfaces.Unsigned_64;
procedure Configure (T : in out Timer; Tick_Hz : Positive := 1_000_000);
procedure Start (T : Timer);
procedure Stop (T : Timer);
procedure Reset (T : Timer); -- reload to 0, running or not
function Value (T : Timer) return Ticks; -- latched, then read
procedure Set_Alarm (T : Timer; At_Ticks : Ticks);
function Alarm_Fired (T : Timer) return Boolean;
procedure Clear_Alarm (T : Timer);
The default 1 MHz tick makes Value read directly in
microseconds. Fifty-four bits at that rate is a bit over five centuries, so
wrap-around is not a design consideration.
Value latches before reading, which matters on a 54-bit counter
sampled by a 32-bit CPU: without the latch you could catch the low word after a
carry and the high word before it, and read a time that never existed.
This is not the runtime's clock.
Ada.Real_Time.Clock and delay until are served by the
runtime's own tick on the systimer — see
step 6. These timers are an independent
measurement resource for your application, which is exactly what makes them
useful for cross-checking the runtime: ./x run esp32s3_timer_count
does that against the wall clock.
Alarm_Fired stays set until Clear_Alarm, so a polled
loop cannot miss the event between samples.
PCNT: counting edges
Four counter units, each counting into a signed 16-bit counter as edges arrive on its input pin. The classic uses are a tachometer, a flow meter, or a quadrature encoder.
type Unit_Index is range 0 .. 3;
procedure Configure (U : in out Unit; Pin : ESP32S3.GPIO.Pin_Id;
Both_Edges : Boolean := False);
function Count (U : Unit) return Integer; -- signed; wraps at +/- 32768
By default each rising edge counts; Both_Edges counts falling
ones too, doubling the resolution of a symmetric signal.
The counter is 16-bit and wraps at ±32768. On a fast input that is not long: 10 kHz overflows in about three seconds. Poll often enough to catch every wrap, or the count you accumulate will be wrong in a way that looks plausible.
This driver exposes the common "count edges on a pin" case; the per-unit direction-control input and the threshold-event comparators are left at their pass-through defaults.