Step 25 of 56
Analog in: the SAR ADC and capacitive touch
Two 12-bit converters on fixed pins, and fourteen touch channels that measure a pad's capacitance by counting — both living in the RTC domain.
The SAR ADC
Two units, each with up to ten 12-bit channels on fixed GPIOs. The mapping is not configurable, so the pin decides the channel:
ADC1 channel n -> GPIO (n + 1) -- ch0 = GPIO1 .. ch9 = GPIO10
ADC2 channel n -> GPIO (n + 11) -- ch0 = GPIO11 .. ch9 = GPIO20
function Channel_Pin (Unit : ADC_Unit; Ch : Channel_Index)
return ESP32S3.GPIO.Pin_Id; -- ask, rather than hard-code the arithmetic
Conversions are software-triggered single shots through the RTC controller, each returning a raw 12-bit code:
type Attenuation is (Db_0, Db_2_5, Db_6, Db_12); -- ~1.1 V .. ~3.3 V full scale
subtype Raw_Value is Natural range 0 .. 4095;
function Read (R : Reader; Ch : Channel_Index;
Atten : Attenuation := Db_12) return Raw_Value;
Attenuation is per channel and per read, so one unit can serve a 1 V
sensor and a 3.3 V divider without reconfiguration between them. The default
Db_12 gives roughly the full 3.3 V range; a lower attenuation
on a small signal buys real resolution.
The result is a raw code, not a voltage. The
driver does not pretend to give you volts, because an accurate conversion needs
the per-chip calibration data and the attenuation curve is not linear at the
extremes. Cal_Code exposes the self-calibrated initial code and
Last_Done whether the most recent conversion completed — both
diagnostics for deciding whether a reading is trustworthy.
Capacitive touch
Fourteen channels on GPIO1 .. GPIO14, and the numbering is the one mnemonic you need: touch channel n is wired to GPIO n.
type Channel is range 1 .. 14;
function Pad (Ch : Channel) return ESP32S3.GPIO.Pin_Id;
procedure Setup; -- bring the controller up, start the FSM
procedure Enable (Ch : Channel); -- put the pad in touch mode, add to the scan
function Read (Ch : Channel) return Natural; -- latest raw count
Each channel measures its pad's self-capacitance by counting charge/discharge
cycles in a fixed window. A finger near the pad raises the capacitance and
changes the count. An FSM scans the enabled channels continuously on the RTC
timer, so Read returns the latest sample rather than triggering
one — it never blocks.
Detection is relative, not absolute: Touched compares a channel's
current count against a reference you supply. That reference is
board-specific — pad size, overlay thickness and stray capacitance all move
it — so sample a known-untouched value at startup rather than hard-coding
a threshold from someone else's board.
Touch needs no tasking runtime: it is register pokes into the
RTC/SENS domain, so unlike most of the HAL it works under
light-tasking too.