@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.
- package/README.md +145 -87
- package/dist/adc-pin.d.ts +53 -0
- package/dist/adc-pin.js +69 -0
- package/dist/async.d.ts +3 -11
- package/dist/async.js +3 -15
- package/dist/ble.d.ts +42 -78
- package/dist/ble.js +79 -98
- package/dist/counter.d.ts +16 -0
- package/dist/counter.js +32 -0
- package/dist/dac-pin.d.ts +13 -0
- package/dist/dac-pin.js +21 -0
- package/dist/emit.d.ts +123 -196
- package/dist/emit.js +146 -208
- package/dist/fs.d.ts +12 -31
- package/dist/fs.js +30 -40
- package/dist/gpio-pin.d.ts +46 -0
- package/dist/gpio-pin.js +77 -0
- package/dist/gpio.d.ts +0 -103
- package/dist/gpio.js +16 -227
- package/dist/http.d.ts +38 -39
- package/dist/http.js +53 -78
- package/dist/i2c-target.d.ts +28 -0
- package/dist/i2c-target.js +52 -0
- package/dist/i2c.d.ts +5 -43
- package/dist/i2c.js +17 -130
- package/dist/index.d.ts +30 -42
- package/dist/index.js +27 -39
- package/dist/math.d.ts +0 -13
- package/dist/math.js +9 -13
- package/dist/mqtt.d.ts +15 -25
- package/dist/mqtt.js +32 -28
- package/dist/power.d.ts +9 -17
- package/dist/power.js +22 -20
- package/dist/preferences.d.ts +17 -25
- package/dist/preferences.js +54 -52
- package/dist/pulse.d.ts +4 -4
- package/dist/pulse.js +6 -2
- package/dist/pwm-pin.d.ts +24 -0
- package/dist/pwm-pin.js +38 -0
- package/dist/random.d.ts +0 -2
- package/dist/random.js +14 -20
- package/dist/sensor-catalog.generated.d.ts +997 -0
- package/dist/sensor-catalog.generated.js +764 -0
- package/dist/sensor.d.ts +46 -0
- package/dist/sensor.js +52 -0
- package/dist/shift-pin.d.ts +2 -0
- package/dist/shift-pin.js +13 -0
- package/dist/spi-target.d.ts +25 -0
- package/dist/spi-target.js +42 -0
- package/dist/spi.d.ts +8 -26
- package/dist/spi.js +15 -87
- package/dist/thread.d.ts +18 -0
- package/dist/thread.js +36 -0
- package/dist/time.d.ts +23 -0
- package/dist/time.js +43 -0
- package/dist/timing.d.ts +17 -7
- package/dist/timing.js +17 -7
- package/dist/types.d.ts +4 -34
- package/dist/types.js +5 -47
- package/dist/uart-port.d.ts +26 -0
- package/dist/uart-port.js +52 -0
- package/dist/uart.d.ts +0 -17
- package/dist/uart.js +10 -53
- package/dist/usb.d.ts +26 -0
- package/dist/usb.js +66 -0
- package/dist/watchdog.d.ts +12 -0
- package/dist/watchdog.js +28 -0
- package/dist/wifi.d.ts +88 -64
- package/dist/wifi.js +112 -126
- package/dist/zephyr-tokens.generated.d.ts +8 -0
- package/dist/zephyr-tokens.generated.js +73 -0
- package/package.json +7 -8
- package/src/adc-pin.ts +89 -0
- package/src/async.ts +3 -16
- package/src/ble.ts +84 -111
- package/src/counter.ts +40 -0
- package/src/dac-pin.ts +28 -0
- package/src/emit.ts +157 -238
- package/src/fs.ts +47 -54
- package/src/gpio-pin.ts +91 -0
- package/src/gpio.ts +49 -320
- package/src/http.ts +75 -95
- package/src/i2c-target.ts +63 -0
- package/src/i2c.ts +29 -167
- package/src/index.ts +32 -46
- package/src/math.ts +9 -14
- package/src/mqtt.ts +39 -31
- package/src/preferences.ts +52 -61
- package/src/pwm-pin.ts +54 -0
- package/src/random.ts +14 -21
- package/src/sensor-catalog.generated.ts +1238 -0
- package/src/sensor.ts +80 -0
- package/src/shift-pin.ts +16 -0
- package/src/spi-target.ts +53 -0
- package/src/spi.ts +27 -121
- package/src/thread.ts +44 -0
- package/src/time.ts +50 -0
- package/src/types.ts +14 -89
- package/src/uart-port.ts +64 -0
- package/src/usb.ts +82 -0
- package/src/watchdog.ts +35 -0
- package/src/wifi.ts +147 -179
- package/src/zephyr-tokens.generated.ts +77 -0
- package/src/adc.ts +0 -30
- package/src/capacitive.ts +0 -31
- package/src/constants.ts +0 -23
- package/src/dac.ts +0 -21
- package/src/eeprom.ts +0 -26
- package/src/interrupts.ts +0 -35
- package/src/mdns.ts +0 -50
- package/src/ota.ts +0 -44
- package/src/power.ts +0 -36
- package/src/pulse.ts +0 -63
- package/src/rmt.ts +0 -125
- package/src/shift.ts +0 -88
- package/src/temperature.ts +0 -20
- package/src/timer.ts +0 -58
- package/src/timing.ts +0 -67
- package/src/uart.ts +0 -77
- package/src/utils.ts +0 -17
- 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
|
+
}
|
package/src/shift-pin.ts
ADDED
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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:
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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,
|
|
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
|
-
}
|
package/src/uart-port.ts
ADDED
|
@@ -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
|
+
}
|