@typecad/framework-zephyr 1.0.0-alpha.18 → 1.0.0-alpha.20
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/dist/audit.d.ts +111 -0
- package/dist/audit.js +416 -0
- package/dist/boardgen.js +63 -5
- package/dist/chips/resolve.js +20 -0
- package/dist/chips/types.d.ts +21 -0
- package/dist/display/bindings.d.ts +55 -0
- package/dist/display/bindings.js +316 -0
- package/dist/display/gfx.d.ts +2 -3
- package/dist/display/gfx.js +166 -154
- package/dist/display/index.js +20 -1
- package/dist/display/mipi-dbi-host.d.ts +9 -0
- package/dist/display/mipi-dbi-host.js +174 -0
- package/dist/display/profiles.d.ts +109 -4
- package/dist/display/profiles.js +270 -7
- package/dist/display/touch-adapter.js +118 -48
- package/dist/display/ui-adapter-eink.d.ts +2 -0
- package/dist/display/ui-adapter-eink.js +4 -0
- package/dist/display/ui-adapter-gray.d.ts +8 -0
- package/dist/display/ui-adapter-gray.js +170 -0
- package/dist/display/ui-adapter-mono.d.ts +13 -0
- package/dist/display/ui-adapter-mono.js +230 -0
- package/dist/display/ui-adapter-native.d.ts +10 -0
- package/dist/display/ui-adapter-native.js +295 -0
- package/dist/display/ui-adapter-shared.d.ts +11 -0
- package/dist/display/ui-adapter-shared.js +122 -0
- package/dist/display/ui-adapter.js +53 -101
- package/dist/dt-config/kconfig.d.ts +62 -1
- package/dist/dt-config/kconfig.js +123 -26
- package/dist/dt-config/overlay.d.ts +13 -0
- package/dist/dt-config/overlay.js +431 -16
- package/dist/framework.manifest.d.ts +9 -3
- package/dist/framework.manifest.js +123 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1
- package/dist/licenses.js +5 -84
- package/dist/lowering/can.d.ts +25 -0
- package/dist/lowering/can.js +97 -0
- package/dist/lowering/clock.d.ts +17 -0
- package/dist/lowering/clock.js +58 -0
- package/dist/lowering/hid.d.ts +27 -0
- package/dist/lowering/hid.js +244 -0
- package/dist/lowering/i2c.d.ts +8 -0
- package/dist/lowering/i2c.js +140 -0
- package/dist/lowering/i2s.d.ts +27 -0
- package/dist/lowering/i2s.js +98 -0
- package/dist/lowering/index.d.ts +9 -1
- package/dist/lowering/index.js +25 -1
- package/dist/lowering/matrix.d.ts +15 -0
- package/dist/lowering/matrix.js +63 -0
- package/dist/lowering/power.d.ts +10 -0
- package/dist/lowering/power.js +35 -0
- package/dist/lowering/pwm.js +25 -0
- package/dist/lowering/sensor.d.ts +2 -2
- package/dist/lowering/sensor.js +8 -4
- package/dist/lowering/strip.d.ts +16 -0
- package/dist/lowering/strip.js +70 -0
- package/dist/lowering/thread.js +5 -1
- package/dist/lowering/trace.d.ts +44 -0
- package/dist/lowering/trace.js +239 -0
- package/dist/lowering/uart.js +6 -1
- package/dist/lowering/usb.d.ts +3 -1
- package/dist/lowering/usb.js +6 -3
- package/dist/sbom.d.ts +181 -0
- package/dist/sbom.js +901 -0
- package/dist/strategy.d.ts +60 -2
- package/dist/strategy.js +485 -25
- package/dist/toolchain/index.d.ts +12 -1
- package/dist/toolchain/index.js +115 -27
- package/dist/toolchain/scaffold.d.ts +4 -1
- package/dist/toolchain/scaffold.js +67 -3
- package/dist/west-inventory.d.ts +25 -0
- package/dist/west-inventory.js +97 -0
- package/package.json +6 -6
- package/src/audit.ts +529 -0
- package/src/boardgen.ts +54 -5
- package/src/chips/resolve.ts +21 -0
- package/src/chips/types.ts +576 -567
- package/src/display/bindings.ts +347 -0
- package/src/display/gfx.ts +318 -306
- package/src/display/index.ts +87 -70
- package/src/display/mipi-dbi-host.ts +183 -0
- package/src/display/profiles.ts +458 -139
- package/src/display/touch-adapter.ts +274 -204
- package/src/display/ui-adapter-eink.ts +13 -0
- package/src/display/ui-adapter-gray.ts +178 -0
- package/src/display/ui-adapter-mono.ts +238 -0
- package/src/display/ui-adapter-native.ts +304 -0
- package/src/display/ui-adapter-shared.ts +125 -0
- package/src/display/ui-adapter.ts +51 -100
- package/src/dt-config/kconfig.ts +647 -511
- package/src/dt-config/overlay.ts +433 -16
- package/src/framework.manifest.ts +131 -10
- package/src/index.ts +11 -1
- package/src/licenses.ts +5 -84
- package/src/lowering/can.ts +140 -0
- package/src/lowering/clock.ts +91 -0
- package/src/lowering/hid.ts +261 -0
- package/src/lowering/i2c.ts +146 -0
- package/src/lowering/i2s.ts +143 -0
- package/src/lowering/index.ts +18 -1
- package/src/lowering/matrix.ts +70 -0
- package/src/lowering/power.ts +41 -0
- package/src/lowering/pwm.ts +192 -167
- package/src/lowering/sensor.ts +159 -155
- package/src/lowering/strip.ts +81 -0
- package/src/lowering/thread.ts +5 -1
- package/src/lowering/trace.ts +270 -0
- package/src/lowering/uart.ts +6 -1
- package/src/lowering/usb.ts +224 -221
- package/src/sbom.ts +1117 -0
- package/src/strategy.ts +440 -25
- package/src/toolchain/index.ts +117 -28
- package/src/toolchain/scaffold.ts +62 -3
- package/src/west-inventory.ts +102 -0
package/src/strategy.ts
CHANGED
|
@@ -187,6 +187,57 @@ function collectPwmOverrideSpecs(program: ProgramIR | undefined): { pin: number;
|
|
|
187
187
|
* -Wunused-variable (Zephyr builds with warnings-as-errors), the same
|
|
188
188
|
* hygiene collectUsedPins enforces for per-channel ADC/PWM state.
|
|
189
189
|
*/
|
|
190
|
+
/**
|
|
191
|
+
* Strips the program drives, keyed by SPI controller index → the
|
|
192
|
+
* construction chain length (the buffer size). Collected from strip.* ops.
|
|
193
|
+
*/
|
|
194
|
+
/** Which HID protocol the program drives — 'mixed' when verbs from BOTH
|
|
195
|
+
* classes appear (one interface per program is the v1 ceiling; the caller
|
|
196
|
+
* turns mixed into a #error so the build names it clearly). */
|
|
197
|
+
function collectHidProtocol(program: ProgramIR | undefined): 'keyboard' | 'mouse' | 'mixed' | undefined {
|
|
198
|
+
if (!program) return undefined;
|
|
199
|
+
let kb = false;
|
|
200
|
+
let mouse = false;
|
|
201
|
+
const visit = (node: unknown): void => {
|
|
202
|
+
if (!node || typeof node !== 'object') return;
|
|
203
|
+
const n = node as Record<string, unknown>;
|
|
204
|
+
const op = n.operation;
|
|
205
|
+
if (op && typeof op === 'object') {
|
|
206
|
+
const name = (op as Record<string, unknown>).operation;
|
|
207
|
+
if (typeof name === 'string') {
|
|
208
|
+
if (name.startsWith('hid.kb_')) kb = true;
|
|
209
|
+
if (name.startsWith('hid.mouse_')) mouse = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const v of Object.values(n)) if (v && typeof v === 'object') visit(v);
|
|
213
|
+
};
|
|
214
|
+
visit(program);
|
|
215
|
+
if (kb && mouse) return 'mixed';
|
|
216
|
+
return kb ? 'keyboard' : mouse ? 'mouse' : undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function collectStrips(program: ProgramIR | undefined): Map<number, number> | undefined {
|
|
220
|
+
if (!program) return undefined;
|
|
221
|
+
const strips = new Map<number, number>();
|
|
222
|
+
const visit = (node: unknown): void => {
|
|
223
|
+
if (!node || typeof node !== 'object') return;
|
|
224
|
+
const n = node as Record<string, unknown>;
|
|
225
|
+
const op = n.operation;
|
|
226
|
+
if (op && typeof op === 'object') {
|
|
227
|
+
const o = op as Record<string, unknown>;
|
|
228
|
+
if (o.operation === 'strip.set_pixel' || o.operation === 'strip.fill' || o.operation === 'strip.show') {
|
|
229
|
+
const m = String(o.bus ?? '').match(/(\d+)$/);
|
|
230
|
+
const idx = m ? parseInt(m[1]!, 10) : 0;
|
|
231
|
+
const count = Number(o.count ?? 1) || 1;
|
|
232
|
+
strips.set(idx, Math.max(strips.get(idx) ?? 0, count));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const v of Object.values(n)) if (v && typeof v === 'object') visit(v);
|
|
236
|
+
};
|
|
237
|
+
visit(program);
|
|
238
|
+
return strips;
|
|
239
|
+
}
|
|
240
|
+
|
|
190
241
|
function collectUsedBusIndices(program: ProgramIR | undefined): { i2c: Set<number>; spi: Set<number>; uart: Set<number> } | undefined {
|
|
191
242
|
if (!program) return undefined;
|
|
192
243
|
const indices = { i2c: new Set<number>(), spi: new Set<number>(), uart: new Set<number>() };
|
|
@@ -228,9 +279,9 @@ function collectUsedBusIndices(program: ProgramIR | undefined): { i2c: Set<numbe
|
|
|
228
279
|
* constructed sensors emit state, and only sensors whose ops were emitted
|
|
229
280
|
* get a DT node.
|
|
230
281
|
*/
|
|
231
|
-
export function collectSensors(program: ProgramIR | undefined): Map<string, { part: string; bus: string; port: number | string; busKind: string; spiHz: number | string; spiMode: number | string; alertPin: number | string }> | undefined {
|
|
282
|
+
export function collectSensors(program: ProgramIR | undefined): Map<string, { part: string; bus: string; port: number | string; busKind: string; spiHz: number | string; spiMode: number | string; alertPin: number | string; resolution: number | string }> | undefined {
|
|
232
283
|
if (!program) return undefined;
|
|
233
|
-
const sensors = new Map<string, { part: string; bus: string; port: number | string; busKind: string; spiHz: number | string; spiMode: number | string; alertPin: number | string }>();
|
|
284
|
+
const sensors = new Map<string, { part: string; bus: string; port: number | string; busKind: string; spiHz: number | string; spiMode: number | string; alertPin: number | string; resolution: number | string }>();
|
|
234
285
|
const visit = (node: unknown): void => {
|
|
235
286
|
if (!node || typeof node !== 'object') return;
|
|
236
287
|
const n = node as Record<string, unknown>;
|
|
@@ -245,8 +296,11 @@ export function collectSensors(program: ProgramIR | undefined): Map<string, { pa
|
|
|
245
296
|
const spiHz = (o.spiHz as number | string) ?? 0;
|
|
246
297
|
const spiMode = (o.spiMode as number | string) ?? 0;
|
|
247
298
|
const alertPin = (o.alertPin as number | string) ?? -1;
|
|
299
|
+
const resolution = (o.resolution as number | string) ?? 12;
|
|
248
300
|
const key = `${part}|${bus}|${port}|${busKind}`;
|
|
249
|
-
|
|
301
|
+
const existing = sensors.get(key);
|
|
302
|
+
if (!existing) sensors.set(key, { part, bus, port, busKind, spiHz, spiMode, alertPin, resolution });
|
|
303
|
+
else if (o.resolution !== undefined) existing.resolution = resolution;
|
|
250
304
|
}
|
|
251
305
|
}
|
|
252
306
|
for (const v of Object.values(n)) {
|
|
@@ -259,6 +313,140 @@ export function collectSensors(program: ProgramIR | undefined): Map<string, { pa
|
|
|
259
313
|
return sensors;
|
|
260
314
|
}
|
|
261
315
|
|
|
316
|
+
/**
|
|
317
|
+
* The I2S construction facts the program's ops carry (the shim's slab and
|
|
318
|
+
* init helpers size from these) — first-seen wins, keyed by instance.
|
|
319
|
+
*/
|
|
320
|
+
export function collectI2s(program: ProgramIR | undefined): Map<number, { hz: number; channels: number; bits: number; blockFrames: number }> | undefined {
|
|
321
|
+
if (!program) return undefined;
|
|
322
|
+
const out = new Map<number, { hz: number; channels: number; bits: number; blockFrames: number }>();
|
|
323
|
+
const visit = (node: unknown): void => {
|
|
324
|
+
if (!node || typeof node !== 'object') return;
|
|
325
|
+
const n = node as Record<string, unknown>;
|
|
326
|
+
const op = n.operation;
|
|
327
|
+
if (op && typeof op === 'object') {
|
|
328
|
+
const o = op as Record<string, unknown>;
|
|
329
|
+
const name = o.operation;
|
|
330
|
+
if (name === 'i2s.write' || name === 'i2s.read') {
|
|
331
|
+
// Raw instance (no clamp): the strategy loop folds usage through
|
|
332
|
+
// lowerI2s's clamp, so the facts must stay keyed by what the ops
|
|
333
|
+
// actually carry.
|
|
334
|
+
const inst = Number(o.instance ?? 0);
|
|
335
|
+
if (!out.has(inst)) {
|
|
336
|
+
out.set(inst, {
|
|
337
|
+
hz: Number(o.hz ?? 16000),
|
|
338
|
+
channels: Number(o.channels ?? 2),
|
|
339
|
+
bits: Number(o.bits ?? 16),
|
|
340
|
+
blockFrames: Number(o.blockFrames ?? 64),
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const v of Object.values(n)) {
|
|
346
|
+
if (Array.isArray(v)) { for (const item of v) visit(item); }
|
|
347
|
+
else if (v && typeof v === 'object') visit(v);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
visit(program);
|
|
351
|
+
visitResolvedHalOps(program, visit);
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Which can / i2s / clock verbs the program's ops actually use — the init
|
|
357
|
+
* blocks emit only the state those verbs reference (Zephyr's -Werror turns
|
|
358
|
+
* an unreferenced static function/variable into a build failure: a
|
|
359
|
+
* send-only CAN program must not carry the rx trampoline, a now()-only
|
|
360
|
+
* Clock program must not carry the set() conversion helper). Returns
|
|
361
|
+
* undefined when there is no program to scan (probe flows) — callers then
|
|
362
|
+
* fall back to emitting every controller with the full state set.
|
|
363
|
+
*/
|
|
364
|
+
export function collectVerbUsage(program: ProgramIR | undefined): {
|
|
365
|
+
can: Map<number, { begin: boolean; send: boolean; onReceive: boolean }>;
|
|
366
|
+
i2s: Map<number, { write: boolean; read: boolean; readAt: boolean }>;
|
|
367
|
+
clock: { set: boolean; now: boolean };
|
|
368
|
+
} | undefined {
|
|
369
|
+
if (!program) return undefined;
|
|
370
|
+
const out = {
|
|
371
|
+
can: new Map<number, { begin: boolean; send: boolean; onReceive: boolean }>(),
|
|
372
|
+
i2s: new Map<number, { write: boolean; read: boolean; readAt: boolean }>(),
|
|
373
|
+
clock: { set: false, now: false },
|
|
374
|
+
};
|
|
375
|
+
const canFlags = (inst: number) => {
|
|
376
|
+
let f = out.can.get(inst);
|
|
377
|
+
if (!f) { f = { begin: false, send: false, onReceive: false }; out.can.set(inst, f); }
|
|
378
|
+
return f;
|
|
379
|
+
};
|
|
380
|
+
const i2sFlags = (inst: number) => {
|
|
381
|
+
let f = out.i2s.get(inst);
|
|
382
|
+
if (!f) { f = { write: false, read: false, readAt: false }; out.i2s.set(inst, f); }
|
|
383
|
+
return f;
|
|
384
|
+
};
|
|
385
|
+
const visit = (node: unknown): void => {
|
|
386
|
+
if (!node || typeof node !== 'object') return;
|
|
387
|
+
const n = node as Record<string, unknown>;
|
|
388
|
+
const op = n.operation;
|
|
389
|
+
if (op && typeof op === 'object') {
|
|
390
|
+
const o = op as Record<string, unknown>;
|
|
391
|
+
const name = o.operation;
|
|
392
|
+
const inst = Number(o.instance ?? 0);
|
|
393
|
+
switch (name) {
|
|
394
|
+
case 'can.begin': canFlags(inst).begin = true; break;
|
|
395
|
+
case 'can.send': canFlags(inst).send = true; break;
|
|
396
|
+
case 'can.on_receive': canFlags(inst).onReceive = true; break;
|
|
397
|
+
case 'i2s.write': i2sFlags(inst).write = true; break;
|
|
398
|
+
case 'i2s.read': i2sFlags(inst).read = true; break;
|
|
399
|
+
case 'i2s.read_at': i2sFlags(inst).readAt = true; break;
|
|
400
|
+
case 'clock.set': out.clock.set = true; break;
|
|
401
|
+
case 'clock.now': out.clock.now = true; break;
|
|
402
|
+
default: break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
for (const v of Object.values(n)) {
|
|
406
|
+
if (Array.isArray(v)) { for (const item of v) visit(item); }
|
|
407
|
+
else if (v && typeof v === 'object') visit(v);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
visit(program);
|
|
411
|
+
visitResolvedHalOps(program, visit);
|
|
412
|
+
return out;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Collect the distinct I2C responders the program's ops reference
|
|
417
|
+
* (i2c.resp_*), keyed `${bus}|${address}` — one target-mode address per
|
|
418
|
+
* constructed responder. Mirrors collectSpiTargets: the construction facts
|
|
419
|
+
* (rx/tx sizes) ride every op, so the first seen wins and the state block
|
|
420
|
+
* cannot drift from the call sites.
|
|
421
|
+
*/
|
|
422
|
+
export function collectI2cResponders(program: ProgramIR | undefined): Map<string, { bus: string; address: number; rx: number; tx: number }> | undefined {
|
|
423
|
+
if (!program) return undefined;
|
|
424
|
+
const responders = new Map<string, { bus: string; address: number; rx: number; tx: number }>();
|
|
425
|
+
const visit = (node: unknown): void => {
|
|
426
|
+
if (!node || typeof node !== 'object') return;
|
|
427
|
+
const n = node as Record<string, unknown>;
|
|
428
|
+
const op = n.operation;
|
|
429
|
+
if (op && typeof op === 'object') {
|
|
430
|
+
const o = op as Record<string, unknown>;
|
|
431
|
+
if (String(o.operation).startsWith('i2c.resp_')) {
|
|
432
|
+
const bus = String(o.bus ?? '');
|
|
433
|
+
const address = Number(o.address ?? 0);
|
|
434
|
+
const key = `${bus}|${address}`;
|
|
435
|
+
if (!responders.has(key)) {
|
|
436
|
+
responders.set(key, { bus, address, rx: Number(o.rx ?? 64), tx: Number(o.tx ?? 32) });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
for (const v of Object.values(n)) {
|
|
441
|
+
if (Array.isArray(v)) { for (const item of v) visit(item); }
|
|
442
|
+
else if (v && typeof v === 'object') visit(v);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
visit(program);
|
|
446
|
+
visitResolvedHalOps(program, visit);
|
|
447
|
+
return responders;
|
|
448
|
+
}
|
|
449
|
+
|
|
262
450
|
/**
|
|
263
451
|
* Collect the distinct thin SPI targets the program's ops reference
|
|
264
452
|
* (spi.transceive / spi.dev_write), keyed `${bus}|${cs}`. Mirrors
|
|
@@ -363,10 +551,18 @@ import { pwmInitLines } from './lowering/pwm.js';
|
|
|
363
551
|
import { dacInitLines } from './lowering/dac.js';
|
|
364
552
|
import { fsInitLines } from './lowering/fs.js';
|
|
365
553
|
import { hwtimerInitLines } from './lowering/hwtimer.js';
|
|
366
|
-
import { i2cInitLines } from './lowering/i2c.js';
|
|
554
|
+
import { i2cInitLines, i2cResponderStateLines } from './lowering/i2c.js';
|
|
555
|
+
import { parseControllerIndex } from './lowering/util.js';
|
|
556
|
+
import { stripInitLines } from './lowering/strip.js';
|
|
557
|
+
import { hidInitLines } from './lowering/hid.js';
|
|
558
|
+
import { matrixInitLines } from './lowering/matrix.js';
|
|
559
|
+
import { clockInitLines } from './lowering/clock.js';
|
|
560
|
+
import { canInitLines } from './lowering/can.js';
|
|
561
|
+
import { i2sInitLines } from './lowering/i2s.js';
|
|
367
562
|
import { sensorStateLines } from './lowering/sensor.js';
|
|
368
563
|
import { spiTargetStateLines } from './lowering/spi.js';
|
|
369
564
|
import { threadStateLines } from './lowering/thread.js';
|
|
565
|
+
import { traceHeartbeatLines, uiFrameTraceLines, clampTraceIntervalMs } from './lowering/trace.js';
|
|
370
566
|
import { uartRingStateLines } from './lowering/uart.js';
|
|
371
567
|
import { spiInitLines } from './lowering/spi.js';
|
|
372
568
|
import { uartInitLines } from './lowering/uart.js';
|
|
@@ -405,7 +601,8 @@ import { randomInitLines } from './lowering/random.js';
|
|
|
405
601
|
import { generateStaticAsyncRuntime } from '@typecad/cuttlefish/api/shared';
|
|
406
602
|
import { resolveZephyrDisplayOp, newDisplayState, type DisplayState } from './display/index.js';
|
|
407
603
|
import { buildDisplayRuntime } from './display/gfx.js';
|
|
408
|
-
import { ZEPHYR_DISPLAY_PROFILES, BUILT_IN_PROFILES } from './display/profiles.js';
|
|
604
|
+
import { ZEPHYR_DISPLAY_PROFILES, BUILT_IN_PROFILES, isDtCompatible, isMonoDisplay, isGrayDisplay, isEinkDisplay } from './display/profiles.js';
|
|
605
|
+
import { listBoundDisplayCompatibles } from './display/bindings.js';
|
|
409
606
|
import { zephyrDisplayAdapterGenerator } from './display/ui-adapter.js';
|
|
410
607
|
import { zephyrTouchAdapter } from './display/touch-adapter.js';
|
|
411
608
|
|
|
@@ -496,6 +693,21 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
496
693
|
if (uses('usesI2C')) inc.push('<zephyr/drivers/i2c.h>');
|
|
497
694
|
if (uses('usesSensor')) inc.push('<zephyr/drivers/sensor.h>');
|
|
498
695
|
if (uses('usesSPI')) inc.push('<zephyr/drivers/spi.h>');
|
|
696
|
+
if (uses('usesStrip')) inc.push('<zephyr/drivers/led_strip.h>');
|
|
697
|
+
if (uses('usesHid')) {
|
|
698
|
+
inc.push('<zephyr/usb/class/hid.h>');
|
|
699
|
+
inc.push('<zephyr/usb/class/usbd_hid.h>');
|
|
700
|
+
inc.push('<zephyr/usb/usbd.h>');
|
|
701
|
+
inc.push('<zephyr/drivers/usb/usb_buf.h>');
|
|
702
|
+
}
|
|
703
|
+
if (uses('usesMatrix')) inc.push('<zephyr/input/input.h>');
|
|
704
|
+
if (uses('usesPower')) inc.push('<zephyr/sys/poweroff.h>');
|
|
705
|
+
// esp_sleep.h exists only on the espressif HAL — gate on the harvested
|
|
706
|
+
// RTC wake-timer fact (the includes' uses() reads op flags, not src).
|
|
707
|
+
if (uses('usesPower') && chip.powerWakeTimer) inc.push('<esp_sleep.h>');
|
|
708
|
+
if (uses('usesClock')) inc.push('<zephyr/drivers/rtc.h>');
|
|
709
|
+
if (uses('usesCan')) inc.push('<zephyr/drivers/can.h>');
|
|
710
|
+
if (uses('usesI2s')) inc.push('<zephyr/drivers/i2s.h>');
|
|
499
711
|
if (uses('usesUart')) inc.push('<zephyr/drivers/uart.h>');
|
|
500
712
|
// USB CDC serial: the class instance is a UART device (uart.h); the
|
|
501
713
|
// device context macros + usbd_* API live in the next-stack header.
|
|
@@ -805,16 +1017,16 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
805
1017
|
'static inline char* __tc_fmt_num_buf(double v, char* out, size_t cap) {',
|
|
806
1018
|
' if (v != v) { snprintf(out, cap, "nan"); return out; }',
|
|
807
1019
|
' double a = v < 0 ? -v : v;',
|
|
808
|
-
' long long ip =
|
|
809
|
-
' long long fr =
|
|
1020
|
+
' long long ip = static_cast<long long>(a);',
|
|
1021
|
+
' long long fr = static_cast<long long>((a - static_cast<double>(ip)) * 1000000.0 + 0.5);',
|
|
810
1022
|
' if (fr >= 1000000LL) { ip += 1LL; fr = 0LL; }',
|
|
811
1023
|
' int used = 0;',
|
|
812
1024
|
' if (v < 0 && (ip != 0LL || fr != 0LL)) { out[used++] = \'-\'; out[used] = \'\\0\'; }',
|
|
813
|
-
' if (fr == 0LL) { snprintf(out + used, cap - (
|
|
1025
|
+
' if (fr == 0LL) { snprintf(out + used, cap - static_cast<size_t>(used), "%lld", ip); return out; }',
|
|
814
1026
|
' char fbuf[8];',
|
|
815
1027
|
' int len = snprintf(fbuf, sizeof(fbuf), "%06lld", fr);',
|
|
816
1028
|
' while (len > 0 && fbuf[len - 1] == \'0\') { fbuf[--len] = \'\\0\'; }',
|
|
817
|
-
' snprintf(out + used, cap - (
|
|
1029
|
+
' snprintf(out + used, cap - static_cast<size_t>(used), "%lld.%s", ip, fbuf);',
|
|
818
1030
|
' return out;',
|
|
819
1031
|
'}',
|
|
820
1032
|
'static void __tc_fmt_num(double v) { char __b[32]; printf("%s", __tc_fmt_num_buf(v, __b, sizeof(__b))); }',
|
|
@@ -824,6 +1036,22 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
824
1036
|
'inline void __tc_println(double v) { __tc_fmt_num(v); printf("\\n"); }',
|
|
825
1037
|
);
|
|
826
1038
|
}
|
|
1039
|
+
// Trace user-event helpers (Trace.mark / Trace.event → trace.mark /
|
|
1040
|
+
// trace.event ops): one [TR:EV:<uptime_ms>:<name>[:<value>] console line
|
|
1041
|
+
// per call, captured host-side as timeline events. Emitted
|
|
1042
|
+
// UNCONDITIONALLY like __tc_print — a Trace.mark() call site must never
|
|
1043
|
+
// fail to link because zephyr.trace was disabled; with no capture
|
|
1044
|
+
// listening the lines are ordinary console output. Value formatting
|
|
1045
|
+
// reuses __tc_fmt_num_buf (integer-only, the picolibc %g trap).
|
|
1046
|
+
guardBody.push(
|
|
1047
|
+
'inline void __tc_trace_mark(const char* name) {',
|
|
1048
|
+
' printf("[TR:EV:%u:%s\\n", static_cast<unsigned int>(k_uptime_get_32()), name);',
|
|
1049
|
+
'}',
|
|
1050
|
+
'inline void __tc_trace_event(const char* name, double v) {',
|
|
1051
|
+
' char __b[32];',
|
|
1052
|
+
' printf("[TR:EV:%u:%s:%s\\n", static_cast<unsigned int>(k_uptime_get_32()), name, __tc_fmt_num_buf(v, __b, sizeof(__b)));',
|
|
1053
|
+
'}',
|
|
1054
|
+
);
|
|
827
1055
|
// Serial-port write helper: writes a scalar to a UART/CDC device a byte at
|
|
828
1056
|
// a time. Overloaded on const char* (strings, snprintf buffers) and double
|
|
829
1057
|
// (numbers/booleans) so a single __tc_dev_put(dev, value) call site formats
|
|
@@ -848,11 +1076,27 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
848
1076
|
// declared controller's __tc_<bus>N_dev trips -Wunused-variable under
|
|
849
1077
|
// Zephyr's -Werror (collectUsedBusIndices; probes with no program emit all).
|
|
850
1078
|
const usedBuses = collectUsedBusIndices(program);
|
|
1079
|
+
// Verb-level usage for the can.*/i2s.*/clock.* init blocks (see
|
|
1080
|
+
// collectVerbUsage) — undefined when there is no program (probe flows
|
|
1081
|
+
// emit the full state set).
|
|
1082
|
+
const verbUsage = collectVerbUsage(program);
|
|
851
1083
|
if (uses('usesI2C') && chip.i2c) {
|
|
852
1084
|
for (let i = 0; i < chip.i2c.controllers.length; i++) {
|
|
853
1085
|
if (usedBuses && !usedBuses.i2c.has(i)) continue;
|
|
854
1086
|
guardBody.push(...i2cInitLines(chip, i));
|
|
855
1087
|
}
|
|
1088
|
+
// I2C responders — one target-mode state block per constructed
|
|
1089
|
+
// responder (bus|address), against the controller handles above
|
|
1090
|
+
// (collectI2cResponders; the ops mark the bus used, so the handle
|
|
1091
|
+
// the registration references is always present).
|
|
1092
|
+
const responders = collectI2cResponders(program);
|
|
1093
|
+
if (responders) {
|
|
1094
|
+
for (const r of responders.values()) {
|
|
1095
|
+
const idx = parseControllerIndex(r.bus);
|
|
1096
|
+
if (idx >= chip.i2c.controllers.length) continue;
|
|
1097
|
+
guardBody.push(...i2cResponderStateLines(chip, idx, r.address, r.rx, r.tx));
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
856
1100
|
}
|
|
857
1101
|
if (uses('usesSPI') && chip.spi) {
|
|
858
1102
|
for (let i = 0; i < chip.spi.controllers.length; i++) {
|
|
@@ -860,6 +1104,77 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
860
1104
|
guardBody.push(...spiInitLines(chip, i));
|
|
861
1105
|
}
|
|
862
1106
|
}
|
|
1107
|
+
// LED strips — one buffer + device handle per driven SPI controller
|
|
1108
|
+
// (chain length from the construction facts).
|
|
1109
|
+
{
|
|
1110
|
+
const strips = collectStrips(program);
|
|
1111
|
+
if (uses('usesStrip') && strips) {
|
|
1112
|
+
for (const [idx, count] of strips) {
|
|
1113
|
+
guardBody.push(...stripInitLines(chip, idx, count));
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
// Key matrix — the input-subsystem trampoline (the DT node comes from
|
|
1118
|
+
// the overlay generator, driven by the toolchain's matrix scan).
|
|
1119
|
+
if (uses('usesMatrix')) {
|
|
1120
|
+
guardBody.push(...matrixInitLines());
|
|
1121
|
+
}
|
|
1122
|
+
// Wall-clock shim — the epoch<->civil helpers + the rtc alias handle
|
|
1123
|
+
// (the DT node comes from the overlay generator's shim synthesis).
|
|
1124
|
+
if (uses('usesClock')) {
|
|
1125
|
+
const clockUsage = verbUsage?.clock ?? { set: true, now: true };
|
|
1126
|
+
guardBody.push(...clockInitLines(clockUsage));
|
|
1127
|
+
}
|
|
1128
|
+
// CAN — per-controller device handle + the rx trampoline (the DT node
|
|
1129
|
+
// is the board's own can@ node, enabled by the overlay). Only the
|
|
1130
|
+
// controllers the program addresses, and only the state its verbs
|
|
1131
|
+
// reference (collectVerbUsage; probes with no program emit all). Usage
|
|
1132
|
+
// folds through the same clamp lowerCan applies to out-of-range
|
|
1133
|
+
// instances, so the state block for the clamped controller emits.
|
|
1134
|
+
if (uses('usesCan') && chip.can) {
|
|
1135
|
+
const nCtrl = chip.can.controllers.length;
|
|
1136
|
+
const canUsage = verbUsage?.can;
|
|
1137
|
+
const folded = Array.from({ length: nCtrl }, () => ({ begin: false, send: false, onReceive: false }));
|
|
1138
|
+
if (canUsage) {
|
|
1139
|
+
for (const [inst, flags] of canUsage) {
|
|
1140
|
+
const f = folded[Math.max(0, Math.min(inst, nCtrl - 1))]!;
|
|
1141
|
+
f.begin = f.begin || flags.begin;
|
|
1142
|
+
f.send = f.send || flags.send;
|
|
1143
|
+
f.onReceive = f.onReceive || flags.onReceive;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
for (let i = 0; i < nCtrl; i++) {
|
|
1147
|
+
const f = folded[i]!;
|
|
1148
|
+
if (canUsage && !f.begin && !f.send && !f.onReceive) continue;
|
|
1149
|
+
guardBody.push(...canInitLines(chip, i, canUsage ? f : { begin: true, send: true, onReceive: true }));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
// I2S — per-controller slab + block buffers + lazy direction inits,
|
|
1153
|
+
// sized from the construction facts the ops carry. Same used-instance
|
|
1154
|
+
// and clamp-fold discipline as CAN.
|
|
1155
|
+
if (uses('usesI2s') && chip.i2s) {
|
|
1156
|
+
const i2sFacts = collectI2s(program);
|
|
1157
|
+
const nCtrl = chip.i2s.controllers.length;
|
|
1158
|
+
const i2sUsage = verbUsage?.i2s;
|
|
1159
|
+
const folded = Array.from({ length: nCtrl }, () => ({ write: false, read: false, readAt: false }));
|
|
1160
|
+
if (i2sUsage) {
|
|
1161
|
+
for (const [inst, flags] of i2sUsage) {
|
|
1162
|
+
const f = folded[Math.max(0, Math.min(inst, nCtrl - 1))]!;
|
|
1163
|
+
f.write = f.write || flags.write;
|
|
1164
|
+
f.read = f.read || flags.read;
|
|
1165
|
+
f.readAt = f.readAt || flags.readAt;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
for (let i = 0; i < nCtrl; i++) {
|
|
1169
|
+
const f = folded[i]!;
|
|
1170
|
+
if (i2sUsage && !f.write && !f.read && !f.readAt) continue;
|
|
1171
|
+
const facts = i2sFacts?.get(i);
|
|
1172
|
+
const channels = facts?.channels ?? 2;
|
|
1173
|
+
const bits = facts?.bits ?? 16;
|
|
1174
|
+
const blockFrames = facts?.blockFrames ?? 64;
|
|
1175
|
+
guardBody.push(...i2sInitLines(chip, i, blockFrames * channels * (bits / 8), i2sUsage ? f : { write: true, read: true, readAt: true }));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
863
1178
|
if (uses('usesUart') && chip.uart) {
|
|
864
1179
|
for (let i = 0; i < chip.uart.controllers.length; i++) {
|
|
865
1180
|
if (usedBuses && !usedBuses.uart.has(i)) continue;
|
|
@@ -881,7 +1196,7 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
881
1196
|
const sensors = collectSensors(program);
|
|
882
1197
|
if (sensors) {
|
|
883
1198
|
for (const s of sensors.values()) {
|
|
884
|
-
guardBody.push(...sensorStateLines(s.part, s.bus, s.port, s.busKind, s.spiHz, s.spiMode, s.alertPin));
|
|
1199
|
+
guardBody.push(...sensorStateLines(s.part, s.bus, s.port, s.busKind, s.spiHz, s.spiMode, s.alertPin, s.resolution));
|
|
885
1200
|
}
|
|
886
1201
|
}
|
|
887
1202
|
}
|
|
@@ -907,10 +1222,56 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
907
1222
|
}
|
|
908
1223
|
}
|
|
909
1224
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1225
|
+
// Trace heartbeat — the runtime-stats sampler behind `typecad-hal trace
|
|
1226
|
+
// capture`. Opt-in via zephyr.trace.enabled in typecad-hal.config.ts (the
|
|
1227
|
+
// config loader threads the zephyr record through platformContext). The
|
|
1228
|
+
// block compiles under CUTTLEFISH_ENTRY_TU, so split-file TUs carry no
|
|
1229
|
+
// second copy (a duplicate SYS_INIT would double every heartbeat).
|
|
1230
|
+
// UI-mounted programs additionally get the frame-stats block (always
|
|
1231
|
+
// defined, active only under CUTTLEFISH_TRACE_UI) so the ui_tick call
|
|
1232
|
+
// site's instrumentation never depends on the trace config.
|
|
1233
|
+
{
|
|
1234
|
+
const traceCfg = (ctx as { zephyr?: { trace?: { enabled?: boolean; intervalMs?: number; alarms?: { stackMinBytes?: number; frameMaxMs?: number } } } } | undefined)
|
|
1235
|
+
?.zephyr?.trace;
|
|
1236
|
+
const traceEnabled = traceCfg?.enabled === true;
|
|
1237
|
+
if (traceEnabled && entryHasUI()) {
|
|
1238
|
+
guardBody.push(
|
|
1239
|
+
'#if defined(CUTTLEFISH_ENTRY_TU)',
|
|
1240
|
+
'#define CUTTLEFISH_TRACE_UI 1',
|
|
1241
|
+
'#endif',
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
if (entryHasUI()) {
|
|
1245
|
+
guardBody.push(...uiFrameTraceLines(traceCfg?.alarms?.frameMaxMs));
|
|
1246
|
+
}
|
|
1247
|
+
if (traceEnabled) {
|
|
1248
|
+
guardBody.push(...traceHeartbeatLines(clampTraceIntervalMs(traceCfg?.intervalMs), entryHasUI(), traceCfg?.alarms));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
// The usbd context is shared by CDC and HID; the CDC per-instance
|
|
1252
|
+
// state only emits when the program actually uses usb.* (an HID-only
|
|
1253
|
+
// program would otherwise carry an unused __tc_usb0_dev).
|
|
1254
|
+
if ((uses('usesUsb') || uses('usesHid')) && chip.usb) {
|
|
1255
|
+
guardBody.push(...usbdDeviceLines(chip, { hid: uses('usesHid') }));
|
|
1256
|
+
if (uses('usesUsb')) {
|
|
1257
|
+
for (let i = 0; i < chip.usb.cdcInstances; i++) guardBody.push(...usbInitLines(chip, i));
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
// USB HID — one interface per program; the protocol comes from which
|
|
1261
|
+
// class's verbs the program used (keyboard vs mouse). Requires the USB
|
|
1262
|
+
// device context (the same one CDC rides — the register helper calls
|
|
1263
|
+
// __tc_usbd_start).
|
|
1264
|
+
if (uses('usesHid') && chip.usb) {
|
|
1265
|
+
const protocol = collectHidProtocol(program) ?? 'keyboard';
|
|
1266
|
+
if (protocol === 'mixed') {
|
|
1267
|
+
// One zephyr,hid-device interface per program (the v1 ceiling) —
|
|
1268
|
+
// name it at compile time instead of an undefined-symbol error.
|
|
1269
|
+
guardBody.push('#error "typecad-hal: one HID interface per program (v1) — Keyboard and Mouse cannot be mixed in one app"');
|
|
1270
|
+
} else {
|
|
1271
|
+
guardBody.push(...hidInitLines(protocol));
|
|
1272
|
+
}
|
|
913
1273
|
}
|
|
1274
|
+
|
|
914
1275
|
// PWM init also fires (with alias vars for the override pins) when a
|
|
915
1276
|
// program drives ONLY inline-override channels on a chip with no pwm
|
|
916
1277
|
// facts — the lowered calls reference those aliases.
|
|
@@ -1042,6 +1403,10 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
1042
1403
|
if (this.needsGpioReadShim(program, ctx)) {
|
|
1043
1404
|
lines.push(
|
|
1044
1405
|
'inline int __tc_gpio_read(int pin) { return gpio_pin_get_raw(__tc_gpio_dev(static_cast<uint32_t>(pin)), __tc_gpio_pin(static_cast<uint32_t>(pin))); }',
|
|
1406
|
+
// Runtime input configuration for ui.watchPin / ui.press setup
|
|
1407
|
+
// (the strategy's setPinMode lowers here). Idempotent — setup calls
|
|
1408
|
+
// it once per watched pin before the poll loop reads it.
|
|
1409
|
+
'inline void __tc_gpio_configure_input(uint32_t pin) { gpio_pin_configure(__tc_gpio_dev(pin), __tc_gpio_pin(pin), GPIO_INPUT | GPIO_PULL_UP); }',
|
|
1045
1410
|
);
|
|
1046
1411
|
}
|
|
1047
1412
|
// __tc_gpio_write / __tc_delay_us are only referenced via @typecad/safety
|
|
@@ -1879,6 +2244,17 @@ export class ZephyrStrategy implements PlatformStrategy {
|
|
|
1879
2244
|
};
|
|
1880
2245
|
}
|
|
1881
2246
|
|
|
2247
|
+
/**
|
|
2248
|
+
* Per-frame wall-time feed for the trace heartbeat's UI stats. The helper
|
|
2249
|
+
* is emitted unconditionally for UI programs (compiles to a no-op unless
|
|
2250
|
+
* zephyr.trace defined CUTTLEFISH_TRACE_UI), so this call is safe with
|
|
2251
|
+
* tracing on or off.
|
|
2252
|
+
*/
|
|
2253
|
+
uiFrameTimingLines(deltaMsExpr: string): string[] | null {
|
|
2254
|
+
if (!entryHasUI()) return null;
|
|
2255
|
+
return [`__tc_trace_ui_frame(static_cast<uint32_t>(${deltaMsExpr}));`];
|
|
2256
|
+
}
|
|
2257
|
+
|
|
1882
2258
|
// ── Type aliases ────────────────────────────────────────────────────────
|
|
1883
2259
|
|
|
1884
2260
|
shouldSkipTypeAlias(): boolean {
|
|
@@ -2131,9 +2507,14 @@ struct __tc_StaticArray {
|
|
|
2131
2507
|
writeDigitalPin(pin: string, val: string): string {
|
|
2132
2508
|
return `__tc_gpio_write(${pin}, ${val})`;
|
|
2133
2509
|
}
|
|
2134
|
-
setPinMode(
|
|
2135
|
-
//
|
|
2136
|
-
|
|
2510
|
+
setPinMode(pin: string, _mode: string): string {
|
|
2511
|
+
// Runtime input configuration: Zephyr has no pinMode — the GPIO API
|
|
2512
|
+
// configures direction/pull explicitly. The mode argument is ignored:
|
|
2513
|
+
// every watchPin/press consumer wants an input with pull-up (buttons are
|
|
2514
|
+
// active-low), and __tc_gpio_configure_input hardcodes exactly that.
|
|
2515
|
+
// The helper's definition rides the GPIO read shim (same gate — UI builds
|
|
2516
|
+
// with pin watchers always carry the read surface too).
|
|
2517
|
+
return `__tc_gpio_configure_input(static_cast<uint32_t>(${pin}));`;
|
|
2137
2518
|
}
|
|
2138
2519
|
delayMs(ms: string): string {
|
|
2139
2520
|
return `k_msleep(${ms})`;
|
|
@@ -2178,17 +2559,30 @@ struct __tc_StaticArray {
|
|
|
2178
2559
|
}
|
|
2179
2560
|
|
|
2180
2561
|
supportedDisplayDrivers(): ReadonlySet<string> {
|
|
2181
|
-
|
|
2562
|
+
// Registry profiles plus every panel compatible with a display binding in
|
|
2563
|
+
// the user's Zephyr tree — the drop-in path (display.driver = compatible)
|
|
2564
|
+
// validates at mount like any curated profile. No tree discoverable →
|
|
2565
|
+
// registry only.
|
|
2566
|
+
const drivers = new Set<string>(Object.keys(ZEPHYR_DISPLAY_PROFILES));
|
|
2567
|
+
try {
|
|
2568
|
+
for (const compatible of listBoundDisplayCompatibles()) drivers.add(compatible);
|
|
2569
|
+
} catch {
|
|
2570
|
+
// Tree unreadable — the registry set stands.
|
|
2571
|
+
}
|
|
2572
|
+
return drivers;
|
|
2182
2573
|
}
|
|
2183
2574
|
|
|
2184
2575
|
// ── Strategy-owned display/touch adapter seam ────────────────────────────
|
|
2185
2576
|
// Zephyr owns its display + touch adapters: the UI display adapter bridges
|
|
2186
|
-
// the in-tree CuttlefishGFX class to the panel
|
|
2187
|
-
//
|
|
2188
|
-
//
|
|
2189
|
-
//
|
|
2190
|
-
//
|
|
2191
|
-
//
|
|
2577
|
+
// the in-tree CuttlefishGFX class to the panel via one of two transports
|
|
2578
|
+
// (per profile — 'direct-spi': per-controller init + wire format over
|
|
2579
|
+
// spi_write, see src/display/ui-adapter.ts; 'zephyr-display': Zephyr's
|
|
2580
|
+
// display API with in-tree panel drivers, see src/display/ui-adapter-native.ts),
|
|
2581
|
+
// and the touch adapters drive the FT6336U (I2C capacitive) and XPT2046
|
|
2582
|
+
// (SPI resistive) controllers via Zephyr's bus APIs (src/display/touch-adapter.ts).
|
|
2583
|
+
// Both live in this package so cuttlefish carries no Zephyr/Wiring-specific
|
|
2584
|
+
// display or touch knowledge. Mirrors ArduinoStrategy's provides*/resolve*
|
|
2585
|
+
// pattern.
|
|
2192
2586
|
|
|
2193
2587
|
providesDisplayAdapter(): boolean { return true; }
|
|
2194
2588
|
|
|
@@ -2212,8 +2606,29 @@ struct __tc_StaticArray {
|
|
|
2212
2606
|
return new Map(Object.entries(BUILT_IN_PROFILES));
|
|
2213
2607
|
}
|
|
2214
2608
|
|
|
2215
|
-
colorFormat(): 'rgb565' | 'rgb666' | 'rgb888' | 'mono' {
|
|
2216
|
-
|
|
2609
|
+
colorFormat(): 'rgb565' | 'rgb666' | 'rgb888' | 'mono' | 'gray8' {
|
|
2610
|
+
// The seeded display state is the truth after display.init (drop-in mono
|
|
2611
|
+
// panels synthesize 'mono'); the default profile keeps the pre-init
|
|
2612
|
+
// rgb565 answer byte-identical with history.
|
|
2613
|
+
return this._displayState.profile.colorFormat;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
colorFormatForDriver(driver: string): 'rgb565' | 'rgb666' | 'rgb888' | 'mono' | 'gray8' | undefined {
|
|
2617
|
+
// Drop-in panel class from the compatible table (DATA): the 1bpp OLED
|
|
2618
|
+
// family lowers mono engine-side and the 16-gray family lowers gray8,
|
|
2619
|
+
// so colors flatten at build time instead of rgb565-lowering into an
|
|
2620
|
+
// adapter that cannot show them. Registry profiles carry their own
|
|
2621
|
+
// colorFormat; non-compatible driver ids keep the default.
|
|
2622
|
+
if (isDtCompatible(driver) && isMonoDisplay({ driver })) return 'mono';
|
|
2623
|
+
if (isDtCompatible(driver) && isEinkDisplay({ driver })) return 'mono';
|
|
2624
|
+
if (isDtCompatible(driver) && isGrayDisplay({ driver })) return 'gray8';
|
|
2625
|
+
return undefined;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
displayClassForDriver(driver: string): 'eink' | 'oled' | undefined {
|
|
2629
|
+
// The compatible table decides (DATA) — ssd16xx/uc81xx are e-ink.
|
|
2630
|
+
if (isDtCompatible(driver) && isEinkDisplay({ driver })) return 'eink';
|
|
2631
|
+
return undefined;
|
|
2217
2632
|
}
|
|
2218
2633
|
|
|
2219
2634
|
graphicsCapacity(): GraphicsCapacity {
|