@typecad/framework-zephyr 1.0.0-alpha.10 → 1.0.0-alpha.12

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.
Files changed (67) hide show
  1. package/dist/chips/esp32.js +12 -0
  2. package/dist/chips/types.d.ts +30 -0
  3. package/dist/chips/xiao-ble.js +6 -0
  4. package/dist/display/gfx.d.ts +12 -3
  5. package/dist/display/gfx.js +130 -17
  6. package/dist/display/profiles.d.ts +25 -0
  7. package/dist/display/profiles.js +30 -0
  8. package/dist/display/touch-adapter.d.ts +3 -4
  9. package/dist/display/touch-adapter.js +119 -16
  10. package/dist/display/ui-adapter.js +308 -139
  11. package/dist/doctor.d.ts +3 -3
  12. package/dist/doctor.js +56 -29
  13. package/dist/dt-config/kconfig.d.ts +8 -1
  14. package/dist/dt-config/kconfig.js +40 -5
  15. package/dist/dt-config/overlay.d.ts +18 -1
  16. package/dist/dt-config/overlay.js +124 -26
  17. package/dist/framework.manifest.d.ts +29 -28
  18. package/dist/framework.manifest.js +46 -17
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +5 -0
  21. package/dist/licenses.d.ts +59 -0
  22. package/dist/licenses.js +347 -0
  23. package/dist/lowering/ble.js +3 -1
  24. package/dist/lowering/dac.d.ts +15 -0
  25. package/dist/lowering/dac.js +69 -0
  26. package/dist/lowering/fs.d.ts +16 -0
  27. package/dist/lowering/fs.js +121 -0
  28. package/dist/lowering/hwtimer.d.ts +15 -0
  29. package/dist/lowering/hwtimer.js +84 -0
  30. package/dist/lowering/index.d.ts +4 -1
  31. package/dist/lowering/index.js +12 -3
  32. package/dist/strategy.d.ts +4 -0
  33. package/dist/strategy.js +275 -35
  34. package/dist/toolchain/compat.js +10 -1
  35. package/dist/toolchain/env-check.d.ts +93 -0
  36. package/dist/toolchain/env-check.js +190 -0
  37. package/dist/toolchain/index.js +39 -9
  38. package/dist/toolchain/scaffold.js +7 -2
  39. package/dist/toolchain/west-discover.d.ts +11 -3
  40. package/dist/toolchain/west-discover.js +84 -7
  41. package/dist/toolchain/west-spawn.js +15 -0
  42. package/package.json +4 -4
  43. package/src/chips/esp32.ts +12 -0
  44. package/src/chips/types.ts +29 -0
  45. package/src/chips/xiao-ble.ts +6 -0
  46. package/src/display/gfx.ts +135 -19
  47. package/src/display/profiles.ts +53 -0
  48. package/src/display/touch-adapter.ts +119 -15
  49. package/src/display/ui-adapter.ts +311 -139
  50. package/src/doctor.ts +77 -56
  51. package/src/dt-config/kconfig.ts +45 -6
  52. package/src/dt-config/overlay.ts +159 -29
  53. package/src/framework.manifest.ts +47 -17
  54. package/src/index.ts +6 -0
  55. package/src/licenses.ts +425 -0
  56. package/src/lowering/ble.ts +3 -1
  57. package/src/lowering/dac.ts +82 -0
  58. package/src/lowering/fs.ts +127 -0
  59. package/src/lowering/hwtimer.ts +101 -0
  60. package/src/lowering/index.ts +9 -2
  61. package/src/strategy.ts +271 -35
  62. package/src/toolchain/compat.ts +154 -145
  63. package/src/toolchain/env-check.ts +285 -0
  64. package/src/toolchain/index.ts +40 -9
  65. package/src/toolchain/scaffold.ts +7 -2
  66. package/src/toolchain/west-discover.ts +92 -9
  67. package/src/toolchain/west-spawn.ts +15 -0
package/dist/doctor.js CHANGED
@@ -1,48 +1,75 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // @typecad/framework-zephyr — Zephyr environment doctor
3
3
  //
4
- // `cuttlefish doctor` (Zephyr framework) — verify the installed Zephyr RTOS is
5
- // reachable and inside the framework's declared compat range, and preview how
6
- // the configured board target resolves for that version. Exits 0 if the
7
- // environment is OK, non-zero otherwise. Mirrors framework-arduino's doctor
8
- // shape (dispatched via the framework's `doctor` export).
4
+ // `cuttlefish doctor` (Zephyr framework) — verify west (the Zephyr build tool)
5
+ // is installed + responsive, the Zephyr RTOS is inside the framework's declared
6
+ // compat range, and the configured board target exists in the checkout. Exits 0
7
+ // if the environment is OK, non-zero otherwise. Mirrors framework-arduino's
8
+ // doctor shape (dispatched via the framework's `doctor` export) and reuses
9
+ // checkZephyrEnv so the detection logic can be shared with the build/test gates.
9
10
  // ---------------------------------------------------------------------------
