@typecad/framework-zephyr 1.0.0-alpha.12 → 1.0.0-alpha.14
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 +13 -1
- package/dist/chips/controllers.d.ts +28 -8
- package/dist/chips/controllers.js +49 -12
- package/dist/chips/resolve.js +32 -6
- package/dist/chips/types.d.ts +63 -12
- package/dist/chips/xiao-ble.js +12 -0
- package/dist/display/index.d.ts +1 -1
- package/dist/display/index.js +1 -1
- package/dist/display/profiles.d.ts +8 -0
- package/dist/display/profiles.js +19 -0
- package/dist/display/ui-adapter.d.ts +4 -0
- package/dist/display/ui-adapter.js +46 -0
- package/dist/dt-config/kconfig.d.ts +11 -0
- package/dist/dt-config/kconfig.js +1 -1
- package/dist/dt-config/overlay.d.ts +8 -1
- package/dist/dt-config/overlay.js +104 -1
- package/dist/framework.manifest.d.ts +20 -30
- package/dist/framework.manifest.js +12 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lowering/adc.d.ts +7 -4
- package/dist/lowering/adc.js +25 -11
- package/dist/lowering/gpio.js +15 -8
- package/dist/lowering/mqtt.js +9 -1
- package/dist/lowering/pulse.js +7 -7
- package/dist/lowering/pwm.d.ts +21 -3
- package/dist/lowering/pwm.js +28 -4
- package/dist/lowering/spi.js +2 -2
- package/dist/lowering/tone.js +3 -2
- package/dist/lowering/wifi.js +28 -5
- package/dist/strategy.d.ts +20 -0
- package/dist/strategy.js +295 -119
- package/dist/toolchain/debug-config.d.ts +43 -2
- package/dist/toolchain/debug-config.js +129 -17
- package/dist/toolchain/index.d.ts +14 -1
- package/dist/toolchain/index.js +146 -20
- package/dist/toolchain/scaffold.d.ts +9 -0
- package/dist/toolchain/scaffold.js +84 -19
- package/dist/toolchain/west-discover.d.ts +4 -1
- package/dist/toolchain/west-discover.js +2 -0
- package/dist/toolchain/west-spawn.js +17 -5
- package/package.json +5 -5
- package/src/chips/controllers.ts +61 -12
- package/src/chips/resolve.ts +32 -5
- package/src/chips/types.ts +63 -12
- package/src/chips/xiao-ble.ts +82 -70
- package/src/display/index.ts +1 -1
- package/src/display/profiles.ts +23 -0
- package/src/display/ui-adapter.ts +51 -0
- package/src/dt-config/kconfig.ts +12 -1
- package/src/dt-config/overlay.ts +123 -0
- package/src/framework.manifest.ts +12 -3
- package/src/index.ts +6 -0
- package/src/lowering/adc.ts +28 -12
- package/src/lowering/gpio.ts +15 -8
- package/src/lowering/mqtt.ts +9 -1
- package/src/lowering/pulse.ts +7 -7
- package/src/lowering/pwm.ts +29 -4
- package/src/lowering/spi.ts +2 -2
- package/src/lowering/tone.ts +3 -3
- package/src/lowering/wifi.ts +29 -5
- package/src/strategy.ts +320 -123
- package/src/toolchain/debug-config.ts +137 -14
- package/src/toolchain/index.ts +645 -513
- package/src/toolchain/scaffold.ts +81 -17
- package/src/toolchain/west-discover.ts +321 -316
- package/src/toolchain/west-spawn.ts +17 -5
package/README.md
CHANGED
|
@@ -35,7 +35,19 @@ fix (`pip install west`, set `ZEPHYR_BASE`, or activate the venv).
|
|
|
35
35
|
symbols for the lowered peripherals). Both are regenerated idempotently —
|
|
36
36
|
only rewritten when their content changes, so Ninja's incremental build is
|
|
37
37
|
preserved.
|
|
38
|
-
-
|
|
38
|
+
- Supported board targets: **Seeed Studio XIAO nRF52840** (`xiao_ble`),
|
|
39
|
+
**ESP32 DevKit** (`esp32_devkitc`), **ESP32-S3 DevKit** (`esp32s3_devkitc`),
|
|
40
|
+
**ESP32-C3 DevKitM** (`esp32c3_devkitm/esp32c3`), **ESP32-C6 DevKitC**
|
|
41
|
+
(`esp32c6_devkitc/esp32c6/hpcore` — the qualified form is required, the
|
|
42
|
+
board also ships an lpcore variant), **Raspberry Pi Pico** (`rpi_pico`),
|
|
43
|
+
**Pico 2** (`rpi_pico2/rp2350a/m33`), and **WeAct Black Pill V2.0**
|
|
44
|
+
(`blackpill_f411ce/stm32f411xe` — the first STM32 target; flashed over USB
|
|
45
|
+
via the ROM DFU bootloader). The rpi_pico, esp32c3, esp32c6, and blackpill
|
|
46
|
+
boards resolve their chip data from the board packages' `zephyr` field (see
|
|
47
|
+
`resolveChipFromBoard`) rather than the hardcoded chip registry; the
|
|
48
|
+
qualified Pico 2 target selects the Cortex-M33 cpucluster (Zephyr 4.3+
|
|
49
|
+
rejects the bare `rpi_pico2` because the board also ships a Hazard3 variant
|
|
50
|
+
with no default).
|
|
39
51
|
|
|
40
52
|
## Installation
|
|
41
53
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ZephyrChipDescriptor } from './types.js';
|
|
1
|
+
import type { ZephyrChipDescriptor, ZephyrGpioController } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Resolve the devicetree nodelabel of the GPIO controller that owns `pin`.
|
|
4
4
|
*
|
|
@@ -9,13 +9,33 @@ import type { ZephyrChipDescriptor } from './types.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function controllerNodelabelForPin(chip: ZephyrChipDescriptor, pin: number): string;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* The owning controller range for a HAL pin, if the chip declares a split.
|
|
13
|
+
*/
|
|
14
|
+
export declare function controllerRangeForPin(chip: ZephyrChipDescriptor, pin: number): ZephyrGpioController | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The PORT-RELATIVE pin index for the raw gpio_pin_*_raw() API — the Zephyr
|
|
17
|
+
* raw calls address the index WITHIN the controller, not the global HAL pin
|
|
18
|
+
* number. For split SoCs each controller's `minPin` is its base (STM32:
|
|
19
|
+
* gpiob minPin 16, so PB12 = pin 28 → raw 12); single-controller SoCs have
|
|
20
|
+
* no offset. Emitting the global number against a port driver would address
|
|
21
|
+
* a nonexistent port bit (STM32 gpiob is 0–15) and fail at runtime.
|
|
22
|
+
*/
|
|
23
|
+
export declare function controllerRawPinForPin(chip: ZephyrChipDescriptor, pin: number): number;
|
|
24
|
+
/**
|
|
25
|
+
* Emit the C++ source for runtime pin → GPIO-device / port-relative-index
|
|
26
|
+
* dispatchers.
|
|
27
|
+
*
|
|
28
|
+
* Returns lines defining:
|
|
29
|
+
* - `static inline const struct device* __tc_gpio_dev(uint32_t pin)` — the
|
|
30
|
+
* owning controller's device, resolved via DEVICE_DT_GET(DT_NODELABEL(...))
|
|
31
|
+
* at compile time (the macro is evaluated per branch, so it is always
|
|
32
|
+
* statically valid); only `pin` is runtime.
|
|
33
|
+
* - `static inline gpio_pin_t __tc_gpio_pin(uint32_t pin)` — the
|
|
34
|
+
* port-relative index for gpio_pin_*_raw() (see controllerRawPinForPin).
|
|
35
|
+
* The runtime shim paths (UI pin-watch, safety voters) take a runtime pin,
|
|
36
|
+
* so they cannot bake the offset in at emit time.
|
|
13
37
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* compile time (the macro is evaluated per branch, so it is always statically
|
|
17
|
-
* valid); only `pin` is runtime. For a single-controller SoC this collapses to
|
|
18
|
-
* a one-liner returning that controller, so the existing XIAO nRF52840 behavior
|
|
19
|
-
* is byte-for-byte unchanged.
|
|
38
|
+
* For a single-controller SoC both collapse to one-liners, so the existing
|
|
39
|
+
* XIAO nRF52840 behavior is byte-for-byte unchanged.
|
|
20
40
|
*/
|
|
21
41
|
export declare function emitGpioDevDispatcher(chip: ZephyrChipDescriptor): string[];
|
|
@@ -27,23 +27,47 @@
|
|
|
27
27
|
* for out-of-range pins (e.g. the manifest probe's synthetic pin 0).
|
|
28
28
|
*/
|
|
29
29
|
export function controllerNodelabelForPin(chip, pin) {
|
|
30
|
+
const hit = controllerRangeForPin(chip, pin);
|
|
31
|
+
return hit ? hit.nodelabel : chip.gpioController;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The owning controller range for a HAL pin, if the chip declares a split.
|
|
35
|
+
*/
|
|
36
|
+
export function controllerRangeForPin(chip, pin) {
|
|
30
37
|
const ranges = chip.gpioControllers;
|
|
31
38
|
if (ranges && ranges.length > 0) {
|
|
32
|
-
|
|
33
|
-
if (hit)
|
|
34
|
-
return hit.nodelabel;
|
|
39
|
+
return ranges.find((r) => pin >= r.minPin && pin <= r.maxPin);
|
|
35
40
|
}
|
|
36
|
-
return
|
|
41
|
+
return undefined;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
39
|
-
*
|
|
44
|
+
* The PORT-RELATIVE pin index for the raw gpio_pin_*_raw() API — the Zephyr
|
|
45
|
+
* raw calls address the index WITHIN the controller, not the global HAL pin
|
|
46
|
+
* number. For split SoCs each controller's `minPin` is its base (STM32:
|
|
47
|
+
* gpiob minPin 16, so PB12 = pin 28 → raw 12); single-controller SoCs have
|
|
48
|
+
* no offset. Emitting the global number against a port driver would address
|
|
49
|
+
* a nonexistent port bit (STM32 gpiob is 0–15) and fail at runtime.
|
|
50
|
+
*/
|
|
51
|
+
export function controllerRawPinForPin(chip, pin) {
|
|
52
|
+
const hit = controllerRangeForPin(chip, pin);
|
|
53
|
+
return pin - (hit?.minPin ?? 0);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Emit the C++ source for runtime pin → GPIO-device / port-relative-index
|
|
57
|
+
* dispatchers.
|
|
58
|
+
*
|
|
59
|
+
* Returns lines defining:
|
|
60
|
+
* - `static inline const struct device* __tc_gpio_dev(uint32_t pin)` — the
|
|
61
|
+
* owning controller's device, resolved via DEVICE_DT_GET(DT_NODELABEL(...))
|
|
62
|
+
* at compile time (the macro is evaluated per branch, so it is always
|
|
63
|
+
* statically valid); only `pin` is runtime.
|
|
64
|
+
* - `static inline gpio_pin_t __tc_gpio_pin(uint32_t pin)` — the
|
|
65
|
+
* port-relative index for gpio_pin_*_raw() (see controllerRawPinForPin).
|
|
66
|
+
* The runtime shim paths (UI pin-watch, safety voters) take a runtime pin,
|
|
67
|
+
* so they cannot bake the offset in at emit time.
|
|
40
68
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* compile time (the macro is evaluated per branch, so it is always statically
|
|
44
|
-
* valid); only `pin` is runtime. For a single-controller SoC this collapses to
|
|
45
|
-
* a one-liner returning that controller, so the existing XIAO nRF52840 behavior
|
|
46
|
-
* is byte-for-byte unchanged.
|
|
69
|
+
* For a single-controller SoC both collapse to one-liners, so the existing
|
|
70
|
+
* XIAO nRF52840 behavior is byte-for-byte unchanged.
|
|
47
71
|
*/
|
|
48
72
|
export function emitGpioDevDispatcher(chip) {
|
|
49
73
|
const ranges = chip.gpioControllers;
|
|
@@ -53,14 +77,27 @@ export function emitGpioDevDispatcher(chip) {
|
|
|
53
77
|
` (void)pin;`,
|
|
54
78
|
` return DEVICE_DT_GET(DT_NODELABEL(${chip.gpioController}));`,
|
|
55
79
|
'}',
|
|
80
|
+
'static inline gpio_pin_t __tc_gpio_pin(uint32_t pin) {',
|
|
81
|
+
` return (gpio_pin_t)pin;`,
|
|
82
|
+
'}',
|
|
56
83
|
];
|
|
57
84
|
}
|
|
58
85
|
const lines = [
|
|
59
86
|
'static inline const struct device* __tc_gpio_dev(uint32_t pin) {',
|
|
60
87
|
];
|
|
88
|
+
const pinLines = [
|
|
89
|
+
'static inline gpio_pin_t __tc_gpio_pin(uint32_t pin) {',
|
|
90
|
+
];
|
|
61
91
|
for (const r of ranges) {
|
|
62
92
|
lines.push(` if (pin >= ${r.minPin} && pin <= ${r.maxPin}) { return DEVICE_DT_GET(DT_NODELABEL(${r.nodelabel})); }`);
|
|
93
|
+
if (r.minPin === 0) {
|
|
94
|
+
pinLines.push(` if (pin >= ${r.minPin} && pin <= ${r.maxPin}) { return (gpio_pin_t)pin; }`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
pinLines.push(` if (pin >= ${r.minPin} && pin <= ${r.maxPin}) { return (gpio_pin_t)(pin - ${r.minPin}); }`);
|
|
98
|
+
}
|
|
63
99
|
}
|
|
64
100
|
lines.push(` return DEVICE_DT_GET(DT_NODELABEL(${chip.gpioController}));`, '}');
|
|
65
|
-
return
|
|
101
|
+
pinLines.push(` return (gpio_pin_t)pin;`, '}');
|
|
102
|
+
return [...lines, ...pinLines];
|
|
66
103
|
}
|
package/dist/chips/resolve.js
CHANGED
|
@@ -81,18 +81,35 @@ export function resolveChipFromBoard(bc) {
|
|
|
81
81
|
const pwmSpecs = collectIndexed(bc, 'zephyr.pwm.specs', (m, i) => {
|
|
82
82
|
const pin = m.get(`zephyr.pwm.specs.${i}.pin`);
|
|
83
83
|
const dtSpec = m.get(`zephyr.pwm.specs.${i}.dtSpec`);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const controller = m.get(`zephyr.pwm.specs.${i}.controller`);
|
|
85
|
+
const channel = m.get(`zephyr.pwm.specs.${i}.channel`);
|
|
86
|
+
const periodNs = m.get(`zephyr.pwm.specs.${i}.periodNs`);
|
|
87
|
+
const polarity = m.get(`zephyr.pwm.specs.${i}.polarity`);
|
|
88
|
+
// Board-shipped alias form (dtSpec) or synthesized form (controller +
|
|
89
|
+
// channel → overlay-generated alias) — at least one, else drop the entry.
|
|
90
|
+
if (pin == null || !(dtSpec || (controller && channel != null)))
|
|
91
|
+
return null;
|
|
92
|
+
return {
|
|
93
|
+
pin,
|
|
94
|
+
...(dtSpec ? { dtSpec } : {}),
|
|
95
|
+
...(controller ? { controller } : {}),
|
|
96
|
+
...(channel != null ? { channel } : {}),
|
|
97
|
+
...(periodNs != null ? { periodNs } : {}),
|
|
98
|
+
...(polarity ? { polarity } : {}),
|
|
99
|
+
};
|
|
87
100
|
});
|
|
88
101
|
const adcNodeLabel = bc.get('zephyr.adc.nodeLabel');
|
|
89
102
|
const adcResolution = bc.get('zephyr.adc.resolution');
|
|
90
103
|
const adcVref = bc.get('zephyr.adc.vrefMv');
|
|
104
|
+
const adcGain = bc.get('zephyr.adc.gain');
|
|
105
|
+
const adcReference = bc.get('zephyr.adc.reference');
|
|
91
106
|
const adcChannels = collectIndexed(bc, 'zephyr.adc.channels', (m, i) => {
|
|
92
107
|
const pin = m.get(`zephyr.adc.channels.${i}.pin`);
|
|
93
108
|
const channel = m.get(`zephyr.adc.channels.${i}.channel`);
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
const pinctrl = m.get(`zephyr.adc.channels.${i}.pinctrl`);
|
|
110
|
+
if (pin != null && channel != null) {
|
|
111
|
+
return { pin, channel, ...(pinctrl ? { pinctrl } : {}) };
|
|
112
|
+
}
|
|
96
113
|
return null;
|
|
97
114
|
});
|
|
98
115
|
const wdtNodeLabel = bc.get('zephyr.wdt.nodeLabel');
|
|
@@ -113,7 +130,16 @@ export function resolveChipFromBoard(bc) {
|
|
|
113
130
|
...(uartControllers.length > 0 ? { uart: { controllers: uartControllers } } : {}),
|
|
114
131
|
...(pwmSpecs.length > 0 ? { pwm: { specs: pwmSpecs } } : {}),
|
|
115
132
|
...(adcNodeLabel || adcResolution != null || adcVref != null || adcChannels.length > 0
|
|
116
|
-
? {
|
|
133
|
+
? {
|
|
134
|
+
adc: {
|
|
135
|
+
nodeLabel: adcNodeLabel ?? 'adc',
|
|
136
|
+
resolution: adcResolution ?? 12,
|
|
137
|
+
vrefMv: adcVref ?? 3000,
|
|
138
|
+
channels: adcChannels,
|
|
139
|
+
...(adcGain ? { gain: adcGain } : {}),
|
|
140
|
+
...(adcReference ? { reference: adcReference } : {}),
|
|
141
|
+
},
|
|
142
|
+
}
|
|
117
143
|
: {}),
|
|
118
144
|
...(wdtNodeLabel ? { wdt: { nodeLabel: wdtNodeLabel } } : {}),
|
|
119
145
|
...(wifiSupported ? { wifi: { supported: true } } : {}),
|
package/dist/chips/types.d.ts
CHANGED
|
@@ -47,14 +47,37 @@ export interface ZephyrBusController {
|
|
|
47
47
|
/**
|
|
48
48
|
* A PWM channel described as a devicetree spec.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* Two forms, mutually exclusive:
|
|
51
|
+
* - **Board-shipped alias:** `dtSpec` names a DT alias the board's own DTS
|
|
52
|
+
* already defines (e.g. the XIAO's `pwm-led0`). Emitted as
|
|
53
|
+
* `PWM_DT_SPEC_GET(DT_ALIAS(<dtSpec>))`.
|
|
54
|
+
* - **Synthesized** (controller + channel): the board DTS enables a PWM
|
|
55
|
+
* controller node (e.g. `pwm4`) but defines no alias for it. The overlay
|
|
56
|
+
* generator synthesizes a `pwm-leds` consumer node + a `tc-pwm<pin>` alias
|
|
57
|
+
* in `<board>.overlay`; the lowering emits
|
|
58
|
+
* `PWM_DT_SPEC_GET(DT_ALIAS(tc-pwm<pin>))`. Both sides derive the alias
|
|
59
|
+
* name from the pin, so they always agree.
|
|
60
|
+
*
|
|
61
|
+
* `pwm_set_pulse_dt(&spec, pulse_ns)` honors the spec's period/polarity.
|
|
52
62
|
*/
|
|
53
63
|
export interface ZephyrPwmSpec {
|
|
54
64
|
/** GPIO number (matches the HAL op `pin` field). */
|
|
55
65
|
readonly pin: number;
|
|
56
|
-
/**
|
|
57
|
-
|
|
66
|
+
/** Board-shipped DT alias, e.g. 'pwm-led0'. Omit when using the
|
|
67
|
+
* synthesized form (controller + channel). */
|
|
68
|
+
readonly dtSpec?: string;
|
|
69
|
+
/** Synthesized form: PWM controller DT nodelabel, e.g. 'pwm4' (the STM32
|
|
70
|
+
* timer's pwm child node). The overlay's pwm-leds node consumes it. */
|
|
71
|
+
readonly controller?: string;
|
|
72
|
+
/** Synthesized form: channel index within the controller (1-based timer
|
|
73
|
+
* channel, matching the `pwms` binding's channel cell). */
|
|
74
|
+
readonly channel?: number;
|
|
75
|
+
/** Synthesized form: period in nanoseconds, baked into the DT spec. The
|
|
76
|
+
* lowering scales duty against `spec.period`. Default 20 000 000 (20 ms /
|
|
77
|
+
* 50 Hz — the servo convention; harmless for LED dimming). */
|
|
78
|
+
readonly periodNs?: number;
|
|
79
|
+
/** Synthesized form: PWM polarity flag. Default PWM_POLARITY_NORMAL. */
|
|
80
|
+
readonly polarity?: string;
|
|
58
81
|
}
|
|
59
82
|
/**
|
|
60
83
|
* A GPIO pin usable as an interrupt source.
|
|
@@ -69,7 +92,7 @@ export interface ZephyrInterruptPin {
|
|
|
69
92
|
readonly dtSpec: string;
|
|
70
93
|
}
|
|
71
94
|
/**
|
|
72
|
-
* An ADC channel: which
|
|
95
|
+
* An ADC channel: which ADC input a given HAL pin maps to.
|
|
73
96
|
*
|
|
74
97
|
* The XIAO nRF52840 has no pre-declared ADC channel nodes in devicetree, so the
|
|
75
98
|
* lowering emits `adc_channel_setup` against `DEVICE_DT_GET(DT_NODELABEL(adc))`
|
|
@@ -78,8 +101,16 @@ export interface ZephyrInterruptPin {
|
|
|
78
101
|
export interface ZephyrAdcChannel {
|
|
79
102
|
/** GPIO number (matches the HAL op `pin` field). */
|
|
80
103
|
readonly pin: number;
|
|
81
|
-
/**
|
|
104
|
+
/** ADC channel index (nRF SAADC AIN0–AIN7; STM32 ADC1_IN0–IN9). */
|
|
82
105
|
readonly channel: number;
|
|
106
|
+
/**
|
|
107
|
+
* Pinctrl node label that muxes this pin to analog mode, e.g.
|
|
108
|
+
* 'adc1_in0_pa0' (STM32). When present, the overlay generator rewrites the
|
|
109
|
+
* ADC node's pinctrl-0 to the channels the program actually reads — SoCs
|
|
110
|
+
* like STM32 leave the pad in GPIO mode otherwise and reads float.
|
|
111
|
+
* Omit on SoCs whose ADC needs no pad muxing (nRF SAADC, RP2040).
|
|
112
|
+
*/
|
|
113
|
+
readonly pinctrl?: string;
|
|
83
114
|
}
|
|
84
115
|
/**
|
|
85
116
|
* A DAC channel: which DAC output a given HAL pin maps to. The lowering emits
|
|
@@ -113,11 +144,18 @@ export interface ZephyrChipDescriptor {
|
|
|
113
144
|
readonly gpioController: string;
|
|
114
145
|
/**
|
|
115
146
|
* Per-range GPIO controllers for SoCs that split GPIO across multiple
|
|
116
|
-
* devicetree nodes (ESP32-S3: `gpio0` 0–31, `gpio1` 32–48
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
147
|
+
* devicetree nodes (ESP32-S3: `gpio0` 0–31, `gpio1` 32–48; STM32: one
|
|
148
|
+
* controller per port — `gpioa` 0–15, `gpiob` 16–31, `gpioc` 32–47).
|
|
149
|
+
* When present, the lowering routes a HAL pin to its owning controller at
|
|
150
|
+
* runtime via the emitted `__tc_gpio_dev(pin)` dispatcher; `gpioController`
|
|
151
|
+
* is the fallback. Omit on single-controller SoCs (RP2040, …) — every pin
|
|
152
|
+
* is on the one controller described by `gpioController`.
|
|
153
|
+
*
|
|
154
|
+
* NUMBERING RULE (load-bearing): `minPin` must equal the controller's port
|
|
155
|
+
* base so the port-relative raw index is `pin - minPin` (STM32 PB12 = pin
|
|
156
|
+
* 28 → raw 12 — the Zephyr raw API addresses the index WITHIN the
|
|
157
|
+
* controller). Number pins by port blocks and never contiguously across
|
|
158
|
+
* unbonded pins.
|
|
121
159
|
*/
|
|
122
160
|
readonly gpioControllers?: readonly ZephyrGpioController[];
|
|
123
161
|
/** GPIO pins with devicetree specs (LEDs, buttons, board-defined pins). */
|
|
@@ -142,7 +180,7 @@ export interface ZephyrChipDescriptor {
|
|
|
142
180
|
readonly pwm?: {
|
|
143
181
|
readonly specs: readonly ZephyrPwmSpec[];
|
|
144
182
|
};
|
|
145
|
-
/** ADC: the
|
|
183
|
+
/** ADC: the ADC device node label + the pin→channel map. */
|
|
146
184
|
readonly adc?: {
|
|
147
185
|
readonly nodeLabel: string;
|
|
148
186
|
readonly channels: readonly ZephyrAdcChannel[];
|
|
@@ -150,6 +188,19 @@ export interface ZephyrChipDescriptor {
|
|
|
150
188
|
readonly vrefMv: number;
|
|
151
189
|
/** ADC resolution in bits. */
|
|
152
190
|
readonly resolution: number;
|
|
191
|
+
/**
|
|
192
|
+
* Zephyr `enum adc_gain` macro for the channel setup, e.g.
|
|
193
|
+
* 'ADC_GAIN_1_4' (nRF SAADC default) or 'ADC_GAIN_1' (STM32 driver
|
|
194
|
+
* requires exactly this). Defaults to 'ADC_GAIN_1_4'.
|
|
195
|
+
*/
|
|
196
|
+
readonly gain?: string;
|
|
197
|
+
/**
|
|
198
|
+
* Zephyr `enum adc_reference` macro, e.g. 'ADC_REF_INTERNAL'. Defaults to
|
|
199
|
+
* 'ADC_REF_INTERNAL' — on nRF that is the 0.6 V internal ref measured
|
|
200
|
+
* through the gain divider; the STM32 driver ALSO requires
|
|
201
|
+
* ADC_REF_INTERNAL (Zephyr maps it to the VREF+ pad) with vrefMv = VDDA.
|
|
202
|
+
*/
|
|
203
|
+
readonly reference?: string;
|
|
153
204
|
};
|
|
154
205
|
/**
|
|
155
206
|
* DAC: the DAC device node label + the pin→channel map. Present only on chips
|
package/dist/chips/xiao-ble.js
CHANGED
|
@@ -8,6 +8,18 @@ export const XIAO_BLE = {
|
|
|
8
8
|
id: 'xiao_ble',
|
|
9
9
|
soc: 'nrf52840',
|
|
10
10
|
gpioController: 'gpio0',
|
|
11
|
+
// GPIO is split across two devicetree controllers: gpio0 (P0.00–P0.31)
|
|
12
|
+
// and gpio1 (P1.00–P1.15, HAL pins 32–47). Declaring the split makes the
|
|
13
|
+
// raw path emit the port-relative index against gpio1 (P1.11 = raw 11),
|
|
14
|
+
// which NRF_GPIO_PIN_MAP(1, 11) resolves to absolute pin 43 — the same
|
|
15
|
+
// physical pin the old single-controller form reached only by accident:
|
|
16
|
+
// gpio0 + global 43 also maps to 43 (MAP(0, 43) = 43), but that form
|
|
17
|
+
// trips the generic layer's port_pin_mask __ASSERT ("Unsupported pin",
|
|
18
|
+
// gpio0's mask covers 0–31) on any assert-enabled build.
|
|
19
|
+
gpioControllers: [
|
|
20
|
+
{ nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
|
|
21
|
+
{ nodelabel: 'gpio1', minPin: 32, maxPin: 47 },
|
|
22
|
+
],
|
|
11
23
|
gpio: {
|
|
12
24
|
dtSpecs: [
|
|
13
25
|
// Onboard RGB LEDs — active-low (GPIO_ACTIVE_LOW in xiao_ble_common.dtsi).
|
package/dist/display/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { DisplayHALOp } from '@typecad/cuttlefish/api/shared';
|
|
|
2
2
|
import { type ZephyrDisplayProfile } from './profiles.js';
|
|
3
3
|
export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
|
|
4
4
|
export { zephyrTouchAdapter } from './touch-adapter.js';
|
|
5
|
-
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE } from './profiles.js';
|
|
5
|
+
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE, BUILT_IN_PROFILES } from './profiles.js';
|
|
6
6
|
export type { ZephyrDisplayProfile } from './profiles.js';
|
|
7
7
|
export interface DisplayState {
|
|
8
8
|
initialized: boolean;
|
package/dist/display/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { DEFAULT_ZEPHYR_DISPLAY_PROFILE } from './profiles.js';
|
|
|
15
15
|
// and consumers can reach them from the package barrel.
|
|
16
16
|
export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
|
|
17
17
|
export { zephyrTouchAdapter } from './touch-adapter.js';
|
|
18
|
-
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE } from './profiles.js';
|
|
18
|
+
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE, BUILT_IN_PROFILES } from './profiles.js';
|
|
19
19
|
/** Fresh display state (used by the strategy per-build). */
|
|
20
20
|
export function newDisplayState() {
|
|
21
21
|
return { initialized: false, profile: DEFAULT_ZEPHYR_DISPLAY_PROFILE };
|
|
@@ -48,3 +48,11 @@ export declare function panelControllerFor(profile: Pick<ZephyrDisplayProfile, '
|
|
|
48
48
|
export declare const ZEPHYR_DISPLAY_PROFILES: Record<string, ZephyrDisplayProfile>;
|
|
49
49
|
/** The default profile used when resolveDisplayOp is probed without a display.init. */
|
|
50
50
|
export declare const DEFAULT_ZEPHYR_DISPLAY_PROFILE: ZephyrDisplayProfile;
|
|
51
|
+
/**
|
|
52
|
+
* The Zephyr profiles mapped to the shared DisplayProfile shape — the single
|
|
53
|
+
* mapping, so no consumer needs to know the DT-binding descriptor layout.
|
|
54
|
+
* The strategy's getProfileRegistry() and the preview's profile-registry
|
|
55
|
+
* loader both consume this (the same role `BUILT_IN_PROFILES` plays in
|
|
56
|
+
* framework-arduino's displays modules).
|
|
57
|
+
*/
|
|
58
|
+
export declare const BUILT_IN_PROFILES: Record<string, import('@typecad/cuttlefish/api/shared').DisplayProfile>;
|
package/dist/display/profiles.js
CHANGED
|
@@ -69,3 +69,22 @@ export const ZEPHYR_DISPLAY_PROFILES = {
|
|
|
69
69
|
};
|
|
70
70
|
/** The default profile used when resolveDisplayOp is probed without a display.init. */
|
|
71
71
|
export const DEFAULT_ZEPHYR_DISPLAY_PROFILE = ZEPHYR_DISPLAY_PROFILES['ili9341-zephyr'];
|
|
72
|
+
/**
|
|
73
|
+
* The Zephyr profiles mapped to the shared DisplayProfile shape — the single
|
|
74
|
+
* mapping, so no consumer needs to know the DT-binding descriptor layout.
|
|
75
|
+
* The strategy's getProfileRegistry() and the preview's profile-registry
|
|
76
|
+
* loader both consume this (the same role `BUILT_IN_PROFILES` plays in
|
|
77
|
+
* framework-arduino's displays modules).
|
|
78
|
+
*/
|
|
79
|
+
export const BUILT_IN_PROFILES = Object.fromEntries(Object.entries(ZEPHYR_DISPLAY_PROFILES).map(([name, p]) => [
|
|
80
|
+
name,
|
|
81
|
+
{
|
|
82
|
+
driver: p.driver,
|
|
83
|
+
width: p.width,
|
|
84
|
+
height: p.height,
|
|
85
|
+
nativeWidth: p.nativeWidth,
|
|
86
|
+
nativeHeight: p.nativeHeight,
|
|
87
|
+
colorFormat: p.colorFormat,
|
|
88
|
+
rotation: p.rotation ?? 1,
|
|
89
|
+
},
|
|
90
|
+
]));
|
|
@@ -10,6 +10,10 @@ export interface ZephyrDisplayReadbackOptions {
|
|
|
10
10
|
scanlineSync?: boolean;
|
|
11
11
|
/** MISO/SDO GPIO; readback is disabled when it is not explicitly wired. */
|
|
12
12
|
miso?: number;
|
|
13
|
+
/** Tearing-effect GPIO (panel TE output). When set, panel updates wait for
|
|
14
|
+
* the TE frame pulse instead of GET_SCANLINE readback — no MISO required.
|
|
15
|
+
* The overlay adds te-gpios to the display DT node from this. */
|
|
16
|
+
tearingEffectPin?: number;
|
|
13
17
|
}
|
|
14
18
|
export declare function zephyrUiDisplayAdapter(profile: ZephyrDisplayProfile, readback?: ZephyrDisplayReadbackOptions): DisplayAdapterCode;
|
|
15
19
|
/**
|
|
@@ -55,6 +55,9 @@ export function zephyrUiDisplayAdapter(profile, readback = {}) {
|
|
|
55
55
|
// react badly to GSCAN reads. Both an explicit opt-in and an explicit MISO
|
|
56
56
|
// pin are required before emitting an active synchronization path.
|
|
57
57
|
const scanlineSync = readback.scanlineSync === true && readback.miso !== undefined;
|
|
58
|
+
// TE (hardware tearing-effect) sync: strictly opt-in via a configured GPIO.
|
|
59
|
+
// Preferred over GET_SCANLINE when wired — no readback traffic, no MISO.
|
|
60
|
+
const tePin = typeof readback.tearingEffectPin === 'number' ? readback.tearingEffectPin : undefined;
|
|
58
61
|
const includes = [
|
|
59
62
|
`// --- Zephyr UI display adapter (${profile.driver}) ---`,
|
|
60
63
|
`// Native CuttlefishGFX path: do NOT #define CuttlefishCanvas16 so the`,
|
|
@@ -118,6 +121,22 @@ export function zephyrUiDisplayAdapter(profile, readback = {}) {
|
|
|
118
121
|
`// controller-specific validation are required; otherwise the display stays`,
|
|
119
122
|
`// on the existing retained/composited path with no extra SPI reads.`,
|
|
120
123
|
`static const bool __tc_pnl_scanline_sync = ${scanlineSync ? 'true' : 'false'};`,
|
|
124
|
+
`// Tearing-effect (TE) hardware sync: the panel pulses its TE line once`,
|
|
125
|
+
`// per frame. When te-gpios is present on the display DT node, panel`,
|
|
126
|
+
`// updates arm on the TE edge — tear-free writes with no MISO readback.`,
|
|
127
|
+
`#if DT_NODE_HAS_PROP(DT_NODELABEL(${dtLabel}), te_gpios)`,
|
|
128
|
+
`#define __TC_TE_SYNC 1`,
|
|
129
|
+
`static const struct gpio_dt_spec __tc_te =`,
|
|
130
|
+
` GPIO_DT_SPEC_GET(DT_NODELABEL(${dtLabel}), te_gpios);`,
|
|
131
|
+
`static struct gpio_callback __tc_te_cb;`,
|
|
132
|
+
`static volatile uint32_t __tc_te_count = 0;`,
|
|
133
|
+
`static void __tc_te_isr(const struct device* port, struct gpio_callback* cb, uint32_t pins) {`,
|
|
134
|
+
` (void)port; (void)cb; (void)pins;`,
|
|
135
|
+
` __tc_te_count++;`,
|
|
136
|
+
`}`,
|
|
137
|
+
`#else`,
|
|
138
|
+
`#define __TC_TE_SYNC 0`,
|
|
139
|
+
`#endif`,
|
|
121
140
|
`// Stashed address window from the last setAddrWindow call. The runtime`,
|
|
122
141
|
`// calls setAddrWindow + writePixels as a matched pair, so we stash the rect`,
|
|
123
142
|
`// here and consume it in writePixels.`,
|
|
@@ -134,6 +153,17 @@ export function zephyrUiDisplayAdapter(profile, readback = {}) {
|
|
|
134
153
|
// with DT_HAS_ALIAS (the safe primitive for an alias that may be absent —
|
|
135
154
|
// DT_NODE_HAS_STATUS(DT_ALIAS(...)) is version-dependent when the alias is
|
|
136
155
|
// missing and can fail the build).
|
|
156
|
+
// TE pin: input + rising-edge interrupt (the ST7796 TE pulse), then tell
|
|
157
|
+
// the controller to drive the line (TEON 0x35, mode 1 = vertical sync only).
|
|
158
|
+
const teInit = tePin !== undefined ? `#if __TC_TE_SYNC
|
|
159
|
+
if (device_is_ready(__tc_te.port)) {
|
|
160
|
+
gpio_pin_configure_dt(&__tc_te, GPIO_INPUT);
|
|
161
|
+
gpio_init_callback(&__tc_te_cb, __tc_te_isr, BIT(__tc_te.pin));
|
|
162
|
+
(void)gpio_add_callback(__tc_te.port, &__tc_te_cb);
|
|
163
|
+
(void)gpio_pin_interrupt_configure_dt(&__tc_te, GPIO_INT_EDGE_RISING);
|
|
164
|
+
__tc_pnl_cmd1(0x35, 0x01); // TEON: TE output = vsync pulse
|
|
165
|
+
}
|
|
166
|
+
#endif` : '';
|
|
137
167
|
const blInit = backlightAlias
|
|
138
168
|
? `#if DT_HAS_ALIAS(${backlightAlias})\n const struct gpio_dt_spec __bl = GPIO_DT_SPEC_GET(DT_ALIAS(${backlightAlias}), gpios);\n if (device_is_ready(__bl.port)) { gpio_pin_configure_dt(&__bl, GPIO_OUTPUT_ACTIVE); }\n#endif`
|
|
139
169
|
: '';
|
|
@@ -452,6 +482,19 @@ static uint16_t __tc_pnl_read_scanline(void) {
|
|
|
452
482
|
// have no safe post-rectangle interval, so they retain the normal single-burst
|
|
453
483
|
// behavior; the caller's framebuffer still prevents intermediate software frames.
|
|
454
484
|
static void __tc_pnl_wait_for_safe_rect(int16_t y, int16_t rh) {
|
|
485
|
+
#if __TC_TE_SYNC
|
|
486
|
+
// TE variant: arm on the next frame pulse, then start the burst — the write
|
|
487
|
+
// chases the scan beam from the top of the rect. Bounded so a stuck TE line
|
|
488
|
+
// can never hang the UI loop.
|
|
489
|
+
if (rh < 8 || y < 0) return;
|
|
490
|
+
uint32_t __was = __tc_te_count;
|
|
491
|
+
uint32_t __deadline = k_uptime_get_32() + 25U;
|
|
492
|
+
while (__tc_te_count == __was) {
|
|
493
|
+
if (static_cast<int32_t>(k_uptime_get_32() - __deadline) >= 0) break;
|
|
494
|
+
k_msleep(0);
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
#endif
|
|
455
498
|
if (!__tc_pnl_scanline_sync || rh < 8 || y < 0) return;
|
|
456
499
|
int16_t __last = static_cast<int16_t>(y + rh - 1);
|
|
457
500
|
if (__last >= static_cast<int16_t>(${h} - 2)) return;
|
|
@@ -551,8 +594,10 @@ const CuttlefishPanelOps __tc_display_ops = {
|
|
|
551
594
|
CuttlefishGFX __tc_display(&__tc_display_ops, nullptr);
|
|
552
595
|
${initBlock}
|
|
553
596
|
// ── display_init (called from setup) ────────────────────────────────────
|
|
597
|
+
|
|
554
598
|
static inline void display_init() {
|
|
555
599
|
printk("TC_DISPLAY: device ready\\n");
|
|
600
|
+
${teInit}
|
|
556
601
|
${blInit}
|
|
557
602
|
gpio_pin_configure_dt(&__tc_pnl_cs, GPIO_OUTPUT);
|
|
558
603
|
gpio_pin_configure_dt(&__tc_pnl_dc, GPIO_OUTPUT);
|
|
@@ -699,5 +744,6 @@ export const zephyrDisplayAdapterGenerator = (display) => {
|
|
|
699
744
|
return zephyrUiDisplayAdapter(profile, {
|
|
700
745
|
scanlineSync: display.scanlineSync,
|
|
701
746
|
miso: display.spiPins?.miso,
|
|
747
|
+
tearingEffectPin: display.tearingEffectPin,
|
|
702
748
|
});
|
|
703
749
|
};
|
|
@@ -25,6 +25,17 @@ export interface KconfigUsage {
|
|
|
25
25
|
/** PSRAM type ('opi' | 'quad') when the target board has PSRAM. Emits the
|
|
26
26
|
* CONFIG_SPIRAM symbols so the ESP heap serves PSRAM for canvas allocations. */
|
|
27
27
|
psram?: 'opi' | 'quad';
|
|
28
|
+
/** HAL pin numbers the program reads with adc.* — scanned from the emitted
|
|
29
|
+
* `__tc_adc<N>_setup()` calls at compile time. Only the overlay generator
|
|
30
|
+
* consumes this (to rewrite the ADC node's pinctrl-0 to the used channels
|
|
31
|
+
* on SoCs that need pad muxing, e.g. STM32); prj.conf ignores it. */
|
|
32
|
+
adcReadPins?: readonly number[];
|
|
33
|
+
/** HAL pin numbers the program drives with pwm.* — scanned from the
|
|
34
|
+
* emitted `__tc_pwm_*` spec references at compile time. Only the overlay
|
|
35
|
+
* generator consumes this (synthesized pwm-leds consumers + aliases are
|
|
36
|
+
* emitted per used pin, so the DT carries no dead channels); prj.conf
|
|
37
|
+
* ignores it. */
|
|
38
|
+
pwmUsedPins?: readonly number[];
|
|
28
39
|
}
|
|
29
40
|
/**
|
|
30
41
|
* Resolve the Kconfig symbol→value map for a prj.conf. Returns a Map preserving
|
|
@@ -97,7 +97,7 @@ export function resolveKconfigFragments(usage, debug) {
|
|
|
97
97
|
// (without it, Kconfig silently forces them all to n).
|
|
98
98
|
m.set('CONFIG_NETWORKING', 'y');
|
|
99
99
|
m.set('CONFIG_WIFI', 'y');
|
|
100
|
-
m.set('CONFIG_WIFI_ESP32', 'y'); //
|
|
100
|
+
m.set('CONFIG_WIFI_ESP32', 'y'); // family-wide ESP32 driver (esp32/s3/c3/c6)
|
|
101
101
|
m.set('CONFIG_NET_L2_ETHERNET', 'y');
|
|
102
102
|
m.set('CONFIG_NET_IPV4', 'y');
|
|
103
103
|
m.set('CONFIG_NET_UDP', 'y'); // transitive dep of NET_DHCPV4
|
|
@@ -10,6 +10,9 @@ export interface DisplayWiring {
|
|
|
10
10
|
cs?: number;
|
|
11
11
|
dc?: number;
|
|
12
12
|
rst?: number;
|
|
13
|
+
/** Tearing-effect (TE) GPIO from display.tearingEffectPin — emitted as
|
|
14
|
+
* te-gpios on the display DT node. Opt-in; most boards don't wire TE. */
|
|
15
|
+
tearingEffectPin?: number;
|
|
13
16
|
spiFrequency?: number;
|
|
14
17
|
/** SPI bus pins. When present, the overlay remuxes the SPI controller's
|
|
15
18
|
* pinctrl to these pins (the board defaults rarely match a breakout's
|
|
@@ -49,4 +52,8 @@ export interface TouchWiring {
|
|
|
49
52
|
* panels need a few hundred 12-bit counts; default 400. */
|
|
50
53
|
minPressure?: number;
|
|
51
54
|
}
|
|
52
|
-
export
|
|
55
|
+
export interface OverlayDiagnostic {
|
|
56
|
+
severity: "warning" | "error";
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
export declare function generateOverlay(chip: ZephyrChipDescriptor, usage: KconfigUsage, display: ZephyrDisplayProfile | undefined, wiring?: DisplayWiring, touch?: TouchWiring, diagnostics?: OverlayDiagnostic[]): string;
|