@typecad/framework-zephyr 1.0.0-alpha.14 → 1.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -47
- package/dist/as-built.d.ts +33 -0
- package/dist/as-built.js +188 -0
- package/dist/async/timer-polyfill.js +1 -1
- package/dist/boardgen.d.ts +128 -0
- package/dist/boardgen.js +1366 -0
- package/dist/chips/board-overrides.d.ts +7 -0
- package/dist/chips/board-overrides.js +11 -0
- package/dist/chips/esp32s3.js +49 -0
- package/dist/chips/index.d.ts +6 -12
- package/dist/chips/index.js +18 -34
- package/dist/chips/resolve.d.ts +16 -2
- package/dist/chips/resolve.js +267 -10
- package/dist/chips/soc/.d.ts +2 -0
- package/dist/chips/soc/.js +129 -0
- package/dist/chips/soc/esp32.d.ts +2 -0
- package/dist/chips/soc/esp32.js +120 -0
- package/dist/chips/soc/esp32c3.d.ts +2 -0
- package/dist/chips/soc/esp32c3.js +90 -0
- package/dist/chips/soc/esp32c6.d.ts +2 -0
- package/dist/chips/soc/esp32c6.js +109 -0
- package/dist/chips/soc/esp32s3.d.ts +2 -0
- package/dist/chips/soc/esp32s3.js +189 -0
- package/dist/chips/soc/index.d.ts +2 -0
- package/dist/chips/soc/index.js +23 -0
- package/dist/chips/soc/nrf52840.d.ts +2 -0
- package/dist/chips/soc/nrf52840.js +130 -0
- package/dist/chips/soc/rp2040.d.ts +2 -0
- package/dist/chips/soc/rp2040.js +141 -0
- package/dist/chips/soc/rp2350a.d.ts +2 -0
- package/dist/chips/soc/rp2350a.js +145 -0
- package/dist/chips/soc/samd21g18a.d.ts +2 -0
- package/dist/chips/soc/samd21g18a.js +143 -0
- package/dist/chips/soc/stm32f411xe.d.ts +2 -0
- package/dist/chips/soc/stm32f411xe.js +251 -0
- package/dist/chips/types.d.ts +319 -0
- package/dist/chips/xiao-ble.js +21 -0
- package/dist/display/profiles.js +1 -1
- package/dist/doctor.js +38 -4
- package/dist/dt-config/custom-board.d.ts +23 -0
- package/dist/dt-config/custom-board.js +227 -0
- package/dist/dt-config/kconfig.d.ts +72 -1
- package/dist/dt-config/kconfig.js +115 -26
- package/dist/dt-config/overlay.js +548 -28
- package/dist/framework.manifest.d.ts +3 -3
- package/dist/framework.manifest.js +122 -185
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/lowering/adc.d.ts +20 -2
- package/dist/lowering/adc.js +121 -29
- package/dist/lowering/ble.js +31 -28
- package/dist/lowering/dac.js +9 -9
- package/dist/lowering/fs.js +18 -10
- package/dist/lowering/gpio.d.ts +2 -0
- package/dist/lowering/gpio.js +82 -5
- package/dist/lowering/http.js +39 -14
- package/dist/lowering/hwtimer.d.ts +4 -0
- package/dist/lowering/hwtimer.js +35 -10
- package/dist/lowering/i2c.js +46 -54
- package/dist/lowering/index.d.ts +5 -5
- package/dist/lowering/index.js +15 -16
- package/dist/lowering/interrupts.d.ts +22 -5
- package/dist/lowering/interrupts.js +104 -22
- package/dist/lowering/mqtt.js +3 -5
- package/dist/lowering/power.js +0 -4
- package/dist/lowering/preferences.js +49 -77
- package/dist/lowering/pulse.js +0 -31
- package/dist/lowering/pwm.d.ts +12 -1
- package/dist/lowering/pwm.js +78 -14
- package/dist/lowering/random.js +3 -3
- package/dist/lowering/sensor.d.ts +37 -0
- package/dist/lowering/sensor.js +119 -0
- package/dist/lowering/spi.d.ts +12 -0
- package/dist/lowering/spi.js +65 -40
- package/dist/lowering/thread.d.ts +14 -0
- package/dist/lowering/thread.js +55 -0
- package/dist/lowering/timing.js +20 -46
- package/dist/lowering/tone.js +16 -4
- package/dist/lowering/uart.d.ts +13 -0
- package/dist/lowering/uart.js +72 -51
- package/dist/lowering/usb.d.ts +34 -0
- package/dist/lowering/usb.js +206 -0
- package/dist/lowering/wdt.d.ts +1 -1
- package/dist/lowering/wdt.js +17 -7
- package/dist/lowering/wifi.d.ts +3 -2
- package/dist/lowering/wifi.js +97 -111
- package/dist/sdk/board-catalog-sync.d.ts +16 -0
- package/dist/sdk/board-catalog-sync.js +41 -0
- package/dist/sdk/board-data.generated.d.ts +2 -0
- package/dist/sdk/board-data.generated.js +4 -0
- package/dist/sdk/catalog-walker.d.ts +90 -0
- package/dist/sdk/catalog-walker.js +682 -0
- package/dist/sdk/dts-reader.d.ts +83 -0
- package/dist/sdk/dts-reader.js +596 -0
- package/dist/strategy.d.ts +136 -17
- package/dist/strategy.js +839 -222
- package/dist/toolchain/bossac-touch.d.ts +18 -0
- package/dist/toolchain/bossac-touch.js +88 -0
- package/dist/toolchain/compat.d.ts +2 -2
- package/dist/toolchain/compat.js +8 -3
- package/dist/toolchain/debug-config.d.ts +21 -26
- package/dist/toolchain/debug-config.js +211 -71
- package/dist/toolchain/env-check.d.ts +4 -4
- package/dist/toolchain/env-check.js +5 -5
- package/dist/toolchain/index.d.ts +66 -1
- package/dist/toolchain/index.js +650 -70
- package/dist/toolchain/scaffold.js +39 -7
- package/dist/toolchain/west-discover.d.ts +4 -0
- package/dist/toolchain/west-discover.js +17 -6
- package/installer/CHANGELOG.md +473 -0
- package/installer/README.md +328 -0
- package/installer/environment.yml +46 -0
- package/installer/etc/conda/activate.d/zephyr.bat +7 -0
- package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
- package/installer/etc/conda/activate.d/zephyr.sh +20 -0
- package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
- package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
- package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
- package/installer/install.mjs +580 -0
- package/installer/install.ps1 +619 -0
- package/installer/install.sh +310 -0
- package/installer/lib/detect-platform.sh +62 -0
- package/installer/lib/fetch-sdk.sh +304 -0
- package/installer/lib/init-workspace.sh +153 -0
- package/installer/lib/write-activation.sh +76 -0
- package/installer/patches/tfm-c-include-separators.patch +19 -0
- package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
- package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
- package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
- package/installer/templates/project/.vscode/settings.json +30 -0
- package/installer/templates/project/README.md +58 -0
- package/installer/versions.env +110 -0
- package/package.json +15 -15
- package/src/as-built.ts +206 -0
- package/src/boardgen.ts +1428 -0
- package/src/chips/index.ts +35 -54
- package/src/chips/resolve.ts +285 -10
- package/src/chips/types.ts +325 -1
- package/src/display/profiles.ts +1 -1
- package/src/doctor.ts +110 -77
- package/src/dt-config/custom-board.ts +325 -0
- package/src/dt-config/kconfig.ts +165 -18
- package/src/dt-config/overlay.ts +537 -25
- package/src/framework.manifest.ts +123 -185
- package/src/index.ts +57 -41
- package/src/lowering/adc.ts +222 -118
- package/src/lowering/ble.ts +32 -28
- package/src/lowering/dac.ts +82 -82
- package/src/lowering/fs.ts +18 -10
- package/src/lowering/gpio.ts +242 -155
- package/src/lowering/http.ts +512 -488
- package/src/lowering/hwtimer.ts +139 -101
- package/src/lowering/i2c.ts +116 -126
- package/src/lowering/index.ts +91 -93
- package/src/lowering/interrupts.ts +113 -21
- package/src/lowering/mqtt.ts +3 -5
- package/src/lowering/preferences.ts +327 -354
- package/src/lowering/pwm.ts +167 -99
- package/src/lowering/random.ts +3 -3
- package/src/lowering/sensor.ts +155 -0
- package/src/lowering/spi.ts +164 -124
- package/src/lowering/thread.ts +63 -0
- package/src/lowering/timing.ts +46 -72
- package/src/lowering/uart.ts +73 -51
- package/src/lowering/usb.ts +221 -0
- package/src/lowering/wdt.ts +95 -84
- package/src/lowering/wifi.ts +97 -112
- package/src/sdk/board-catalog-sync.ts +62 -0
- package/src/strategy.ts +848 -237
- package/src/toolchain/bossac-touch.ts +112 -0
- package/src/toolchain/compat.ts +8 -3
- package/src/toolchain/debug-config.ts +666 -522
- package/src/toolchain/env-check.ts +9 -9
- package/src/toolchain/index.ts +1286 -645
- package/src/toolchain/scaffold.ts +38 -7
- package/src/toolchain/west-discover.ts +19 -6
- package/src/async/timer-polyfill.ts +0 -107
- package/src/chips/esp32.ts +0 -74
- package/src/chips/esp32s3.ts +0 -57
- package/src/chips/xiao-ble.ts +0 -82
- package/src/lowering/power.ts +0 -70
- package/src/lowering/pulse.ts +0 -93
- package/src/lowering/tone.ts +0 -60
- package/src/lowering/worker-backing.ts +0 -94
- package/src/lowering/worker.ts +0 -17
package/src/dt-config/overlay.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// descriptor knows, emit a `&nodelabel { status = "okay"; }` block enabling it.
|
|
7
7
|
// West merges this over the board's base DT; we never rewrite the base.
|
|
8
8
|
//
|
|
9
|
-
//
|
|
9
|
+
// Framework library-resolution hook: the artifact
|
|
10
10
|
// that brings external capabilities into a build. (Arduino does it by parsing
|
|
11
11
|
// library headers into .d.ts; Zephyr does it by enabling DT nodes + Kconfig.)
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -14,8 +14,13 @@
|
|
|
14
14
|
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
15
15
|
import type { ZephyrDisplayProfile } from '../display/profiles.js';
|
|
16
16
|
import { PANEL_CONTROLLER_DEFAULTS, panelControllerFor } from '../display/profiles.js';
|
|
17
|
+
import { SENSOR_PART_INFO } from '@typecad/hal';
|
|
17
18
|
import type { KconfigUsage } from './kconfig.js';
|
|
18
19
|
|
|
20
|
+
/** Display panel CS default — shared by the display block and the SPI-sensor
|
|
21
|
+
* cs-gpios merge so both sites write the same entry. */
|
|
22
|
+
const DEFAULT_DISPLAY_CS = 5;
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* Generate the overlay source for a board + usage. Returns the overlay text
|
|
21
26
|
* (caller writes it to app/boards/<board>.overlay via writeIfChanged).
|
|
@@ -108,14 +113,129 @@ export function generateOverlay(
|
|
|
108
113
|
lines.push('');
|
|
109
114
|
};
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
// Controllers whose board DT ships no default pinctrl group (rp2xxx uart1)
|
|
117
|
+
// carry synthesis data: emit the group under &pinctrl, then enable the
|
|
118
|
+
// controller referencing it. Controllers with a pinctrlRef wire an existing
|
|
119
|
+
// group instead; `props` adds raw property lines for bindings with
|
|
120
|
+
// required properties the board only sets on its own wired nodes.
|
|
121
|
+
// The pinmux header include is collected and spliced before all blocks
|
|
122
|
+
// (tokens must be defined before use).
|
|
123
|
+
const pinctrlIncludes = new Map<string, string[]>();
|
|
124
|
+
const controllerBlock = (c: { nodeLabel: string; pinctrlRef?: string; props?: readonly string[]; pinctrl?: { include: string; pinmux: readonly string[]; inputPinmux?: readonly string[]; defines?: readonly string[] } }): void => {
|
|
125
|
+
const extra: string[] = [...(c.props ?? [])];
|
|
126
|
+
if (c.pinctrl) {
|
|
127
|
+
pinctrlIncludes.set(c.pinctrl.include, c.pinctrl.defines ? [...c.pinctrl.defines] : []);
|
|
128
|
+
const group = `${c.nodeLabel}_default`;
|
|
129
|
+
lines.push('&pinctrl {');
|
|
130
|
+
lines.push(` ${group}: ${group} {`);
|
|
131
|
+
lines.push(` group1 {`);
|
|
132
|
+
lines.push(` pinmux = <${c.pinctrl.pinmux.join(', ')}>;`);
|
|
133
|
+
lines.push(` };`);
|
|
134
|
+
if (c.pinctrl.inputPinmux && c.pinctrl.inputPinmux.length > 0) {
|
|
135
|
+
lines.push(` group2 {`);
|
|
136
|
+
lines.push(` pinmux = <${c.pinctrl.inputPinmux.join(', ')}>;`);
|
|
137
|
+
lines.push(` input-enable;`);
|
|
138
|
+
lines.push(` };`);
|
|
139
|
+
}
|
|
140
|
+
lines.push(` };`);
|
|
141
|
+
lines.push('};');
|
|
142
|
+
lines.push('');
|
|
143
|
+
extra.push(`pinctrl-0 = <&${group}>;`, `pinctrl-names = "default";`);
|
|
144
|
+
} else if (c.pinctrlRef) {
|
|
145
|
+
extra.push(`pinctrl-0 = <&${c.pinctrlRef}>;`, `pinctrl-names = "default";`);
|
|
146
|
+
}
|
|
147
|
+
block(c.nodeLabel, extra);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Sensors ride a bus: a constructed sensor must enable its controller even
|
|
151
|
+
// when the program never calls i2c.*/spi.* directly (the sensor device
|
|
152
|
+
// handle is the only bus user) — merge their instances into the used sets.
|
|
153
|
+
const sensorParts = usage.sensorParts ?? [];
|
|
154
|
+
const i2cSensors = sensorParts.filter((s) => s.busKind !== 'spi');
|
|
155
|
+
const spiSensors = sensorParts.filter((s) => s.busKind === 'spi');
|
|
156
|
+
const spiTargets = usage.spiTargets ?? [];
|
|
157
|
+
const i2cInstances = new Set(usage.i2cUsedInstances ?? []);
|
|
158
|
+
for (const sp of i2cSensors) i2cInstances.add(sp.busIndex);
|
|
159
|
+
if ((usage.usesI2c || i2cSensors.length > 0) && chip.i2c) {
|
|
160
|
+
for (const [i, c] of chip.i2c.controllers.entries()) {
|
|
161
|
+
if (usage.i2cUsedInstances && !i2cInstances.has(i)) continue;
|
|
162
|
+
controllerBlock(c);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const spiInstances = new Set(usage.spiUsedInstances ?? []);
|
|
166
|
+
for (const sp of spiSensors) spiInstances.add(sp.busIndex);
|
|
167
|
+
for (const t of spiTargets) spiInstances.add(t.busIndex);
|
|
168
|
+
if ((usage.usesSpi || spiSensors.length > 0 || spiTargets.length > 0) && chip.spi) {
|
|
169
|
+
for (const [i, c] of chip.spi.controllers.entries()) {
|
|
170
|
+
if (usage.spiUsedInstances && !spiInstances.has(i)) continue;
|
|
171
|
+
controllerBlock(c);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// One DT child node per constructed I2C sensor — THE enable switch for the
|
|
175
|
+
// driver (its Kconfig is `default y` on this node's presence). The nodelabel
|
|
176
|
+
// is derived identically to the lowering's DEVICE_DT_GET target
|
|
177
|
+
// (lowering/sensor.ts sensorNames) — the two cannot drift.
|
|
178
|
+
for (const sp of i2cSensors) {
|
|
179
|
+
const ctrl = chip.i2c?.controllers[sp.busIndex];
|
|
180
|
+
const info = SENSOR_PART_INFO[sp.part.replace(/^SENSOR\./, '')];
|
|
181
|
+
if (!ctrl || !info) continue; // the lowering already threw on unknown parts
|
|
182
|
+
const nodeName = info.compatible.split(',')[1] ?? sp.part;
|
|
183
|
+
const addrHex = sp.port.toString(16);
|
|
184
|
+
lines.push(`&${ctrl.nodeLabel} {`);
|
|
185
|
+
lines.push(` tc_${sp.part.replace(/^SENSOR\./, '')}_i2c${sp.busIndex}_0x${addrHex}: ${nodeName}@${addrHex} {`);
|
|
186
|
+
lines.push(` compatible = "${info.compatible}";`);
|
|
187
|
+
lines.push(` reg = <0x${addrHex}>;`);
|
|
188
|
+
emitAlert(lines, chip, sp.alertPin, info.alert, 8);
|
|
189
|
+
lines.push(' };');
|
|
190
|
+
lines.push('};');
|
|
191
|
+
lines.push('');
|
|
113
192
|
}
|
|
114
193
|
if (usage.usesSpi && chip.spi) {
|
|
115
|
-
for (const c of chip.spi.controllers)
|
|
194
|
+
for (const [i, c] of chip.spi.controllers.entries()) {
|
|
195
|
+
if (usage.spiUsedInstances && !usage.spiUsedInstances.includes(i)) continue;
|
|
196
|
+
controllerBlock(c);
|
|
197
|
+
}
|
|
116
198
|
}
|
|
117
199
|
if (usage.usesUart && chip.uart) {
|
|
118
|
-
for (const c of chip.uart.controllers)
|
|
200
|
+
for (const [i, c] of chip.uart.controllers.entries()) {
|
|
201
|
+
if (usage.uartUsedInstances && !usage.uartUsedInstances.includes(i)) continue;
|
|
202
|
+
controllerBlock(c);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Synthesized pinctrl groups reference pinmux tokens — splice their header
|
|
206
|
+
// includes ahead of every block (same slot the display path uses), with
|
|
207
|
+
// ifndef-guarded #defines for upstream header bugs in front.
|
|
208
|
+
for (const [include, defines] of pinctrlIncludes) {
|
|
209
|
+
const pre: string[] = [];
|
|
210
|
+
for (const d of defines) {
|
|
211
|
+
const name = d.trim().split(/\s+/)[0];
|
|
212
|
+
pre.push(`#ifndef ${name}`, `#define ${d}`, `#endif`);
|
|
213
|
+
}
|
|
214
|
+
lines.splice(2, 0, ...pre, `#include <${include}>`, '');
|
|
215
|
+
}
|
|
216
|
+
// Watchdog: the SoC dtsi declares the node disabled (STM32 iwdg ships
|
|
217
|
+
// status = "disabled" until a board enables it) — the wdt.* lowering
|
|
218
|
+
// addresses DEVICE_DT_GET(DT_NODELABEL(<nodeLabel>)), which needs the
|
|
219
|
+
// driver instance built, so enable the node when wdt_* is used.
|
|
220
|
+
if (usage.usesWdt && chip.wdt) {
|
|
221
|
+
block(chip.wdt.nodeLabel);
|
|
222
|
+
}
|
|
223
|
+
// USB CDC-ACM: the "next" USB device stack composes classes as devicetree
|
|
224
|
+
// children of the UDC controller node. Enable the controller and declare one
|
|
225
|
+
// cdc_acm_uart<N> child per instance the chip descriptor declares — the
|
|
226
|
+
// lowering addresses instance N as DT_NODELABEL(cdc_acm_uart<N>), so the
|
|
227
|
+
// nodelabels here and in lowering/usb.ts cannot drift. Endpoints and
|
|
228
|
+
// descriptors are assigned by the class driver at build time.
|
|
229
|
+
if (usage.usesUsb && chip.usb) {
|
|
230
|
+
lines.push(`&${chip.usb.controller} {`);
|
|
231
|
+
lines.push(' status = "okay";');
|
|
232
|
+
for (let i = 0; i < chip.usb.cdcInstances; i++) {
|
|
233
|
+
lines.push(` cdc_acm_uart${i}: cdc-acm-uart${i} {`);
|
|
234
|
+
lines.push(' compatible = "zephyr,cdc-acm-uart";');
|
|
235
|
+
lines.push(' };');
|
|
236
|
+
}
|
|
237
|
+
lines.push('};');
|
|
238
|
+
lines.push('');
|
|
119
239
|
}
|
|
120
240
|
// PWM: synthesized specs (controller + channel, no board-shipped alias) get
|
|
121
241
|
// a pwm-leds consumer node + tc-pwm<pin> alias here — the lowering addresses
|
|
@@ -136,9 +256,34 @@ export function generateOverlay(
|
|
|
136
256
|
emitAdcNode(lines, chip, usage.adcReadPins);
|
|
137
257
|
}
|
|
138
258
|
// DAC: enable the chip's DAC device node when the program uses dac.*. The
|
|
139
|
-
// lowering references DEVICE_DT_GET(DT_NODELABEL(<dac.device>))
|
|
259
|
+
// lowering references DEVICE_DT_GET(DT_NODELABEL(<dac.device>)); the used
|
|
260
|
+
// channels' pinctrl groups route the output to its pad.
|
|
140
261
|
if (usage.usesDac && chip.dac) {
|
|
141
|
-
|
|
262
|
+
emitDacNode(lines, chip, usage.dacWritePins);
|
|
263
|
+
}
|
|
264
|
+
// Counters: enable the free counter devices when the program uses hwtimer.
|
|
265
|
+
// Self form (nRF rtc1, STM32 rtc): enable the labeled node. Child form
|
|
266
|
+
// (ESP32 timer0-3): the counter device is an unlabeled `counter {}`
|
|
267
|
+
// child — the overlay defines the tc_counter<N> label the descriptor's
|
|
268
|
+
// nodeLabel references (the same label-less-child move the STM32 pwm
|
|
269
|
+
// path uses).
|
|
270
|
+
if (usage.usesHwtimer && chip.hwtimer) {
|
|
271
|
+
for (const c of chip.hwtimer.controllers) {
|
|
272
|
+
if (c.counterParent) {
|
|
273
|
+
lines.push(`&${c.counterParent} {`);
|
|
274
|
+
lines.push(' status = "okay";');
|
|
275
|
+
lines.push(` ${c.nodeLabel}: counter {`);
|
|
276
|
+
lines.push(' status = "okay";');
|
|
277
|
+
lines.push(' };');
|
|
278
|
+
lines.push('};');
|
|
279
|
+
lines.push('');
|
|
280
|
+
} else {
|
|
281
|
+
lines.push(`&${c.nodeLabel} {`);
|
|
282
|
+
lines.push(' status = "okay";');
|
|
283
|
+
lines.push('};');
|
|
284
|
+
lines.push('');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
142
287
|
}
|
|
143
288
|
if (display) {
|
|
144
289
|
// Emit a full display DT node definition. Boards like the ESP32 devkit
|
|
@@ -173,19 +318,128 @@ export function generateOverlay(
|
|
|
173
318
|
// chosen entry makes the lookup explicit and survives boards that name the
|
|
174
319
|
// partition differently. It only adds a /chosen pointer (never redeclares
|
|
175
320
|
// the partition node — west errors if a node is multiply-defined).
|
|
176
|
-
if (usage.usesPreferences) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
321
|
+
if (usage.usesPreferences || usage.usesFS) {
|
|
322
|
+
// Boards whose DTS ships no storage_partition (most STM32s define only
|
|
323
|
+
// the MCUboot boot/slot/scratch set) declare a synthesis region in the
|
|
324
|
+
// chip descriptor — emit it as a partition@<offset> child of the flash0
|
|
325
|
+
// partitions node. Adding a NEW child via overlay is legal; redeclaring
|
|
326
|
+
// an existing one is not, so boards that already carry the label
|
|
327
|
+
// (descriptor storage.preexisting — ESP32's AMP layout) skip the
|
|
328
|
+
// synthesis block and only the /chosen pointer lands here.
|
|
329
|
+
if (chip.storage && !chip.storage.preexisting) {
|
|
330
|
+
lines.push('&flash0 {');
|
|
331
|
+
// Explicit compatible + cells: boards whose DTS already carries a
|
|
332
|
+
// partitions node (rp2040, STM32 MCUboot sets) merge identically, and
|
|
333
|
+
// boards without one (rpi_pico2) would otherwise hit dtc's spec
|
|
334
|
+
// default of #address-cells 2 / #size-cells 1 — a 3-cell reg and a
|
|
335
|
+
// "length 8 not divisible by 12" error for the 2-cell reg below.
|
|
336
|
+
lines.push(' partitions {');
|
|
337
|
+
lines.push(' compatible = "fixed-partitions";');
|
|
338
|
+
lines.push(' #address-cells = <1>;');
|
|
339
|
+
lines.push(' #size-cells = <1>;');
|
|
340
|
+
lines.push(` storage_partition: partition@${chip.storage.offset.toString(16)} {`);
|
|
341
|
+
lines.push(' label = "storage";');
|
|
342
|
+
lines.push(` reg = <0x${chip.storage.offset.toString(16).padStart(8, '0')} 0x${chip.storage.size.toString(16).padStart(8, '0')}>;`);
|
|
343
|
+
lines.push(' };');
|
|
344
|
+
lines.push(' };');
|
|
345
|
+
lines.push('};');
|
|
346
|
+
lines.push('');
|
|
347
|
+
}
|
|
348
|
+
if (usage.usesPreferences) {
|
|
349
|
+
lines.push('/ {');
|
|
350
|
+
lines.push(' chosen {');
|
|
351
|
+
lines.push(' zephyr,settings-partition = &storage_partition;');
|
|
352
|
+
lines.push(' };');
|
|
353
|
+
lines.push('};');
|
|
354
|
+
lines.push('');
|
|
355
|
+
}
|
|
183
356
|
}
|
|
184
357
|
|
|
185
358
|
// PSRAM: enable the psram0 DT node with the correct size. The devkitc board
|
|
186
359
|
// DT defaults to a no-PSRAM module variant (e.g. wroom_n8); a PSRAM-capable
|
|
187
360
|
// module (N16R8, N8R8) needs the node enabled + sized so the linker maps
|
|
188
361
|
// .ext_ram sections into the real PSRAM. OPI on ESP32-S3 = 8MB octal PSRAM.
|
|
362
|
+
// SPI sensors: children whose reg is their cs-gpios INDEX. DT property
|
|
363
|
+
// assignment replaces the whole property, so cs-gpios is written here with
|
|
364
|
+
// every sensor CS on the controller, after the display block — a display
|
|
365
|
+
// panel sharing the bus is a wiring conflict this layer cannot merge (its
|
|
366
|
+
// CS comes from config wiring), so warn instead of silently dropping it.
|
|
367
|
+
// spi-max-frequency defaults to 1 MHz — conservative for sensors whose
|
|
368
|
+
// datasheet max isn't in any binding; the constructor opts override it.
|
|
369
|
+
const SPI_SENSOR_HZ = 1000000;
|
|
370
|
+
const spiByCtrl = new Map<number, typeof spiSensors>();
|
|
371
|
+
for (const sp of spiSensors) {
|
|
372
|
+
const list = spiByCtrl.get(sp.busIndex) ?? [];
|
|
373
|
+
list.push(sp);
|
|
374
|
+
spiByCtrl.set(sp.busIndex, list);
|
|
375
|
+
}
|
|
376
|
+
// Target-only controllers (no sensors) still need the cs-gpios block.
|
|
377
|
+
for (const t of spiTargets) {
|
|
378
|
+
if (!spiByCtrl.has(t.busIndex)) spiByCtrl.set(t.busIndex, []);
|
|
379
|
+
}
|
|
380
|
+
for (const [idx, sensors] of spiByCtrl) {
|
|
381
|
+
const ctrl = chip.spi?.controllers[idx];
|
|
382
|
+
if (!ctrl) continue;
|
|
383
|
+
// A display panel on the same controller already wrote cs-gpios earlier
|
|
384
|
+
// in this overlay (its CS at index 0, an XPT2046 at 1). DT property
|
|
385
|
+
// assignment replaces, so this rewrite carries BOTH: the display entries
|
|
386
|
+
// first — keeping the panel/touch reg indexes stable — then the sensor CS
|
|
387
|
+
// pins. The entry construction mirrors emitDisplayNode's defaults via the
|
|
388
|
+
// shared DEFAULT_DISPLAY_CS/DEFAULT_XPT2046_CS constants.
|
|
389
|
+
const displayEntries: string[] = [];
|
|
390
|
+
if (display && (display.busLabel ?? 'spi2') === ctrl.nodeLabel) {
|
|
391
|
+
const displayCs = wiring?.cs ?? DEFAULT_DISPLAY_CS;
|
|
392
|
+
displayEntries.push(`<&gpio${displayCs <= 31 ? '0' : '1'} ${displayCs} GPIO_ACTIVE_LOW>`);
|
|
393
|
+
if (touch?.controller === 'xpt2046') {
|
|
394
|
+
const touchCs = touch.cs ?? DEFAULT_XPT2046_CS;
|
|
395
|
+
displayEntries.push(`<&gpio${touchCs <= 31 ? '0' : '1'} ${touchCs} GPIO_ACTIVE_LOW>`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Thin SPI targets ride this controller too: their CS entries append
|
|
399
|
+
// after the sensor pins, and their child nodes carry no compatible —
|
|
400
|
+
// a raw spi_dt_spec peer, not a driver node.
|
|
401
|
+
const ctrlTargets = spiTargets.filter((t) => t.busIndex === idx);
|
|
402
|
+
lines.push(`&${ctrl.nodeLabel} {`);
|
|
403
|
+
lines.push(` cs-gpios = ${displayEntries.concat(sensors.map((sp) => {
|
|
404
|
+
const gc = chip.gpioControllers?.find((g) => sp.port >= g.minPin && sp.port <= g.maxPin);
|
|
405
|
+
const ctrlLabel = gc?.nodelabel ?? 'gpio0';
|
|
406
|
+
return `<&${ctrlLabel} ${sp.port} GPIO_ACTIVE_LOW>`;
|
|
407
|
+
}), ctrlTargets.map((t) => {
|
|
408
|
+
const gc = chip.gpioControllers?.find((g) => t.cs >= g.minPin && t.cs <= g.maxPin);
|
|
409
|
+
const ctrlLabel = gc?.nodelabel ?? 'gpio0';
|
|
410
|
+
return `<&${ctrlLabel} ${t.cs} GPIO_ACTIVE_LOW>`;
|
|
411
|
+
})).join(', ')};`);
|
|
412
|
+
sensors.forEach((sp, sensorIdx) => {
|
|
413
|
+
const info = SENSOR_PART_INFO[sp.part.replace(/^SENSOR\./, '')];
|
|
414
|
+
if (!info) return;
|
|
415
|
+
const nodeName = info.compatible.split(',')[1] ?? sp.part;
|
|
416
|
+
const csIndex = displayEntries.length + sensorIdx;
|
|
417
|
+
lines.push(` tc_${sp.part}_spi${idx}_cs${sp.port}: ${nodeName}@${csIndex} {`);
|
|
418
|
+
lines.push(` compatible = "${info.compatible}";`);
|
|
419
|
+
lines.push(` reg = <${csIndex}>;`);
|
|
420
|
+
lines.push(` spi-max-frequency = <${sp.spiHz && sp.spiHz > 0 ? sp.spiHz : SPI_SENSOR_HZ}>;`);
|
|
421
|
+
// mode bits: 1 = CPHA, 2 = CPOL (presence props, no value).
|
|
422
|
+
if ((sp.spiMode ?? 0) & 2) lines.push(' spi-cpol;');
|
|
423
|
+
if ((sp.spiMode ?? 0) & 1) lines.push(' spi-cpha;');
|
|
424
|
+
emitAlert(lines, chip, sp.alertPin, info.alert, 8);
|
|
425
|
+
lines.push(' };');
|
|
426
|
+
});
|
|
427
|
+
ctrlTargets.forEach((t, targetIdx) => {
|
|
428
|
+
const csIndex = displayEntries.length + sensors.length + targetIdx;
|
|
429
|
+
lines.push(` tc_spit_spi${idx}_cs${t.cs}: spidev@${csIndex} {`);
|
|
430
|
+
// The app-local cuttlefish,spi-target binding (no driver — it makes
|
|
431
|
+
// gen_defines emit the spi properties SPI_DT_SPEC_GET reads).
|
|
432
|
+
lines.push(` compatible = "cuttlefish,spi-target";`);
|
|
433
|
+
lines.push(` reg = <${csIndex}>;`);
|
|
434
|
+
lines.push(` spi-max-frequency = <${t.hz && t.hz > 0 ? t.hz : SPI_SENSOR_HZ}>;`);
|
|
435
|
+
if ((t.mode ?? 0) & 2) lines.push(' spi-cpol;');
|
|
436
|
+
if ((t.mode ?? 0) & 1) lines.push(' spi-cpha;');
|
|
437
|
+
lines.push(' };');
|
|
438
|
+
});
|
|
439
|
+
lines.push('};');
|
|
440
|
+
lines.push('');
|
|
441
|
+
}
|
|
442
|
+
|
|
189
443
|
if (usage.psram) {
|
|
190
444
|
lines.push('&psram0 {');
|
|
191
445
|
lines.push(' status = "okay";');
|
|
@@ -212,14 +466,190 @@ function emitPwmNodes(
|
|
|
212
466
|
chip: ZephyrChipDescriptor,
|
|
213
467
|
usedPins?: readonly number[],
|
|
214
468
|
): void {
|
|
215
|
-
const
|
|
469
|
+
const staticSynth = (chip.pwm?.specs ?? []).filter(
|
|
216
470
|
(s) => s.controller && s.channel != null && !s.dtSpec,
|
|
217
471
|
).filter((s) => !usedPins || usedPins.includes(s.pin));
|
|
472
|
+
|
|
473
|
+
// Matrix PWM (ESP32 LEDC): channels are assigned here, over the pins the
|
|
474
|
+
// program actually drives (ascending — the same order lowering/pwm.ts
|
|
475
|
+
// emits the alias vars in; the C++ is channel-blind, so this is the single
|
|
476
|
+
// place a channel number is decided). The espressif,esp32-ledc binding
|
|
477
|
+
// needs three pieces per channel: a pinctrl pinmux token routing the
|
|
478
|
+
// channel to the pad (LEDC_CH<ch>_GPIO<pin> — every channel×pad pair
|
|
479
|
+
// exists in esp32s3-pinctrl.h, already included via the SoC dtsi), a
|
|
480
|
+
// channel child node (the driver's DT_INST_FOREACH_CHILD reads reg +
|
|
481
|
+
// timer from each), and the pwm-leds consumer the alias resolves to.
|
|
482
|
+
const m = chip.pwm?.matrix;
|
|
483
|
+
let matrixSynth: { pin: number; controller: string; channel: number }[] = [];
|
|
484
|
+
if (m) {
|
|
485
|
+
const pins = [...new Set(usedPins ? usedPins.filter((p) => m.pins.includes(p)) : m.pins)]
|
|
486
|
+
.sort((a, b) => a - b);
|
|
487
|
+
if (pins.length > m.channelCount) {
|
|
488
|
+
throw new Error(
|
|
489
|
+
`framework-zephyr: the program drives ${pins.length} PWM pins but ${chip.id}'s ` +
|
|
490
|
+
`${m.controller} exposes only ${m.channelCount} channels — drop ` +
|
|
491
|
+
`${pins.length - m.channelCount} pwm pin(s).`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
matrixSynth = pins.map((pin, ch) => ({ pin, controller: m.controller, channel: ch }));
|
|
495
|
+
// nRF shape: channels route to pads via psel (pinctrl psel order IS
|
|
496
|
+
// channel order — pwm_nrfx reads PSEL.OUT[ch] back), no channel child
|
|
497
|
+
// nodes, and the overlay carries the nrf pinctrl header for NRF_PSEL.
|
|
498
|
+
// P<port>.<pin> maps from the global number through the nRF controller
|
|
499
|
+
// layout (P0 = 0-31, P1 = 32-63).
|
|
500
|
+
const isNrfMatrix = /^nrf/.test(chip.soc);
|
|
501
|
+
if (isNrfMatrix) {
|
|
502
|
+
const inc = '#include <zephyr/dt-bindings/pinctrl/nrf-pinctrl.h>';
|
|
503
|
+
if (!lines.includes(inc)) lines.splice(2, 0, inc, '');
|
|
504
|
+
lines.push('&pinctrl {');
|
|
505
|
+
lines.push(` tc_${m.controller}_default: tc-${m.controller}-default {`);
|
|
506
|
+
lines.push(' group1 {');
|
|
507
|
+
lines.push(` psel = ${pins.map((p, ch) => `<NRF_PSEL(PWM_OUT${ch}, ${Math.floor(p / 32)}, ${p % 32})>`).join(', ')};`);
|
|
508
|
+
lines.push(' };');
|
|
509
|
+
lines.push(' };');
|
|
510
|
+
lines.push('};');
|
|
511
|
+
lines.push('');
|
|
512
|
+
lines.push(`&${m.controller} {`);
|
|
513
|
+
lines.push(' status = "okay";');
|
|
514
|
+
lines.push(` pinctrl-0 = <&tc_${m.controller}_default>;`);
|
|
515
|
+
lines.push(' pinctrl-names = "default";');
|
|
516
|
+
lines.push('};');
|
|
517
|
+
lines.push('');
|
|
518
|
+
}
|
|
519
|
+
if (!isNrfMatrix) {
|
|
520
|
+
lines.push('&pinctrl {');
|
|
521
|
+
lines.push(` tc_${m.controller}_default: tc-${m.controller}-default {`);
|
|
522
|
+
lines.push(' group1 {');
|
|
523
|
+
lines.push(` pinmux = ${pins.map((p, ch) => `<LEDC_CH${ch}_GPIO${p}>`).join(', ')};`);
|
|
524
|
+
lines.push(' output-enable;');
|
|
525
|
+
lines.push(' };');
|
|
526
|
+
lines.push(' };');
|
|
527
|
+
lines.push('};');
|
|
528
|
+
lines.push('');
|
|
529
|
+
// The four LEDC timers are round-robined so co-driven channels share a
|
|
530
|
+
// timer only past four (sharing is fine at equal periods; the driver
|
|
531
|
+
// reconfigures on pwm_set_dt when they differ).
|
|
532
|
+
lines.push(`&${m.controller} {`);
|
|
533
|
+
lines.push(' status = "okay";');
|
|
534
|
+
lines.push(` pinctrl-0 = <&tc_${m.controller}_default>;`);
|
|
535
|
+
lines.push(' pinctrl-names = "default";');
|
|
536
|
+
lines.push(' #address-cells = <1>;');
|
|
537
|
+
lines.push(' #size-cells = <0>;');
|
|
538
|
+
for (let ch = 0; ch < pins.length; ch++) {
|
|
539
|
+
lines.push(` channel${ch}@${ch} {`);
|
|
540
|
+
lines.push(` reg = <${ch}>;`);
|
|
541
|
+
lines.push(` timer = <${ch % 4}>;`);
|
|
542
|
+
lines.push(' };');
|
|
543
|
+
}
|
|
544
|
+
lines.push('};');
|
|
545
|
+
lines.push('');
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const synthesized = staticSynth.concat(matrixSynth);
|
|
218
550
|
if (synthesized.length === 0) return;
|
|
551
|
+
// The pwms cells name the polarity as a macro (PWM_POLARITY_NORMAL). It is
|
|
552
|
+
// NOT universally available: only dts include chains that already use PWM
|
|
553
|
+
// bindings pull <zephyr/dt-bindings/pwm/pwm.h> in (STM32 does; the
|
|
554
|
+
// ESP32-S3 chain does not), so the overlay carries its own include.
|
|
555
|
+
// Spliced after the header comments, the established pattern (see the
|
|
556
|
+
// esp32s3-pinctrl.h splice above); guarded for the double-emit case.
|
|
557
|
+
if (!lines.includes('#include <zephyr/dt-bindings/pwm/pwm.h>')) {
|
|
558
|
+
lines.splice(2, 0, '#include <zephyr/dt-bindings/pwm/pwm.h>', '');
|
|
559
|
+
}
|
|
219
560
|
// Enable each distinct PWM controller node (idempotent when already okay).
|
|
220
|
-
|
|
561
|
+
// 16-bit fit: STM32 timers count period cycles in a 16-bit ARR, and the
|
|
562
|
+
// SoC dtsi default is st,prescaler = <0> (÷1) — a 20 ms servo period at
|
|
563
|
+
// 96 MHz is 1.92M cycles and pwm_stm32 rejects the channel. When the
|
|
564
|
+
// descriptor declares the timer clock, derive the smallest divider that
|
|
565
|
+
// fits the controller's slowest used period and override the prescaler
|
|
566
|
+
// on the timers parent node (the property lives there, not on the pwm
|
|
567
|
+
// child). pwm4 → timers4 is the STM32 nodelabel convention. Matrix
|
|
568
|
+
// controllers are skipped — the block above already enabled them (with
|
|
569
|
+
// their pinctrl + channel children, which this loop doesn't know).
|
|
570
|
+
const clockHz = chip.pwm?.clockHz;
|
|
571
|
+
for (const controller of [...new Set(staticSynth.map((s) => s.controller!))]) {
|
|
572
|
+
const timersMatch = controller.match(/^pwm(\d+)$/);
|
|
573
|
+
const specs = staticSynth.filter((s) => s.controller === controller);
|
|
574
|
+
// Macro-form pinctrl tokens (uppercase — DT labels are lowercase by
|
|
575
|
+
// spec): the overlay synthesizes the pad-routing group itself. RP2's
|
|
576
|
+
// macros live in a dt-bindings header the board chain may not pull in
|
|
577
|
+
// (spliced via rp2PinctrlHeader); Kinetis/LPC/GD32 macros come from the
|
|
578
|
+
// part headers the board's own pinctrl dtsi includes, and Zephyr
|
|
579
|
+
// preprocesses overlays together with the base DTS — no splice needed.
|
|
580
|
+
const isMacro = (p: string | undefined): boolean => !!p && /^[A-Z][A-Z0-9_]*$/.test(p);
|
|
581
|
+
if (!timersMatch && specs.some((s) => isMacro(s.pinctrl))) {
|
|
582
|
+
const header = rp2PinctrlHeader(chip.soc);
|
|
583
|
+
if (header) {
|
|
584
|
+
const inc = `#include <zephyr/dt-bindings/pinctrl/${header}>`;
|
|
585
|
+
if (!lines.includes(inc)) lines.splice(2, 0, inc, '');
|
|
586
|
+
}
|
|
587
|
+
lines.push('&pinctrl {');
|
|
588
|
+
lines.push(' tc_pwm_default: tc-pwm-default {');
|
|
589
|
+
lines.push(' group1 {');
|
|
590
|
+
lines.push(` pinmux = ${specs.filter((s) => s.pinctrl).map((s) => `<${s.pinctrl}>`).join(', ')};`);
|
|
591
|
+
lines.push(' };');
|
|
592
|
+
lines.push(' };');
|
|
593
|
+
lines.push('};');
|
|
594
|
+
lines.push('');
|
|
595
|
+
lines.push(`&${controller} {`);
|
|
596
|
+
lines.push(' status = "okay";');
|
|
597
|
+
// Atmel SAM TCC/TC in PWM mode: the SoC dtsi declares the base timer
|
|
598
|
+
// node (compatible atmel,sam0-tcc / …-tc, with clocks/assigned-clocks/
|
|
599
|
+
// channels/counter-size already present). The overlay overrides the
|
|
600
|
+
// compatible to the PWM binding, sets the required prescaler and the
|
|
601
|
+
// #pwm-cells the binding consts, and reuses the shared pinctrl group.
|
|
602
|
+
const samPwm = controller.match(/^(tcc|tc)(\d+)$/);
|
|
603
|
+
if (samPwm) {
|
|
604
|
+
lines.push(` compatible = "${samPwm[1] === 'tcc' ? 'atmel,sam0-tcc-pwm' : 'atmel,sam0-tc-pwm'}";`);
|
|
605
|
+
lines.push(' prescaler = <1024>;');
|
|
606
|
+
lines.push(' #pwm-cells = <2>;');
|
|
607
|
+
}
|
|
608
|
+
lines.push(' pinctrl-0 = <&tc_pwm_default>;');
|
|
609
|
+
lines.push(' pinctrl-names = "default";');
|
|
610
|
+
lines.push('};');
|
|
611
|
+
lines.push('');
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (timersMatch) {
|
|
615
|
+
// STM32 shape: the SoC dtsi declares a LABEL-LESS pwm child under
|
|
616
|
+
// timers{N} — the overlay defines the label itself (pwmN: pwm), which
|
|
617
|
+
// the synthesized pwms cell below references. One block carries the
|
|
618
|
+
// parent enable, the pad-routing pinctrl groups (harvested silicon
|
|
619
|
+
// routes on the specs — without them the PWM runs but nothing leaves
|
|
620
|
+
// the chip), the prescaler, and the child enable.
|
|
621
|
+
const groups = specs.filter((s) => s.pinctrl).map((s) => `&${s.pinctrl}`);
|
|
622
|
+
const maxPeriodNs = Math.max(...specs.map((s) => s.periodNs ?? 20_000_000));
|
|
623
|
+
// cycles = clockHz * period_s / divider ≤ 65536 (driver allows
|
|
624
|
+
// UINT16_MAX + 1); binding value is divider - 1 (CLK/(prescaler+1)).
|
|
625
|
+
const divider = clockHz
|
|
626
|
+
? Math.max(1, Math.ceil((clockHz * maxPeriodNs) / 1e9 / 65536))
|
|
627
|
+
: 1;
|
|
628
|
+
lines.push(`&timers${timersMatch[1]} {`);
|
|
629
|
+
lines.push(' status = "okay";');
|
|
630
|
+
if (divider > 1) {
|
|
631
|
+
lines.push(` st,prescaler = <${divider - 1}>;`);
|
|
632
|
+
}
|
|
633
|
+
lines.push(` ${controller}: pwm {`);
|
|
634
|
+
lines.push(' status = "okay";');
|
|
635
|
+
if (groups.length > 0) {
|
|
636
|
+
lines.push(` pinctrl-0 = <${groups.join(' ')}>;`);
|
|
637
|
+
lines.push(' pinctrl-names = "default";');
|
|
638
|
+
}
|
|
639
|
+
lines.push(' };');
|
|
640
|
+
lines.push('};');
|
|
641
|
+
lines.push('');
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
// Lowercase pinctrl tokens (i.MX `iomuxc_…` node labels from the part
|
|
645
|
+
// dtsi) ride the controller's pinctrl-0 directly.
|
|
646
|
+
const pinRefs = specs.filter((s) => s.pinctrl && !isMacro(s.pinctrl)).map((s) => `&${s.pinctrl}`);
|
|
221
647
|
lines.push(`&${controller} {`);
|
|
222
648
|
lines.push(' status = "okay";');
|
|
649
|
+
if (pinRefs.length > 0) {
|
|
650
|
+
lines.push(` pinctrl-0 = <${pinRefs.join(' ')}>;`);
|
|
651
|
+
lines.push(' pinctrl-names = "default";');
|
|
652
|
+
}
|
|
223
653
|
lines.push('};');
|
|
224
654
|
lines.push('');
|
|
225
655
|
}
|
|
@@ -251,26 +681,99 @@ function emitPwmNodes(
|
|
|
251
681
|
* AND the caller knows which pins the program reads (compile-time regen scans
|
|
252
682
|
* the emitted `__tc_adc<N>_setup()` calls); the prepare-time overlay omits it
|
|
253
683
|
* and the compile regen rewrites the file before west runs.
|
|
684
|
+
*
|
|
685
|
+
* Two pinctrl forms by family: NODE labels (STM32's harvested
|
|
686
|
+
* `adc1_in1_pa1` groups — referenced as `&label`) and MACRO tokens (RP2's
|
|
687
|
+
* `ADC_CH0_P26` — DT labels are lowercase by spec and macros uppercase, so
|
|
688
|
+
* the case decides). The macro form synthesizes its own group under
|
|
689
|
+
* &pinctrl and splices the SoC's pinctrl dt-binding header so the token
|
|
690
|
+
* resolves in the overlay's compile.
|
|
691
|
+
*
|
|
692
|
+
* Multi-controller SoCs: channels may name their owning controller (STM32
|
|
693
|
+
* adc3-only routes, ESP32 units). One enable block is emitted per controller
|
|
694
|
+
* that owns used channels — the primary first, others sorted for
|
|
695
|
+
* deterministic output. Single-controller boards emit exactly one block, as
|
|
696
|
+
* before.
|
|
254
697
|
*/
|
|
698
|
+
/** The SoC's pinctrl dt-binding header (where the macro tokens are defined)
|
|
699
|
+
* — RP2 shapes; undefined for socs that use node-label pinctrl exclusively. */
|
|
700
|
+
function rp2PinctrlHeader(soc: string): string | undefined {
|
|
701
|
+
if (soc === 'rp2040') return 'rpi-pico-rp2040-pinctrl.h';
|
|
702
|
+
if (soc.startsWith('rp2350a')) return 'rpi-pico-rp2350a-pinctrl.h';
|
|
703
|
+
if (soc.startsWith('rp2350b')) return 'rpi-pico-rp2350b-pinctrl.h';
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
|
|
255
707
|
function emitAdcNode(
|
|
256
708
|
lines: string[],
|
|
257
709
|
chip: ZephyrChipDescriptor,
|
|
258
710
|
readPins?: readonly number[],
|
|
259
711
|
): void {
|
|
260
712
|
const adc = chip.adc!;
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
713
|
+
const primary = adc.nodeLabel;
|
|
714
|
+
const controllerOf = (c: { controller?: string }) => c.controller ?? primary;
|
|
715
|
+
const used = readPins ? adc.channels.filter((c) => readPins.includes(c.pin)) : adc.channels;
|
|
716
|
+
const isMacro = (p: string | undefined): boolean => !!p && /^[A-Z][A-Z0-9_]*$/.test(p);
|
|
717
|
+
const macroChannels = used.filter((c) => isMacro(c.pinctrl));
|
|
718
|
+
if (macroChannels.length > 0) {
|
|
719
|
+
const header = rp2PinctrlHeader(chip.soc);
|
|
720
|
+
if (header) {
|
|
721
|
+
const inc = `#include <zephyr/dt-bindings/pinctrl/${header}>`;
|
|
722
|
+
if (!lines.includes(inc)) lines.splice(2, 0, inc, '');
|
|
723
|
+
}
|
|
724
|
+
lines.push('&pinctrl {');
|
|
725
|
+
lines.push(' tc_adc_default: tc-adc-default {');
|
|
726
|
+
lines.push(' group1 {');
|
|
727
|
+
lines.push(` pinmux = ${macroChannels.map((c) => `<${c.pinctrl}>`).join(', ')};`);
|
|
728
|
+
lines.push(' };');
|
|
729
|
+
lines.push(' };');
|
|
730
|
+
lines.push('};');
|
|
731
|
+
lines.push('');
|
|
732
|
+
}
|
|
733
|
+
// Every controller that owns a USED channel gets an enable block — a
|
|
734
|
+
// controller left "disabled" in the SoC dtsi has no device instance, so a
|
|
735
|
+
// read against it fails to link. Nothing used: enable the primary only.
|
|
736
|
+
const controllers = used.length > 0
|
|
737
|
+
? [...new Set(used.map(controllerOf))]
|
|
738
|
+
.sort((a, b) => (a === primary ? -1 : b === primary ? 1 : a.localeCompare(b, undefined, { numeric: true })))
|
|
739
|
+
: [primary];
|
|
740
|
+
for (const label of controllers) {
|
|
741
|
+
const mine = used.filter((c) => controllerOf(c) === label && c.pinctrl);
|
|
742
|
+
lines.push(`&${label} {`);
|
|
265
743
|
lines.push(' status = "okay";');
|
|
266
|
-
|
|
267
|
-
|
|
744
|
+
if (mine.length > 0) {
|
|
745
|
+
if (mine.every((c) => isMacro(c.pinctrl))) {
|
|
746
|
+
lines.push(' pinctrl-0 = <&tc_adc_default>;');
|
|
747
|
+
} else {
|
|
748
|
+
lines.push(` pinctrl-0 = <${mine.map((c) => `&${c.pinctrl}`).join(' ')}>;`);
|
|
749
|
+
}
|
|
750
|
+
lines.push(' pinctrl-names = "default";');
|
|
751
|
+
}
|
|
268
752
|
lines.push('};');
|
|
269
753
|
lines.push('');
|
|
270
|
-
return;
|
|
271
754
|
}
|
|
272
|
-
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Emit the DAC device-node enable + the used output channels' pinctrl
|
|
759
|
+
* groups (the harvested `dac1_out1_pa4`-style nodes — the st,stm32-dac
|
|
760
|
+
* binding includes pinctrl-device, so without the group the channel never
|
|
761
|
+
* reaches its pad). Mirrors emitAdcNode.
|
|
762
|
+
*/
|
|
763
|
+
function emitDacNode(
|
|
764
|
+
lines: string[],
|
|
765
|
+
chip: ZephyrChipDescriptor,
|
|
766
|
+
writePins?: readonly number[],
|
|
767
|
+
): void {
|
|
768
|
+
const dac = chip.dac!;
|
|
769
|
+
const labeled = dac.channels.filter((c) => c.pinctrl);
|
|
770
|
+
const used = labeled.filter((c) => !writePins || writePins.includes(c.pin));
|
|
771
|
+
lines.push(`&${dac.device} {`);
|
|
273
772
|
lines.push(' status = "okay";');
|
|
773
|
+
if (used.length > 0) {
|
|
774
|
+
lines.push(` pinctrl-0 = <${used.map((c) => `&${c.pinctrl}`).join(' ')}>;`);
|
|
775
|
+
lines.push(' pinctrl-names = "default";');
|
|
776
|
+
}
|
|
274
777
|
lines.push('};');
|
|
275
778
|
lines.push('');
|
|
276
779
|
}
|
|
@@ -287,6 +790,15 @@ function emitAdcNode(
|
|
|
287
790
|
* appended as the second cs-gpios entry (the touch node uses reg = <1>) — DT
|
|
288
791
|
* property assignment replaces, so both entries must be written together.
|
|
289
792
|
*/
|
|
793
|
+
/** Emit alert-gpios for parts whose binding declares it (active high — the
|
|
794
|
+
* bindings describe the sensor's output level, e.g. sht3xd's ALERT). */
|
|
795
|
+
function emitAlert(lines: string[], chip: ZephyrChipDescriptor, alertPin: number | undefined, supported: boolean, indent: number): void {
|
|
796
|
+
if (alertPin === undefined || alertPin < 0 || !supported) return;
|
|
797
|
+
const gc = chip.gpioControllers?.find((g) => alertPin >= g.minPin && alertPin <= g.maxPin);
|
|
798
|
+
const pad = ' '.repeat(indent);
|
|
799
|
+
lines.push(`${pad}alert-gpios = <&${gc?.nodelabel ?? 'gpio0'} ${alertPin} GPIO_ACTIVE_HIGH>;`);
|
|
800
|
+
}
|
|
801
|
+
|
|
290
802
|
function emitDisplayNode(
|
|
291
803
|
lines: string[],
|
|
292
804
|
display: ZephyrDisplayProfile,
|
|
@@ -298,7 +810,7 @@ function emitDisplayNode(
|
|
|
298
810
|
const compatible = display.dtCompatible ?? PANEL_CONTROLLER_DEFAULTS[controller].dtCompatible;
|
|
299
811
|
const dc = wiring?.dc ?? 17;
|
|
300
812
|
const rst = wiring?.rst ?? 16;
|
|
301
|
-
const cs = wiring?.cs ??
|
|
813
|
+
const cs = wiring?.cs ?? DEFAULT_DISPLAY_CS;
|
|
302
814
|
const freq = wiring?.spiFrequency ?? 80000000;
|
|
303
815
|
// DT node describes the NATIVE panel geometry; the effective (rotated)
|
|
304
816
|
// dimensions live in the display profile.
|