Skip to content

machine/esp32: add ADC driver - #5595

Open
zombieleet wants to merge 1 commit into
tinygo-org:devfrom
zombieleet:esp32-adc
Open

machine/esp32: add ADC driver#5595
zombieleet wants to merge 1 commit into
tinygo-org:devfrom
zombieleet:esp32-adc

Conversation

@zombieleet

@zombieleet zombieleet commented Aug 15, 2026

Copy link
Copy Markdown

machine/esp32: add ADC driver

Adds SAR ADC1 support for the original Xtensa ESP32. Until now machine.ADC
existed as a type on this target but had no InitADC, Configure or Get,
so any program using it failed to compile:

undefined: machine.InitADC
a.Get undefined (type machine.ADC has no field or method Get)

What it does

Implements the standard three-symbol ADC contract for //go:build esp32:

func InitADC()
func (a ADC) Configure(config ADCConfig) error
func (a ADC) Get() uint16

All eight ADC1 channels are supported:

Channel 0 1 2 3 4 5 6 7
GPIO 36 37 38 39 32 33 34 35

Get() returns the 12-bit sample scaled to 0..65520 (uint16(raw&0xfff) << 4), matching the ESP32-C3, S3 and C6 drivers.

Conversions run through the RTC controller under software control:
SAR1_DIG_FORCE selects the RTC controller, MEAS1_START_FORCE and
SAR1_EN_PAD_FORCE hand triggering and channel selection to software, then
MEAS1_START_SAR 0→1 starts a conversion and the result is read from
MEAS1_DATA_SAR. This mirrors the sequence in machine_esp32s3_adc.go.

Decisions worth reviewing

Single file, no PinAnalog. The sibling drivers call
a.Pin.Configure(PinConfig{Mode: PinAnalog}). On this chip the analog pads are
spread across three unrelated RTC_IO registers, SENSOR_PADS (GPIO36-39),
XTAL_32K_PAD (GPIO32/33) and ADC_PAD (GPIO34/35), so routing that through
the generic pin path would put a large ADC-specific switch into
machine_esp32.go. Pad setup is kept local as configureADCPad() instead,
keeping this to one new file. Happy to switch to PinAnalog if preferred.

No ADCn pin constants. board_esp32-coreboard-v2.go already defines
ADC0-ADC3, so adding chip-level constants to machine_esp32.go would
conflict with it. Also, this chip's ADC pins are non-contiguous, so the
ADC0 = GPIO1 style used on the C3/S3 does not transfer cleanly. Left alone
pending a naming decision.

Uncalibrated. No eFuse reads and no self-calibration, unlike the C3/S3/C6
drivers. Values are raw; the file documents that accurate voltage mapping needs
a two-point calibration in user code. Calibration can be added later without
changing the API.

ADC2 omitted. It is shared with the Wi-Fi radio on this chip and cannot be
used reliably while the radio is active. The registers are present, so it can
be added later if there is demand.

Testing

Hardware: ESP32 Coreboard V2, photoresistor divider on GPIO36.

Sweeping the light level across its range, 142 samples:

min=5056   max=59824   swing=54768   avg=29600

All eight channels read independently, with only GPIO36 connected:

GPIO36 ch0 = 34768 (12bit 2161)   <- connected sensor, stable
GPIO37 ch1 =  2672 (12bit  147)
GPIO38 ch2 =     0 (12bit    0)
GPIO39 ch3 =  2320 (12bit  128)
GPIO32 ch4 = 12000 (12bit  733)
GPIO33 ch5 =  2320 (12bit  140)
GPIO34 ch6 =     0 (12bit    0)
GPIO35 ch7 =     0 (12bit    0)

Error paths:

invalid-pin Configure -> error OK: invalid ADC pin for ESP32
invalid-pin Get() = 0 (expect 0)

48 conversions across all three pad register groups with no hangs.

Scope of the testing: only GPIO36 (channel 0) had a signal connected. The
other seven channels were confirmed to convert and to return independent
values, so channel selection and pad setup work, but they have not been checked
against a known input. The XTAL_32K_PAD group used for GPIO32/GPIO33 is the
one I would most like a second pair of eyes on, since those pads are configured
differently from the rest. Happy to test specific channels if a reviewer wants
particular numbers.

Smoke test

Adds examples/adc for esp32-coreboard-v2 to the XTENSA block of
GNUmakefile, since that board already defines the ADC2 alias the example
uses:

$(TINYGO) build -size short -o test.bin -target=esp32-coreboard-v2  examples/adc
   code    data     bss |   flash     ram
   3886       0    4160 |    3886    4160

Implements ADC1 on the Xtensa ESP32: InitADC, Configure and Get for
GPIO36, GPIO37, GPIO38, GPIO39, GPIO32, GPIO33, GPIO34 and GPIO35
(channels 0-7). The ADC pins are not contiguous on this chip, so the
pin to channel mapping is a lookup rather than arithmetic as on the
ESP32-S3.

Conversions are driven by the RTC controller under software control,
and Get returns the 12-bit sample scaled to 0..65520 to match the
other ESP ADC drivers.

The analog pads are spread over three unrelated RTC_IO registers
(SENSOR_PADS, XTAL_32K_PAD and ADC_PAD), so pad setup is kept local to
this file rather than adding a PinAnalog mode to machine_esp32.go.
That keeps the change to a single file.

Values are raw and uncalibrated. Unlike the ESP32-C3, S3 and C6
drivers there is no eFuse or self-calibration step; accurate voltage
mapping should be done with a two-point calibration in user code.

ADC2 is not implemented. On the ESP32 it is shared with the Wi-Fi
radio and cannot be used reliably while the radio is active.

Tested on an ESP32 Coreboard V2 with a photoresistor divider on
GPIO36. Readings swept 5056..59824 over the light range, all eight
channels returned independent values, and an invalid pin returned an
error from Configure and 0 from Get.

Signed-off-by: zombieleet <osikwemhev@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant