@typecad/hal 1.0.0-alpha.13 → 1.0.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +145 -87
  2. package/dist/adc-pin.d.ts +53 -0
  3. package/dist/adc-pin.js +69 -0
  4. package/dist/async.d.ts +3 -11
  5. package/dist/async.js +3 -15
  6. package/dist/ble.d.ts +42 -78
  7. package/dist/ble.js +79 -98
  8. package/dist/counter.d.ts +16 -0
  9. package/dist/counter.js +32 -0
  10. package/dist/dac-pin.d.ts +13 -0
  11. package/dist/dac-pin.js +21 -0
  12. package/dist/emit.d.ts +123 -196
  13. package/dist/emit.js +146 -208
  14. package/dist/fs.d.ts +12 -31
  15. package/dist/fs.js +30 -40
  16. package/dist/gpio-pin.d.ts +46 -0
  17. package/dist/gpio-pin.js +77 -0
  18. package/dist/gpio.d.ts +0 -103
  19. package/dist/gpio.js +16 -227
  20. package/dist/http.d.ts +38 -39
  21. package/dist/http.js +53 -78
  22. package/dist/i2c-target.d.ts +28 -0
  23. package/dist/i2c-target.js +52 -0
  24. package/dist/i2c.d.ts +5 -43
  25. package/dist/i2c.js +17 -130
  26. package/dist/index.d.ts +30 -42
  27. package/dist/index.js +27 -39
  28. package/dist/math.d.ts +0 -13
  29. package/dist/math.js +9 -13
  30. package/dist/mqtt.d.ts +15 -25
  31. package/dist/mqtt.js +32 -28
  32. package/dist/power.d.ts +9 -17
  33. package/dist/power.js +22 -20
  34. package/dist/preferences.d.ts +17 -25
  35. package/dist/preferences.js +54 -52
  36. package/dist/pulse.d.ts +4 -4
  37. package/dist/pulse.js +6 -2
  38. package/dist/pwm-pin.d.ts +24 -0
  39. package/dist/pwm-pin.js +38 -0
  40. package/dist/random.d.ts +0 -2
  41. package/dist/random.js +14 -20
  42. package/dist/sensor-catalog.generated.d.ts +997 -0
  43. package/dist/sensor-catalog.generated.js +764 -0
  44. package/dist/sensor.d.ts +46 -0
  45. package/dist/sensor.js +52 -0
  46. package/dist/shift-pin.d.ts +2 -0
  47. package/dist/shift-pin.js +13 -0
  48. package/dist/spi-target.d.ts +25 -0
  49. package/dist/spi-target.js +42 -0
  50. package/dist/spi.d.ts +8 -26
  51. package/dist/spi.js +15 -87
  52. package/dist/thread.d.ts +18 -0
  53. package/dist/thread.js +36 -0
  54. package/dist/time.d.ts +23 -0
  55. package/dist/time.js +43 -0
  56. package/dist/timing.d.ts +17 -7
  57. package/dist/timing.js +17 -7
  58. package/dist/types.d.ts +4 -34
  59. package/dist/types.js +5 -47
  60. package/dist/uart-port.d.ts +26 -0
  61. package/dist/uart-port.js +52 -0
  62. package/dist/uart.d.ts +0 -17
  63. package/dist/uart.js +10 -53
  64. package/dist/usb.d.ts +26 -0
  65. package/dist/usb.js +66 -0
  66. package/dist/watchdog.d.ts +12 -0
  67. package/dist/watchdog.js +28 -0
  68. package/dist/wifi.d.ts +88 -64
  69. package/dist/wifi.js +112 -126
  70. package/dist/zephyr-tokens.generated.d.ts +8 -0
  71. package/dist/zephyr-tokens.generated.js +73 -0
  72. package/package.json +7 -8
  73. package/src/adc-pin.ts +89 -0
  74. package/src/async.ts +3 -16
  75. package/src/ble.ts +84 -111
  76. package/src/counter.ts +40 -0
  77. package/src/dac-pin.ts +28 -0
  78. package/src/emit.ts +157 -238
  79. package/src/fs.ts +47 -54
  80. package/src/gpio-pin.ts +91 -0
  81. package/src/gpio.ts +49 -320
  82. package/src/http.ts +75 -95
  83. package/src/i2c-target.ts +63 -0
  84. package/src/i2c.ts +29 -167
  85. package/src/index.ts +32 -46
  86. package/src/math.ts +9 -14
  87. package/src/mqtt.ts +39 -31
  88. package/src/preferences.ts +52 -61
  89. package/src/pwm-pin.ts +54 -0
  90. package/src/random.ts +14 -21
  91. package/src/sensor-catalog.generated.ts +1238 -0
  92. package/src/sensor.ts +80 -0
  93. package/src/shift-pin.ts +16 -0
  94. package/src/spi-target.ts +53 -0
  95. package/src/spi.ts +27 -121
  96. package/src/thread.ts +44 -0
  97. package/src/time.ts +50 -0
  98. package/src/types.ts +14 -89
  99. package/src/uart-port.ts +64 -0
  100. package/src/usb.ts +82 -0
  101. package/src/watchdog.ts +35 -0
  102. package/src/wifi.ts +147 -179
  103. package/src/zephyr-tokens.generated.ts +77 -0
  104. package/src/adc.ts +0 -30
  105. package/src/capacitive.ts +0 -31
  106. package/src/constants.ts +0 -23
  107. package/src/dac.ts +0 -21
  108. package/src/eeprom.ts +0 -26
  109. package/src/interrupts.ts +0 -35
  110. package/src/mdns.ts +0 -50
  111. package/src/ota.ts +0 -44
  112. package/src/power.ts +0 -36
  113. package/src/pulse.ts +0 -63
  114. package/src/rmt.ts +0 -125
  115. package/src/shift.ts +0 -88
  116. package/src/temperature.ts +0 -20
  117. package/src/timer.ts +0 -58
  118. package/src/timing.ts +0 -67
  119. package/src/uart.ts +0 -77
  120. package/src/utils.ts +0 -17
  121. package/src/wdt.ts +0 -17