10
11
  import * as ui from '@typecad/cuttlefish/utils/ui';
11
12
  import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
12
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './toolchain/compat.js';
13
+ import { checkZephyrEnv } from './toolchain/env-check.js';
13
14
  /**
14
- * Verify the installed Zephyr is reachable + inside the supported range, and
15
- * preview board-target normalization for the configured target. Sets
16
- * process.exitCode = 1 on an out-of-range Zephyr.
15
+ * Verify west is installed + responsive, the Zephyr RTOS is inside the supported
16
+ * range, and the configured board target exists in the checkout. Sets
17
+ * process.exitCode = 1 on failure. Thin presenter over checkZephyrEnv.
17
18
  */
18
19
  export function runDoctor() {
19
20
  ui.printHeader();
20
21
  ui.printStep('Checking Zephyr environment...');
21
- const base = process.env.ZEPHYR_BASE;
22
- const version = detectZephyrVersion();
23
- const result = checkZephyrCompat(version);
24
- ui.printInfo(`ZEPHYR_BASE ..... ${base ?? '(not set)'}`);
25
- ui.printInfo(`Zephyr version .. ${version ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
26
- ui.printInfo(`Supported range . ${result.range ?? '(none declared)'}`);
27
- if (result.status === 'out-of-range') {
28
- ui.printError(`Zephyr ${version} is OUTSIDE the supported range (${result.range}).`);
29
- ui.printInfo("Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.");
30
- process.exitCode = 1;
31
- return;
32
- }
33
- if (result.status === 'undetectable') {
22
+ const config = loadCuttlefishConfig(process.cwd());
23
+ const buildTarget = config?.buildTarget;
24
+ const result = checkZephyrEnv(buildTarget);
25
+ const c = result.check;
26
+ // west (the Zephyr build tool) the analog of arduino-cli presence.
27
+ if (c.westFound) {
28
+ const ver = c.westVersion ?? 'found';
29
+ const src = c.westSource ? ` (${c.westSource})` : '';
30
+ ui.printInfo(`west ............. ${ver} ✓${src}`);
31
+ }
32
+ else {
33
+ ui.printError('west ............. NOT FOUND');
34
+ }
35
+ // Zephyr RTOS version + declared compat range.
36
+ ui.printInfo(`ZEPHYR_BASE ...... ${c.zephyrBase ?? '(not set)'}`);
37
+ ui.printInfo(`Zephyr version .. ${c.zephyrVersion ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
38
+ ui.printInfo(`Supported range . ${c.compatRange ?? '(none declared)'}`);
39
+ if (c.compatStatus === 'out-of-range') {
40
+ ui.printError(`Zephyr ${c.zephyrVersion} is OUTSIDE the supported range (${c.compatRange}).`);
41
+ }
42
+ else if (c.compatStatus === 'undetectable') {
34
43
  ui.printWarning('Could not detect the Zephyr version (is ZEPHYR_BASE set?) — compat check skipped.');
35
44
  }
36
45
  else {
37
46
  ui.printInfo('Zephyr compat ... OK');
38
47
  }
39
- // Preview how the configured board target resolves for this Zephyr version
40
- // (e.g. a stale bare id would be qualified at build time). The loader extracts
41
- // frameworkData.buildTarget to a top-level field.
42
- const config = loadCuttlefishConfig(process.cwd());
43
- const buildTarget = config?.buildTarget;
48
+ // Board target the analog of the Arduino core presence check.
44
49
  if (buildTarget) {
45
- const resolved = resolveBoardTarget(buildTarget, version);
46
- ui.printInfo(`Board target .... ${buildTarget}${resolved === buildTarget ? '' : ` → ${resolved}`}`);
50
+ const resolved = c.resolvedBoardTarget ?? buildTarget;
51
+ const arrow = resolved === buildTarget ? '' : ` → ${resolved}`;
52
+ if (c.boardTargetSupported === false) {
53
+ ui.printError(`Board target .... ${buildTarget}${arrow} NOT found in this Zephyr checkout`);
54
+ ui.printInfo(' → check the board id, or run: west boards');
55
+ }
56
+ else if (c.boardTargetSupported === undefined) {
57
+ ui.printInfo(`Board target .... ${buildTarget}${arrow}`);
58
+ ui.printInfo('(could not verify board presence — no ZEPHYR_BASE boards/ tree found)');
59
+ }
60
+ else {
61
+ ui.printInfo(`Board target .... ${buildTarget}${arrow}`);
62
+ }
63
+ }
64
+ else {
65
+ ui.printInfo('(no buildTarget in cuttlefish.config.ts — skipping board check)');
66
+ }
67
+ // Exit code — mirrors framework-arduino's doctor.
68
+ if (result.ok) {
69
+ ui.printSuccess('Environment OK');
70
+ return; // exitCode stays unset => 0
47
71
  }
72
+ for (const line of result.messages)
73
+ ui.printInfo(line);
74
+ process.exitCode = 1;
48
75
  }
@@ -1,6 +1,9 @@
1
1
  export interface KconfigUsage {
2
2
  usesAdc?: boolean;
3
3
  usesPwm?: boolean;
4
+ usesDac?: boolean;
5
+ usesFS?: boolean;
6
+ usesHwtimer?: boolean;
4
7
  usesI2c?: boolean;
5
8
  usesSpi?: boolean;
6
9
  usesUart?: boolean;
@@ -13,8 +16,12 @@ export interface KconfigUsage {
13
16
  usesMqtt?: boolean;
14
17
  usesPreferences?: boolean;
15
18
  usesRandom?: boolean;
16
- /** Touch controller referenced (UI touch adapter emits DT_NODELABEL(ft6336u)). */
19
+ /** Touch controller referenced (UI touch adapter emits DT_NODELABEL(ft6336u)
20
+ * or DT_NODELABEL(xpt2046)). Selects the bus driver the node needs. */
17
21
  usesTouch?: boolean;
22
+ /** Which touch controller the program uses — FT6336U rides I2C, XPT2046
23
+ * rides the display's SPI bus. Only meaningful with usesTouch. */
24
+ touchController?: 'ft6336u' | 'xpt2046';
18
25
  /** PSRAM type ('opi' | 'quad') when the target board has PSRAM. Emits the
19
26
  * CONFIG_SPIRAM symbols so the ESP heap serves PSRAM for canvas allocations. */
20
27
  psram?: 'opi' | 'quad';
@@ -23,12 +23,17 @@ export function resolveKconfigFragments(usage, debug) {
23
23
  m.set('CONFIG_ADC', 'y');
24
24
  if (usage.usesPwm)
25
25
  m.set('CONFIG_PWM', 'y');
26
+ if (usage.usesDac)
27
+ m.set('CONFIG_DAC', 'y');
26
28
  if (usage.usesI2c)
27
29
  m.set('CONFIG_I2C', 'y');
28
30
  if (usage.usesSpi)
29
31
  m.set('CONFIG_SPI', 'y');
30
32
  if (usage.usesWdt)
31
33
  m.set('CONFIG_WATCHDOG', 'y');
34
+ // Hardware timers via the counter driver.
35
+ if (usage.usesHwtimer)
36
+ m.set('CONFIG_COUNTER', 'y');
32
37
  if (usage.usesDisplay) {
33
38
  m.set('CONFIG_DISPLAY', 'y');
34
39
  m.set('CONFIG_SPI', 'y');
@@ -39,15 +44,32 @@ export function resolveKconfigFragments(usage, debug) {
39
44
  // configured SPI clock (~80MHz) and drops into the low tens of ms. The
40
45
  // display overlay pairs this with dma-enabled + dmas on the spi2 node.
41
46
  m.set('CONFIG_DMA', 'y');
42
- // Disable the MIPI DBI SPI bridge + ST7796S drivers. The display adapter
43
- // drives the panel directly via spi_write. Binding these drivers would
44
- // allocate a tearing-effect GPIO interrupt that conflicts with the SPI/I2C
45
- // driver interrupts the VECDESC_FL_SHARED assertion crashes on touch.
47
+ // Disable the MIPI DBI SPI bridge + in-tree panel drivers (ILI9341,
48
+ // ST7796S). The display adapter drives the panel directly via spi_write.
49
+ // Binding these drivers would allocate a tearing-effect GPIO interrupt
50
+ // that conflicts with the SPI/I2C driver interrupts the
51
+ // VECDESC_FL_SHARED assertion crashes on touch. ILI9341 matters as much
52
+ // as the bridge: the driver auto-defaults on from the overlay's
53
+ // ilitek,ili9341 node and references the (disabled) mipi-dbi-spi
54
+ // controller's device struct, failing at link time with
55
+ // "undefined reference to __device_dts_ord_N". (Assign the prompted
56
+ // ILI9341, not the hidden ILI9XXX — promptless symbols reject prj.conf
57
+ // assignments.)
46
58
  m.set('CONFIG_MIPI_DBI_SPI', 'n');
59
+ m.set('CONFIG_ILI9341', 'n');
47
60
  m.set('CONFIG_ST7796S', 'n');
48
61
  }
49
62
  if (usage.usesTouch) {
50
- m.set('CONFIG_I2C', 'y'); // FT6336U touch on I2C
63
+ // FT6336U touch is on I2C; the XPT2046 shares the display's SPI bus.
64
+ // CONFIG_INPUT stays off either way: the adapters drive the controllers
65
+ // directly, and enabling it would build the in-tree input drivers
66
+ // (ft5336 / xpt2046) against nodes these adapters already own.
67
+ if (usage.touchController === 'xpt2046') {
68
+ m.set('CONFIG_SPI', 'y');
69
+ }
70
+ else {
71
+ m.set('CONFIG_I2C', 'y');
72
+ }
51
73
  }
52
74
  // PSRAM: enable the ESP SPIRAM driver + route malloc/heap to external RAM so
53
75
  // large canvas allocations (scroll viewports, lists) can use PSRAM instead of
@@ -237,6 +259,19 @@ export function resolveKconfigFragments(usage, debug) {
237
259
  m.set('CONFIG_SETTINGS', 'y');
238
260
  m.set('CONFIG_SETTINGS_ZMS', 'y');
239
261
  }
262
+ // Filesystem: littlefs on the storage partition. CONFIG_FILE_SYSTEM_LITTLEFS
263
+ // selects the littlefs backend but NOT FLASH/FLASH_MAP (the partition lookup
264
+ // needs them), so all three are set explicitly — same shape as the
265
+ // preferences/ZMS block. The overlay points the storage_partition at the FS
266
+ // (see dt-config/overlay.ts). NOTE: a program using BOTH fs.* and
267
+ // preferences.* shares the one storage_partition between littlefs and ZMS —
268
+ // dedicate separate partitions if both are needed (the manifest flags this).
269
+ if (usage.usesFS) {
270
+ m.set('CONFIG_FLASH', 'y');
271
+ m.set('CONFIG_FLASH_MAP', 'y');
272
+ m.set('CONFIG_FILE_SYSTEM', 'y');
273
+ m.set('CONFIG_FILE_SYSTEM_LITTLEFS', 'y');
274
+ }
240
275
  // usesUart: the board enables the console UART by default; the overlay (not
241
276
  // Kconfig) is where a UART node would be enabled, so no symbol here.
242
277
  // Random: <zephyr/random/random.h> sys_rand_get is backed by the random
@@ -25,11 +25,28 @@ export interface DisplayWiring {
25
25
  * backlight init is guarded by `DT_HAS_ALIAS` and compiles away. */
26
26
  backlightPin?: number;
27
27
  }
28
- /** Touch wiring from cuttlefish.config.ts (irq/resetPin/sda/scl). */
28
+ /** Touch wiring from cuttlefish.config.ts (irq/resetPin/sda/scl; cs for SPI
29
+ * resistive controllers, calibration for the XPT2046 DT binding). */
29
30
  export interface TouchWiring {
31
+ /** Touch controller kind — selects the DT node shape (FT6336U node on I2C0
32
+ * vs XPT2046 node on the display's SPI bus). Default 'ft6336u'. */
33
+ controller?: 'ft6336u' | 'xpt2046';
30
34
  irq?: number;
31
35
  resetPin?: number;
32
36
  sda?: number;
33
37
  scl?: number;
38
+ /** XPT2046 only: SPI CS pin (second cs-gpios entry on the panel's bus). */
39
+ cs?: number;
40
+ /** XPT2046 only: raw ADC calibration — feeds the binding's min-x/max-x/
41
+ * min-y/max-y (required props). Defaults span the full 12-bit range. */
42
+ calibration?: {
43
+ xMin: number;
44
+ xMax: number;
45
+ yMin: number;
46
+ yMax: number;
47
+ };
48
+ /** XPT2046 only: pen-detect Z1 threshold (binding's z-threshold). Resistive
49
+ * panels need a few hundred 12-bit counts; default 400. */
50
+ minPressure?: number;
34
51
  }
35
52
  export declare function generateOverlay(chip: ZephyrChipDescriptor, usage: KconfigUsage, display: ZephyrDisplayProfile | undefined, wiring?: DisplayWiring, touch?: TouchWiring): string;
@@ -10,6 +10,7 @@
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
  // ---------------------------------------------------------------------------
13
+ import { PANEL_CONTROLLER_DEFAULTS, panelControllerFor } from '../display/profiles.js';
13
14
  export function generateOverlay(chip, usage, display, wiring, touch) {
14
15
  const lines = [
15
16
  '/* Auto-generated by @typecad/framework-zephyr from cuttlefish.config.ts. */',
@@ -41,6 +42,11 @@ export function generateOverlay(chip, usage, display, wiring, touch) {
41
42
  for (const c of chip.uart.controllers)
42
43
  block(c.nodeLabel);
43
44
  }
45
+ // DAC: enable the chip's DAC device node when the program uses dac.*. The
46
+ // lowering references DEVICE_DT_GET(DT_NODELABEL(<dac.device>)).
47
+ if (usage.usesDac && chip.dac) {
48
+ block(chip.dac.device);
49
+ }
44
50
  if (display) {
45
51
  // Emit a full display DT node definition. Boards like the ESP32 devkit
46
52
  // have no display node in their base DT, so a bare `&display0 { status }`
@@ -48,14 +54,18 @@ export function generateOverlay(chip, usage, display, wiring, touch) {
48
54
  // controller with the panel's compatible string + dimensions. The pin
49
55
  // wiring (cs/dc/rst) uses ESP32 GPIO defaults from the demo config; a
50
56
  // real board overlay would carry its own binding.
51
- emitDisplayNode(lines, display, wiring);
57
+ // An XPT2046 on the same bus needs its CS as the second cs-gpios entry,
58
+ // so thread it into the display block (DT assignment replaces the whole
59
+ // property — both entries must be written together).
60
+ emitDisplayNode(lines, display, wiring, touch?.controller === 'xpt2046' ? (touch?.cs ?? DEFAULT_XPT2046_CS) : undefined);
52
61
  }
53
- // FT6336U touch on I2C defined when the program uses touch (the UI touch
54
- // adapter references DT_NODELABEL(ft6336u)). Same rationale: the bare devkit
55
- // has no such node. Gated on usesTouch (not usesI2c/chip.i2c) so it emits
56
- // even when the chip descriptor doesn't declare I2C controllers (ESP32).
62
+ // Touch — FT6336U capacitive on I2C (references DT_NODELABEL(ft6336u)) or
63
+ // XPT2046 resistive on the display's SPI bus (references
64
+ // DT_NODELABEL(xpt2046)). Same rationale as the display node: the bare
65
+ // devkit has no such node. Gated on usesTouch (not usesI2c/chip.i2c) so it
66
+ // emits even when the chip descriptor doesn't declare bus controllers.
57
67
  if (usage.usesTouch) {
58
- emitTouchNode(lines, touch);
68
+ emitTouchNode(lines, touch, display);
59
69
  }
60
70
  // Preferences (ZMS settings backend): point the settings subsystem at the
61
71
  // board's storage_partition. The backend looks for /chosen
@@ -86,11 +96,21 @@ export function generateOverlay(chip, usage, display, wiring, touch) {
86
96
  return lines.join('\n');
87
97
  }
88
98
  /**
89
- * Emit a display DT node definition. The node is attached to spi2 (the ESP32's
90
- * first user SPI controller) via a MIPI DBI SPI bridge. Pin wiring comes from
91
- * the display config (cs/dc/rst); defaults match the demo-st wiring if absent.
99
+ * Emit a display DT node definition. The node is attached to the profile's SPI
100
+ * controller (default spi2, the ESP32's first user SPI controller) via a MIPI
101
+ * DBI SPI bridge. Pin wiring comes from the display config (cs/dc/rst);
102
+ * defaults match the demo-st wiring if absent. The compatible string + node
103
+ * props come from the profile's panel controller (st7796s carries the required
104
+ * pgc/ngc gamma + madctl; ili9341's binding defaults everything else).
105
+ *
106
+ * spiTouchCs: when an XPT2046 SPI touch controller shares the bus, its CS is
107
+ * appended as the second cs-gpios entry (the touch node uses reg = <1>) — DT
108
+ * property assignment replaces, so both entries must be written together.
92
109
  */
93
- function emitDisplayNode(lines, display, wiring) {
110
+ function emitDisplayNode(lines, display, wiring, spiTouchCs) {
111
+ const bus = display.busLabel ?? 'spi2';
112
+ const controller = panelControllerFor(display);
113
+ const compatible = display.dtCompatible ?? PANEL_CONTROLLER_DEFAULTS[controller].dtCompatible;
94
114
  const dc = wiring?.dc ?? 17;
95
115
  const rst = wiring?.rst ?? 16;
96
116
  const cs = wiring?.cs ?? 5;
@@ -111,7 +131,9 @@ function emitDisplayNode(lines, display, wiring) {
111
131
  // macros are used instead of the named SPIM2_*_GPIOxx tokens because the
112
132
  // bindings header omits GPIOs 22-25 from those lists.
113
133
  if (sck !== undefined && mosi !== undefined) {
114
- lines.push('&spim2_default {');
134
+ // 'spi2' → pinctrl group 'spim2_default' (ESP32 SPI-master naming).
135
+ const pinctrlGroup = bus.replace(/^spi(\d)$/, 'spim$1') + '_default';
136
+ lines.push(`&${pinctrlGroup} {`);
115
137
  lines.push(' group1 {');
116
138
  lines.push(` pinmux = <ESP32_PINMUX(${miso ?? 19}, ESP_FSPIQ_IN, ESP_NOSIG)>,`);
117
139
  lines.push(` <ESP32_PINMUX(${sck}, ESP_NOSIG, ESP_FSPICLK_OUT)>,`);
@@ -128,9 +150,9 @@ function emitDisplayNode(lines, display, wiring) {
128
150
  lines.push(' status = "okay";');
129
151
  lines.push('};');
130
152
  lines.push('');
131
- lines.push('&spi2 {');
153
+ lines.push(`&${bus} {`);
132
154
  lines.push(' status = "okay";');
133
- lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>;`);
155
+ lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>${spiTouchCs !== undefined ? `, <&${gpioController(spiTouchCs)} ${spiTouchCs} GPIO_ACTIVE_LOW>` : ''};`);
134
156
  // Enable GDMA for the SPI2 host. The ESP32 SPI driver uses DMA only when
135
157
  // dma-enabled is set AND dmas wires tx/rx channels to the GDMA controller;
136
158
  // without it, transfers run PIO through the 64-byte FIFO (~4MHz effective at
@@ -146,25 +168,37 @@ function emitDisplayNode(lines, display, wiring) {
146
168
  lines.push('/ {');
147
169
  lines.push(' mipi_dbi: mipi-dbi {');
148
170
  lines.push(' compatible = "zephyr,mipi-dbi-spi";');
149
- lines.push(' spi-dev = <&spi2>;');
171
+ lines.push(` spi-dev = <&${bus}>;`);
150
172
  lines.push(` dc-gpios = <&${gpioController(dc)} ${dc} GPIO_ACTIVE_HIGH>;`);
151
173
  lines.push(` reset-gpios = <&${gpioController(rst)} ${rst} GPIO_ACTIVE_LOW>;`);
152
174
  lines.push(' write-only;');
153
175
  lines.push(' #address-cells = <1>;');
154
176
  lines.push(' #size-cells = <0>;');
155
177
  lines.push(` ${display.dtLabel}: display@0 {`);
156
- lines.push(' compatible = "sitronix,st7796s";');
178
+ lines.push(` compatible = "${compatible}";`);
157
179
  lines.push(' reg = <0>;');
158
180
  lines.push(` mipi-max-frequency = <${freq}>;`);
159
181
  lines.push(' mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE";');
182
+ // Required by the lcd-controller binding (Zephyr 4.x): 0 = RGB565,
183
+ // matching upstream ILI9341 boards (esp_wrover_kit) and the C++
184
+ // runtime, which drives these SPI TFTs as RGB565.
185
+ lines.push(' pixel-format = <0>;');
160
186
  lines.push(` width = <${nativeW}>;`);
161
187
  lines.push(` height = <${nativeH}>;`);
162
- // MADCTL: rotation 1 (landscape, MV=1) + BGR bit, matching the adapter's
163
- // direct-drive init (0x28). The DT copy keeps the stock driver's init
164
- // consistent if it is ever exercised.
165
- lines.push(' madctl = <0x28>;');
166
- lines.push(' pgc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
167
- lines.push(' ngc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
188
+ if (controller === 'st7796s') {
189
+ // MADCTL: rotation 1 (landscape, MV=1) + BGR bit, matching the adapter's
190
+ // direct-drive init (0x28). The DT copy keeps the stock driver's init
191
+ // consistent if it is ever exercised.
192
+ lines.push(' madctl = <0x28>;');
193
+ lines.push(' pgc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
194
+ lines.push(' ngc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
195
+ }
196
+ else {
197
+ // ILI9341: the ilitek,ili9341 binding carries defaults for every register
198
+ // (gamma, power, porch) and expresses orientation via `rotation` (degrees)
199
+ // instead of a raw MADCTL — no panel-specific props are required.
200
+ lines.push(` rotation = <${display.rotation ?? 0}>;`);
201
+ }
168
202
  lines.push(' };');
169
203
  lines.push(' };');
170
204
  lines.push('};');
@@ -190,13 +224,33 @@ function emitDisplayNode(lines, display, wiring) {
190
224
  lines.push('');
191
225
  }
192
226
  }
227
+ /** Default XPT2046 CS/IRQ pins (ESP32-S3 GPIOs clear of the demo-st display
228
+ * wiring: 5/17/16/15 and the remuxed SPI pins). Config values override. */
229
+ const DEFAULT_XPT2046_CS = 6;
230
+ const DEFAULT_XPT2046_IRQ = 7;
193
231
  /**
194
- * Emit an FT6336U touch DT node on the first I2C controller. The node address
195
- * is the FT6336U default (0x38). The UI touch adapter references
196
- * DT_NODELABEL(ft6336u). Pin wiring (irq/resetPin/sda/scl) comes from the
197
- * touch config; defaults match the demo-st wiring if absent.
232
+ * Emit the touch DT node for the configured controller.
233
+ *
234
+ * FT6336U (capacitive, I2C): node on i2c0 at the FT6336U default address
235
+ * (0x38); the C++ touch adapter reads it via i2c_write_read_dt.
236
+ *
237
+ * XPT2046 (resistive, SPI): node on the display's SPI bus as CS index 1. The
238
+ * in-tree xptek,xpt2046 binding (drivers/input) is register-matched for the
239
+ * raw SPI access — CONFIG_INPUT stays off, so the in-tree input driver does
240
+ * not build and the adapter owns the chip (same pattern as FT6336U reusing
241
+ * the ft5336 binding). The binding requires int-gpios, touchscreen-size-*,
242
+ * and min/max calibration props, so defaults are filled for anything the
243
+ * config omits.
198
244
  */
199
- function emitTouchNode(lines, touch) {
245
+ function emitTouchNode(lines, touch, display) {
246
+ if (touch?.controller === 'xpt2046') {
247
+ emitXpt2046Node(lines, touch, display);
248
+ return;
249
+ }
250
+ emitFt6336uNode(lines, touch);
251
+ }
252
+ /** FT6336U capacitive touch node on the first I2C controller. */
253
+ function emitFt6336uNode(lines, touch) {
200
254
  const irq = touch?.irq ?? 15;
201
255
  const resetPin = touch?.resetPin;
202
256
  const sda = touch?.sda;
@@ -244,3 +298,47 @@ function emitTouchNode(lines, touch) {
244
298
  lines.push('};');
245
299
  lines.push('');
246
300
  }
301
+ /** XPT2046 resistive touch node on the display's SPI bus (CS index 1). */
302
+ function emitXpt2046Node(lines, touch, display) {
303
+ const bus = display?.busLabel ?? 'spi2';
304
+ const cs = touch.cs ?? DEFAULT_XPT2046_CS;
305
+ const irq = touch.irq ?? DEFAULT_XPT2046_IRQ;
306
+ const cal = touch.calibration;
307
+ // ESP32-S3 GPIOs 0-31 are on gpio0, 32-48 on gpio1.
308
+ const gpioController = (pin) => pin <= 31 ? 'gpio0' : 'gpio1';
309
+ const zThreshold = touch.minPressure ?? 400;
310
+ // touchscreen-size-* describe the panel the touch layer sits on (the display
311
+ // profile's effective size); a touch-only build falls back to the 12-bit
312
+ // full-scale range so the binding's required props still resolve.
313
+ const sizeX = display?.width ?? cal?.xMax ?? 320;
314
+ const sizeY = display?.height ?? cal?.yMax ?? 240;
315
+ // The display block already wrote cs-gpios with both entries (its CS at
316
+ // index 0, the touch CS at index 1). When there is no display block, enable
317
+ // the bus here with the touch CS as the only entry.
318
+ if (!display) {
319
+ lines.push(`&${bus} {`);
320
+ lines.push(' status = "okay";');
321
+ lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>;`);
322
+ lines.push('};');
323
+ lines.push('');
324
+ }
325
+ lines.push(`&${bus} {`);
326
+ lines.push(' xpt2046: xpt2046@1 {');
327
+ lines.push(' compatible = "xptek,xpt2046";');
328
+ lines.push(' reg = <1>;');
329
+ // The XPT2046 datasheet max SPI clock is 2.5MHz — the panel bus may run at
330
+ // 80MHz, but this node's spi-max-frequency gates only its own transactions
331
+ // (the adapter's SPI_DT_SPEC picks it up).
332
+ lines.push(' spi-max-frequency = <2500000>;');
333
+ lines.push(` int-gpios = <&${gpioController(irq)} ${irq} GPIO_ACTIVE_LOW>;`);
334
+ lines.push(` touchscreen-size-x = <${sizeX}>;`);
335
+ lines.push(` touchscreen-size-y = <${sizeY}>;`);
336
+ lines.push(` min-x = <${cal?.xMin ?? 0}>;`);
337
+ lines.push(` max-x = <${cal?.xMax ?? 4095}>;`);
338
+ lines.push(` min-y = <${cal?.yMin ?? 0}>;`);
339
+ lines.push(` max-y = <${cal?.yMax ?? 4095}>;`);
340
+ lines.push(` z-threshold = <${zThreshold}>;`);
341
+ lines.push(' };');
342
+ lines.push('};');
343
+ lines.push('');
344
+ }
@@ -1,40 +1,40 @@
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;
2
19
  schemaVersion: 1;
3
20
  frameworkId: string;
4
- packageName: string;
5
21
  canonical: boolean;
6
22
  displayName: string;
7
23
  description: string;
8
24
  implementationMode: "from-scratch" | "extends-canonical" | "extends-other";
9
25
  entrypoint: {
26
+ sourceExtension: "cpp" | "ino" | "cc" | "h";
10
27
  entrypointFunctionName: string;
11
28
  requiresLoopFunction: boolean;
12
- sourceExtension: "ino" | "cc" | "cpp" | "h";
13
29
  generateHeaderFile: boolean;
14
30
  overrideBaseName?: string | undefined;
15
31
  outputSubdirectory?: string | undefined;
16
32
  customBridgeShim?: string | undefined;
17
33
  };
18
34
  profile: {
19
- targets: string[];
20
35
  forcedIncludes: string[];
21
36
  symbolAliases: Record<string, string>;
22
- };
23
- hal: {
24
- [x: string]: {
25
- supported: boolean;
26
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
27
- partialCoverage: boolean;
28
- unsupportedReason?: string | undefined;
29
- };
30
- raw?: unknown;
31
- } & {
32
- [k: string]: {
33
- supported: boolean;
34
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
35
- partialCoverage: boolean;
36
- unsupportedReason?: string | undefined;
37
- };
37
+ targets: string[];
38
38
  };
39
39
  polyfills: {
40
40
  emitted: {
@@ -54,16 +54,17 @@ declare const _default: {
54
54
  compile: boolean;
55
55
  upload: boolean;
56
56
  monitor: boolean;
57
+ debug?: boolean | undefined;
57
58
  };
58
59
  reexportedFrom?: string | undefined;
59
60
  };
60
61
  typeEmission: {
61
- normalizeCppType: boolean;
62
- mathHeader: "none" | "<math.h>" | "<Arduino.h>";
62
+ needsIostream: boolean;
63
63
  needsStdString: boolean;
64
64
  needsStdVector: boolean;
65
- needsIostream: boolean;
66
65
  needsStdFunction: boolean;
66
+ mathHeader: "none" | "<math.h>" | "<Arduino.h>";
67
+ normalizeCppType: boolean;
67
68
  stdlibSupport: {
68
69
  hasVector: boolean;
69
70
  hasString: boolean;
@@ -79,6 +80,12 @@ declare const _default: {
79
80
  hardwareTestGroups: string[];
80
81
  halResolutionTests: string[];
81
82
  };
83
+ doctor?: {
84
+ available: boolean;
85
+ } | undefined;
86
+ licenses?: {
87
+ available: boolean;
88
+ } | undefined;
82
89
  basedOn?: string | undefined;
83
90
  inheritsStrategyId?: string | undefined;
84
91
  libraryResolution?: {
@@ -88,12 +95,6 @@ declare const _default: {
88
95
  tryGenerateLibDecl: boolean;
89
96
  reexportedFrom?: string | undefined;
90
97
  } | undefined;
91
- doctor?: {
92
- available: boolean;
93
- } | undefined;
94
- licenses?: {
95
- available: boolean;
96
- } | undefined;
97
98
  compat?: {
98
99
  zephyr?: string | undefined;
99
100
  } | undefined;