@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,207 @@
|
|
|
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
|
+
export interface CapturedVariable {
|
|
22
|
+
name: string;
|
|
23
|
+
isFunction?: boolean;
|
|
24
|
+
/** Coarse C++ type category (inferred by the preprocessor without a checker). */
|
|
25
|
+
cppType?: 'bool' | 'int' | 'long' | 'float' | 'string' | 'unknown';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LogMessagePart {
|
|
29
|
+
type: 'text' | 'variable';
|
|
30
|
+
value: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate the debug-mode banner. printk is always wired (Zephyr console), so
|
|
35
|
+
* there is no init to perform — just confirm debug instrumentation is live.
|
|
36
|
+
*/
|
|
37
|
+
export function generateZephyrInitCode(): string[] {
|
|
38
|
+
return [
|
|
39
|
+
`// === DEBUG: Zephyr console (printk) ===`,
|
|
40
|
+
`printk("[TypeCAD] Debug Mode Active\\n");`,
|
|
41
|
+
`// === END DEBUG INIT ===`,
|
|
42
|
+
``,
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate Zephyr code for a breakpoint.
|
|
48
|
+
*
|
|
49
|
+
* The halt is __tc_debug_wait_for_continue() (emitted in ZephyrStrategy.shimLines
|
|
50
|
+
* under --debug): blocks until a byte arrives on the console, yielding to the
|
|
51
|
+
* scheduler between polls. The user must have a console attached (`west serial`
|
|
52
|
+
* or a terminal on the USB-CDC port); ENTER continues, 's' skips this breakpoint
|
|
53
|
+
* for the rest of the run.
|
|
54
|
+
*
|
|
55
|
+
* When `breakpointId` is set, the whole halt block is wrapped in
|
|
56
|
+
* `if (!__tc_bp_disabled_<id>)`, so a skipped breakpoint becomes a no-op until
|
|
57
|
+
* reboot. The flag is `static` so it persists across loop() iterations.
|
|
58
|
+
*/
|
|
59
|
+
export function generateZephyrBreakpointCode(
|
|
60
|
+
fileName: string,
|
|
61
|
+
lineNum: number,
|
|
62
|
+
originalLine: string,
|
|
63
|
+
variables: CapturedVariable[],
|
|
64
|
+
normalizedCondition: string | undefined,
|
|
65
|
+
breakpointId?: number,
|
|
66
|
+
): string[] {
|
|
67
|
+
const lines: string[] = [];
|
|
68
|
+
const hasDisableGuard = breakpointId !== undefined;
|
|
69
|
+
// Each wrap level adds two spaces of indent: the disable guard (outermost)
|
|
70
|
+
// then the condition (if any).
|
|
71
|
+
const indent = ' ' + (hasDisableGuard ? ' ' : '') + (normalizedCondition ? ' ' : '');
|
|
72
|
+
|
|
73
|
+
lines.push(` // === BREAKPOINT: ${fileName}:${lineNum} ===`);
|
|
74
|
+
|
|
75
|
+
// Disable guard: a skipped breakpoint becomes a no-op until reboot. The
|
|
76
|
+
// disable state lives in the framework's debug shim (a static registry keyed
|
|
77
|
+
// by breakpointId) — NOT as a per-breakpoint declaration here, because the
|
|
78
|
+
// transpiler mangles `static bool` declarations injected into the source.
|
|
79
|
+
if (hasDisableGuard) {
|
|
80
|
+
lines.push(` if (!__tc_bp_is_disabled(${breakpointId})) {`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (normalizedCondition) {
|
|
84
|
+
lines.push(` ${hasDisableGuard ? ' ' : ''}if (${normalizedCondition}) {`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lines.push(`${indent}printk("----------------------------------------\\n");`);
|
|
88
|
+
|
|
89
|
+
const headerText = normalizedCondition
|
|
90
|
+
? `[BREAK] ${fileName}:${lineNum} (condition: ${escapeString(normalizedCondition)})`
|
|
91
|
+
: `[BREAK] ${fileName}:${lineNum}`;
|
|
92
|
+
lines.push(`${indent}printk("%s\\n", "${headerText}");`);
|
|
93
|
+
|
|
94
|
+
lines.push(`${indent}printk(" %s\\n", "${escapeString(originalLine)}");`);
|
|
95
|
+
|
|
96
|
+
if (variables.length > 0) {
|
|
97
|
+
lines.push(`${indent}printk(" Variables:\\n");`);
|
|
98
|
+
for (const v of variables) {
|
|
99
|
+
if (v.isFunction) {
|
|
100
|
+
lines.push(`${indent}printk(" - %s = [function]\\n", "${v.name}");`);
|
|
101
|
+
} else {
|
|
102
|
+
const { spec, arg } = formatSpecFor(v.name, v.cppType);
|
|
103
|
+
lines.push(`${indent}printk(" - %s = ${spec}\\n", "${v.name}", ${arg});`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
lines.push(`${indent}printk(" (no variables in scope)\\n");`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
lines.push(`${indent}printk(" [ENTER: continue | s: skip this breakpoint]\\n");`);
|
|
111
|
+
lines.push(`${indent}printk("----------------------------------------\\n");`);
|
|
112
|
+
|
|
113
|
+
// Halt — block on console input, scheduler-yielding. 's' records this id as
|
|
114
|
+
// disabled in the shim's registry. Pass -1 when no id (never disable).
|
|
115
|
+
lines.push(`${indent}__tc_debug_wait_for_continue(${hasDisableGuard ? breakpointId : -1});`);
|
|
116
|
+
|
|
117
|
+
if (normalizedCondition) {
|
|
118
|
+
lines.push(` ${hasDisableGuard ? ' ' : ''}}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (hasDisableGuard) {
|
|
122
|
+
lines.push(` }`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
lines.push(` // === END BREAKPOINT ===`);
|
|
126
|
+
|
|
127
|
+
return lines;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate Zephyr code for a logpoint (logs a message without halting).
|
|
132
|
+
*/
|
|
133
|
+
export function generateZephyrLogpointCode(
|
|
134
|
+
fileName: string,
|
|
135
|
+
lineNum: number,
|
|
136
|
+
parts: LogMessagePart[],
|
|
137
|
+
variables: CapturedVariable[],
|
|
138
|
+
): string[] {
|
|
139
|
+
const lines: string[] = [];
|
|
140
|
+
|
|
141
|
+
lines.push(` // === LOGPOINT: ${fileName}:${lineNum} ===`);
|
|
142
|
+
lines.push(` printk("[LOG ${fileName}:${lineNum}] ");`);
|
|
143
|
+
|
|
144
|
+
for (const part of parts) {
|
|
145
|
+
if (part.type === 'text') {
|
|
146
|
+
lines.push(` printk("${escapeString(part.value)}");`);
|
|
147
|
+
} else {
|
|
148
|
+
// variable part: only printable if it is a non-function value in scope.
|
|
149
|
+
const match = variables.find((v) => v.name === part.value && !v.isFunction);
|
|
150
|
+
if (match) {
|
|
151
|
+
const { spec, arg } = formatSpecFor(match.name, match.cppType);
|
|
152
|
+
lines.push(` printk("${spec}", ${arg});`);
|
|
153
|
+
} else {
|
|
154
|
+
lines.push(` printk("{${escapeString(part.value)}}"); // variable not in scope`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
lines.push(` printk("\\n");`);
|
|
160
|
+
lines.push(` // === END LOGPOINT ===`);
|
|
161
|
+
|
|
162
|
+
return lines;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Escape a string for use inside printk("..."). Same rules as the ESP-IDF
|
|
167
|
+
* generator: backslash, double-quote, newline, carriage return.
|
|
168
|
+
*/
|
|
169
|
+
function escapeString(s: string): string {
|
|
170
|
+
return s
|
|
171
|
+
.replace(/\\/g, '\\\\')
|
|
172
|
+
.replace(/"/g, '\\"')
|
|
173
|
+
.replace(/\n/g, '\\n')
|
|
174
|
+
.replace(/\r/g, '\\r');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Pick a printk format specifier + argument expression for a debug variable.
|
|
179
|
+
*
|
|
180
|
+
* The debug preprocessor has no TypeChecker, so `cppType` is a coarse category
|
|
181
|
+
* inferred from AST shape. For `unknown` we cast to `double` and use `%g` so
|
|
182
|
+
* the generated printk always compiles for any numeric/bool variable. printk
|
|
183
|
+
* supports the standard printf format specifiers.
|
|
184
|
+
*
|
|
185
|
+
* Returns `{ spec, arg }` where `spec` is the `%`-specifier substring to embed
|
|
186
|
+
* in the format string and `arg` is the C++ argument expression.
|
|
187
|
+
*/
|
|
188
|
+
function formatSpecFor(
|
|
189
|
+
varName: string,
|
|
190
|
+
cppType: CapturedVariable['cppType'],
|
|
191
|
+
): { spec: string; arg: string } {
|
|
192
|
+
switch (cppType) {
|
|
193
|
+
case 'bool':
|
|
194
|
+
case 'int':
|
|
195
|
+
return { spec: '%d', arg: varName };
|
|
196
|
+
case 'long':
|
|
197
|
+
return { spec: '%ld', arg: varName };
|
|
198
|
+
case 'float':
|
|
199
|
+
return { spec: '%g', arg: varName };
|
|
200
|
+
case 'string':
|
|
201
|
+
return { spec: '%s', arg: varName };
|
|
202
|
+
case 'unknown':
|
|
203
|
+
default:
|
|
204
|
+
// Cast to double so printk compiles regardless of the real C++ type.
|
|
205
|
+
return { spec: '%g', arg: `static_cast<double>(${varName})` };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
|
|
14
|
+
import type { ZephyrDisplayProfile } from './profiles.js';
|
|
15
|
+
|
|
16
|
+
export interface DisplayRuntimeResult {
|
|
17
|
+
/** #include lines to force when the display is used. */
|
|
18
|
+
readonly includes: string[];
|
|
19
|
+
/** File-scope C++ declarations (device handle + line buffer). */
|
|
20
|
+
readonly stateLines: string[];
|
|
21
|
+
/** The C++ helper functions (display_init/fill_rect/draw_rect/draw_text/flush). */
|
|
22
|
+
readonly helpers: string;
|
|
23
|
+
/** The C++ font-table source (exposed for testing). */
|
|
24
|
+
readonly fontTable: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 5x7 bitmap glyphs for 0-9 and A-Z. Each glyph is 5 bytes (columns), MSB=top.
|
|
28
|
+
// Unknown characters fall back to the space (all-zero columns).
|
|
29
|
+
// (Compact hand-authored bitmaps; the test asserts presence of '0'..'9','A'..'Z'.)
|
|
30
|
+
function fontTableCpp(): string {
|
|
31
|
+
// A flat map: char -> 5 bytes. Built so the test sees '0'..'9' and 'A'..'Z'.
|
|
32
|
+
const glyphs: Record<string, number[]> = {
|
|
33
|
+
'0': [0x3e, 0x51, 0x49, 0x45, 0x3e],
|
|
34
|
+
'1': [0x00, 0x42, 0x7f, 0x40, 0x00],
|
|
35
|
+
'2': [0x42, 0x61, 0x51, 0x49, 0x46],
|
|
36
|
+
'3': [0x21, 0x41, 0x45, 0x4b, 0x31],
|
|
37
|
+
'4': [0x18, 0x14, 0x12, 0x7f, 0x10],
|
|
38
|
+
'5': [0x27, 0x45, 0x45, 0x45, 0x39],
|
|
39
|
+
'6': [0x3c, 0x4a, 0x49, 0x49, 0x30],
|
|
40
|
+
'7': [0x01, 0x71, 0x09, 0x05, 0x03],
|
|
41
|
+
'8': [0x36, 0x49, 0x49, 0x49, 0x36],
|
|
42
|
+
'9': [0x06, 0x49, 0x49, 0x29, 0x1e],
|
|
43
|
+
'A': [0x7e, 0x11, 0x11, 0x11, 0x7e],
|
|
44
|
+
'B': [0x7f, 0x49, 0x49, 0x49, 0x36],
|
|
45
|
+
'C': [0x3e, 0x41, 0x41, 0x41, 0x22],
|
|
46
|
+
'D': [0x7f, 0x41, 0x41, 0x22, 0x1c],
|
|
47
|
+
'E': [0x7f, 0x49, 0x49, 0x49, 0x41],
|
|
48
|
+
'F': [0x7f, 0x09, 0x09, 0x09, 0x01],
|
|
49
|
+
'G': [0x3e, 0x41, 0x49, 0x49, 0x7a],
|
|
50
|
+
'H': [0x7f, 0x08, 0x08, 0x08, 0x7f],
|
|
51
|
+
'I': [0x00, 0x41, 0x7f, 0x41, 0x00],
|
|
52
|
+
'J': [0x20, 0x40, 0x41, 0x3f, 0x01],
|
|
53
|
+
'K': [0x7f, 0x08, 0x14, 0x22, 0x41],
|
|
54
|
+
'L': [0x7f, 0x40, 0x40, 0x40, 0x40],
|
|
55
|
+
'M': [0x7f, 0x02, 0x0c, 0x02, 0x7f],
|
|
56
|
+
'N': [0x7f, 0x04, 0x08, 0x10, 0x7f],
|
|
57
|
+
'O': [0x3e, 0x41, 0x41, 0x41, 0x3e],
|
|
58
|
+
'P': [0x7f, 0x09, 0x09, 0x09, 0x06],
|
|
59
|
+
'Q': [0x3e, 0x41, 0x51, 0x21, 0x5e],
|
|
60
|
+
'R': [0x7f, 0x09, 0x19, 0x29, 0x46],
|
|
61
|
+
'S': [0x46, 0x49, 0x49, 0x49, 0x31],
|
|
62
|
+
'T': [0x01, 0x01, 0x7f, 0x01, 0x01],
|
|
63
|
+
'U': [0x3f, 0x40, 0x40, 0x40, 0x3f],
|
|
64
|
+
'V': [0x1f, 0x20, 0x40, 0x20, 0x1f],
|
|
65
|
+
'W': [0x3f, 0x40, 0x38, 0x40, 0x3f],
|
|
66
|
+
'X': [0x63, 0x14, 0x08, 0x14, 0x63],
|
|
67
|
+
'Y': [0x07, 0x08, 0x70, 0x08, 0x07],
|
|
68
|
+
'Z': [0x61, 0x51, 0x49, 0x45, 0x43],
|
|
69
|
+
};
|
|
70
|
+
const lines: string[] = [
|
|
71
|
+
'// Minimal 5x7 font: digits 0-9 and uppercase A-Z. Unknown chars → space.',
|
|
72
|
+
'// (Rows for other codes are all-zero — the table is indexed by char code.)',
|
|
73
|
+
'static const uint8_t __tc_font5x7[128][5] = {',
|
|
74
|
+
];
|
|
75
|
+
for (let c = 0; c < 128; c++) {
|
|
76
|
+
const ch = String.fromCharCode(c);
|
|
77
|
+
const known = Object.prototype.hasOwnProperty.call(glyphs, ch);
|
|
78
|
+
const g = glyphs[ch] ?? [0x00, 0x00, 0x00, 0x00, 0x00];
|
|
79
|
+
// Annotate known glyphs (0-9, A-Z) with their char literal; other rows use
|
|
80
|
+
// only the code point so the table carries no stray char literals.
|
|
81
|
+
const label = known ? ` '${ch}'` : ` ${c}`;
|
|
82
|
+
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}`);
|
|
83
|
+
}
|
|
84
|
+
lines.push('};');
|
|
85
|
+
return lines.join('\n');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Build the display runtime C++ for a profile. The line buffer is one row
|
|
90
|
+
* (width pixels × 2 bytes rgb565); fill_rect/draw_rect/draw_text compute into
|
|
91
|
+
* it row-by-row and display_write each row. No full framebuffer.
|
|
92
|
+
*/
|
|
93
|
+
export function buildDisplayRuntime(profile: ZephyrDisplayProfile): DisplayRuntimeResult {
|
|
94
|
+
const w = profile.width;
|
|
95
|
+
const stateLines: string[] = [
|
|
96
|
+
'// CUTTLEFISH_DISPLAY_BEGIN',
|
|
97
|
+
`static const struct device* __tc_display = DEVICE_DT_GET(DT_NODELABEL(${profile.dtLabel}));`,
|
|
98
|
+
`static uint16_t __tc_display_line[${w}]; // one-row line buffer (rgb565)`,
|
|
99
|
+
'// CUTTLEFISH_DISPLAY_END',
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
// Backlight is optional: the overlay emits the DT alias only when a backlight
|
|
103
|
+
// GPIO is configured. Guard with DT_HAS_ALIAS so this compiles whether or not
|
|
104
|
+
// the alias exists (DT_NODE_HAS_STATUS(DT_ALIAS(...)) is version-dependent
|
|
105
|
+
// when the alias is missing).
|
|
106
|
+
const blInit = profile.backlight
|
|
107
|
+
? `#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`
|
|
108
|
+
: '';
|
|
109
|
+
|
|
110
|
+
const helpers = `
|
|
111
|
+
// Write a single row of \`rw\` rgb565 pixels at (x,y). Builds the
|
|
112
|
+
// display_buffer_descriptor the Zephyr display_write API requires (rgb565 =
|
|
113
|
+
// 2 bytes/pixel) and pushes the one-row line buffer.
|
|
114
|
+
static inline void __tc_display_write_row(uint16_t x, uint16_t y, uint16_t rw) {
|
|
115
|
+
struct display_buffer_descriptor __desc;
|
|
116
|
+
__desc.buf_size = static_cast<uint32_t>(rw) * 2U; // 2 bytes per rgb565 pixel
|
|
117
|
+
__desc.width = rw;
|
|
118
|
+
__desc.height = 1U;
|
|
119
|
+
__desc.pitch = rw;
|
|
120
|
+
__desc.frame_incomplete = false;
|
|
121
|
+
(void)display_write(__tc_display, x, y, &__desc, __tc_display_line);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static inline void display_init(void) {
|
|
125
|
+
if (!device_is_ready(__tc_display)) { for (;;) { k_msleep(1000); } }
|
|
126
|
+
${blInit}
|
|
127
|
+
display_blanking_off(__tc_display);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static inline void display_fill_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t h, uint16_t color) {
|
|
131
|
+
for (uint16_t row = 0; row < h; row++) {
|
|
132
|
+
for (uint16_t i = 0; i < rw; i++) { __tc_display_line[i] = color; }
|
|
133
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + row), rw);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static inline void display_draw_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t h, uint16_t color) {
|
|
138
|
+
// Top + bottom edges.
|
|
139
|
+
for (uint16_t i = 0; i < rw; i++) { __tc_display_line[i] = color; }
|
|
140
|
+
__tc_display_write_row(x, y, rw);
|
|
141
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + h - 1U), rw);
|
|
142
|
+
// Left + right edges.
|
|
143
|
+
for (uint16_t row = 1U; row < h - 1U; row++) {
|
|
144
|
+
__tc_display_line[0] = color;
|
|
145
|
+
if (rw > 1U) { __tc_display_line[rw - 1U] = color; }
|
|
146
|
+
__tc_display_write_row(x, static_cast<uint16_t>(y + row), rw);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static inline void display_draw_text(uint16_t x, uint16_t y, const char* text, uint16_t color) {
|
|
151
|
+
uint16_t cx = x;
|
|
152
|
+
for (const char* p = text; *p != 0; p++) {
|
|
153
|
+
// Read the byte UNSIGNED so bytes >= 0x80 don't index the 128-entry
|
|
154
|
+
// font table negatively (signed char would make 0x80 == -128).
|
|
155
|
+
uint8_t uc = static_cast<uint8_t>(*p);
|
|
156
|
+
if (uc >= 128U) { uc = static_cast<uint8_t>(' '); } // unknown → space
|
|
157
|
+
const uint8_t* glyph = &__tc_font5x7[uc][0];
|
|
158
|
+
// Lowercase → uppercase; anything outside 0-9/A-Z → space glyph.
|
|
159
|
+
if (uc >= static_cast<uint8_t>('a') && uc <= static_cast<uint8_t>('z')) {
|
|
160
|
+
glyph = &__tc_font5x7[uc - 32U][0];
|
|
161
|
+
} else if (uc < static_cast<uint8_t>('0')
|
|
162
|
+
|| (uc > static_cast<uint8_t>('9') && uc < static_cast<uint8_t>('A'))
|
|
163
|
+
|| uc > static_cast<uint8_t>('Z')) {
|
|
164
|
+
glyph = &__tc_font5x7[static_cast<uint8_t>(' ')][0];
|
|
165
|
+
}
|
|
166
|
+
for (uint16_t col = 0; col < 5U; col++) {
|
|
167
|
+
uint8_t bits = glyph[col];
|
|
168
|
+
for (uint16_t row = 0; row < 7U; row++) {
|
|
169
|
+
__tc_display_line[0] = ((bits & static_cast<uint8_t>(1U << row)) != 0U) ? color : 0U;
|
|
170
|
+
__tc_display_write_row(static_cast<uint16_t>(cx + col), static_cast<uint16_t>(y + row), 1U);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
cx = static_cast<uint16_t>(cx + 6U); // 5 cols + 1 space
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
static inline void display_flush(void) {
|
|
178
|
+
// No-op: writes are immediate via display_write; there is no framebuffer to push.
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
|
|
182
|
+
const fontTable = fontTableCpp();
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
includes: ['<zephyr/drivers/display.h>'],
|
|
186
|
+
stateLines,
|
|
187
|
+
helpers,
|
|
188
|
+
fontTable,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr display op resolver — maps DisplayHALOp → GFX runtime calls
|
|
3
|
+
//
|
|
4
|
+
// resolveZephyrDisplayOp is a pure function of (op, state): the strategy holds
|
|
5
|
+
// a DisplayState (mirrors Arduino's _displayCtx) and passes it in. On
|
|
6
|
+
// display.init the state is seeded with the active profile; subsequent ops
|
|
7
|
+
// lower to the helpers in gfx.ts.
|
|
8
|
+
//
|
|
9
|
+
// Validator probes call fill_rect/draw_text/draw_rect/flush WITHOUT a prior
|
|
10
|
+
// init, so when state is uninitialized we seed the DEFAULT profile and lower
|
|
11
|
+
// anyway (the probe only checks code/expression is non-undefined).
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
import type { DisplayHALOp } from '@typecad/cuttlefish/api/shared';
|
|
15
|
+
import { DEFAULT_ZEPHYR_DISPLAY_PROFILE, type ZephyrDisplayProfile } from './profiles.js';
|
|
16
|
+
|
|
17
|
+
// Re-export the UI display/touch adapters + profile registry so the strategy
|
|
18
|
+
// and consumers can reach them from the package barrel.
|
|
19
|
+
export { zephyrUiDisplayAdapter, zephyrDisplayAdapterGenerator } from './ui-adapter.js';
|
|
20
|
+
export { zephyrTouchAdapter } from './touch-adapter.js';
|
|
21
|
+
export { ZEPHYR_DISPLAY_PROFILES, DEFAULT_ZEPHYR_DISPLAY_PROFILE } from './profiles.js';
|
|
22
|
+
export type { ZephyrDisplayProfile } from './profiles.js';
|
|
23
|
+
|
|
24
|
+
export interface DisplayState {
|
|
25
|
+
initialized: boolean;
|
|
26
|
+
profile: ZephyrDisplayProfile;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Fresh display state (used by the strategy per-build). */
|
|
30
|
+
export function newDisplayState(): DisplayState {
|
|
31
|
+
return { initialized: false, profile: DEFAULT_ZEPHYR_DISPLAY_PROFILE };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a display.* op. Returns { code } for statement ops, undefined when
|
|
36
|
+
* the op is not recognized.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveZephyrDisplayOp(
|
|
39
|
+
op: DisplayHALOp,
|
|
40
|
+
state: DisplayState,
|
|
41
|
+
): { code?: string; expression?: string } | undefined {
|
|
42
|
+
const o = op as any;
|
|
43
|
+
|
|
44
|
+
// display.init seeds the state. The probe payload for init has no profile
|
|
45
|
+
// fields, so use the default profile.
|
|
46
|
+
if (op.operation === 'display.init') {
|
|
47
|
+
state.initialized = true;
|
|
48
|
+
return { code: 'display_init();' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// For non-init ops: if not initialized (validator probe path), seed the
|
|
52
|
+
// default so the op still lowers.
|
|
53
|
+
if (!state.initialized) {
|
|
54
|
+
state.initialized = true;
|
|
55
|
+
state.profile = DEFAULT_ZEPHYR_DISPLAY_PROFILE;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
switch (op.operation) {
|
|
59
|
+
case 'display.fill_rect':
|
|
60
|
+
return { code: `display_fill_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
|
|
61
|
+
case 'display.draw_rect':
|
|
62
|
+
return { code: `display_draw_rect(${o.x}, ${o.y}, ${o.w}, ${o.h}, ${o.color});` };
|
|
63
|
+
case 'display.draw_text':
|
|
64
|
+
return { code: `display_draw_text(${o.x}, ${o.y}, "${o.text}", ${o.color});` };
|
|
65
|
+
case 'display.flush':
|
|
66
|
+
return { code: 'display_flush();' };
|
|
67
|
+
default:
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr display profiles — DT-binding descriptors
|
|
3
|
+
//
|
|
4
|
+
// Analog of framework-arduino/src/displays/, but profiles describe a Zephyr
|
|
5
|
+
// devicetree node (DT_NODELABEL) to resolve via DEVICE_DT_GET, not Adafruit
|
|
6
|
+
// pin wiring. The GFX runtime (gfx.ts) reads width/height/colorFormat from the
|
|
7
|
+
// active profile to size its line buffer.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export interface ZephyrDisplayProfile {
|
|
11
|
+
/** Driver id, matched against ctx.frameworkData.display / manifest drivers. */
|
|
12
|
+
readonly driver: string;
|
|
13
|
+
/** Devicetree nodelabel, e.g. 'display0'. Emitted as DT_NODELABEL(<dtLabel>). */
|
|
14
|
+
readonly dtLabel: string;
|
|
15
|
+
/** Effective (screen-space) dimensions after rotation. */
|
|
16
|
+
readonly width: number;
|
|
17
|
+
readonly height: number;
|
|
18
|
+
/** Native panel dimensions before rotation, when they differ from the
|
|
19
|
+
* effective size (e.g. a 320x480 panel mounted landscape = 480x320). */
|
|
20
|
+
readonly nativeWidth?: number;
|
|
21
|
+
readonly nativeHeight?: number;
|
|
22
|
+
readonly colorFormat: 'rgb565' | 'mono';
|
|
23
|
+
/** Applied via display_set_orientation (0/90/180/270). */
|
|
24
|
+
readonly rotation?: number;
|
|
25
|
+
/** DT alias for the backlight GPIO (set high at init), if any. */
|
|
26
|
+
readonly backlight?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Built-in profile registry. Looked up by driver id. Add a profile here when a
|
|
31
|
+
* new board's display node is wired into its devicetree.
|
|
32
|
+
*/
|
|
33
|
+
export const ZEPHYR_DISPLAY_PROFILES: Record<string, ZephyrDisplayProfile> = {
|
|
34
|
+
'ili9341-zephyr': {
|
|
35
|
+
driver: 'ili9341-zephyr',
|
|
36
|
+
dtLabel: 'display0',
|
|
37
|
+
width: 320,
|
|
38
|
+
height: 240,
|
|
39
|
+
colorFormat: 'rgb565',
|
|
40
|
+
rotation: 90,
|
|
41
|
+
backlight: 'backlight',
|
|
42
|
+
},
|
|
43
|
+
'st7796-zephyr': {
|
|
44
|
+
// ST7796S SPI TFT, 320x480 RGB565 mounted landscape (effective 480x320).
|
|
45
|
+
// Same DT nodelabel convention as the ILI9341 — the board's devicetree
|
|
46
|
+
// carries the `display0` node bound to the ST7796 driver; the overlay
|
|
47
|
+
// enables it via status="okay". Effective dims match the Arduino
|
|
48
|
+
// st7796-spi profile (480x320 landscape, rotation 1).
|
|
49
|
+
driver: 'st7796-zephyr',
|
|
50
|
+
dtLabel: 'display0',
|
|
51
|
+
width: 480,
|
|
52
|
+
height: 320,
|
|
53
|
+
nativeWidth: 320,
|
|
54
|
+
nativeHeight: 480,
|
|
55
|
+
colorFormat: 'rgb565',
|
|
56
|
+
rotation: 1,
|
|
57
|
+
backlight: 'backlight',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** The default profile used when resolveDisplayOp is probed without a display.init. */
|
|
62
|
+
export const DEFAULT_ZEPHYR_DISPLAY_PROFILE: ZephyrDisplayProfile =
|
|
63
|
+
ZEPHYR_DISPLAY_PROFILES['ili9341-zephyr'];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Zephyr touch adapter for the Cuttlefish UI rendering pipeline.
|
|
3
|
+
//
|
|
4
|
+
// FT6336U capacitive touch controller over I2C, driven via Zephyr's
|
|
5
|
+
// device-tree-bound I2C API (i2c_write_read_dt). This is the Zephyr analog of
|
|
6
|
+
// the Arduino FT6336U adapter in framework-arduino (touch-adapters-codegen.ts)
|
|
7
|
+
// and the stale native ESP32 reference (demo-display/.../main.cc).
|
|
8
|
+
//
|
|
9
|
+
// The touch node is resolved via DEVICE_DT_GET(DT_NODELABEL(ft6336u)). The
|
|
10
|
+
// board's devicetree must carry an `ft6336u` nodelabel with the I2C spec +
|
|
11
|
+
// the `focaltech,ft6336u` (or compatible) binding so the device is ready.
|
|
12
|
+
//
|
|
13
|
+
// EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
|
|
14
|
+
// Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
import type { TouchAdapterCodegen } from "@typecad/cuttlefish/api/shared";
|
|
18
|
+
import type { TouchProfile } from "@typecad/cuttlefish/api/shared";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generate the FT6336U touch adapter for Zephyr. Emits touch_init /
|
|
22
|
+
* touch_isTouched / touch_readRaw — the three symbols the runtime's
|
|
23
|
+
* ui_poll_touch body calls. Returns undefined for other libraries so the
|
|
24
|
+
* strategy can decline.
|
|
25
|
+
*/
|
|
26
|
+
export function zephyrTouchAdapter(touch: TouchProfile): TouchAdapterCodegen | undefined {
|
|
27
|
+
if (touch.library !== "FT6336U") return undefined;
|
|
28
|
+
|
|
29
|
+
const addr = touch.i2cAddress ?? 0x38;
|
|
30
|
+
const addrHex = "0x" + addr.toString(16).toUpperCase();
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
includes: [
|
|
34
|
+
`#include <zephyr/drivers/i2c.h>`,
|
|
35
|
+
],
|
|
36
|
+
declaration: [
|
|
37
|
+
`// Zephyr FT6336U touch — I2C device resolved via devicetree.`,
|
|
38
|
+
`// The board DT must define an 'ft6336u' nodelabel on an I2C bus.`,
|
|
39
|
+
`static const struct i2c_dt_spec __tc_touch = I2C_DT_SPEC_GET(DT_NODELABEL(ft6336u));`,
|
|
40
|
+
`static int16_t __tc_touch_cached_x = 0;`,
|
|
41
|
+
`static int16_t __tc_touch_cached_y = 0;`,
|
|
42
|
+
`static int16_t __tc_touch_cached_z = 0;`,
|
|
43
|
+
`static uint8_t __tc_touch_cached_valid = 0;`,
|
|
44
|
+
].join("\n"),
|
|
45
|
+
functions: [
|
|
46
|
+
`// Read N bytes starting from a register address. Returns 1 on success.`,
|
|
47
|
+
`// Uses i2c_write_read_dt for a combined write-read transaction (register`,
|
|
48
|
+
`// address write, then repeated-start read). The FT6336U is polled every`,
|
|
49
|
+
`// frame from ui_poll_touch, so a failed read (stuck SDA, no controller) is`,
|
|
50
|
+
`// non-fatal — returns 0 and touch_isTreated() returns false.`,
|
|
51
|
+
`static inline uint8_t __tc_touch_read_block(uint8_t reg, uint8_t* buf, uint8_t len) {`,
|
|
52
|
+
` if (!device_is_ready(__tc_touch.bus)) return 0U;`,
|
|
53
|
+
` int rc = i2c_write_read_dt(&__tc_touch, ®, 1U, buf, len);`,
|
|
54
|
+
` return (rc == 0) ? 1U : 0U;`,
|
|
55
|
+
`}`,
|
|
56
|
+
``,
|
|
57
|
+
`static inline void touch_init() {`,
|
|
58
|
+
` // The I2C bus + device are configured by devicetree. The FT6336U needs a`,
|
|
59
|
+
` // hardware reset (reset pin low → high) to enter normal mode after power-on.`,
|
|
60
|
+
`#if DT_NODE_HAS_STATUS(DT_NODELABEL(ft6336u), okay) && DT_NODE_HAS_PROP(DT_NODELABEL(ft6336u), reset_gpios)`,
|
|
61
|
+
` static const struct gpio_dt_spec __tc_touch_rst = GPIO_DT_SPEC_GET(DT_NODELABEL(ft6336u), reset_gpios);`,
|
|
62
|
+
` if (device_is_ready(__tc_touch_rst.port)) {`,
|
|
63
|
+
` gpio_pin_configure_dt(&__tc_touch_rst, GPIO_OUTPUT_ACTIVE); // assert reset (active=low)`,
|
|
64
|
+
` k_msleep(10);`,
|
|
65
|
+
` gpio_pin_set_dt(&__tc_touch_rst, 0); // release reset (inactive=high)`,
|
|
66
|
+
` k_msleep(200); // FT6336U needs ~50ms after reset before responding`,
|
|
67
|
+
` }`,
|
|
68
|
+
`#endif`,
|
|
69
|
+
` (void)device_is_ready(__tc_touch.bus);`,
|
|
70
|
+
`}`,
|
|
71
|
+
``,
|
|
72
|
+
`static inline bool touch_isTouched() {`,
|
|
73
|
+
` __tc_touch_cached_valid = 0;`,
|
|
74
|
+
` __tc_touch_cached_z = 0;`,
|
|
75
|
+
` // Read TD_STATUS (0x02) + P1_XH/XL/YH/YL — 5 bytes in one transaction.`,
|
|
76
|
+
` uint8_t buf[5] = {0, 0, 0, 0, 0};`,
|
|
77
|
+
` if (!__tc_touch_read_block(0x02, buf, 5U)) return false;`,
|
|
78
|
+
` uint8_t count = buf[0] & 0x0F;`,
|
|
79
|
+
` if (count == 0U) return false;`,
|
|
80
|
+
` __tc_touch_cached_x = static_cast<int16_t>((static_cast<uint16_t>(buf[1] & 0x0F) << 8) | buf[2]);`,
|
|
81
|
+
` __tc_touch_cached_y = static_cast<int16_t>((static_cast<uint16_t>(buf[3] & 0x0F) << 8) | buf[4]);`,
|
|
82
|
+
` __tc_touch_cached_z = 255;`,
|
|
83
|
+
` __tc_touch_cached_valid = 1;`,
|
|
84
|
+
` return true;`,
|
|
85
|
+
`}`,
|
|
86
|
+
``,
|
|
87
|
+
`static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
|
|
88
|
+
` // touch_isTouched() is polled each frame by ui_poll_touch; the cached`,
|
|
89
|
+
` // values are fresh. If something calls readRaw before isTouched, probe now.`,
|
|
90
|
+
` if (!__tc_touch_cached_valid) {`,
|
|
91
|
+
` (void)touch_isTouched();`,
|
|
92
|
+
` }`,
|
|
93
|
+
` if (x) *x = __tc_touch_cached_x;`,
|
|
94
|
+
` if (y) *y = __tc_touch_cached_y;`,
|
|
95
|
+
` if (z) *z = __tc_touch_cached_z;`,
|
|
96
|
+
` __tc_touch_cached_valid = 0;`,
|
|
97
|
+
`}`,
|
|
98
|
+
].join("\n"),
|
|
99
|
+
};
|
|
100
|
+
}
|