package/src/sensor.ts ADDED
@@ -0,0 +1,80 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Sensor — generic DT-bound sensor part (Zephyr's uniform sensor API)
3
+ //
4
+ // `new Sensor(SENSOR.sensirion_sht3xd, I2C1.device(0x44))` covers every part
5
+ // in the generated catalog: the constructor's facts (part token + bus +
6
+ // address) become a devicetree child node in the generated overlay, and the
7
+ // methods lower to Zephyr's one-shape sensor API (sensor_sample_fetch /
8
+ // sensor_channel_get). No per-part code exists anywhere — the catalog
9
+ // (sensor-catalog.generated.ts) is data, generated from Zephyr's own binding
10
+ // YAMLs and driver channel tables.
11
+ //
12
+ // The channel names are Zephyr verbiage minus the SENSOR_CHAN_ prefix
13
+ // (CHAN.AMBIENT_TEMP ↔ SENSOR_CHAN_AMBIENT_TEMP), so any Zephyr sample or
14
+ // binding doc greps straight into user code.
15
+ // ---------------------------------------------------------------------------
16
+
17
+ import { sensorFetch, sensorGet } from './emit.js';
18
+ import { include } from './include.js';
19
+ import type { I2CTarget } from './i2c-target.js';
20
+ import type { SPITarget } from './spi-target.js';
21
+ import type { SensorChannelOf, SensorBusOf } from './sensor-catalog.generated.js';
22
+
23
+ /** Part ids the catalog knows (the Sensor<P> narrowing keys). */
24
+ type SensorPartId = keyof SensorChannelOf;
25
+
26
+ /** The bus-device argument a part accepts, from its generated bus map:
27
+ * SPI-only parts take SPITarget, I2C-only take I2CTarget, dual-bus either. */
28
+ type BusDeviceFor<B extends string> = B extends 'spi' ? SPITarget : B extends 'i2c' ? I2CTarget : I2CTarget | SPITarget;
29
+ export type SensorBusDevice<P extends SensorPartId = SensorPartId> = BusDeviceFor<SensorBusOf[P]>;
30
+
31
+ /** Construction options. */
32
+ export interface SensorOptions {
33
+ /** SPI clock in Hz, SPI parts only. Default 1 MHz (safe for every
34
+ * catalog part); raise to the breakout's datasheet maximum. */
35
+ spiHz?: number;
36
+ /** SPI mode (CPOL/CPHA bits), 0-3. Default 0. */
37
+ mode?: 0 | 1 | 2 | 3;
38
+ /** Alert GPIO, for parts whose binding declares alert-gpios. */
39
+ alert?: number;
40
+ }
41
+
42
+ export class Sensor<P extends SensorPartId = SensorPartId> {
43
+ private readonly _part: string;
44
+ private readonly _bus: string = '';
45
+ private readonly _port: number = 0;
46
+ private readonly _kind: string = 'i2c';
47
+ private readonly _spiHz: number = 1000000;
48
+ private readonly _mode: number = 0;
49
+ private readonly _alert: number = -1;
50
+
51
+ /** Construct a sensor handle. `part` is a `SENSOR.<name>` token;
52
+ * `dev` is the bus device (e.g. `I2C1.device(0x44)`) carrying the bus
53
+ * and address. The transpiler resolves the bus instance and address from
54
+ * `dev` — the class fields are the IR carrier, not runtime state.
55
+ * The `P` parameter is editor-only: it narrows `get()` to this part's
56
+ * channels (from the generated SensorChannelOf map); the transpiler
57
+ * re-validates at build time with the driver's full channel list. */
58
+ constructor(part: P, dev: SensorBusDevice<P>, opts?: SensorOptions) {
59
+ this._part = part;
60
+ void dev;
61
+ void opts;
62
+ }
63
+
64
+ /** Fetch a fresh sample (sensor_sample_fetch). Reads reflect the last
65
+ * fetch — Zephyr's own fetch/get split, kept verbatim. */
66
+ fetch(): void {
67
+ include('<zephyr/drivers/sensor.h>');
68
+ sensorFetch(this._part, this._bus, this._port, this._kind, this._spiHz, this._mode, this._alert);
69
+ }
70
+
71
+ /** Read one channel from the fetched sample (sensor_channel_get).
72
+ * Returns the value as a double (val1 + val2/1e6). The parameter is
73
+ * narrowed to this part's channels when constructed from a literal
74
+ * SENSOR token — `sht3x.get(` completes AMBIENT_TEMP | HUMIDITY and
75
+ * rejects the rest in the editor. */
76
+ get(chan: SensorChannelOf[P]): number {
77
+ include('<zephyr/drivers/sensor.h>');
78
+ return sensorGet(this._part, this._bus, this._port, this._kind, this._spiHz, this._mode, this._alert, chan);
79
+ }
80
+ }
@@ -0,0 +1,16 @@
1
+ // ---------------------------------------------------------------------------
2
+ // shiftOut / shiftIn — the thin bit-bang shift (the legacy shiftOut/shiftIn
3
+ // shape, Zephyr-lowered): clock idles low; each bit sets data then pulses the
4
+ // clock. For real SPI, use SPITarget. Pin arguments accept board pin aliases
5
+ // (resolved to their numbers by the transpiler).
6
+ // ---------------------------------------------------------------------------
7
+
8
+ import { gpioShiftOut, gpioShiftIn } from './emit.js';
9
+
10
+ export function shiftOut(dataPin: number, clockPin: number, value: number, msbFirst: boolean = true): void {
11
+ gpioShiftOut(dataPin, clockPin, value, msbFirst);
12
+ }
13
+
14
+ export function shiftIn(dataPin: number, clockPin: number, msbFirst: boolean = true): number {
15
+ return gpioShiftIn(dataPin, clockPin, msbFirst);
16
+ }
@@ -0,0 +1,53 @@
1
+ // ---------------------------------------------------------------------------
2
+ // SPITarget — the thin Zephyr-shaped SPI device
3
+ //
4
+ // A chip-select peer on an SPI bus, addressed the Zephyr way: construction
5
+ // emits a devicetree child node (cs-gpios index + spi-max-frequency + mode
6
+ // bits — the same machinery sensors ride), and the verbs lower to
7
+ // spi_transceive_dt / spi_write_dt against a static spi_dt_spec. Hardware
8
+ // CS, no manual GPIO toggling, no runtime spi_config rebuilding.
9
+ //
10
+ // `bus` is the board's bus instance export (SPI0/SPI1), `cs` the chip-select
11
+ // pin, and the options are the wire facts: `hz` (spi-max-frequency) and
12
+ // `mode` (CPOL/CPHA bits 0–3 → spi-cpol/spi-cpha).
13
+ // ----------------------------------------------------------------------------
14
+
15
+ import { spiTransceiveDt, spiWriteDt, spiReadReg } from './emit.js';
16
+ import type { Pin } from './gpio.js';
17
+
18
+ export class SPITarget {
19
+ private readonly _bus: string;
20
+ private readonly _cs: number;
21
+ private readonly _hz: number;
22
+ private readonly _mode: number;
23
+
24
+ /** Construct a device handle. `bus` is the bus instance (SPI0, …); `cs`
25
+ * is the chip-select pin; `hz` the bus clock (spi-max-frequency, default
26
+ * 1 MHz conservative); `mode` the SPI mode 0–3. */
27
+ constructor(bus: string, cs: number | Pin, opts?: { hz?: number; mode?: 0 | 1 | 2 | 3 }) {
28
+ this._bus = bus;
29
+ this._cs = typeof cs === 'number' ? cs : cs.number;
30
+ this._hz = opts?.hz ?? 1000000;
31
+ this._mode = opts?.mode ?? 0;
32
+ }
33
+
34
+ /** Full-duplex transfer (spi_transceive_dt): `tx` bytes out while `rx`
35
+ * captures — the buffer you pass is the buffer filled, so declare it and
36
+ * read it directly (rx.length decides the capture length). Omit `rx`
37
+ * for a write-only transfer. */
38
+ transceive(tx: number[] | Uint8Array, rx?: Uint8Array): void {
39
+ spiTransceiveDt(this._bus, this._cs, this._hz, this._mode, tx, rx ?? new Uint8Array(0));
40
+ }
41
+
42
+ /** Write bytes (spi_write_dt) — no capture. */
43
+ write(tx: number[] | Uint8Array): void {
44
+ spiWriteDt(this._bus, this._cs, this._hz, this._mode, tx);
45
+ }
46
+
47
+ /** Register read sugar: sends `reg`, returns the one captured byte
48
+ * (spi_transceive_dt against an internal buffer — e.g. a BME280's ID
49
+ * register 0xD0 reads 0x60). */
50
+ readReg(reg: number): number {
51
+ return spiReadReg(this._bus, this._cs, this._hz, this._mode, reg);
52
+ }
53
+ }
package/src/spi.ts CHANGED
@@ -1,121 +1,27 @@
1
- import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, spiReadBuffer, rawCpp } from './emit.js';
2
- import { include } from './include.js';
3
- import type { Pin } from './gpio.js';
4
- import type { SPIMode, SPISettings } from './types.js';
5
-
6
- export class SPIDevice {
7
- private _bus: string;
8
- private _cs: number;
9
-
10
- constructor(bus: string, chipSelect: number) {
11
- this._bus = bus;
12
- this._cs = chipSelect;
13
- }
14
-
15
- transfer(data: number | Uint8Array): number {
16
- include("<SPI.h>");
17
- spiCsLow(this._cs);
18
- rawCpp(`auto __res = ${this._bus}.transfer(${data});`);
19
- spiCsHigh(this._cs);
20
- rawCpp(`return __res;`);
21
- return 0;
22
- }
23
-
24
- write(data: number | Uint8Array): void {
25
- include("<SPI.h>");
26
- spiCsLow(this._cs);
27
- spiTransfer(this._bus, data);
28
- spiCsHigh(this._cs);
29
- }
30
-
31
- readRegister(register: number, count: number): Uint8Array {
32
- include("<SPI.h>");
33
- spiCsLow(this._cs);
34
- spiTransfer(this._bus, register);
35
- // Clock `count` dummy bytes and drain them into the caller's buffer
36
- // (declared by the Uint8Array return marker as `uint8_t data[count]`).
37
- // Using the semantic primitive — NOT rawCpp — keeps the buffer in user
38
- // scope so it survives the return (no decayed pointer) and `data.length`
39
- // / `data[i]` work, mirroring I2CDevice.readBytes.
40
- spiReadBuffer(this._bus, count, new Uint8Array(count));
41
- spiCsHigh(this._cs);
42
- return new Uint8Array(count);
43
- }
44
-
45
- writeRegister(register: number, value: number): void {
46
- include("<SPI.h>");
47
- spiCsLow(this._cs);
48
- spiTransfer(this._bus, register);
49
- spiTransfer(this._bus, value);
50
- spiCsHigh(this._cs);
51
- }
52
- }
53
-
54
- export class SPIBus {
55
- static readonly __includes = ["<SPI.h>"];
56
- private _bus: string;
57
-
58
- constructor(bus: string) {
59
- this._bus = bus;
60
- }
61
-
62
- device(chipSelect: Pin): SPIDevice {
63
- return new SPIDevice(this._bus, chipSelect.number);
64
- }
65
-
66
- begin(): this {
67
- include("<SPI.h>");
68
- spiBegin(this._bus);
69
- return this;
70
- }
71
-
72
- end(): void {
73
- spiEnd(this._bus);
74
- }
75
-
76
- take(): this | null {
77
- return this;
78
- }
79
-
80
- release(): void {
81
- // No-op for standard Arduino.
82
- }
83
-
84
-
85
- transfer(value: number | Uint8Array): number {
86
- return spiTransfer(this._bus, value);
87
- }
88
-
89
- setFrequency(hz: number): void {
90
- spiBeginTx(this._bus, `SPISettings(${hz}, MSBFIRST, SPI_MODE0)`);
91
- }
92
-
93
- beginTransaction(settings: SPISettings): void {
94
- spiBeginTx(this._bus, settings);
95
- }
96
-
97
- endTransaction(): void {
98
- spiEndTx(this._bus);
99
- }
100
-
101
- setMode(mode: SPIMode): void {
102
- spiSetMode(this._bus, mode);
103
- }
104
-
105
- setBitOrder(order: 'lsb' | 'msb'): void {
106
- spiSetBitOrder(this._bus, order);
107
- }
108
-
109
- write(value: number): void {
110
- spiTransfer(this._bus, value);
111
- }
112
-
113
- write16(value: number): void {
114
- rawCpp(`${this._bus}.transfer16(${value});`);
115
- }
116
- }
117
-
118
- /** Map TypeCAD SPI instance number to Arduino C++ object name. SPI0→SPI, SPI1→SPI1 */
119
- export function spiName(instance: number): string {
120
- return instance === 0 ? "SPI" : `SPI${instance}`;
121
- }
1
+ // ---------------------------------------------------------------------------
2
+ // SPI controller selector (legacy SPI API removed)
3
+ //
4
+ // The bus singletons (SPI0/SPI1…) are CONTROLLER SELECTORS; device(cs, opts?)
5
+ // hands back the FUNCTIONAL SPITarget directly — transceive/write/regRead
6
+ // verbs callable with no further construction. Equivalent to
7
+ // `new SPITarget(this, cs, opts)`. The standalone SPIDevice fact-carrier was
8
+ // absorbed by SPITarget.
9
+ // ----------------------------------------------------------------------------
10
+
11
+ import { SPITarget } from './spi-target.js';
12
+ import type { Pin } from './gpio.js';
13
+
14
+ export class SPIBus {
15
+ private _bus: string;
16
+
17
+ constructor(bus: string) {
18
+ this._bus = bus;
19
+ }
20
+
21
+ /** The functional device target behind `chipSelect` on this controller —
22
+ * transceive/write/regRead verbs callable directly, and the fact-carrier
23
+ * `new Sensor(...)` accepts. */
24
+ device(chipSelect: Pin, opts?: { hz?: number; mode?: 0 | 1 | 2 | 3 }): SPITarget {
25
+ return new SPITarget(this._bus, chipSelect, opts);
26
+ }
27
+ }
package/src/thread.ts ADDED
@@ -0,0 +1,44 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Thread — the thin Zephyr-shaped thread
3
+ //
4
+ // k_thread_create / k_thread_join with Zephyr's own model: construction
5
+ // carries the thread's facts (stack size, priority); start(fn) creates and
6
+ // schedules it (K_NO_WAIT — running immediately); join() blocks until it
7
+ // exits (K_FOREVER). The entry function is a no-argument closure, trampolined
8
+ // through Zephyr's (void*, void*, void*) entry signature — the same
9
+ // callback machinery interrupts use.
10
+ //
11
+ // The `index` is the thread's identity (0, 1, 2, …) — the slot its stack and
12
+ // k_thread state occupy, like Counter's instance. Priorities: Zephyr's scale
13
+ // — 5 (the default) is preemptive, below main; negative would be cooperative.
14
+ // ----------------------------------------------------------------------------
15
+
16
+ import { threadStart, threadJoin } from './emit.js';
17
+ import { callback } from './callback.js';
18
+
19
+ export class Thread {
20
+ private readonly _index: number;
21
+ private readonly _stackBytes: number;
22
+ private readonly _priority: number;
23
+
24
+ /** Construct a thread handle. `stackKb` defaults to 2 (generous for
25
+ * generated code); `priority` defaults to 5 — preemptive, below main. */
26
+ constructor(index: number, opts?: { stackKb?: number; priority?: number }) {
27
+ this._index = index;
28
+ this._stackBytes = (opts?.stackKb ?? 2) * 1024;
29
+ this._priority = opts?.priority ?? 5;
30
+ }
31
+
32
+ /** Create the thread and schedule it immediately (k_thread_create with
33
+ * K_NO_WAIT). The entry function runs concurrently with main from here. */
34
+ start(fn: () => void): void {
35
+ threadStart(this._index, this._stackBytes, this._priority, callback(fn));
36
+ }
37
+
38
+ /** Block until the thread exits (k_thread_join with K_FOREVER). Requires
39
+ * a prior start() on the same index — an unstarted slot is a build
40
+ * error naming the slot's symbol. */
41
+ join(): void {
42
+ threadJoin(this._index);
43
+ }
44
+ }
package/src/time.ts ADDED
@@ -0,0 +1,50 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Time — the TS-flavored timing surface
3
+ //
4
+ // The Arduino-named forms (delay/millis/micros/delayMicroseconds, module
5
+ // functions and the Timing class) are deprecated for new code but keep
6
+ // lowering — framework-arduino is frozen on them. Time is the replacement:
7
+ // plain-number milliseconds like the rest of the JS ecosystem (Date.now() is
8
+ // ms), microseconds explicit in the name, and the honest Zephyr verbs —
9
+ // k_msleep (yielding sleep), k_uptime_get (ms clock), k_busy_wait (spin).
10
+ // ---------------------------------------------------------------------------
11
+
12
+ import { timeSleep, timeNow, timeNowUs, timeBusyWaitUs } from './emit.js';
13
+
14
+ export class TimeClass {
15
+ static readonly __instance_name = 'Time';
16
+
17
+ /** Yielding sleep in milliseconds (k_msleep). On the generated
18
+ * single-threaded main this blocks the caller — the same semantics
19
+ * delay() had, JS-spelled. Inside an async function, `await`-ed it
20
+ * becomes cooperative: the async state machine arms a deadline and
21
+ * yields (other tasks and timers run) until it passes — the same
22
+ * machinery `await delay()` rides. */
23
+ sleep(ms: number): Promise<void> {
24
+ timeSleep(ms);
25
+ return Promise.resolve();
26
+ }
27
+
28
+ /** Milliseconds since boot as a double (k_uptime_get) — Date.now()-shaped:
29
+ * milliseconds, monotonic, no uint32 wrap. */
30
+ now(): number {
31
+ return timeNow();
32
+ }
33
+
34
+ /** Microseconds since boot as a double — uptime-derived
35
+ * (k_uptime_get() * 1000) on every board: the cycle-counter form reads a
36
+ * constant on SoCs without a free-running 64-bit counter, so the one
37
+ * uniform, monotonic expression wins. Resolution is the uptime tick
38
+ * (millisecond); for sub-ms determinism use Counter (hardware timer). */
39
+ nowUs(): number {
40
+ return timeNowUs();
41
+ }
42
+
43
+ /** Spin-wait the given microseconds (k_busy_wait) — no yield; for sub-ms
44
+ * protocol timing where a schedule point would break the waveform. */
45
+ busyWaitUs(us: number): void {
46
+ timeBusyWaitUs(us);
47
+ }
48
+ }
49
+
50
+ export const Time = new TimeClass();
package/src/types.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // @typecad/hal — Public type definitions
3
3
  //
