@typecad/framework-zephyr 1.0.0-alpha.13 → 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/dt-config/kconfig.d.ts +11 -0
- package/dist/dt-config/kconfig.js +1 -1
- package/dist/dt-config/overlay.js +85 -0
- package/dist/framework.manifest.d.ts +20 -30
- package/dist/framework.manifest.js +12 -3
- package/dist/lowering/adc.d.ts +7 -4
- package/dist/lowering/adc.js +25 -11
- package/dist/lowering/gpio.js +9 -6
- 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.js +56 -4
- package/dist/toolchain/debug-config.js +1 -1
- package/dist/toolchain/index.d.ts +1 -1
- package/dist/toolchain/index.js +83 -8
- package/dist/toolchain/scaffold.d.ts +9 -0
- package/dist/toolchain/scaffold.js +45 -0
- 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/dt-config/kconfig.ts +12 -1
- package/src/dt-config/overlay.ts +546 -450
- package/src/framework.manifest.ts +12 -3
- package/src/lowering/adc.ts +28 -12
- package/src/lowering/gpio.ts +9 -6
- 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 +52 -4
- package/src/toolchain/debug-config.ts +1 -1
- package/src/toolchain/index.ts +645 -565
- package/src/toolchain/scaffold.ts +43 -0
- 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).
|
|
@@ -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
|
|
@@ -53,6 +53,24 @@ export function generateOverlay(chip, usage, display, wiring, touch, diagnostics
|
|
|
53
53
|
for (const c of chip.uart.controllers)
|
|
54
54
|
block(c.nodeLabel);
|
|
55
55
|
}
|
|
56
|
+
// PWM: synthesized specs (controller + channel, no board-shipped alias) get
|
|
57
|
+
// a pwm-leds consumer node + tc-pwm<pin> alias here — the lowering addresses
|
|
58
|
+
// the channel as PWM_DT_SPEC_GET(DT_ALIAS(tc-pwm<pin>)), so the alias must
|
|
59
|
+
// exist in the merged DT. pwmDtAlias (lowering/pwm.ts) derives the same
|
|
60
|
+
// name from the pin; the two sides cannot drift. Filtered to the pins the
|
|
61
|
+
// program actually drives (pwmUsedPins) so the DT carries no dead channels;
|
|
62
|
+
// an undefined list (prepare-time overlay) emits all specs.
|
|
63
|
+
if (usage.usesPwm && chip.pwm) {
|
|
64
|
+
emitPwmNodes(lines, chip, usage.pwmUsedPins);
|
|
65
|
+
}
|
|
66
|
+
// ADC: enable the ADC device node, and on SoCs whose channels carry a
|
|
67
|
+
// pinctrl label (STM32: the pad stays in GPIO mode unless the ADC node's
|
|
68
|
+
// pinctrl-0 muxes it analog), rewrite pinctrl-0 to exactly the channels the
|
|
69
|
+
// program reads. DT assignment replaces the whole property — the board
|
|
70
|
+
// default (one pad) is deliberately dropped in favor of the used set.
|
|
71
|
+
if (usage.usesAdc && chip.adc) {
|
|
72
|
+
emitAdcNode(lines, chip, usage.adcReadPins);
|
|
73
|
+
}
|
|
56
74
|
// DAC: enable the chip's DAC device node when the program uses dac.*. The
|
|
57
75
|
// lowering references DEVICE_DT_GET(DT_NODELABEL(<dac.device>)).
|
|
58
76
|
if (usage.usesDac && chip.dac) {
|
|
@@ -106,6 +124,73 @@ export function generateOverlay(chip, usage, display, wiring, touch, diagnostics
|
|
|
106
124
|
}
|
|
107
125
|
return lines.join('\n');
|
|
108
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Emit the pwm-leds consumer node + aliases for synthesized PWM specs.
|
|
129
|
+
*
|
|
130
|
+
* A board DTS may enable a PWM controller (`pwm4`) without aliasing any
|
|
131
|
+
* channel — the STM32 pattern — while the lowering addresses channels via
|
|
132
|
+
* DT_ALIAS. Each synthesized spec gets a `pwm-leds` child whose `pwms` cell
|
|
133
|
+
* binds the controller+channel (+ period/polarity), and a `tc-pwm<pin>`
|
|
134
|
+
* alias pointing at it. Alias/label names are valid DT identifiers (lowercase
|
|
135
|
+
* alphanumerics + dash/underscore).
|
|
136
|
+
*/
|
|
137
|
+
function emitPwmNodes(lines, chip, usedPins) {
|
|
138
|
+
const synthesized = (chip.pwm?.specs ?? []).filter((s) => s.controller && s.channel != null && !s.dtSpec).filter((s) => !usedPins || usedPins.includes(s.pin));
|
|
139
|
+
if (synthesized.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
// Enable each distinct PWM controller node (idempotent when already okay).
|
|
142
|
+
for (const controller of [...new Set(synthesized.map((s) => s.controller))]) {
|
|
143
|
+
lines.push(`&${controller} {`);
|
|
144
|
+
lines.push(' status = "okay";');
|
|
145
|
+
lines.push('};');
|
|
146
|
+
lines.push('');
|
|
147
|
+
}
|
|
148
|
+
lines.push('/ {');
|
|
149
|
+
lines.push(' tc_pwm_leds: tc-pwm-leds {');
|
|
150
|
+
lines.push(' compatible = "pwm-leds";');
|
|
151
|
+
for (const s of synthesized) {
|
|
152
|
+
// 20 ms / 50 Hz default — the servo convention; the duty scaling in the
|
|
153
|
+
// lowering normalizes 0-255 against whatever period is baked in.
|
|
154
|
+
const period = s.periodNs ?? 20_000_000;
|
|
155
|
+
const polarity = s.polarity ?? 'PWM_POLARITY_NORMAL';
|
|
156
|
+
lines.push(` tc_pwm_${s.pin}: pwm-led-${s.pin} {`);
|
|
157
|
+
lines.push(` pwms = <&${s.controller} ${s.channel} ${period} ${polarity}>;`);
|
|
158
|
+
lines.push(' };');
|
|
159
|
+
}
|
|
160
|
+
lines.push(' };');
|
|
161
|
+
lines.push(' aliases {');
|
|
162
|
+
for (const s of synthesized) {
|
|
163
|
+
lines.push(` tc-pwm${s.pin} = &tc_pwm_${s.pin};`);
|
|
164
|
+
}
|
|
165
|
+
lines.push(' };');
|
|
166
|
+
lines.push('};');
|
|
167
|
+
lines.push('');
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Emit the ADC device-node enable (+ optional pinctrl override). The pinctrl
|
|
171
|
+
* override applies only when the descriptor's channels carry pinctrl labels
|
|
172
|
+
* AND the caller knows which pins the program reads (compile-time regen scans
|
|
173
|
+
* the emitted `__tc_adc<N>_setup()` calls); the prepare-time overlay omits it
|
|
174
|
+
* and the compile regen rewrites the file before west runs.
|
|
175
|
+
*/
|
|
176
|
+
function emitAdcNode(lines, chip, readPins) {
|
|
177
|
+
const adc = chip.adc;
|
|
178
|
+
const labeled = adc.channels.filter((c) => c.pinctrl);
|
|
179
|
+
const used = labeled.filter((c) => !readPins || readPins.includes(c.pin));
|
|
180
|
+
if (labeled.length > 0 && used.length > 0) {
|
|
181
|
+
lines.push(`&${adc.nodeLabel} {`);
|
|
182
|
+
lines.push(' status = "okay";');
|
|
183
|
+
lines.push(` pinctrl-0 = <${used.map((c) => `&${c.pinctrl}`).join(' ')}>;`);
|
|
184
|
+
lines.push(' pinctrl-names = "default";');
|
|
185
|
+
lines.push('};');
|
|
186
|
+
lines.push('');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
lines.push(`&${adc.nodeLabel} {`);
|
|
190
|
+
lines.push(' status = "okay";');
|
|
191
|
+
lines.push('};');
|
|
192
|
+
lines.push('');
|
|
193
|
+
}
|
|
109
194
|
/**
|
|
110
195
|
* Emit a display DT node definition. The node is attached to the profile's SPI
|
|
111
196
|
* controller (default spi2, the ESP32's first user SPI controller) via a MIPI
|
|
@@ -1,41 +1,31 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
|
-
hal: {
|
|
3
|
-
[x: string]: {
|
|
4
|
-
supported: boolean;
|
|
5
|
-
ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
|
|
6
|
-
partialCoverage: boolean;
|
|
7
|
-
unsupportedReason?: string | undefined;
|
|
8
|
-
};
|
|
9
|
-
raw?: unknown;
|
|
10
|
-
} & {
|
|
11
|
-
[k: string]: {
|
|
12
|
-
supported: boolean;
|
|
13
|
-
ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
|
|
14
|
-
partialCoverage: boolean;
|
|
15
|
-
unsupportedReason?: string | undefined;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
packageName: string;
|
|
19
2
|
schemaVersion: 1;
|
|
20
3
|
frameworkId: string;
|
|
4
|
+
packageName: string;
|
|
21
5
|
canonical: boolean;
|
|
22
6
|
displayName: string;
|
|
23
7
|
description: string;
|
|
24
8
|
implementationMode: "from-scratch" | "extends-canonical" | "extends-other";
|
|
25
9
|
entrypoint: {
|
|
26
|
-
sourceExtension: "cpp" | "ino" | "cc" | "h";
|
|
27
10
|
entrypointFunctionName: string;
|
|
28
11
|
requiresLoopFunction: boolean;
|
|
12
|
+
sourceExtension: "cpp" | "ino" | "cc" | "h";
|
|
29
13
|
generateHeaderFile: boolean;
|
|
30
14
|
overrideBaseName?: string | undefined;
|
|
31
15
|
outputSubdirectory?: string | undefined;
|
|
32
16
|
customBridgeShim?: string | undefined;
|
|
33
17
|
};
|
|
34
18
|
profile: {
|
|
19
|
+
targets: string[];
|
|
35
20
|
forcedIncludes: string[];
|
|
36
21
|
symbolAliases: Record<string, string>;
|
|
37
|
-
targets: string[];
|
|
38
22
|
};
|
|
23
|
+
hal: Record<string, {
|
|
24
|
+
supported: boolean;
|
|
25
|
+
ops: Record<string, "unsupported" | "supported" | "polyfill" | "probe-inconclusive" | "stub">;
|
|
26
|
+
partialCoverage: boolean;
|
|
27
|
+
unsupportedReason?: string | undefined;
|
|
28
|
+
}>;
|
|
39
29
|
polyfills: {
|
|
40
30
|
emitted: {
|
|
41
31
|
id: string;
|
|
@@ -59,20 +49,20 @@ declare const _default: {
|
|
|
59
49
|
reexportedFrom?: string | undefined;
|
|
60
50
|
};
|
|
61
51
|
typeEmission: {
|
|
62
|
-
|
|
52
|
+
normalizeCppType: boolean;
|
|
53
|
+
mathHeader: "<math.h>" | "none" | "<Arduino.h>";
|
|
63
54
|
needsStdString: boolean;
|
|
64
55
|
needsStdVector: boolean;
|
|
56
|
+
needsIostream: boolean;
|
|
65
57
|
needsStdFunction: boolean;
|
|
66
|
-
mathHeader: "none" | "<math.h>" | "<Arduino.h>";
|
|
67
|
-
normalizeCppType: boolean;
|
|
68
58
|
stdlibSupport: {
|
|
69
59
|
hasVector: boolean;
|
|
70
60
|
hasString: boolean;
|
|
71
61
|
hasIostream: boolean;
|
|
72
62
|
hasExceptions: boolean;
|
|
73
63
|
hasRTTI: boolean;
|
|
74
|
-
recommendedArrayImpl: "
|
|
75
|
-
recommendedStringImpl: "
|
|
64
|
+
recommendedArrayImpl: "static_array" | "std_vector";
|
|
65
|
+
recommendedStringImpl: "static_string" | "std_string";
|
|
76
66
|
};
|
|
77
67
|
};
|
|
78
68
|
ambientTypes: string[];
|
|
@@ -80,12 +70,6 @@ declare const _default: {
|
|
|
80
70
|
hardwareTestGroups: string[];
|
|
81
71
|
halResolutionTests: string[];
|
|
82
72
|
};
|
|
83
|
-
doctor?: {
|
|
84
|
-
available: boolean;
|
|
85
|
-
} | undefined;
|
|
86
|
-
licenses?: {
|
|
87
|
-
available: boolean;
|
|
88
|
-
} | undefined;
|
|
89
73
|
basedOn?: string | undefined;
|
|
90
74
|
inheritsStrategyId?: string | undefined;
|
|
91
75
|
libraryResolution?: {
|
|
@@ -95,6 +79,12 @@ declare const _default: {
|
|
|
95
79
|
tryGenerateLibDecl: boolean;
|
|
96
80
|
reexportedFrom?: string | undefined;
|
|
97
81
|
} | undefined;
|
|
82
|
+
doctor?: {
|
|
83
|
+
available: boolean;
|
|
84
|
+
} | undefined;
|
|
85
|
+
licenses?: {
|
|
86
|
+
available: boolean;
|
|
87
|
+
} | undefined;
|
|
98
88
|
compat?: {
|
|
99
89
|
zephyr?: string | undefined;
|
|
100
90
|
} | undefined;
|
|
@@ -45,8 +45,16 @@ export default defineFrameworkManifest({
|
|
|
45
45
|
profile: {
|
|
46
46
|
// Informational list of supported board targets. The manifest validator
|
|
47
47
|
// never iterates this; chipForTarget (src/chips/index.ts) is the real
|
|
48
|
-
// resolver. ESP32-S3 + plain ESP32 added alongside the nRF52840 MVP target
|
|
49
|
-
|
|
48
|
+
// resolver. ESP32-S3 + plain ESP32 added alongside the nRF52840 MVP target;
|
|
49
|
+
// rpi_pico / rpi_pico2 / esp32c3 / esp32c6 / blackpill_f411ce resolve via
|
|
50
|
+
// the board packages' zephyr chip data (resolveChipFromBoard), not the
|
|
51
|
+
// hardcoded chip registry.
|
|
52
|
+
targets: [
|
|
53
|
+
'xiao_ble', 'esp32s3_devkitc', 'esp32_devkitc',
|
|
54
|
+
'rpi_pico', 'rpi_pico2/rp2350a/m33',
|
|
55
|
+
'esp32c3_devkitm/esp32c3', 'esp32c6_devkitc/esp32c6/hpcore',
|
|
56
|
+
'blackpill_f411ce/stm32f411xe',
|
|
57
|
+
],
|
|
50
58
|
forcedIncludes: ['<zephyr/kernel.h>', '<zephyr/drivers/gpio.h>', '<cstdint>'],
|
|
51
59
|
symbolAliases: {},
|
|
52
60
|
},
|
|
@@ -204,7 +212,8 @@ export default defineFrameworkManifest({
|
|
|
204
212
|
},
|
|
205
213
|
},
|
|
206
214
|
// ── Partial: WiFi (STA connect + scan + config via conn_mgr/net_mgmt) ────
|
|
207
|
-
// ESP32
|
|
215
|
+
// ESP32 family only (esp32, esp32s3, esp32c3, esp32c6) — profileDiagnostics
|
|
216
|
+
// flags wifi usage on radioless chips (nRF52840, RP2040/RP2350).
|
|
208
217
|
// AP mode, credential persistence, static IP, and event callbacks deferred.
|
|
209
218
|
wifi: {
|
|
210
219
|
supported: true,
|
package/dist/lowering/adc.d.ts
CHANGED
|
@@ -4,11 +4,14 @@ import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
|
4
4
|
export declare function adcChannelForPin(chip: ZephyrChipDescriptor, pin: number): number;
|
|
5
5
|
/**
|
|
6
6
|
* Emit the per-channel ADC setup state. One block per channel in the chip
|
|
7
|
-
* descriptor
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* descriptor that the PROGRAM ACTUALLY READS (`usedPins`) — an unread
|
|
8
|
+
* channel's `static` setup function would trip -Wunused-function in the
|
|
9
|
+
* single generated TU. When `usedPins` is omitted (probe paths with no
|
|
10
|
+
* program), every descriptor channel is emitted. Each block is guarded by a
|
|
11
|
+
* `static bool __tc_adc<N>_ready` so the first read configures it and
|
|
12
|
+
* subsequent reads skip. Called from shimLines when the program uses ADC.
|
|
10
13
|
*/
|
|
11
|
-
export declare function adcInitLines(chip: ZephyrChipDescriptor): string[];
|
|
14
|
+
export declare function adcInitLines(chip: ZephyrChipDescriptor, usedPins?: ReadonlySet<number>): string[];
|
|
12
15
|
/**
|
|
13
16
|
* Resolve a HAL adc.* op to Zephyr C++.
|
|
14
17
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|