Analog & PWM

Three thin classes for varying signals: ADC (analog in), DAC (analog out), and PWM (pulse-width out). All three follow the HAL rule — construction is the channel setup, and the units are Zephyr’s own: raw counts, millivolts, nanoseconds. No 0–255, no 0–1023.


ADC — ADC

Construction sets up the channel: gain and reference are constructor options, defaulting to the pair your chip’s driver validates against when omitted.

import { ADC } from '@typecad/hal';
import { A1 } from '@typecad/board';

const sense = new ADC(A1, {
  gain: ADC.GAIN_1_3,
  reference: ADC.REF_INTERNAL,
});

const counts = sense.read();              // raw counts at the chip's resolution
const mv = sense.readMillivolts();        // converted to millivolts

There is no setReference() — Zephyr applies the reference at channel-setup time, and the class does not pretend otherwise. The gain and reference tokens are Zephyr’s enum names under the ADC. namespace (ADC_GAIN_1_4ADC.GAIN_1_4); the token set is generated from the Zephyr headers, so a misspelling is an editor error and an invalid pair is a build error naming the valid tokens for your chip.

DAC — DAC

Construction carries the resolution in bits (omitted = your chip’s channel resolution). write() takes the raw code — 0–255 for an 8-bit channel, 0–4095 for 12-bit:

import { DAC } from '@typecad/hal';

const out = new DAC(DAC0);
out.write(128);       // mid-scale on an 8-bit channel

PWM — PWM

The period is a required construction fact, in nanoseconds — the channel’s time base exists before any pulse does:

import { PWM } from '@typecad/hal';

const servo = new PWM(6, { periodNs: 20_000_000 });    // 50 Hz
servo.setPulse(1_500_000);                              // 1.5 ms — center

const dimmer = new PWM(7, { periodNs: 1_000_000 });    // 1 kHz LED dimming
dimmer.setDuty(0.5);                                    // fraction 0.0–1.0
  • setPulse(ns) — the verbatim pwm_set_pulse_dt.
  • setDuty(fraction) — sugar over the constructed period, still one call.
  • setPeriod(ns) — changes the period; the pulse resets to idle, so follow it with setPulse/setDuty.

On ESP32 targets the PWM channels are assigned at build time to the pins your program actually drives; a pin outside the chip’s PWM-capable set is flagged at build time, naming the pins that can.

When your board’s facts don’t cover a pin — or you know a routing the catalog doesn’t — you can supply the routing yourself: a project facts file or per-construction overrides. Both are facts, not bypasses. See Custom Pin Facts.


API Reference

MemberDescription
new ADC(pin, opts?)gain / referenceADC.GAIN_* / ADC.REF_* tokens.
read() / readMillivolts()Raw counts / millivolts.
new DAC(pin, opts?)resolution in bits; omitted = chip default.
write(value)Raw code at the channel’s resolution.
new PWM(pin, { periodNs })Period is required (ns).
setPulse(ns) / setDuty(f) / setPeriod(ns)Pulse control; one Zephyr call each.