4
- // Transpiler-shim type surface: enums, type aliases, and the pin-group API
5
- // consumed by the HAL's transpiler-shim classes (Pin/OutputPin/InputPin,
6
- // I2CBus/SPIBus/SerialPort) and re-exported for downstream type-checking.
4
+ // Transpiler-shim type surface: value aliases and the enums consumed by the
5
+ // HAL's transpiler-shim classes (Pin, GPIO, I2CBus/SPIBus) and re-exported
6
+ // for downstream type-checking.
7
7
  //
8
8
  // Runtime-contract interfaces (BasePin, II2CBus, ISPIBus, ISerialPort, the
9
- // status enums, capability guards, etc.) have been relocated to
9
+ // status enums, capability guards, and the legacy Wire/SPI protocol-shape
10
+ // types I2CAddress/SPIMode/SPIBitOrder/SPISettings) have been relocated to
10
11
  // @typecad/simulator/src/contracts.ts — they describe the runtime objects the
11
12
  // simulator implements, not the transpiler shims that live here.
12
13
  // ---------------------------------------------------------------------------
@@ -21,6 +22,15 @@ export type DigitalValue = boolean;
21
22
  /** An analog value is a plain number (resolution-dependent). */
22
23
  export type AnalogValue = number;
23
24
 
25
+ // ---------------------------------------------------------------------------
26
+ // Serial write value
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /** A value a serial port (UART / USB console) can write: text, a number, or
30
+ * a boolean. Numbers format as decimal (integer + trimmed fractional);
31
+ * booleans print as 1/0. */
32
+ export type SerialValue = string | number | boolean;
33
+
24
34
  // ---------------------------------------------------------------------------
