@typecad/framework-zephyr 1.0.0-alpha.8
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/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/async/timer-polyfill.d.ts +10 -0
- package/dist/async/timer-polyfill.js +95 -0
- package/dist/chips/controllers.d.ts +21 -0
- package/dist/chips/controllers.js +66 -0
- package/dist/chips/esp32.d.ts +2 -0
- package/dist/chips/esp32.js +59 -0
- package/dist/chips/esp32s3.d.ts +2 -0
- package/dist/chips/esp32s3.js +54 -0
- package/dist/chips/index.d.ts +15 -0
- package/dist/chips/index.js +45 -0
- package/dist/chips/resolve.d.ts +9 -0
- package/dist/chips/resolve.js +121 -0
- package/dist/chips/types.d.ts +154 -0
- package/dist/chips/types.js +14 -0
- package/dist/chips/xiao-ble.d.ts +2 -0
- package/dist/chips/xiao-ble.js +61 -0
- package/dist/debug-codegen.d.ts +33 -0
- package/dist/debug-codegen.js +162 -0
- package/dist/display/gfx.d.ts +17 -0
- package/dist/display/gfx.js +171 -0
- package/dist/display/index.d.ts +20 -0
- package/dist/display/index.js +53 -0
- package/dist/display/profiles.d.ts +25 -0
- package/dist/display/profiles.js +41 -0
- package/dist/display/touch-adapter.d.ts +9 -0
- package/dist/display/touch-adapter.js +95 -0
- package/dist/display/ui-adapter.d.ts +21 -0
- package/dist/display/ui-adapter.js +534 -0
- package/dist/dt-config/kconfig.d.ts +27 -0
- package/dist/dt-config/kconfig.js +283 -0
- package/dist/dt-config/overlay.d.ts +35 -0
- package/dist/dt-config/overlay.js +246 -0
- package/dist/framework.manifest.d.ts +98 -0
- package/dist/framework.manifest.js +525 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +12 -0
- package/dist/lowering/adc.d.ts +19 -0
- package/dist/lowering/adc.js +76 -0
- package/dist/lowering/ble.d.ts +20 -0
- package/dist/lowering/ble.js +419 -0
- package/dist/lowering/board.d.ts +9 -0
- package/dist/lowering/board.js +33 -0
- package/dist/lowering/gpio.d.ts +12 -0
- package/dist/lowering/gpio.js +123 -0
- package/dist/lowering/http.d.ts +23 -0
- package/dist/lowering/http.js +482 -0
- package/dist/lowering/i2c.d.ts +15 -0
- package/dist/lowering/i2c.js +114 -0
- package/dist/lowering/index.d.ts +29 -0
- package/dist/lowering/index.js +93 -0
- package/dist/lowering/interrupts.d.ts +21 -0
- package/dist/lowering/interrupts.js +91 -0
- package/dist/lowering/mqtt.d.ts +19 -0
- package/dist/lowering/mqtt.js +366 -0
- package/dist/lowering/power.d.ts +9 -0
- package/dist/lowering/power.js +64 -0
- package/dist/lowering/preferences.d.ts +23 -0
- package/dist/lowering/preferences.js +345 -0
- package/dist/lowering/pulse.d.ts +7 -0
- package/dist/lowering/pulse.js +82 -0
- package/dist/lowering/pwm.d.ts +15 -0
- package/dist/lowering/pwm.js +59 -0
- package/dist/lowering/random.d.ts +18 -0
- package/dist/lowering/random.js +129 -0
- package/dist/lowering/spi.d.ts +17 -0
- package/dist/lowering/spi.js +113 -0
- package/dist/lowering/timing.d.ts +9 -0
- package/dist/lowering/timing.js +63 -0
- package/dist/lowering/tone.d.ts +10 -0
- package/dist/lowering/tone.js +50 -0
- package/dist/lowering/uart.d.ts +15 -0
- package/dist/lowering/uart.js +102 -0
- package/dist/lowering/util.d.ts +4 -0
- package/dist/lowering/util.js +12 -0
- package/dist/lowering/wdt.d.ts +15 -0
- package/dist/lowering/wdt.js +80 -0
- package/dist/lowering/wifi.d.ts +21 -0
- package/dist/lowering/wifi.js +447 -0
- package/dist/lowering/worker-backing.d.ts +14 -0
- package/dist/lowering/worker-backing.js +79 -0
- package/dist/lowering/worker.d.ts +6 -0
- package/dist/lowering/worker.js +14 -0
- package/dist/strategy.d.ts +168 -0
- package/dist/strategy.js +1094 -0
- package/dist/toolchain/debug-config.d.ts +82 -0
- package/dist/toolchain/debug-config.js +359 -0
- package/dist/toolchain/index.d.ts +73 -0
- package/dist/toolchain/index.js +427 -0
- package/dist/toolchain/scaffold.d.ts +15 -0
- package/dist/toolchain/scaffold.js +196 -0
- package/dist/toolchain/west-discover.d.ts +40 -0
- package/dist/toolchain/west-discover.js +203 -0
- package/dist/toolchain/west-spawn.d.ts +39 -0
- package/dist/toolchain/west-spawn.js +117 -0
- package/package.json +82 -0
- package/src/async/timer-polyfill.ts +107 -0
- package/src/chips/controllers.ts +74 -0
- package/src/chips/esp32.ts +62 -0
- package/src/chips/esp32s3.ts +57 -0
- package/src/chips/index.ts +54 -0
- package/src/chips/resolve.ts +146 -0
- package/src/chips/types.ts +163 -0
- package/src/chips/xiao-ble.ts +64 -0
- package/src/debug-codegen.ts +207 -0
- package/src/display/gfx.ts +190 -0
- package/src/display/index.ts +70 -0
- package/src/display/profiles.ts +63 -0
- package/src/display/touch-adapter.ts +100 -0
- package/src/display/ui-adapter.ts +558 -0
- package/src/dt-config/kconfig.ts +308 -0
- package/src/dt-config/overlay.ts +293 -0
- package/src/framework.manifest.ts +545 -0
- package/src/index.ts +23 -0
- package/src/lowering/adc.ts +102 -0
- package/src/lowering/ble.ts +426 -0
- package/src/lowering/board.ts +38 -0
- package/src/lowering/gpio.ts +148 -0
- package/src/lowering/http.ts +488 -0
- package/src/lowering/i2c.ts +126 -0
- package/src/lowering/index.ts +86 -0
- package/src/lowering/interrupts.ts +114 -0
- package/src/lowering/mqtt.ts +371 -0
- package/src/lowering/power.ts +70 -0
- package/src/lowering/preferences.ts +354 -0
- package/src/lowering/pulse.ts +93 -0
- package/src/lowering/pwm.ts +74 -0
- package/src/lowering/random.ts +135 -0
- package/src/lowering/spi.ts +124 -0
- package/src/lowering/timing.ts +72 -0
- package/src/lowering/tone.ts +60 -0
- package/src/lowering/uart.ts +111 -0
- package/src/lowering/util.ts +12 -0
- package/src/lowering/wdt.ts +84 -0
- package/src/lowering/wifi.ts +452 -0
- package/src/lowering/worker-backing.ts +94 -0
- package/src/lowering/worker.ts +17 -0
- package/src/strategy.ts +1265 -0
- package/src/toolchain/debug-config.ts +399 -0
- package/src/toolchain/index.ts +459 -0
- package/src/toolchain/scaffold.ts +196 -0
- package/src/toolchain/west-discover.ts +233 -0
- package/src/toolchain/west-spawn.ts +141 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A GPIO pin described as a devicetree spec.
|
|
3
|
+
*
|
|
4
|
+
* Zephyr addresses GPIO via `struct gpio_dt_spec` resolved from devicetree
|
|
5
|
+
* aliases/labels (e.g. DT_ALIAS(led0)). The dtSpec form is preferred because
|
|
6
|
+
* `gpio_pin_set_dt()` honors the node's polarity flags — so an active-low LED
|
|
7
|
+
* (logical 1 = LED on) is handled by the DT `GPIO_ACTIVE_LOW` flag, not by the
|
|
8
|
+
* generated C++.
|
|
9
|
+
*/
|
|
10
|
+
export interface ZephyrGpioDtSpec {
|
|
11
|
+
/** GPIO number — matches the HAL op `pin` field (P0.X → X, P1.X → 32+X). */
|
|
12
|
+
readonly pin: number;
|
|
13
|
+
/**
|
|
14
|
+
* Devicetree alias/nodelabel macro, e.g. `led0`, `sw0`, `led1`.
|
|
15
|
+
* Emitted as `GPIO_DT_SPEC_GET(DT_ALIAS(<dtSpec>), gpios)`.
|
|
16
|
+
*/
|
|
17
|
+
readonly dtSpec: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A GPIO controller devicetree node, and the HAL pin-number range it owns.
|
|
21
|
+
*
|
|
22
|
+
* Most SoCs expose a single GPIO controller (nRF52840: `gpio0` owns every
|
|
23
|
+
* pin). SoCs that split GPIO across multiple devicetree nodes — e.g. the
|
|
24
|
+
* ESP32-S3 (`gpio0`: pins 0–31, `gpio1`: pins 32–48) — list one entry per
|
|
25
|
+
* controller so the lowering can route a HAL pin to the owning controller at
|
|
26
|
+
* runtime. Pin numbers match the HAL op `pin` field.
|
|
27
|
+
*/
|
|
28
|
+
export interface ZephyrGpioController {
|
|
29
|
+
/** Devicetree nodelabel, e.g. 'gpio0', 'gpio1'. */
|
|
30
|
+
readonly nodelabel: string;
|
|
31
|
+
/** First HAL pin number owned by this controller (inclusive). */
|
|
32
|
+
readonly minPin: number;
|
|
33
|
+
/** Last HAL pin number owned by this controller (inclusive). */
|
|
34
|
+
readonly maxPin: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A bus controller (I2C / SPI / UART) described by its devicetree nodelabel.
|
|
38
|
+
*
|
|
39
|
+
* Zephyr resolves the `const struct device*` at compile time via
|
|
40
|
+
* `DEVICE_DT_GET(DT_NODELABEL(<nodeLabel>))`, so unlike ESP-IDF there is no
|
|
41
|
+
* lazy handle resolution — the lowering emits the macro directly.
|
|
42
|
+
*/
|
|
43
|
+
export interface ZephyrBusController {
|
|
44
|
+
/** Devicetree nodelabel, e.g. 'i2c1', 'spi2', 'uart0'. */
|
|
45
|
+
readonly nodeLabel: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A PWM channel described as a devicetree spec.
|
|
49
|
+
*
|
|
50
|
+
* Emitted as `struct pwm_dt_spec __tc_pwm<N> = PWM_DT_SPEC_GET(DT_ALIAS(<dtSpec>))`.
|
|
51
|
+
* `pwm_set_pulse_dt(&__tc_pwm<N>, pulse_ns)` honors the spec's period/polarity.
|
|
52
|
+
*/
|
|
53
|
+
export interface ZephyrPwmSpec {
|
|
54
|
+
/** GPIO number (matches the HAL op `pin` field). */
|
|
55
|
+
readonly pin: number;
|
|
56
|
+
/** Devicetree alias, e.g. 'pwm-led0'. */
|
|
57
|
+
readonly dtSpec: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A GPIO pin usable as an interrupt source.
|
|
61
|
+
*
|
|
62
|
+
* Emitted as `struct gpio_dt_spec __tc_int<N> = GPIO_DT_SPEC_GET(DT_ALIAS(<dtSpec>), gpios)`
|
|
63
|
+
* + a `struct gpio_callback` registered via `gpio_add_callback`.
|
|
64
|
+
*/
|
|
65
|
+
export interface ZephyrInterruptPin {
|
|
66
|
+
/** GPIO number (matches the HAL op `pin` field). */
|
|
67
|
+
readonly pin: number;
|
|
68
|
+
/** Devicetree alias, e.g. 'sw0', or a nodelabel. */
|
|
69
|
+
readonly dtSpec: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* An ADC channel: which SAADC input a given HAL pin maps to.
|
|
73
|
+
*
|
|
74
|
+
* The XIAO nRF52840 has no pre-declared ADC channel nodes in devicetree, so the
|
|
75
|
+
* lowering emits `adc_channel_setup` against `DEVICE_DT_GET(DT_NODELABEL(adc))`
|
|
76
|
+
* using the `channel` index here (SAADC AIN0–AIN7).
|
|
77
|
+
*/
|
|
78
|
+
export interface ZephyrAdcChannel {
|
|
79
|
+
/** GPIO number (matches the HAL op `pin` field). */
|
|
80
|
+
readonly pin: number;
|
|
81
|
+
/** SAADC channel index (AIN0–AIN7). */
|
|
82
|
+
readonly channel: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Pure-data descriptor for a Zephyr board + its SoC's peripheral layout.
|
|
86
|
+
*/
|
|
87
|
+
export interface ZephyrChipDescriptor {
|
|
88
|
+
/** Zephyr board target (the `west build -b <id>` argument), e.g. 'xiao_ble'. */
|
|
89
|
+
readonly id: string;
|
|
90
|
+
/** SoC family, e.g. 'nrf52840'. */
|
|
91
|
+
readonly soc: string;
|
|
92
|
+
/**
|
|
93
|
+
* Default GPIO controller nodelabel for the raw-pin fallback. Pins not in
|
|
94
|
+
* `gpio.dtSpecs` are addressed via
|
|
95
|
+
* `DEVICE_DT_GET(DT_NODELABEL(<gpioController>))` + gpio_pin_*_raw().
|
|
96
|
+
*
|
|
97
|
+
* For SoCs that split GPIO across multiple devicetree nodes, `gpioControllers`
|
|
98
|
+
* overrides this per pin range; this field is then only the out-of-range
|
|
99
|
+
* fallback (so a single-controller board is unaffected by it).
|
|
100
|
+
*/
|
|
101
|
+
readonly gpioController: string;
|
|
102
|
+
/**
|
|
103
|
+
* Per-range GPIO controllers for SoCs that split GPIO across multiple
|
|
104
|
+
* devicetree nodes (ESP32-S3: `gpio0` 0–31, `gpio1` 32–48). When present, the
|
|
105
|
+
* lowering routes a HAL pin to its owning controller at runtime via the
|
|
106
|
+
* emitted `__tc_gpio_dev(pin)` dispatcher; `gpioController` is the fallback.
|
|
107
|
+
* Omit on single-controller SoCs (nRF52840, RP2040, …) — every pin is on the
|
|
108
|
+
* one controller described by `gpioController`.
|
|
109
|
+
*/
|
|
110
|
+
readonly gpioControllers?: readonly ZephyrGpioController[];
|
|
111
|
+
/** GPIO pins with devicetree specs (LEDs, buttons, board-defined pins). */
|
|
112
|
+
readonly gpio: {
|
|
113
|
+
readonly dtSpecs: readonly ZephyrGpioDtSpec[];
|
|
114
|
+
/** Interrupt-capable pins with DT specs (buttons etc.). */
|
|
115
|
+
readonly interruptPins?: readonly ZephyrInterruptPin[];
|
|
116
|
+
};
|
|
117
|
+
/** I2C controllers (board-wired). Index 0 = the primary bus. */
|
|
118
|
+
readonly i2c?: {
|
|
119
|
+
readonly controllers: readonly ZephyrBusController[];
|
|
120
|
+
};
|
|
121
|
+
/** SPI controllers (board-wired). Index 0 = the primary bus. */
|
|
122
|
+
readonly spi?: {
|
|
123
|
+
readonly controllers: readonly ZephyrBusController[];
|
|
124
|
+
};
|
|
125
|
+
/** UART controllers (board-wired). Index 0 = the primary port. */
|
|
126
|
+
readonly uart?: {
|
|
127
|
+
readonly controllers: readonly ZephyrBusController[];
|
|
128
|
+
};
|
|
129
|
+
/** PWM channels with DT specs. */
|
|
130
|
+
readonly pwm?: {
|
|
131
|
+
readonly specs: readonly ZephyrPwmSpec[];
|
|
132
|
+
};
|
|
133
|
+
/** ADC: the SAADC node label + the pin→channel map. */
|
|
134
|
+
readonly adc?: {
|
|
135
|
+
readonly nodeLabel: string;
|
|
136
|
+
readonly channels: readonly ZephyrAdcChannel[];
|
|
137
|
+
/** Reference voltage in millivolts (nRF internal = 3000 for VDD/4 + gain 1/4… use 3000). */
|
|
138
|
+
readonly vrefMv: number;
|
|
139
|
+
/** ADC resolution in bits. */
|
|
140
|
+
readonly resolution: number;
|
|
141
|
+
};
|
|
142
|
+
/** Watchdog node label, e.g. 'wdt0'. */
|
|
143
|
+
readonly wdt?: {
|
|
144
|
+
readonly nodeLabel: string;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* WiFi capability marker. Present only on chips with a WiFi radio (ESP32-S3).
|
|
148
|
+
* Read by profileDiagnostics to flag wifi.* usage on chips without a radio.
|
|
149
|
+
* Omit on radioless chips (nRF52840) — its absence is the "no WiFi" signal.
|
|
150
|
+
*/
|
|
151
|
+
readonly wifi?: {
|
|
152
|
+
readonly supported: true;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr chip descriptor — pure-data model
|
|
3
|
+
//
|
|
4
|
+
// Mirrors the per-framework chip-descriptor pattern established by
|
|
5
|
+
// framework-esp32 (Esp32ChipDescriptor) and framework-avr (AVRChipDescriptor):
|
|
6
|
+
// the framework owns its own device-tree-derived descriptor, keyed off the
|
|
7
|
+
// Zephyr board target string in frameworkData.buildTarget. The board/MCU
|
|
8
|
+
// packages are deliberately NOT read at emit time (see the framework-package
|
|
9
|
+
// data-flow split); this descriptor is the source the lowering reads.
|
|
10
|
+
//
|
|
11
|
+
// No behavior — the lowering code in src/lowering/*.ts reads getActiveChip()
|
|
12
|
+
// to resolve pins to devicetree specs and raw-controller fallbacks.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Seeed Studio XIAO nRF52840 — Zephyr board descriptor
|
|
3
|
+
//
|
|
4
|
+
// Board target: `xiao_ble` (mainline Zephyr, boards/seeed/xiao_ble).
|
|
5
|
+
// The onboard user LED is active-low and exposed as the DT alias `led0`.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export const XIAO_BLE = {
|
|
8
|
+
id: 'xiao_ble',
|
|
9
|
+
soc: 'nrf52840',
|
|
10
|
+
gpioController: 'gpio0',
|
|
11
|
+
gpio: {
|
|
12
|
+
dtSpecs: [
|
|
13
|
+
// Onboard RGB LEDs — active-low (GPIO_ACTIVE_LOW in xiao_ble_common.dtsi).
|
|
14
|
+
// Verified against the board's `aliases { led0 = &led0; led1 = &led1; led2 = &led2; }`.
|
|
15
|
+
{ pin: 26, dtSpec: 'led0' }, // Red (P0.26)
|
|
16
|
+
{ pin: 30, dtSpec: 'led1' }, // Green (P0.30)
|
|
17
|
+
{ pin: 6, dtSpec: 'led2' }, // Blue (P0.06)
|
|
18
|
+
],
|
|
19
|
+
// The XIAO nRF52840 has a user button on P0.04, but mainline Zephyr's
|
|
20
|
+
// xiao_ble board (verified against Zephyr 4.3.99) does NOT expose it as a
|
|
21
|
+
// DT `sw0` alias — there is no gpio-keys node in xiao_ble.dts. Emitting
|
|
22
|
+
// GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios) therefore fails to compile
|
|
23
|
+
// ('DT_N_ALIAS_sw0_... was not declared'). Until a button node + alias is
|
|
24
|
+
// added (via an overlay or an upstream board update), interruptPins stays
|
|
25
|
+
// empty: attachInterrupt on this board lowers to the no-DT-spec comment
|
|
26
|
+
// fallback rather than a hard compile error.
|
|
27
|
+
interruptPins: [],
|
|
28
|
+
},
|
|
29
|
+
// The XIAO connector wiring (from seeed_xiao_connector.dtsi + xiao_ble-pinctrl.dtsi):
|
|
30
|
+
// xiao_i2c → i2c1 (SDA P0.04/D4, SCL P0.05/D5)
|
|
31
|
+
// xiao_spi → spi2 (SCK P1.13/D8, MOSI P1.15/D10, MISO P1.14/D9)
|
|
32
|
+
// xiao_serial → uart0 (TX P1.11/D6, RX P1.12/D7)
|
|
33
|
+
// The board DTS leaves i2c0/spi0/spi1 disabled (shared instances).
|
|
34
|
+
i2c: {
|
|
35
|
+
controllers: [{ nodeLabel: 'i2c1' }],
|
|
36
|
+
},
|
|
37
|
+
spi: {
|
|
38
|
+
controllers: [{ nodeLabel: 'spi2' }],
|
|
39
|
+
},
|
|
40
|
+
uart: {
|
|
41
|
+
controllers: [{ nodeLabel: 'uart0' }],
|
|
42
|
+
},
|
|
43
|
+
pwm: {
|
|
44
|
+
// pwm-led0 drives the board PWM LED (PWM_OUT0 on P0.17, inverted).
|
|
45
|
+
specs: [{ pin: 17, dtSpec: 'pwm-led0' }],
|
|
46
|
+
},
|
|
47
|
+
adc: {
|
|
48
|
+
// SAADC node is `adc`; no pre-declared channels. XIAO D0–D3 = AIN0–AIN3
|
|
49
|
+
// (P0.02/P0.03/P0.28/P0.29). Internal VREF ~0.6V with VDD/4 gain ⇒ 3000mV.
|
|
50
|
+
nodeLabel: 'adc',
|
|
51
|
+
resolution: 12,
|
|
52
|
+
vrefMv: 3000,
|
|
53
|
+
channels: [
|
|
54
|
+
{ pin: 2, channel: 0 }, // P0.02 / D0 / AIN0
|
|
55
|
+
{ pin: 3, channel: 1 }, // P0.03 / D1 / AIN1
|
|
56
|
+
{ pin: 28, channel: 2 }, // P0.28 / D2 / AIN2
|
|
57
|
+
{ pin: 29, channel: 3 }, // P0.29 / D3 / AIN3
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
wdt: { nodeLabel: 'wdt0' },
|
|
61
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface CapturedVariable {
|
|
2
|
+
name: string;
|
|
3
|
+
isFunction?: boolean;
|
|
4
|
+
/** Coarse C++ type category (inferred by the preprocessor without a checker). */
|
|
5
|
+
cppType?: 'bool' | 'int' | 'long' | 'float' | 'string' | 'unknown';
|
|
6
|
+
}
|
|
7
|
+
export interface LogMessagePart {
|
|
8
|
+
type: 'text' | 'variable';
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate the debug-mode banner. printk is always wired (Zephyr console), so
|
|
13
|
+
* there is no init to perform — just confirm debug instrumentation is live.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateZephyrInitCode(): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Generate Zephyr code for a breakpoint.
|
|
18
|
+
*
|
|
19
|
+
* The halt is __tc_debug_wait_for_continue() (emitted in ZephyrStrategy.shimLines
|
|
20
|
+
* under --debug): blocks until a byte arrives on the console, yielding to the
|
|
21
|
+
* scheduler between polls. The user must have a console attached (`west serial`
|
|
22
|
+
* or a terminal on the USB-CDC port); ENTER continues, 's' skips this breakpoint
|
|
23
|
+
* for the rest of the run.
|
|
24
|
+
*
|
|
25
|
+
* When `breakpointId` is set, the whole halt block is wrapped in
|
|
26
|
+
* `if (!__tc_bp_disabled_<id>)`, so a skipped breakpoint becomes a no-op until
|
|
27
|
+
* reboot. The flag is `static` so it persists across loop() iterations.
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateZephyrBreakpointCode(fileName: string, lineNum: number, originalLine: string, variables: CapturedVariable[], normalizedCondition: string | undefined, breakpointId?: number): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Generate Zephyr code for a logpoint (logs a message without halting).
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateZephyrLogpointCode(fileName: string, lineNum: number, parts: LogMessagePart[], variables: CapturedVariable[]): string[];
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr debug code generator — printk-based debug output
|
|
3
|
+
//
|
|
4
|
+
// The Zephyr counterpart to framework-esp32/src/debug-codegen.ts. Zephyr's
|
|
5
|
+
// minimal C++ config has no <iostream> (manifest: needsIostream=false,
|
|
6
|
+
// stdlibSupport.hasIostream=false), so the GenericStrategy's std::cout fallback
|
|
7
|
+
// used by the debug preprocessor would NOT compile. This module routes every
|
|
8
|
+
// debug line through printk("...\n") — the always-available Zephyr console,
|
|
9
|
+
// with no CONFIG_CONSOLE dependency — so `cuttlefish build --debug` produces
|
|
10
|
+
// compiling output.
|
|
11
|
+
//
|
|
12
|
+
// The halt primitive is __tc_debug_wait_for_continue() (emitted in
|
|
13
|
+
// ZephyrStrategy.shimLines when --debug is active): a k_console-input / busy
|
|
14
|
+
// poll that yields to the scheduler so an unattended breakpoint does not trip
|
|
15
|
+
// any watchdog. ENTER continues; 's' disables this breakpoint for the run.
|
|
16
|
+
//
|
|
17
|
+
// Used by ZephyrStrategy's generateDebug* overrides, dispatched by the
|
|
18
|
+
// cuttlefish debug preprocessor (cuttlefish/src/debug/preprocessor.ts).
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
/**
|
|
21
|
+
* Generate the debug-mode banner. printk is always wired (Zephyr console), so
|
|
22
|
+
* there is no init to perform — just confirm debug instrumentation is live.
|
|
23
|
+
*/
|
|
24
|
+
export function generateZephyrInitCode() {
|
|
25
|
+
return [
|
|
26
|
+
`// === DEBUG: Zephyr console (printk) ===`,
|
|
27
|
+
`printk("[TypeCAD] Debug Mode Active\\n");`,
|
|
28
|
+
`// === END DEBUG INIT ===`,
|
|
29
|
+
``,
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate Zephyr code for a breakpoint.
|
|
34
|
+
*
|
|
35
|
+
* The halt is __tc_debug_wait_for_continue() (emitted in ZephyrStrategy.shimLines
|
|
36
|
+
* under --debug): blocks until a byte arrives on the console, yielding to the
|
|
37
|
+
* scheduler between polls. The user must have a console attached (`west serial`
|
|
38
|
+
* or a terminal on the USB-CDC port); ENTER continues, 's' skips this breakpoint
|
|
39
|
+
* for the rest of the run.
|
|
40
|
+
*
|
|
41
|
+
* When `breakpointId` is set, the whole halt block is wrapped in
|
|
42
|
+
* `if (!__tc_bp_disabled_<id>)`, so a skipped breakpoint becomes a no-op until
|
|
43
|
+
* reboot. The flag is `static` so it persists across loop() iterations.
|
|
44
|
+
*/
|
|
45
|
+
export function generateZephyrBreakpointCode(fileName, lineNum, originalLine, variables, normalizedCondition, breakpointId) {
|
|
46
|
+
const lines = [];
|
|
47
|
+
const hasDisableGuard = breakpointId !== undefined;
|
|
48
|
+
// Each wrap level adds two spaces of indent: the disable guard (outermost)
|
|
49
|
+
// then the condition (if any).
|
|
50
|
+
const indent = ' ' + (hasDisableGuard ? ' ' : '') + (normalizedCondition ? ' ' : '');
|
|
51
|
+
lines.push(` // === BREAKPOINT: ${fileName}:${lineNum} ===`);
|
|
52
|
+
// Disable guard: a skipped breakpoint becomes a no-op until reboot. The
|
|
53
|
+
// disable state lives in the framework's debug shim (a static registry keyed
|
|
54
|
+
// by breakpointId) — NOT as a per-breakpoint declaration here, because the
|
|
55
|
+
// transpiler mangles `static bool` declarations injected into the source.
|
|
56
|
+
if (hasDisableGuard) {
|
|
57
|
+
lines.push(` if (!__tc_bp_is_disabled(${breakpointId})) {`);
|
|
58
|
+
}
|
|
59
|
+
if (normalizedCondition) {
|
|
60
|
+
lines.push(` ${hasDisableGuard ? ' ' : ''}if (${normalizedCondition}) {`);
|
|
61
|
+
}
|
|
62
|
+
lines.push(`${indent}printk("----------------------------------------\\n");`);
|
|
63
|
+
const headerText = normalizedCondition
|
|
64
|
+
? `[BREAK] ${fileName}:${lineNum} (condition: ${escapeString(normalizedCondition)})`
|
|
65
|
+
: `[BREAK] ${fileName}:${lineNum}`;
|
|
66
|
+
lines.push(`${indent}printk("%s\\n", "${headerText}");`);
|
|
67
|
+
lines.push(`${indent}printk(" %s\\n", "${escapeString(originalLine)}");`);
|
|
68
|
+
if (variables.length > 0) {
|
|
69
|
+
lines.push(`${indent}printk(" Variables:\\n");`);
|
|
70
|
+
for (const v of variables) {
|
|
71
|
+
if (v.isFunction) {
|
|
72
|
+
lines.push(`${indent}printk(" - %s = [function]\\n", "${v.name}");`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const { spec, arg } = formatSpecFor(v.name, v.cppType);
|
|
76
|
+
lines.push(`${indent}printk(" - %s = ${spec}\\n", "${v.name}", ${arg});`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
lines.push(`${indent}printk(" (no variables in scope)\\n");`);
|
|
82
|
+
}
|
|
83
|
+
lines.push(`${indent}printk(" [ENTER: continue | s: skip this breakpoint]\\n");`);
|
|
84
|
+
lines.push(`${indent}printk("----------------------------------------\\n");`);
|
|
85
|
+
// Halt — block on console input, scheduler-yielding. 's' records this id as
|
|
86
|
+
// disabled in the shim's registry. Pass -1 when no id (never disable).
|
|
87
|
+
lines.push(`${indent}__tc_debug_wait_for_continue(${hasDisableGuard ? breakpointId : -1});`);
|
|
88
|
+
if (normalizedCondition) {
|
|
89
|
+
lines.push(` ${hasDisableGuard ? ' ' : ''}}`);
|
|
90
|
+
}
|
|
91
|
+
if (hasDisableGuard) {
|
|
92
|
+
lines.push(` }`);
|
|
93
|
+
}
|
|
94
|
+
lines.push(` // === END BREAKPOINT ===`);
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generate Zephyr code for a logpoint (logs a message without halting).
|
|
99
|
+
*/
|
|
100
|
+
export function generateZephyrLogpointCode(fileName, lineNum, parts, variables) {
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(` // === LOGPOINT: ${fileName}:${lineNum} ===`);
|
|
103
|
+
lines.push(` printk("[LOG ${fileName}:${lineNum}] ");`);
|
|
104
|
+
for (const part of parts) {
|
|
105
|
+
if (part.type === 'text') {
|
|
106
|
+
lines.push(` printk("${escapeString(part.value)}");`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// variable part: only printable if it is a non-function value in scope.
|
|
110
|
+
const match = variables.find((v) => v.name === part.value && !v.isFunction);
|
|
111
|
+
if (match) {
|
|
112
|
+
const { spec, arg } = formatSpecFor(match.name, match.cppType);
|
|
113
|
+
lines.push(` printk("${spec}", ${arg});`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
lines.push(` printk("{${escapeString(part.value)}}"); // variable not in scope`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
lines.push(` printk("\\n");`);
|
|
121
|
+
lines.push(` // === END LOGPOINT ===`);
|
|
122
|
+
return lines;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Escape a string for use inside printk("..."). Same rules as the ESP-IDF
|
|
126
|
+
* generator: backslash, double-quote, newline, carriage return.
|
|
127
|
+
*/
|
|
128
|
+
function escapeString(s) {
|
|
129
|
+
return s
|
|
130
|
+
.replace(/\\/g, '\\\\')
|
|
131
|
+
.replace(/"/g, '\\"')
|
|
132
|
+
.replace(/\n/g, '\\n')
|
|
133
|
+
.replace(/\r/g, '\\r');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Pick a printk format specifier + argument expression for a debug variable.
|
|
137
|
+
*
|
|
138
|
+
* The debug preprocessor has no TypeChecker, so `cppType` is a coarse category
|
|
139
|
+
* inferred from AST shape. For `unknown` we cast to `double` and use `%g` so
|
|
140
|
+
* the generated printk always compiles for any numeric/bool variable. printk
|
|
141
|
+
* supports the standard printf format specifiers.
|
|
142
|
+
*
|
|
143
|
+
* Returns `{ spec, arg }` where `spec` is the `%`-specifier substring to embed
|
|
144
|
+
* in the format string and `arg` is the C++ argument expression.
|
|
145
|
+
*/
|
|
146
|
+
function formatSpecFor(varName, cppType) {
|
|
147
|
+
switch (cppType) {
|
|
148
|
+
case 'bool':
|
|
149
|
+
case 'int':
|
|
150
|
+
return { spec: '%d', arg: varName };
|
|
151
|
+
case 'long':
|
|
152
|
+
return { spec: '%ld', arg: varName };
|
|
153
|
+
case 'float':
|
|
154
|
+
return { spec: '%g', arg: varName };
|
|
155
|
+
case 'string':
|
|
156
|
+
return { spec: '%s', arg: varName };
|
|
157
|
+
case 'unknown':
|
|
158
|
+
default:
|
|
159
|
+
// Cast to double so printk compiles regardless of the real C++ type.
|
|
160
|
+
return { spec: '%g', arg: `static_cast<double>(${varName})` };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ZephyrDisplayProfile } from './profiles.js';
|
|
2
|
+
export interface DisplayRuntimeResult {
|
|
3
|
+
/** #include lines to force when the display is used. */
|
|
4
|
+
readonly includes: string[];
|
|
5
|
+
/** File-scope C++ declarations (device handle + line buffer). */
|
|
6
|
+
readonly stateLines: string[];
|
|
7
|
+
/** The C++ helper functions (display_init/fill_rect/draw_rect/draw_text/flush). */
|
|
8
|
+
readonly helpers: string;
|
|
9
|
+
/** The C++ font-table source (exposed for testing). */
|
|
10
|
+
readonly fontTable: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the display runtime C++ for a profile. The line buffer is one row
|
|
14
|
+
* (width pixels × 2 bytes rgb565); fill_rect/draw_rect/draw_text compute into
|
|
15
|
+
* it row-by-row and display_write each row. No full framebuffer.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildDisplayRuntime(profile: ZephyrDisplayProfile): DisplayRuntimeResult;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr display GFX runtime — generic display_write + ported GFX primitives
|
|
3
|
+
//
|
|
4
|
+
// Emits C++ that resolves a DT-bound display device, keeps a one-row line
|
|
5
|
+
// buffer (no full framebuffer — see AGENTS.md rendering guardrails), and
|
|
6
|
+
// implements fill_rect / draw_rect / draw_text / flush on top of display_write.
|
|
7
|
+
// Text uses a minimal 5x7 bitmap font covering digits 0-9 and uppercase A-Z
|
|
8
|
+
// only; unknown characters render as a space.
|
|
9
|
+
//
|
|
10
|
+
// EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
|
|
11
|
+
// Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// 5x7 bitmap glyphs for 0-9 and A-Z. Each glyph is 5 bytes (columns), MSB=top.
|
|
14
|
+
// Unknown characters fall back to the space (all-zero columns).
|
|
15
|
+
// (Compact hand-authored bitmaps; the test asserts presence of '0'..'9','A'..'Z'.)
|
|
16
|
+
function fontTableCpp() {
|
|
17
|
+
// A flat map: char -> 5 bytes. Built so the test sees '0'..'9' and 'A'..'Z'.
|
|
18
|
+
const glyphs = {
|
|
19
|
+
'0': [0x3e, 0x51, 0x49, 0x45, 0x3e],
|
|
20
|
+
'1': [0x00, 0x42, 0x7f, 0x40, 0x00],
|
|
21
|
+
'2': [0x42, 0x61, 0x51, 0x49, 0x46],
|
|
22
|
+
'3': [0x21, 0x41, 0x45, 0x4b, 0x31],
|
|
23
|
+
'4': [0x18, 0x14, 0x12, 0x7f, 0x10],
|
|
24
|
+
'5': [0x27, 0x45, 0x45, 0x45, 0x39],
|
|
25
|
+
'6': [0x3c, 0x4a, 0x49, 0x49, 0x30],
|
|
26
|
+
'7': [0x01, 0x71, 0x09, 0x05, 0x03],
|
|
27
|
+
'8': [0x36, 0x49, 0x49, 0x49, 0x36],
|
|
28
|
+
'9': [0x06, 0x49, 0x49, 0x29, 0x1e],
|
|
29
|
+
'A': [0x7e, 0x11, 0x11, 0x11, 0x7e],
|
|
30
|
+
'B': [0x7f, 0x49, 0x49, 0x49, 0x36],
|
|
31
|
+
'C': [0x3e, 0x41, 0x41, 0x41, 0x22],
|
|
32
|
+
'D': [0x7f, 0x41, 0x41, 0x22, 0x1c],
|
|
33
|
+
'E': [0x7f, 0x49, 0x49, 0x49, 0x41],
|
|
34
|
+
'F': [0x7f, 0x09, 0x09, 0x09, 0x01],
|
|
35
|
+
'G': [0x3e, 0x41, 0x49, 0x49, 0x7a],
|
|
36
|
+
'H': [0x7f, 0x08, 0x08, 0x08, 0x7f],
|
|
37
|
+
'I': [0x00, 0x41, 0x7f, 0x41, 0x00],
|
|
38
|
+
'J': [0x20, 0x40, 0x41, 0x3f, 0x01],
|
|
39
|
+
'K': [0x7f, 0x08, 0x14, 0x22, 0x41],
|
|
40
|
+
'L': [0x7f, 0x40, 0x40, 0x40, 0x40],
|
|
41
|
+
'M': [0x7f, 0x02, 0x0c, 0x02, 0x7f],
|
|
42
|
+
'N': [0x7f, 0x04, 0x08, 0x10, 0x7f],
|
|
43
|
+
'O': [0x3e, 0x41, 0x41, 0x41, 0x3e],
|
|
44
|
+
'P': [0x7f, 0x09, 0x09, 0x09, 0x06],
|
|
45
|
+
'Q': [0x3e, 0x41, 0x51, 0x21, 0x5e],
|
|
46
|
+
'R': [0x7f, 0x09, 0x19, 0x29, 0x46],
|
|
47
|
+
'S': [0x46, 0x49, 0x49, 0x49, 0x31],
|
|
48
|
+
'T': [0x01, 0x01, 0x7f, 0x01, 0x01],
|
|
49
|
+
'U': [0x3f, 0x40, 0x40, 0x40, 0x3f],
|
|
50
|
+
'V': [0x1f, 0x20, 0x40, 0x20, 0x1f],
|
|
51
|
+
'W': [0x3f, 0x40, 0x38, 0x40, 0x3f],
|
|
52
|
+
'X': [0x63, 0x14, 0x08, 0x14, 0x63],
|
|
53
|
+
'Y': [0x07, 0x08, 0x70, 0x08, 0x07],
|
|
54
|
+
'Z': [0x61, 0x51, 0x49, 0x45, 0x43],
|
|
55
|
+
};
|
|
56
|
+
const lines = [
|
|
57
|
+
'// Minimal 5x7 font: digits 0-9 and uppercase A-Z. Unknown chars → space.',
|
|
58
|
+
'// (Rows for other codes are all-zero — the table is indexed by char code.)',
|
|
59
|
+
'static const uint8_t __tc_font5x7[128][5] = {',
|
|
60
|
+
];
|
|
61
|
+
for (let c = 0; c < 128; c++) {
|
|
62
|
+
const ch = String.fromCharCode(c);
|
|
63
|
+
const known = Object.prototype.hasOwnProperty.call(glyphs, ch);
|
|
64
|
+
const g = glyphs[ch] ?? [0x00, 0x00, 0x00, 0x00, 0x00];
|
|
65
|
+
// Annotate known glyphs (0-9, A-Z) with their char literal; other rows use
|
|
66
|
+
// only the code point so the table carries no stray char literals.
|
|
67
|
+
const label = known ? ` '${ch}'` : ` ${c}`;
|
|
68
|
+
lines.push(` {0x${g[0].toString(16).padStart(2, '0')}, 0x${g[1].toString(16).padStart(2, '0')}, 0x${g[2].toString(16).padStart(2, '0')}, 0x${g[3].toString(16).padStart(2, '0')}, 0x${g[4].toString(16).padStart(2, '0')}}, //${label}`);
|
|
69
|
+
}
|
|
70
|
+
lines.push('};');
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the display runtime C++ for a profile. The line buffer is one row
|
|
75
|
+
* (width pixels × 2 bytes rgb565); fill_rect/draw_rect/draw_text compute into
|
|
76
|
+
* it row-by-row and display_write each row. No full framebuffer.
|
|
77
|
+
*/
|
|
78
|
+
export function buildDisplayRuntime(profile) {
|
|
79
|
+
const w = profile.width;
|
|
80
|
+
const stateLines = [
|
|
81
|
+
'// CUTTLEFISH_DISPLAY_BEGIN',
|
|
82
|
+
`static const struct device* __tc_display = DEVICE_DT_GET(DT_NODELABEL(${profile.dtLabel}));`,
|
|
83
|
+
`static uint16_t __tc_display_line[${w}]; // one-row line buffer (rgb565)`,
|
|
84
|
+
'// CUTTLEFISH_DISPLAY_END',
|
|
85
|
+
];
|
|
86
|
+
// Backlight is optional: the overlay emits the DT alias only when a backlight
|
|
87
|
+
// GPIO is configured. Guard with DT_HAS_ALIAS so this compiles whether or not
|
|
88
|
+
// the alias exists (DT_NODE_HAS_STATUS(DT_ALIAS(...)) is version-dependent
|
|
89
|
+
// when the alias is missing).
|
|
90
|
+
const blInit = profile.backlight
|
|
91
|
+
? `#if DT_HAS_ALIAS(${profile.backlight})\n const struct device* __bl = DEVICE_DT_GET(DT_ALIAS(${profile.backlight}));\n gpio_pin_configure(__bl, 0, GPIO_OUTPUT); gpio_pin_set(__bl, 0, 1);\n#endif`
|
|
92
|
+
: '';
|
|
93
|
+
const helpers = `
|
|
94
|
+
// Write a single row of \`rw\` rgb565 pixels at (x,y). Builds the
|
|
95
|
+
// display_buffer_descriptor the Zephyr display_write API requires (rgb565 =
|
|
96
|
+
// 2 bytes/pixel) and pushes the one-row line buffer.
|
|
97
|
+
static inline void __tc_display_write_row(uint16_t x, uint16_t y, uint16_t rw) {
|
|
98
|
+
struct display_buffer_descriptor __desc;
|
|
99
|
+
__desc.buf_size = static_cast<uint32_t>(rw) * 2U; // 2 bytes per rgb565 pixel
|
|
100
|
+
__desc.width = rw;
|
|
101
|
+
__desc.height = 1U;
|
|
102
|
+
__desc.pitch = rw;
|
|
103
|
+
__desc.frame_incomplete = false;
|
|
104
|
+
(void)display_write(__tc_display, x, y, &__desc, __tc_display_line);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static inline void display_init(void) {
|
|
108
|
+
if (!device_is_ready(__tc_display)) { for (;;) { k_msleep(1000); } }
|
|
109
|
+
${blInit}
|
|
110
|
+
display_blanking_off(__tc_display);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static inline void display_fill_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t h, uint16_t color) {
|
|
114
|
+
for (uint16_t row = 0; row < h; row++) {
|
|
115
|
+
for (uint16_t i = 0; i < rw; i++) { __tc_display_line[i] = color; }
|
|
116
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + row), rw);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static inline void display_draw_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t h, uint16_t color) {
|
|
121
|
+
// Top + bottom edges.
|
|
122
|
+
for (uint16_t i = 0; i < rw; i++) { __tc_display_line[i] = color; }
|
|
123
|
+
__tc_display_write_row(x, y, rw);
|
|
124
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + h - 1U), rw);
|
|
125
|
+
// Left + right edges.
|
|
126
|
+
for (uint16_t row = 1U; row < h - 1U; row++) {
|
|
127
|
+
__tc_display_line[0] = color;
|
|
128
|
+
if (rw > 1U) { __tc_display_line[rw - 1U] = color; }
|
|
129
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + row), rw);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static inline void display_draw_text(uint16_t x, uint16_t y, const char* text, uint16_t color) {
|
|
134
|
+
uint16_t cx = x;
|
|
135
|
+
for (const char* p = text; *p != 0; p++) {
|
|
136
|
+
// Read the byte UNSIGNED so bytes >= 0x80 don't index the 128-entry
|
|
137
|
+
// font table negatively (signed char would make 0x80 == -128).
|
|
138
|
+
uint8_t uc = static_cast<uint8_t>(*p);
|
|
139
|
+
if (uc >= 128U) { uc = static_cast<uint8_t>(' '); } // unknown → space
|
|
140
|
+
const uint8_t* glyph = &__tc_font5x7[uc][0];
|
|
141
|
+
// Lowercase → uppercase; anything outside 0-9/A-Z → space glyph.
|
|
142
|
+
if (uc >= static_cast<uint8_t>('a') && uc <= static_cast<uint8_t>('z')) {
|
|
143
|
+
glyph = &__tc_font5x7[uc - 32U][0];
|
|
144
|
+
} else if (uc < static_cast<uint8_t>('0')
|
|
145
|
+
|| (uc > static_cast<uint8_t>('9') && uc < static_cast<uint8_t>('A'))
|
|
146
|
+
|| uc > static_cast<uint8_t>('Z')) {
|
|
147
|
+
glyph = &__tc_font5x7[static_cast<uint8_t>(' ')][0];
|
|
148
|
+
}
|
|
149
|
+
for (uint16_t col = 0; col < 5U; col++) {
|
|
150
|
+
uint8_t bits = glyph[col];
|
|
151
|
+
for (uint16_t row = 0; row < 7U; row++) {
|
|
152
|
+
__tc_display_line[0] = ((bits & static_cast<uint8_t>(1U << row)) != 0U) ? color : 0U;
|
|
153
|
+
__tc_display_write_row(static_cast<uint16_t>(cx + col), static_cast<uint16_t>(y + row), 1U);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
cx = static_cast<uint16_t>(cx + 6U); // 5 cols + 1 space
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static inline void display_flush(void) {
|
|
161
|
+
// No-op: writes are immediate via display_write; there is no framebuffer to push.
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
const fontTable = fontTableCpp();
|
|
165
|
+
return {
|
|
166
|
+
includes: ['<zephyr/drivers/display.h>'],
|
|
167
|
+
stateLines,
|
|
168
|
+
helpers,
|
|
169
|
+
fontTable,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DisplayHALOp } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
import { type ZephyrDisplayProfile } from './profiles.js';
|
|
3
|
+
export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
|
|
4
|
+
export { zephyrTouchAdapter } from './touch-adapter.js';
|
|
5
|
+
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE } from './profiles.js';
|
|
6
|
+
export type { ZephyrDisplayProfile } from './profiles.js';
|
|
7
|
+
export interface DisplayState {
|
|
8
|
+
initialized: boolean;
|
|
9
|
+
profile: ZephyrDisplayProfile;
|
|
10
|
+
}
|
|
11
|
+
/** Fresh display state (used by the strategy per-build). */
|
|
12
|
+
export declare function newDisplayState(): DisplayState;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a display.* op. Returns { code } for statement ops, undefined when
|
|
15
|
+
* the op is not recognized.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveZephyrDisplayOp(op: DisplayHALOp, state: DisplayState): {
|
|
18
|
+
code?: string;
|
|
19
|
+
expression?: string;
|
|
20
|
+
} | undefined;
|