@typecad/hal 0.1.0-alpha.2 → 1.0.0-alpha.6
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 +5 -5
- package/dist/emit.d.ts +17 -0
- package/dist/emit.js +17 -0
- package/dist/i2c.d.ts +7 -0
- package/dist/i2c.js +15 -4
- package/dist/register.d.ts +12 -4
- package/dist/register.js +18 -1
- package/dist/spi.js +10 -7
- package/dist/types.d.ts +1 -1
- package/package.json +4 -4
- package/src/emit.ts +17 -0
- package/src/i2c.ts +16 -4
- package/src/register.ts +16 -4
- package/src/spi.ts +10 -7
- package/src/types.ts +1 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `@typecad/hal`
|
|
2
2
|
|
|
3
|
-
Hardware abstraction layer for [TypeCAD](https://
|
|
3
|
+
Hardware abstraction layer for [TypeCAD](https://cuttlefish.typecad.net) —
|
|
4
4
|
GPIO, I2C, SPI, UART, timers, ADC/DAC, EEPROM, and more, written as regular
|
|
5
5
|
TypeScript.
|
|
6
6
|
|
|
@@ -77,7 +77,7 @@ For I2C/SPI/UART, instantiate the bus class with the board's pinned instance
|
|
|
77
77
|
|
|
78
78
|
The [`tests/`](./tests/) directory contains a hardware test suite that
|
|
79
79
|
exercises every AVR-compilable HAL subsystem against real Arduino Uno hardware,
|
|
80
|
-
using [`@typecad/expect`](https://
|
|
80
|
+
using [`@typecad/expect`](https://cuttlefish.typecad.net)
|
|
81
81
|
(`describe()` / `.it()` / `.expect()` / `done()`) over serial. Each file covers
|
|
82
82
|
one subsystem: GPIO, timing, math, random, pulse, shift, interrupts, UART,
|
|
83
83
|
I2C, SPI, ADC, EEPROM, WDT, Preferences, async, and constants.
|
|
@@ -92,7 +92,7 @@ npm exec --workspace @typecad/hal -- cuttlefish-test
|
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
The suite is configured by [`cuttlefish.config.ts`](./cuttlefish.config.ts).
|
|
95
|
-
Like the [`@typecad/framework-arduino`](https://
|
|
95
|
+
Like the [`@typecad/framework-arduino`](https://cuttlefish.typecad.net)
|
|
96
96
|
tests, it imports through `@typecad/board` (the board package) and ambient globals
|
|
97
97
|
declared in `cuttlefish-env.d.ts`, never directly from `@typecad/board`, so the
|
|
98
98
|
transpiler resolves each call against the active MCU/board packages.
|
|
@@ -117,8 +117,8 @@ entirely — they require an ESP32 target.
|
|
|
117
117
|
|
|
118
118
|
## Ecosystem
|
|
119
119
|
|
|
120
|
-
- [`@typecad/cuttlefish`](https://
|
|
121
|
-
- [`@typecad/ui`](https://
|
|
120
|
+
- [`@typecad/cuttlefish`](https://cuttlefish.typecad.net) — the transpiler that resolves HAL calls to C++.
|
|
121
|
+
- [`@typecad/ui`](https://cuttlefish.typecad.net) — HTML/CSS-driven display graphics.
|
|
122
122
|
- `@typecad/mcu-*` — silicon pin/port/peripheral definitions.
|
|
123
123
|
- `@typecad/board-*` — board-level pin mappings and bus aliases.
|
|
124
124
|
|
package/dist/emit.d.ts
CHANGED
|
@@ -53,6 +53,15 @@ export declare function i2cRequestFrom(bus: string, address: number, quantity: n
|
|
|
53
53
|
export declare function i2cAvailable(bus: string): number;
|
|
54
54
|
/** Read a byte from I2C. */
|
|
55
55
|
export declare function i2cRead(bus: string): number;
|
|
56
|
+
/**
|
|
57
|
+
* Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
|
|
58
|
+
* Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
|
|
59
|
+
* argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
|
|
60
|
+
* transformer rewrites to the caller's own buffer variable, so bytes land in
|
|
61
|
+
* the `uint8_t data[N]` declared in user scope — NOT an internal temp that
|
|
62
|
+
* decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
|
|
63
|
+
*/
|
|
64
|
+
export declare function i2cReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void;
|
|
56
65
|
/** Initialize SPI bus. */
|
|
57
66
|
export declare function spiBegin(bus: string): void;
|
|
58
67
|
/** Disable SPI bus. */
|
|
@@ -67,6 +76,14 @@ export declare function spiEndTx(bus: string): void;
|
|
|
67
76
|
export declare function spiCsLow(pin: number | string): void;
|
|
68
77
|
/** Set SPI chip-select pin HIGH. */
|
|
69
78
|
export declare function spiCsHigh(pin: number | string): void;
|
|
79
|
+
/**
|
|
80
|
+
* Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
|
|
81
|
+
* dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
|
|
82
|
+
* HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
|
|
83
|
+
* rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
|
|
84
|
+
* responsible for asserting/de-asserting chip-select around it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function spiReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void;
|
|
70
87
|
/** Set SPI data mode. */
|
|
71
88
|
export declare function spiSetMode(bus: string, mode: number): void;
|
|
72
89
|
/** Set SPI bit order. */
|
package/dist/emit.js
CHANGED
|
@@ -85,6 +85,15 @@ export function i2cRequestFrom(bus, address, quantity, stop) { return 0; }
|
|
|
85
85
|
export function i2cAvailable(bus) { return 0; }
|
|
86
86
|
/** Read a byte from I2C. */
|
|
87
87
|
export function i2cRead(bus) { return 0; }
|
|
88
|
+
/**
|
|
89
|
+
* Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
|
|
90
|
+
* Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
|
|
91
|
+
* argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
|
|
92
|
+
* transformer rewrites to the caller's own buffer variable, so bytes land in
|
|
93
|
+
* the `uint8_t data[N]` declared in user scope — NOT an internal temp that
|
|
94
|
+
* decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
|
|
95
|
+
*/
|
|
96
|
+
export function i2cReadBuffer(bus, count, buffer) { }
|
|
88
97
|
// ---------------------------------------------------------------------------
|
|
89
98
|
// SPI — serial peripheral interface
|
|
90
99
|
// ---------------------------------------------------------------------------
|
|
@@ -102,6 +111,14 @@ export function spiEndTx(bus) { }
|
|
|
102
111
|
export function spiCsLow(pin) { }
|
|
103
112
|
/** Set SPI chip-select pin HIGH. */
|
|
104
113
|
export function spiCsHigh(pin) { }
|
|
114
|
+
/**
|
|
115
|
+
* Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
|
|
116
|
+
* dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
|
|
117
|
+
* HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
|
|
118
|
+
* rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
|
|
119
|
+
* responsible for asserting/de-asserting chip-select around it.
|
|
120
|
+
*/
|
|
121
|
+
export function spiReadBuffer(bus, count, buffer) { }
|
|
105
122
|
/** Set SPI data mode. */
|
|
106
123
|
export function spiSetMode(bus, mode) { }
|
|
107
124
|
/** Set SPI bit order. */
|
package/dist/i2c.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ export declare class I2CDevice {
|
|
|
2
2
|
private _bus;
|
|
3
3
|
private _address;
|
|
4
4
|
constructor(bus: string, address: number);
|
|
5
|
+
/** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
|
|
6
|
+
* structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
|
|
7
|
+
* (which declares `readonly address`), letting the same driver function be
|
|
8
|
+
* typed against the contract and accept either a real board device or a
|
|
9
|
+
* simulated one. The transpiler strips HAL class bodies to IR, so this
|
|
10
|
+
* getter carries no runtime cost in the generated C++. */
|
|
11
|
+
get address(): number;
|
|
5
12
|
writeByte(register: number, value: number): void;
|
|
6
13
|
readByte(register: number): number;
|
|
7
14
|
writeBytes(register: number, data: number[] | Uint8Array): void;
|
package/dist/i2c.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, rawCpp } from './emit.js';
|
|
1
|
+
import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, i2cReadBuffer, rawCpp } from './emit.js';
|
|
2
2
|
import { include } from './include.js';
|
|
3
3
|
export class I2CDevice {
|
|
4
4
|
constructor(bus, address) {
|
|
5
5
|
this._bus = bus;
|
|
6
6
|
this._address = address;
|
|
7
7
|
}
|
|
8
|
+
/** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
|
|
9
|
+
* structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
|
|
10
|
+
* (which declares `readonly address`), letting the same driver function be
|
|
11
|
+
* typed against the contract and accept either a real board device or a
|
|
12
|
+
* simulated one. The transpiler strips HAL class bodies to IR, so this
|
|
13
|
+
* getter carries no runtime cost in the generated C++. */
|
|
14
|
+
get address() {
|
|
15
|
+
return this._address;
|
|
16
|
+
}
|
|
8
17
|
writeByte(register, value) {
|
|
9
18
|
include("<Wire.h>");
|
|
10
19
|
i2cBeginTx(this._bus, this._address);
|
|
@@ -33,9 +42,11 @@ export class I2CDevice {
|
|
|
33
42
|
i2cWrite(this._bus, register);
|
|
34
43
|
i2cEndTx(this._bus, false);
|
|
35
44
|
i2cRequestFrom(this._bus, this._address, count, true);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
rawCpp
|
|
45
|
+
// Drain the requested bytes into the caller's buffer (declared by the
|
|
46
|
+
// Uint8Array return marker as `uint8_t data[count]`). Using the semantic
|
|
47
|
+
// primitive — NOT rawCpp — keeps the buffer in user scope so it survives
|
|
48
|
+
// the return (no decayed pointer) and `data.length` / `data[i]` work.
|
|
49
|
+
i2cReadBuffer(this._bus, count, new Uint8Array(count));
|
|
39
50
|
return new Uint8Array(count);
|
|
40
51
|
}
|
|
41
52
|
}
|
package/dist/register.d.ts
CHANGED
|
@@ -4,8 +4,16 @@ export type Bit = 0 | 1;
|
|
|
4
4
|
* the bit width for documentation only; the value is the raw field contents. */
|
|
5
5
|
export type Bits<N extends number = number> = number;
|
|
6
6
|
/** Class decorator marking a struct as a memory-mapped register at `address`.
|
|
7
|
-
* Erased at transpile time — the class becomes a `volatile uint32_t*`.
|
|
8
|
-
|
|
7
|
+
* Erased at transpile time — the class becomes a `volatile uint32_t*`.
|
|
8
|
+
*
|
|
9
|
+
* These carry real (inert) runtime bodies rather than `declare`, so the
|
|
10
|
+
* `export { register, bits }` re-export in index.ts resolves under Node's ESM
|
|
11
|
+
* loader, which validates that re-exported bindings exist at runtime. They are
|
|
12
|
+
* never invoked: the cuttlefish transpiler detects them by name and lowers the
|
|
13
|
+
* decorated struct away, so these stubs are only reached when the decorator
|
|
14
|
+
* source is imported without transpilation (e.g. host-side tests). */
|
|
15
|
+
export declare function register(_address: number): ClassDecorator;
|
|
9
16
|
/** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
|
|
10
|
-
* within its register. Erased at transpile time.
|
|
11
|
-
|
|
17
|
+
* within its register. Erased at transpile time. See `register` for why these
|
|
18
|
+
* have runtime bodies. */
|
|
19
|
+
export declare function bits(_hi: number, _lo: number): PropertyDecorator;
|
package/dist/register.js
CHANGED
|
@@ -18,4 +18,21 @@
|
|
|
18
18
|
// USART1.UE = 1; // (*USART1 & ~1UL) | ((1 & 1UL) << 0)
|
|
19
19
|
// const parity = USART1.PS; // ((*USART1 >> 8) & ((1UL << 2) - 1))
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
21
|
+
/** Class decorator marking a struct as a memory-mapped register at `address`.
|
|
22
|
+
* Erased at transpile time — the class becomes a `volatile uint32_t*`.
|
|
23
|
+
*
|
|
24
|
+
* These carry real (inert) runtime bodies rather than `declare`, so the
|
|
25
|
+
* `export { register, bits }` re-export in index.ts resolves under Node's ESM
|
|
26
|
+
* loader, which validates that re-exported bindings exist at runtime. They are
|
|
27
|
+
* never invoked: the cuttlefish transpiler detects them by name and lowers the
|
|
28
|
+
* decorated struct away, so these stubs are only reached when the decorator
|
|
29
|
+
* source is imported without transpilation (e.g. host-side tests). */
|
|
30
|
+
export function register(_address) {
|
|
31
|
+
return () => { };
|
|
32
|
+
}
|
|
33
|
+
/** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
|
|
34
|
+
* within its register. Erased at transpile time. See `register` for why these
|
|
35
|
+
* have runtime bodies. */
|
|
36
|
+
export function bits(_hi, _lo) {
|
|
37
|
+
return () => { };
|
|
38
|
+
}
|
package/dist/spi.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, rawCpp } from './emit.js';
|
|
1
|
+
import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, spiReadBuffer, rawCpp } from './emit.js';
|
|
2
2
|
import { include } from './include.js';
|
|
3
3
|
export class SPIDevice {
|
|
4
4
|
constructor(bus, chipSelect) {
|
|
@@ -21,12 +21,15 @@ export class SPIDevice {
|
|
|
21
21
|
}
|
|
22
22
|
readRegister(register, count) {
|
|
23
23
|
include("<SPI.h>");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
rawCpp
|
|
29
|
-
|
|
24
|
+
spiCsLow(this._cs);
|
|
25
|
+
spiTransfer(this._bus, register);
|
|
26
|
+
// Clock `count` dummy bytes and drain them into the caller's buffer
|
|
27
|
+
// (declared by the Uint8Array return marker as `uint8_t data[count]`).
|
|
28
|
+
// Using the semantic primitive — NOT rawCpp — keeps the buffer in user
|
|
29
|
+
// scope so it survives the return (no decayed pointer) and `data.length`
|
|
30
|
+
// / `data[i]` work, mirroring I2CDevice.readBytes.
|
|
31
|
+
spiReadBuffer(this._bus, count, new Uint8Array(count));
|
|
32
|
+
spiCsHigh(this._cs);
|
|
30
33
|
return new Uint8Array(count);
|
|
31
34
|
}
|
|
32
35
|
writeRegister(register, value) {
|
package/dist/types.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export interface IPinGroup<T extends PinGroupMember = PinGroupMember> {
|
|
|
37
37
|
fill(value: DigitalValue): void;
|
|
38
38
|
}
|
|
39
39
|
export declare function createPinGroup<T extends PinGroupMember>(pins: T[]): IPinGroup<T>;
|
|
40
|
-
export type ArchitectureIdentifier = 'avr' | 'esp32' | 'esp32s2' | 'esp32s3' | 'esp32c3' | 'esp32c6' | 'rp2040' | 'samd' | 'stm32' | 'nrf52' | (string & {});
|
|
40
|
+
export type ArchitectureIdentifier = 'avr' | 'esp32' | 'esp32s2' | 'esp32s3' | 'esp32c3' | 'esp32c6' | 'rp2040' | 'rp2350' | 'samd' | 'stm32' | 'nrf52' | (string & {});
|
|
41
41
|
export type I2CAddress = number;
|
|
42
42
|
export type SPIBitOrder = 'msb' | 'lsb';
|
|
43
43
|
export type SPIMode = 0 | 1 | 2 | 3;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/hal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-alpha.6",
|
|
4
4
|
"description": "TypeCAD hardware abstraction layer — GPIO, I2C, SPI, UART as regular TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
},
|
|
50
50
|
"author": "typecad0",
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@typecad/expect": "
|
|
53
|
-
"@typecad/board-arduino-uno": "
|
|
54
|
-
"@typecad/mcu-atmega328p": "
|
|
52
|
+
"@typecad/expect": "1.0.0-alpha.6",
|
|
53
|
+
"@typecad/board-arduino-uno": "1.0.0-alpha.6",
|
|
54
|
+
"@typecad/mcu-atmega328p": "1.0.0-alpha.6"
|
|
55
55
|
},
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"exports": {
|
package/src/emit.ts
CHANGED
|
@@ -99,6 +99,15 @@ export function i2cRequestFrom(bus: string, address: number, quantity: number, s
|
|
|
99
99
|
export function i2cAvailable(bus: string): number { return 0; }
|
|
100
100
|
/** Read a byte from I2C. */
|
|
101
101
|
export function i2cRead(bus: string): number { return 0; }
|
|
102
|
+
/**
|
|
103
|
+
* Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
|
|
104
|
+
* Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
|
|
105
|
+
* argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
|
|
106
|
+
* transformer rewrites to the caller's own buffer variable, so bytes land in
|
|
107
|
+
* the `uint8_t data[N]` declared in user scope — NOT an internal temp that
|
|
108
|
+
* decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
|
|
109
|
+
*/
|
|
110
|
+
export function i2cReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
|
|
102
111
|
|
|
103
112
|
// ---------------------------------------------------------------------------
|
|
104
113
|
// SPI — serial peripheral interface
|
|
@@ -118,6 +127,14 @@ export function spiEndTx(bus: string): void {}
|
|
|
118
127
|
export function spiCsLow(pin: number | string): void {}
|
|
119
128
|
/** Set SPI chip-select pin HIGH. */
|
|
120
129
|
export function spiCsHigh(pin: number | string): void {}
|
|
130
|
+
/**
|
|
131
|
+
* Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
|
|
132
|
+
* dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
|
|
133
|
+
* HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
|
|
134
|
+
* rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
|
|
135
|
+
* responsible for asserting/de-asserting chip-select around it.
|
|
136
|
+
*/
|
|
137
|
+
export function spiReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
|
|
121
138
|
/** Set SPI data mode. */
|
|
122
139
|
export function spiSetMode(bus: string, mode: number): void {}
|
|
123
140
|
/** Set SPI bit order. */
|
package/src/i2c.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, rawCpp } from './emit.js';
|
|
1
|
+
import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, i2cReadBuffer, rawCpp } from './emit.js';
|
|
2
2
|
import { include } from './include.js';
|
|
3
3
|
|
|
4
4
|
export class I2CDevice {
|
|
@@ -10,6 +10,16 @@ export class I2CDevice {
|
|
|
10
10
|
this._address = address;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
|
|
14
|
+
* structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
|
|
15
|
+
* (which declares `readonly address`), letting the same driver function be
|
|
16
|
+
* typed against the contract and accept either a real board device or a
|
|
17
|
+
* simulated one. The transpiler strips HAL class bodies to IR, so this
|
|
18
|
+
* getter carries no runtime cost in the generated C++. */
|
|
19
|
+
get address(): number {
|
|
20
|
+
return this._address;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
writeByte(register: number, value: number): void {
|
|
14
24
|
include("<Wire.h>");
|
|
15
25
|
i2cBeginTx(this._bus, this._address);
|
|
@@ -41,9 +51,11 @@ export class I2CDevice {
|
|
|
41
51
|
i2cWrite(this._bus, register);
|
|
42
52
|
i2cEndTx(this._bus, false);
|
|
43
53
|
i2cRequestFrom(this._bus, this._address, count, true);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
rawCpp
|
|
54
|
+
// Drain the requested bytes into the caller's buffer (declared by the
|
|
55
|
+
// Uint8Array return marker as `uint8_t data[count]`). Using the semantic
|
|
56
|
+
// primitive — NOT rawCpp — keeps the buffer in user scope so it survives
|
|
57
|
+
// the return (no decayed pointer) and `data.length` / `data[i]` work.
|
|
58
|
+
i2cReadBuffer(this._bus, count, new Uint8Array(count));
|
|
47
59
|
return new Uint8Array(count);
|
|
48
60
|
}
|
|
49
61
|
}
|
package/src/register.ts
CHANGED
|
@@ -27,9 +27,21 @@ export type Bit = 0 | 1;
|
|
|
27
27
|
export type Bits<N extends number = number> = number;
|
|
28
28
|
|
|
29
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
|
-
|
|
30
|
+
* Erased at transpile time — the class becomes a `volatile uint32_t*`.
|
|
31
|
+
*
|
|
32
|
+
* These carry real (inert) runtime bodies rather than `declare`, so the
|
|
33
|
+
* `export { register, bits }` re-export in index.ts resolves under Node's ESM
|
|
34
|
+
* loader, which validates that re-exported bindings exist at runtime. They are
|
|
35
|
+
* never invoked: the cuttlefish transpiler detects them by name and lowers the
|
|
36
|
+
* decorated struct away, so these stubs are only reached when the decorator
|
|
37
|
+
* source is imported without transpilation (e.g. host-side tests). */
|
|
38
|
+
export function register(_address: number): ClassDecorator {
|
|
39
|
+
return () => {};
|
|
40
|
+
}
|
|
32
41
|
|
|
33
42
|
/** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
|
|
34
|
-
* within its register. Erased at transpile time.
|
|
35
|
-
|
|
43
|
+
* within its register. Erased at transpile time. See `register` for why these
|
|
44
|
+
* have runtime bodies. */
|
|
45
|
+
export function bits(_hi: number, _lo: number): PropertyDecorator {
|
|
46
|
+
return () => {};
|
|
47
|
+
}
|
package/src/spi.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, rawCpp } from './emit.js';
|
|
1
|
+
import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, spiReadBuffer, rawCpp } from './emit.js';
|
|
2
2
|
import { include } from './include.js';
|
|
3
3
|
import type { Pin } from './gpio.js';
|
|
4
4
|
import type { SPIMode, SPISettings } from './types.js';
|
|
@@ -30,12 +30,15 @@ export class SPIDevice {
|
|
|
30
30
|
|
|
31
31
|
readRegister(register: number, count: number): Uint8Array {
|
|
32
32
|
include("<SPI.h>");
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
rawCpp
|
|
38
|
-
|
|
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);
|
|
39
42
|
return new Uint8Array(count);
|
|
40
43
|
}
|
|
41
44
|
|