25
35
  // Pin mode enum
26
36
  // ---------------------------------------------------------------------------
@@ -34,83 +44,12 @@ export enum PinMode {
34
44
  ANALOG = 'ANALOG',
35
45
  }
36
46
 
37
- // ---------------------------------------------------------------------------
38
- // Interrupt mode enum
39
- // ---------------------------------------------------------------------------
40
-
41
- export enum InterruptMode {
42
- RISING = 'RISING',
43
- FALLING = 'FALLING',
44
- CHANGE = 'CHANGE',
45
- LOW = 'LOW',
46
- HIGH = 'HIGH',
47
- }
48
-
49
47
  // ---------------------------------------------------------------------------
50
48
  // Interrupt handler type
51
49
  // ---------------------------------------------------------------------------
52
50
 
53
51
  export type InterruptHandler = () => void;
54
52
 
55
- // ---------------------------------------------------------------------------
56
- // Pin groups
57
- // ---------------------------------------------------------------------------
58
-
59
- export interface PinGroupMember {
60
- readonly number: number;
61
- readonly gpio: number;
62
- write(value: DigitalValue): void;
63
- high(): void;
64
- low(): void;
65
- toggle(): void;
66
- read(): DigitalValue;
67
- isHigh(): boolean;
68
- isLow(): boolean;
69
- }
70
-
71
- export interface IPinGroup<T extends PinGroupMember = PinGroupMember> {
72
- readonly name: string;
73
- readonly pins: ReadonlyArray<T>;
74
- writePattern(pattern: number): void;
75
- readPattern(): number;
76
- fill(value: DigitalValue): void;
77
- }
78
-
79
- export function createPinGroup<T extends PinGroupMember>(
80
- pins: T[]
81
- ): IPinGroup<T> {
82
- return {
83
- name: 'PinGroup',
84
- pins: Object.freeze(pins) as ReadonlyArray<T>,
85
- writePattern(pattern: number): void {
86
- for (let i = 0; i < this.pins.length; i++) {
87
- const bit = (pattern >> i) & 1;
88
- const pin = this.pins[i];
89
- if ('write' in pin && typeof pin.write === 'function') {
90
- pin.write(bit === 1);
91
- }
92
- }
93
- },
94
- readPattern(): number {
95
- let value = 0;
96
- for (let i = 0; i < this.pins.length; i++) {
97
- const pin = this.pins[i];
98
- if ('isHigh' in pin && typeof pin.isHigh === 'function' && pin.isHigh()) {
99
- value |= (1 << i);
100
- }
101
- }
102
- return value;
103
- },
104
- fill(value: DigitalValue): void {
105
- for (const pin of this.pins) {
106
- if ('write' in pin && typeof pin.write === 'function') {
107
- pin.write(value);
108
- }
109
- }
110
- },
111
- };
112
- }
113
-
114
53
  // ---------------------------------------------------------------------------
