@typecad/hal 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/adc.d.ts +13 -0
- package/dist/adc.js +22 -0
- package/dist/async.d.ts +30 -0
- package/dist/async.js +56 -0
- package/dist/board.d.ts +3 -0
- package/dist/board.js +5 -0
- package/dist/callback.d.ts +4 -0
- package/dist/callback.js +6 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +23 -0
- package/dist/dac.d.ts +12 -0
- package/dist/dac.js +17 -0
- package/dist/eeprom.d.ts +16 -0
- package/dist/eeprom.js +21 -0
- package/dist/emit.d.ts +121 -0
- package/dist/emit.js +177 -0
- package/dist/fs.d.ts +13 -0
- package/dist/fs.js +39 -0
- package/dist/gpio.d.ts +108 -0
- package/dist/gpio.js +230 -0
- package/dist/i2c.d.ts +40 -0
- package/dist/i2c.js +126 -0
- package/dist/include.d.ts +5 -0
- package/dist/include.js +5 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +30 -0
- package/dist/interrupts.d.ts +17 -0
- package/dist/interrupts.js +25 -0
- package/dist/math.d.ts +22 -0
- package/dist/math.js +28 -0
- package/dist/power.d.ts +14 -0
- package/dist/power.js +21 -0
- package/dist/preferences.d.ts +18 -0
- package/dist/preferences.js +55 -0
- package/dist/pulse.d.ts +24 -0
- package/dist/pulse.js +53 -0
- package/dist/random.d.ts +12 -0
- package/dist/random.js +26 -0
- package/dist/register.d.ts +11 -0
- package/dist/register.js +21 -0
- package/dist/shift.d.ts +31 -0
- package/dist/shift.js +71 -0
- package/dist/spi.d.ts +31 -0
- package/dist/spi.js +90 -0
- package/dist/timer.d.ts +35 -0
- package/dist/timer.js +50 -0
- package/dist/timing.d.ts +29 -0
- package/dist/timing.js +53 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +67 -0
- package/dist/uart.d.ts +21 -0
- package/dist/uart.js +58 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +14 -0
- package/dist/wdt.d.ts +7 -0
- package/dist/wdt.js +14 -0
- package/package.json +64 -0
- package/src/adc.ts +30 -0
- package/src/async.ts +63 -0
- package/src/board.ts +5 -0
- package/src/callback.ts +6 -0
- package/src/constants.ts +23 -0
- package/src/dac.ts +21 -0
- package/src/eeprom.ts +26 -0
- package/src/emit.ts +210 -0
- package/src/fs.ts +46 -0
- package/src/gpio.ts +298 -0
- package/src/i2c.ts +155 -0
- package/src/include.ts +5 -0
- package/src/index.ts +48 -0
- package/src/interrupts.ts +35 -0
- package/src/math.ts +32 -0
- package/src/power.ts +26 -0
- package/src/preferences.ts +58 -0
- package/src/pulse.ts +63 -0
- package/src/random.ts +31 -0
- package/src/register.ts +35 -0
- package/src/shift.ts +88 -0
- package/src/spi.ts +118 -0
- package/src/timer.ts +59 -0
- package/src/timing.ts +67 -0
- package/src/types.ts +144 -0
- package/src/uart.ts +77 -0
- package/src/utils.ts +17 -0
- package/src/wdt.ts +17 -0
package/src/pulse.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { rawCpp } from './emit.js';
|
|
2
|
+
import { HIGH, LOW } from './constants.js';
|
|
3
|
+
import type { Pin, InputPin } from './gpio.js';
|
|
4
|
+
|
|
5
|
+
export function pulseIn(pin: number, value: number, timeout?: number): number {
|
|
6
|
+
if (timeout !== undefined) {
|
|
7
|
+
rawCpp(`return pulseIn(${pin}, ${value}, ${timeout});`);
|
|
8
|
+
} else {
|
|
9
|
+
rawCpp(`return pulseIn(${pin}, ${value});`);
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
export function pulseInLong(pin: number, value: number, timeout?: number): number {
|
|
14
|
+
if (timeout !== undefined) {
|
|
15
|
+
rawCpp(`return pulseInLong(${pin}, ${value}, ${timeout});`);
|
|
16
|
+
} else {
|
|
17
|
+
rawCpp(`return pulseInLong(${pin}, ${value});`);
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class Pulse {
|
|
23
|
+
/**
|
|
24
|
+
* Functional entrypoint for measuring pulses.
|
|
25
|
+
* @example Pulse.on(D7).high()
|
|
26
|
+
*/
|
|
27
|
+
static on(pin: Pin | InputPin): PulseMeasurement {
|
|
28
|
+
return new PulseMeasurement(pin);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Measure long pulses using high-precision 64-bit timers. */
|
|
32
|
+
static long(pin: Pin | InputPin, value: number): number {
|
|
33
|
+
rawCpp(`return pulseInLong(${pin.number}, ${value});`);
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class PulseMeasurement {
|
|
39
|
+
private _pin: number;
|
|
40
|
+
private _timeout: number | undefined;
|
|
41
|
+
|
|
42
|
+
constructor(pin: Pin | InputPin) {
|
|
43
|
+
this._pin = pin.number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Set the maximum wait time for a pulse (in microseconds). */
|
|
47
|
+
timeout(us: number): this {
|
|
48
|
+
this._timeout = us;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Measures the next HIGH pulse duration in microseconds. */
|
|
53
|
+
high(): number {
|
|
54
|
+
rawCpp(`return pulseIn(${this._pin}, HIGH${this._timeout !== undefined ? `, ${this._timeout}` : ""});`);
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Measures the next LOW pulse duration in microseconds. */
|
|
59
|
+
low(): number {
|
|
60
|
+
rawCpp(`return pulseIn(${this._pin}, LOW${this._timeout !== undefined ? `, ${this._timeout}` : ""});`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/random.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { rawCpp } from './emit.js';
|
|
2
|
+
|
|
3
|
+
export function randomSeed(seed: number): void {}
|
|
4
|
+
export function random(minOrMax: number, max?: number): number { return 0; }
|
|
5
|
+
|
|
6
|
+
export class Random {
|
|
7
|
+
/** Seeds the PRNG with a starting value. */
|
|
8
|
+
static seed(val: number): void {
|
|
9
|
+
rawCpp(`randomSeed(${val});`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Returns a random number in range [0, max-1]. */
|
|
13
|
+
static upTo(max: number): number {
|
|
14
|
+
rawCpp(`return random(${max});`);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Returns a random number in range [min, max-1]. */
|
|
19
|
+
static between(min: number, max: number): number {
|
|
20
|
+
rawCpp(`return random(${min}, ${max});`);
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Returns a random non-negative 31-bit integer [0, 2147483646]. */
|
|
25
|
+
static int(): number {
|
|
26
|
+
// Arduino random(max) returns [0, max-1]; 2147483647 = INT_MAX gives a
|
|
27
|
+
// non-negative 31-bit range (sign bit always 0).
|
|
28
|
+
rawCpp(`return random(2147483647);`);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/register.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Register-mapped struct decorators — compile-time markers
|
|
3
|
+
//
|
|
4
|
+
// `@register(address)` and `@bits(hi, lo)` are recognized by-name by the
|
|
5
|
+
// cuttlefish transpiler (packages/cuttlefish/src/ir/register-decorators.ts).
|
|
6
|
+
// The transpiler intercepts a class carrying @register, lowers it to a
|
|
7
|
+
// `volatile uint32_t*` pointer at the given address, and rewrites field
|
|
8
|
+
// reads/writes to shift/mask arithmetic. The decorators themselves are erased
|
|
9
|
+
// and never execute, so these are ambient stubs that exist purely so user code
|
|
10
|
+
// type-checks under tsc before transpilation.
|
|
11
|
+
//
|
|
12
|
+
// @register(0x40011000)
|
|
13
|
+
// class USART1 {
|
|
14
|
+
// @bits(0, 0) static UE: Bit = 0;
|
|
15
|
+
// @bits(9, 8) static PS: Bits<2> = 0;
|
|
16
|
+
// @bits(15, 8) static BAUD: Bits<8> = 0;
|
|
17
|
+
// }
|
|
18
|
+
// USART1.UE = 1; // (*USART1 & ~1UL) | ((1 & 1UL) << 0)
|
|
19
|
+
// const parity = USART1.PS; // ((*USART1 >> 8) & ((1UL << 2) - 1))
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/** A single-bit register field (0 or 1). */
|
|
23
|
+
export type Bit = 0 | 1;
|
|
24
|
+
|
|
25
|
+
/** A multi-bit register field spanning N bits. The numeric type arg carries
|
|
26
|
+
* the bit width for documentation only; the value is the raw field contents. */
|
|
27
|
+
export type Bits<N extends number = number> = number;
|
|
28
|
+
|
|
29
|
+
/** Class decorator marking a struct as a memory-mapped register at `address`.
|
|
30
|
+
* Erased at transpile time — the class becomes a `volatile uint32_t*`. */
|
|
31
|
+
export declare function register(address: number): ClassDecorator;
|
|
32
|
+
|
|
33
|
+
/** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
|
|
34
|
+
* within its register. Erased at transpile time. */
|
|
35
|
+
export declare function bits(hi: number, lo: number): PropertyDecorator;
|
package/src/shift.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { rawCpp } from './emit.js';
|
|
2
|
+
import { LSBFIRST, MSBFIRST } from './constants.js';
|
|
3
|
+
import type { Pin, OutputPin, InputPin } from './gpio.js';
|
|
4
|
+
|
|
5
|
+
export function shiftIn(dataPin: number, clockPin: number, bitOrder: number): number {
|
|
6
|
+
rawCpp(`return shiftIn(${dataPin}, ${clockPin}, ${bitOrder});`);
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
export function shiftOut(dataPin: number, clockPin: number, bitOrder: number, value: number): void {
|
|
10
|
+
rawCpp(`shiftOut(${dataPin}, ${clockPin}, ${bitOrder}, ${value});`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Shift {
|
|
14
|
+
/** Directly shifts a byte out to a pin. */
|
|
15
|
+
static out(dataPin: Pin | OutputPin, clockPin: Pin | OutputPin, order: 'lsb' | 'msb', value: number): void {
|
|
16
|
+
const bitOrder = order === 'lsb' ? LSBFIRST : MSBFIRST;
|
|
17
|
+
rawCpp(`shiftOut(${dataPin.number}, ${clockPin.number}, ${bitOrder}, ${value});`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Directly shifts a byte in from a pin. */
|
|
21
|
+
static in(dataPin: Pin | InputPin, clockPin: Pin | OutputPin, order: 'lsb' | 'msb'): number {
|
|
22
|
+
const bitOrder = order === 'lsb' ? LSBFIRST : MSBFIRST;
|
|
23
|
+
rawCpp(`return shiftIn(${dataPin.number}, ${clockPin.number}, ${bitOrder});`);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Fluent builder for shifting out. */
|
|
28
|
+
static write(dataPin: Pin | OutputPin, value: number): ShiftOutBuilder {
|
|
29
|
+
return new ShiftOutBuilder(dataPin, value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Fluent builder for shifting in. */
|
|
33
|
+
static read(dataPin: Pin | InputPin): ShiftInBuilder {
|
|
34
|
+
return new ShiftInBuilder(dataPin);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class ShiftOutBuilder {
|
|
39
|
+
private _dataPin: number;
|
|
40
|
+
private _value: number;
|
|
41
|
+
private _clockPin: number | undefined;
|
|
42
|
+
|
|
43
|
+
constructor(dataPin: Pin | OutputPin, value: number) {
|
|
44
|
+
this._dataPin = dataPin.number;
|
|
45
|
+
this._value = value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clock(clk: Pin | OutputPin): this {
|
|
49
|
+
this._clockPin = clk.number;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
msbFirst(): void {
|
|
54
|
+
if (this._clockPin === undefined) throw new Error("Clock pin must be specified");
|
|
55
|
+
rawCpp(`shiftOut(${this._dataPin}, ${this._clockPin}, MSBFIRST, ${this._value});`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
lsbFirst(): void {
|
|
59
|
+
if (this._clockPin === undefined) throw new Error("Clock pin must be specified");
|
|
60
|
+
rawCpp(`shiftOut(${this._dataPin}, ${this._clockPin}, LSBFIRST, ${this._value});`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class ShiftInBuilder {
|
|
65
|
+
private _dataPin: number;
|
|
66
|
+
private _clockPin: number | undefined;
|
|
67
|
+
|
|
68
|
+
constructor(dataPin: Pin | InputPin) {
|
|
69
|
+
this._dataPin = dataPin.number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
clock(clk: Pin | OutputPin): this {
|
|
73
|
+
this._clockPin = clk.number;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
msbFirst(): number {
|
|
78
|
+
if (this._clockPin === undefined) throw new Error("Clock pin must be specified");
|
|
79
|
+
rawCpp(`return shiftIn(${this._dataPin}, ${this._clockPin}, MSBFIRST);`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lsbFirst(): number {
|
|
84
|
+
if (this._clockPin === undefined) throw new Error("Clock pin must be specified");
|
|
85
|
+
rawCpp(`return shiftIn(${this._dataPin}, ${this._clockPin}, LSBFIRST);`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/spi.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, 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
|
+
rawCpp(`digitalWrite(${this._cs}, LOW);`);
|
|
34
|
+
rawCpp(`${this._bus}.transfer(${register});`);
|
|
35
|
+
rawCpp(`static uint8_t __spi_buf[${count}];`);
|
|
36
|
+
rawCpp(`for (int i=0; i<${count}; i++) __spi_buf[i] = ${this._bus}.transfer(0x00);`);
|
|
37
|
+
rawCpp(`digitalWrite(${this._cs}, HIGH);`);
|
|
38
|
+
rawCpp(`return __spi_buf;`);
|
|
39
|
+
return new Uint8Array(count);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
writeRegister(register: number, value: number): void {
|
|
43
|
+
include("<SPI.h>");
|
|
44
|
+
spiCsLow(this._cs);
|
|
45
|
+
spiTransfer(this._bus, register);
|
|
46
|
+
spiTransfer(this._bus, value);
|
|
47
|
+
spiCsHigh(this._cs);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class SPIBus {
|
|
52
|
+
static readonly __includes = ["<SPI.h>"];
|
|
53
|
+
private _bus: string;
|
|
54
|
+
|
|
55
|
+
constructor(bus: string) {
|
|
56
|
+
this._bus = bus;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
device(chipSelect: Pin): SPIDevice {
|
|
60
|
+
return new SPIDevice(this._bus, chipSelect.number);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
begin(): this {
|
|
64
|
+
include("<SPI.h>");
|
|
65
|
+
spiBegin(this._bus);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
end(): void {
|
|
70
|
+
spiEnd(this._bus);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
take(): this | null {
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
release(): void {
|
|
78
|
+
// No-op for standard Arduino.
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
transfer(value: number | Uint8Array): number {
|
|
83
|
+
return spiTransfer(this._bus, value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setFrequency(hz: number): void {
|
|
87
|
+
spiBeginTx(this._bus, `SPISettings(${hz}, MSBFIRST, SPI_MODE0)`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
beginTransaction(settings: SPISettings): void {
|
|
91
|
+
spiBeginTx(this._bus, settings);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
endTransaction(): void {
|
|
95
|
+
spiEndTx(this._bus);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
setMode(mode: SPIMode): void {
|
|
99
|
+
spiSetMode(this._bus, mode);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setBitOrder(order: 'lsb' | 'msb'): void {
|
|
103
|
+
spiSetBitOrder(this._bus, order);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
write(value: number): void {
|
|
107
|
+
spiTransfer(this._bus, value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
write16(value: number): void {
|
|
111
|
+
rawCpp(`${this._bus}.transfer16(${value});`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Map TypeCAD SPI instance number to Arduino C++ object name. SPI0→SPI, SPI1→SPI1 */
|
|
116
|
+
export function spiName(instance: number): string {
|
|
117
|
+
return instance === 0 ? "SPI" : `SPI${instance}`;
|
|
118
|
+
}
|
package/src/timer.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { rawCpp, boardResolve } from './emit.js';
|
|
2
|
+
import { callback } from './callback.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HardwareTimer provides direct control over the board's hardware timers.
|
|
6
|
+
*
|
|
7
|
+
* Hardware timers are distinct from the software-based setInterval/setTimeout
|
|
8
|
+
* and are typically used for high-precision timing, PWM generation, or
|
|
9
|
+
* interrupt-driven tasks.
|
|
10
|
+
*/
|
|
11
|
+
export class HardwareTimer {
|
|
12
|
+
private _instance: number;
|
|
13
|
+
|
|
14
|
+
constructor(instance: number) {
|
|
15
|
+
this._instance = instance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Sets the timer frequency in Hertz.
|
|
20
|
+
* Note: The actual frequency may be limited by the hardware's clock dividers.
|
|
21
|
+
*/
|
|
22
|
+
setFrequency(hz: number): void {
|
|
23
|
+
rawCpp(`Timer${this._instance}.setFrequency(${hz});`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Attaches an interrupt handler that executes when the timer overflows.
|
|
28
|
+
*/
|
|
29
|
+
onOverflow(handler: () => void): void {
|
|
30
|
+
rawCpp(`Timer${this._instance}.onOverflow(${callback(handler)});`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Starts the timer.
|
|
35
|
+
*/
|
|
36
|
+
start(): void {
|
|
37
|
+
rawCpp(`Timer${this._instance}.start();`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Stops the timer.
|
|
42
|
+
*/
|
|
43
|
+
stop(): void {
|
|
44
|
+
rawCpp(`Timer${this._instance}.stop();`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns the bit resolution of the timer (e.g., 8, 16, 32).
|
|
49
|
+
*/
|
|
50
|
+
getBits(): number {
|
|
51
|
+
// "peripherals.timer" (singular) matches the board-resolver's array-key
|
|
52
|
+
// derivation (TIMER_INSTANCES → peripherals.timer.<index>.*).
|
|
53
|
+
return boardResolve("peripherals.timer." + this._instance + ".bits");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const Timer0 = new HardwareTimer(0);
|
|
58
|
+
export const Timer1 = new HardwareTimer(1);
|
|
59
|
+
export const Timer2 = new HardwareTimer(2);
|
package/src/timing.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { rawCpp, getMillis, getMicros, getFreeHeap, delayMs, delayMicro } from './emit.js';
|
|
2
|
+
import { callback } from './callback.js';
|
|
3
|
+
|
|
4
|
+
export class TimingClass {
|
|
5
|
+
static readonly __instance_name = "Timing";
|
|
6
|
+
millis(): number { return getMillis(); }
|
|
7
|
+
micros(): number { return getMicros(); }
|
|
8
|
+
delay(ms: number): void { delayMs(ms); }
|
|
9
|
+
delayMicroseconds(us: number): void { delayMicro(us); }
|
|
10
|
+
freeHeap(): number {
|
|
11
|
+
return getFreeHeap();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
setInterval(handler: () => void, timeout: number): number {
|
|
16
|
+
rawCpp(`return __tc_setInterval(${callback(handler)}, ${timeout});`);
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setTimeout(handler: () => void, timeout: number): number {
|
|
21
|
+
rawCpp(`return __tc_setTimeout(${callback(handler)}, ${timeout});`);
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
clearInterval(id: number): void {
|
|
26
|
+
rawCpp(`__tc_clearInterval(${id});`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clearTimeout(id: number): void {
|
|
30
|
+
rawCpp(`__tc_clearTimeout(${id});`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Timing = new TimingClass();
|
|
35
|
+
|
|
36
|
+
export function delay(ms: number): void { delayMs(ms); }
|
|
37
|
+
export function millis(): number { return getMillis(); }
|
|
38
|
+
export function micros(): number { return getMicros(); }
|
|
39
|
+
export function delayMicroseconds(us: number): void { delayMicro(us); }
|
|
40
|
+
export function freeHeap(): number { return Timing.freeHeap(); }
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
export function setInterval(handler: () => void, timeout: number): number {
|
|
44
|
+
rawCpp(`return __tc_setInterval(${callback(handler)}, ${timeout});`);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function setTimeout(handler: () => void, timeout: number): number {
|
|
49
|
+
rawCpp(`return __tc_setTimeout(${callback(handler)}, ${timeout});`);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function clearInterval(id: number): void {
|
|
54
|
+
rawCpp(`__tc_clearInterval(${id});`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function clearTimeout(id: number): void {
|
|
58
|
+
rawCpp(`__tc_clearTimeout(${id});`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Re-map a number from one range to another. Passes through to the Arduino
|
|
62
|
+
* core `map()` macro — the transpiler lowers this to a bare `map(...)` call,
|
|
63
|
+
* so the target framework must provide the implementation (Arduino.h does). */
|
|
64
|
+
export function map(value: number, fromLow: number, fromHigh: number, toLow: number, toHigh: number): number { return 0; }
|
|
65
|
+
/** Constrain a number to a range. Passes through to the Arduino core
|
|
66
|
+
* `constrain()` macro — see note on `map()` above. */
|
|
67
|
+
export function constrain(value: number, low: number, high: number): number { return 0; }
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/hal — Public type definitions
|
|
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.
|
|
7
|
+
//
|
|
8
|
+
// Runtime-contract interfaces (BasePin, II2CBus, ISPIBus, ISerialPort, the
|
|
9
|
+
// status enums, capability guards, etc.) have been relocated to
|
|
10
|
+
// @typecad/simulator/src/contracts.ts — they describe the runtime objects the
|
|
11
|
+
// simulator implements, not the transpiler shims that live here.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Digital/analog value types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/** A digital value is a plain boolean. */
|
|
19
|
+
export type DigitalValue = boolean;
|
|
20
|
+
|
|
21
|
+
/** An analog value is a plain number (resolution-dependent). */
|
|
22
|
+
export type AnalogValue = number;
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Pin mode enum
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export enum PinMode {
|
|
29
|
+
INPUT = 'INPUT',
|
|
30
|
+
OUTPUT = 'OUTPUT',
|
|
31
|
+
INPUT_PULLUP = 'INPUT_PULLUP',
|
|
32
|
+
INPUT_PULLDOWN = 'INPUT_PULLDOWN',
|
|
33
|
+
OUTPUT_OPEN_DRAIN = 'OUTPUT_OPEN_DRAIN',
|
|
34
|
+
ANALOG = 'ANALOG',
|
|
35
|
+
}
|
|
36
|
+
|
|
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
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Interrupt handler type
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
export type InterruptHandler = () => void;
|
|
54
|
+
|
|
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
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Architecture identifier
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
export type ArchitectureIdentifier =
|
|
119
|
+
| 'avr'
|
|
120
|
+
| 'esp32'
|
|
121
|
+
| 'esp32s2'
|
|
122
|
+
| 'esp32s3'
|
|
123
|
+
| 'esp32c3'
|
|
124
|
+
| 'esp32c6'
|
|
125
|
+
| 'rp2040'
|
|
126
|
+
| 'samd'
|
|
127
|
+
| 'stm32'
|
|
128
|
+
| 'nrf52'
|
|
129
|
+
| (string & {});
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Protocol-shape types (re-exported by @typecad/framework-arduino)
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
export type I2CAddress = number;
|
|
136
|
+
|
|
137
|
+
export type SPIBitOrder = 'msb' | 'lsb';
|
|
138
|
+
export type SPIMode = 0 | 1 | 2 | 3;
|
|
139
|
+
|
|
140
|
+
export interface SPISettings {
|
|
141
|
+
frequency: number;
|
|
142
|
+
mode: SPIMode;
|
|
143
|
+
bitOrder: SPIBitOrder;
|
|
144
|
+
}
|
package/src/uart.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { uartBegin, uartEnd, uartPrint, uartPrintln, uartPrintf, uartWrite, uartRead, uartPeek, uartAvailable, uartFlush, rawCpp } from './emit.js';
|
|
2
|
+
|
|
3
|
+
export class SerialPort {
|
|
4
|
+
static readonly __includes = ["<Arduino.h>"];
|
|
5
|
+
private _port: string;
|
|
6
|
+
|
|
7
|
+
constructor(port: string) {
|
|
8
|
+
this._port = port;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
begin(baud: number = 9600): this {
|
|
12
|
+
uartBegin(this._port, baud);
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
end(): void {
|
|
17
|
+
uartEnd(this._port);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
print(value: any): void {
|
|
21
|
+
uartPrint(this._port, value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
println(value: any): void {
|
|
25
|
+
uartPrintln(this._port, value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
printf(format: string, ...args: any[]): void {
|
|
29
|
+
uartPrintf(this._port, format, args);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
write(data: any): void {
|
|
33
|
+
uartWrite(this._port, data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
read(): number {
|
|
37
|
+
return uartRead(this._port);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
readLine(): string {
|
|
41
|
+
rawCpp(`return ${this._port}.readStringUntil('\\n');`);
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
peek(): number {
|
|
46
|
+
return uartPeek(this._port);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
available(): number {
|
|
50
|
+
return uartAvailable(this._port);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
flush(): void {
|
|
54
|
+
uartFlush(this._port);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
waitForConnection(timeout?: number): Promise<void> {
|
|
58
|
+
rawCpp(`while (!${this._port}) { delay(10); }`);
|
|
59
|
+
return Promise.resolve();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
take(): this | null {
|
|
63
|
+
// Basic implementation for single-threaded Arduino;
|
|
64
|
+
// real locking would be framework-specific.
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
release(): void {
|
|
69
|
+
// No-op for standard Arduino.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/** Map TypeCAD UART instance number to Arduino C++ object name. UART0→Serial, UART1→Serial1 */
|
|
75
|
+
export function serialName(instance: number): string {
|
|
76
|
+
return instance === 0 ? "Serial" : `Serial${instance}`;
|
|
77
|
+
}
|