115
54
  // Architecture identifier
116
55
  // ---------------------------------------------------------------------------
@@ -129,17 +68,3 @@ export type ArchitectureIdentifier =
129
68
  | 'nrf52'
130
69
  | (string & {});
131
70
 
132
- // ---------------------------------------------------------------------------
133
- // Protocol-shape types (re-exported by @typecad/framework-arduino)
134
- // ---------------------------------------------------------------------------
135
-
136
- export type I2CAddress = number;
137
-
138
- export type SPIBitOrder = 'msb' | 'lsb';
139
- export type SPIMode = 0 | 1 | 2 | 3;
140
-
141
- export interface SPISettings {
142
- frequency: number;
143
- mode: SPIMode;
144
- bitOrder: SPIBitOrder;
145
- }
@@ -0,0 +1,64 @@
1
+ // ---------------------------------------------------------------------------
2
+ // UART — the thin Zephyr-shaped serial port
3
+ //
4
+ // TX is poll-based (uart_poll_out — synchronous, fine for writes); RX is
5
+ // INTERRUPT-backed: the first receive call arms the driver's IRQ callback,
6
+ // which drains the FIFO into a construction-sized ring buffer. That makes
7
+ // the honest versions of the calls the poll API couldn't support:
8
+ // available() — bytes waiting in the ring (the poll driver cannot report
9
+ // this without draining the byte you want next)
10
+ // peek() — the oldest byte WITHOUT consuming it
11
+ // read() — pop the oldest byte, or -1 when the ring is empty
12
+ //
13
+ // Construction: `new UART('UART0', { baud: 9600, rxBufferBytes: 128 })` —
14
+ // the ring size (default 64) is a construction fact that sizes the shim's
15
+ // static buffer; bytes arriving with a full ring are dropped (the embedded
16
+ // honest answer — no unbounded buffering).
17
+ // ----------------------------------------------------------------------------
18
+
19
+ import { uartPollWrite, uartRxAvailable, uartRxPeek, uartRxRead } from './emit.js';
20
+ import type { SerialValue } from './types.js';
21
+
22
+ export class UART {
23
+ private readonly _port: string;
24
+ private readonly _baud: number;
25
+ private readonly _rxBufferBytes: number;
26
+
27
+ /** Construct a port handle. `port` is the UART instance (UART0, UART1, …);
28
+ * `baud` applies once at first use (default 115200, 8N1);
29
+ * `rxBufferBytes` sizes the receive ring (default 64). */
30
+ constructor(port: string, opts?: { baud?: number; rxBufferBytes?: number }) {
31
+ this._port = port;
32
+ this._baud = opts?.baud ?? 115200;
33
+ this._rxBufferBytes = opts?.rxBufferBytes ?? 64;
34
+ }
35
+
36
+ /** Write text, a number, or a boolean (uart_poll_out per byte). No newline
37
+ * appended. Numbers format as decimal; booleans print as 1/0. */
38
+ write(data: SerialValue): void {
39
+ uartPollWrite(this._port, this._baud, data);
40
+ }
41
+
42
+ /** Write a value followed by a newline. */
43
+ writeLine(data: SerialValue): void {
44
+ uartPollWrite(this._port, this._baud, data);
45
+ uartPollWrite(this._port, this._baud, "\n");
46
+ }
47
+
48
+ /** Bytes waiting in the receive ring. Arms the RX interrupt on first
49
+ * use (uart_irq_callback_user_data_set + uart_irq_rx_enable — the
50
+ * callback drains the FIFO into the ring). */
51
+ available(): number {
52
+ return uartRxAvailable(this._port, this._rxBufferBytes);
53
+ }
54
+
55
+ /** The oldest received byte WITHOUT consuming it; -1 when empty. */
56
+ peek(): number {
57
+ return uartRxPeek(this._port, this._rxBufferBytes);
58
+ }
59
+
60
+ /** Pop the oldest received byte; -1 when the ring is empty. */
61
+ read(): number {
62
+ return uartRxRead(this._port, this._rxBufferBytes);
63
+ }
64
+ }
package/src/usb.ts ADDED
@@ -0,0 +1,82 @@
1
+ // ---------------------------------------------------------------------------
2
+ // USBConsole — the thin CDC-ACM console
3
+ //
4
+ // A serial pipe over the board's USB connector (Communication Device Class,
5
+ // Abstract Control Model). The construction fact is the port identity
6
+ // ('USB0' — the CDC instance index the devicetree overlay composes); the
7
+ // verbs map 1:1 onto Zephyr's CDC device stack:
8
+ //
9
+ // open() → start the USB device stack (idempotent; CDC has no
10
+ // wire baud — line coding is the host's business)
11
+ // close() → observable no-op (tearing down the shared device
12
+ // stack would drop every other CDC instance)
13
+ // write(v) → one typed write (text | number | boolean — the
14
+ // writeLine(v) __tc_print contract, not print(any))
15
+ // linked() → the host has the port open (DTR asserted); output
16
+ // written before this is silently dropped by hosts
17
+ // waitLinked(ms) → ONE op: bounded DTR poll in the shim (k_msleep
18
+ // slices), boolean, no user-code busy loop. 0 = forever.
19
+ // read()/available()→ poll RX (the byte or -1 / a ready count)
20
+ //
21
+ // Unlike a hardware UART, output before the host opens the port is lost —
22
+ // gate on linked()/waitLinked() when early output matters.
23
+ // ----------------------------------------------------------------------------
24
+
25
+ import {
26
+ usbBegin, usbEnd, usbPrint, usbPrintln,
27
+ usbRead, usbAvailable, usbConnected, usbWaitReady,
28
+ } from './emit.js';
29
+ import type { SerialValue } from './types.js';
30
+
31
+ export class USBConsole {
32
+ private readonly _port: string;
33
+
34
+ /** Construct the console for a CDC instance ('USB0' = instance 0). */
35
+ constructor(port: string = 'USB0') {
36
+ this._port = port;
37
+ }
38
+
39
+ /** Start the USB device stack and bring the CDC port up. Idempotent. */
40
+ open(): void {
41
+ usbBegin(this._port);
42
+ }
43
+
44
+ /** Stop using the port. Observable no-op on Zephyr — the device stack is
45
+ * shared by every CDC instance and is not torn down per-port. */
46
+ close(): void {
47
+ usbEnd(this._port);
48
+ }
49
+
50
+ /** Write text, a number, or a boolean. Numbers format as decimal;
51
+ * booleans print as 1/0. */
52
+ write(value: SerialValue): void {
53
+ usbPrint(this._port, value);
54
+ }
55
+
56
+ /** Write a value followed by a newline. */
57
+ writeLine(value: SerialValue): void {
58
+ usbPrintln(this._port, value);
59
+ }
60
+
61
+ /** True when the host has the port open (DTR asserted). Output written
62
+ * before this is true is silently dropped by most hosts. */
63
+ linked(): boolean {
64
+ return usbConnected(this._port);
65
+ }
66
+
67
+ /** Block until the host opens the port, polling in the shim (no user-code
68
+ * busy loop). `timeoutMs` 0 = wait forever. Returns true once linked. */
69
+ waitLinked(timeoutMs: number = 0): boolean {
70
+ return usbWaitReady(this._port, timeoutMs);
71
+ }
72
+
73
+ /** Read one byte, or -1 when no byte is ready (non-blocking). */
74
+ read(): number {
75
+ return usbRead(this._port);
76
+ }
77
+
78
+ /** Bytes ready to read (the CDC poll path reports 0/1 — see the lowering). */
79
+ available(): number {
80
+ return usbAvailable(this._port);
81
+ }
82
+ }