@typecad/framework-zephyr 1.0.0-alpha.10 → 1.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chips/esp32.js +12 -0
- package/dist/chips/types.d.ts +30 -0
- package/dist/chips/xiao-ble.js +6 -0
- package/dist/display/gfx.d.ts +12 -3
- package/dist/display/gfx.js +130 -17
- package/dist/display/profiles.d.ts +25 -0
- package/dist/display/profiles.js +30 -0
- package/dist/display/touch-adapter.d.ts +3 -4
- package/dist/display/touch-adapter.js +119 -16
- package/dist/display/ui-adapter.js +308 -139
- package/dist/doctor.d.ts +3 -3
- package/dist/doctor.js +56 -29
- package/dist/dt-config/kconfig.d.ts +8 -1
- package/dist/dt-config/kconfig.js +40 -5
- package/dist/dt-config/overlay.d.ts +18 -1
- package/dist/dt-config/overlay.js +124 -26
- package/dist/framework.manifest.d.ts +29 -28
- package/dist/framework.manifest.js +46 -17
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/licenses.d.ts +59 -0
- package/dist/licenses.js +347 -0
- package/dist/lowering/ble.js +3 -1
- package/dist/lowering/dac.d.ts +15 -0
- package/dist/lowering/dac.js +69 -0
- package/dist/lowering/fs.d.ts +16 -0
- package/dist/lowering/fs.js +121 -0
- package/dist/lowering/hwtimer.d.ts +15 -0
- package/dist/lowering/hwtimer.js +84 -0
- package/dist/lowering/index.d.ts +4 -1
- package/dist/lowering/index.js +12 -3
- package/dist/strategy.d.ts +4 -0
- package/dist/strategy.js +275 -35
- package/dist/toolchain/compat.js +10 -1
- package/dist/toolchain/env-check.d.ts +93 -0
- package/dist/toolchain/env-check.js +190 -0
- package/dist/toolchain/index.js +39 -9
- package/dist/toolchain/scaffold.js +7 -2
- package/dist/toolchain/west-discover.d.ts +11 -3
- package/dist/toolchain/west-discover.js +84 -7
- package/dist/toolchain/west-spawn.js +15 -0
- package/package.json +4 -4
- package/src/chips/esp32.ts +12 -0
- package/src/chips/types.ts +29 -0
- package/src/chips/xiao-ble.ts +6 -0
- package/src/display/gfx.ts +135 -19
- package/src/display/profiles.ts +53 -0
- package/src/display/touch-adapter.ts +119 -15
- package/src/display/ui-adapter.ts +311 -139
- package/src/doctor.ts +77 -56
- package/src/dt-config/kconfig.ts +45 -6
- package/src/dt-config/overlay.ts +159 -29
- package/src/framework.manifest.ts +47 -17
- package/src/index.ts +6 -0
- package/src/licenses.ts +425 -0
- package/src/lowering/ble.ts +3 -1
- package/src/lowering/dac.ts +82 -0
- package/src/lowering/fs.ts +127 -0
- package/src/lowering/hwtimer.ts +101 -0
- package/src/lowering/index.ts +9 -2
- package/src/strategy.ts +271 -35
- package/src/toolchain/compat.ts +154 -145
- package/src/toolchain/env-check.ts +285 -0
- package/src/toolchain/index.ts +40 -9
- package/src/toolchain/scaffold.ts +7 -2
- package/src/toolchain/west-discover.ts +92 -9
- package/src/toolchain/west-spawn.ts +15 -0
package/dist/chips/esp32.js
CHANGED
|
@@ -56,4 +56,16 @@ export const ESP32_DEVKITC = {
|
|
|
56
56
|
// the ESP32 is AMP (dual-image procpu/appcpu), not SMP, by default — so the
|
|
57
57
|
// dependency is satisfied. Omitted on radioless targets.
|
|
58
58
|
wifi: { supported: true },
|
|
59
|
+
// DAC: the ESP32 has two 8-bit DAC channels on GPIO25 (channel 1) and GPIO26
|
|
60
|
+
// (channel 2). The Zephyr esp32 DAC driver (drivers/dac/dac_esp32.c) exposes
|
|
61
|
+
// them via the `dac0` node; the lowering emits dac_channel_setup +
|
|
62
|
+
// dac_write_value against DEVICE_DT_GET(DT_NODELABEL(dac0)). The overlay
|
|
63
|
+
// enables the node when the program uses dac.*. ESP32-S3 has no DAC.
|
|
64
|
+
dac: {
|
|
65
|
+
device: 'dac0',
|
|
66
|
+
channels: [
|
|
67
|
+
{ pin: 25, channel: 1, resolution: 8 },
|
|
68
|
+
{ pin: 26, channel: 2, resolution: 8 },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
59
71
|
};
|
package/dist/chips/types.d.ts
CHANGED
|
@@ -81,6 +81,18 @@ export interface ZephyrAdcChannel {
|
|
|
81
81
|
/** SAADC channel index (AIN0–AIN7). */
|
|
82
82
|
readonly channel: number;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* A DAC channel: which DAC output a given HAL pin maps to. The lowering emits
|
|
86
|
+
* `dac_channel_setup` + `dac_write_value` against the DAC device node.
|
|
87
|
+
*/
|
|
88
|
+
export interface ZephyrDacChannel {
|
|
89
|
+
/** GPIO number (matches the HAL op `pin` field). */
|
|
90
|
+
readonly pin: number;
|
|
91
|
+
/** DAC channel index (ESP32: GPIO25 → 1, GPIO26 → 2). */
|
|
92
|
+
readonly channel: number;
|
|
93
|
+
/** DAC resolution in bits (ESP32 DAC is 8-bit). */
|
|
94
|
+
readonly resolution: number;
|
|
95
|
+
}
|
|
84
96
|
/**
|
|
85
97
|
* Pure-data descriptor for a Zephyr board + its SoC's peripheral layout.
|
|
86
98
|
*/
|
|
@@ -139,10 +151,28 @@ export interface ZephyrChipDescriptor {
|
|
|
139
151
|
/** ADC resolution in bits. */
|
|
140
152
|
readonly resolution: number;
|
|
141
153
|
};
|
|
154
|
+
/**
|
|
155
|
+
* DAC: the DAC device node label + the pin→channel map. Present only on chips
|
|
156
|
+
* with a DAC (ESP32 has 2 channels on GPIO25/26; ESP32-S3 and nRF52840 have
|
|
157
|
+
* none). Read by profileDiagnostics to flag dac.* usage on chips without it.
|
|
158
|
+
*/
|
|
159
|
+
readonly dac?: {
|
|
160
|
+
readonly device: string;
|
|
161
|
+
readonly channels: readonly ZephyrDacChannel[];
|
|
162
|
+
};
|
|
142
163
|
/** Watchdog node label, e.g. 'wdt0'. */
|
|
143
164
|
readonly wdt?: {
|
|
144
165
|
readonly nodeLabel: string;
|
|
145
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* Hardware timers exposed as Zephyr counter devices. `instance` (the HAL
|
|
169
|
+
* hwtimer.* op's instance index) maps to `controllers[instance].nodeLabel`.
|
|
170
|
+
* Omit on chips whose counter nodes are kernel-owned or unavailable; the
|
|
171
|
+
* lowering then lowers to a comment and profileDiagnostics flags usage.
|
|
172
|
+
*/
|
|
173
|
+
readonly hwtimer?: {
|
|
174
|
+
readonly controllers: readonly ZephyrBusController[];
|
|
175
|
+
};
|
|
146
176
|
/**
|
|
147
177
|
* WiFi capability marker. Present only on chips with a WiFi radio (ESP32-S3).
|
|
148
178
|
* Read by profileDiagnostics to flag wifi.* usage on chips without a radio.
|
package/dist/chips/xiao-ble.js
CHANGED
|
@@ -58,4 +58,10 @@ export const XIAO_BLE = {
|
|
|
58
58
|
],
|
|
59
59
|
},
|
|
60
60
|
wdt: { nodeLabel: 'wdt0' },
|
|
61
|
+
// Hardware timer: nRF RTC1 is the free counter (RTC0 is kernel-owned by the
|
|
62
|
+
// softdevice/clock driver). The hwtimer lowering drives it as a Zephyr
|
|
63
|
+
// counter device (counter_start/stop + a top-value alarm for set_frequency).
|
|
64
|
+
// Verified against the nRF52840 SoC dtsi (rtc0/rtc1 nodes). The kernel uses
|
|
65
|
+
// RTC0 for the system tick; RTC1 is available for application use.
|
|
66
|
+
hwtimer: { controllers: [{ nodeLabel: 'rtc1' }] },
|
|
61
67
|
};
|
package/dist/display/gfx.d.ts
CHANGED
|
@@ -10,8 +10,17 @@ export interface DisplayRuntimeResult {
|
|
|
10
10
|
readonly fontTable: string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Build the display runtime C++ for a profile.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Build the display runtime C++ for a profile.
|
|
14
|
+
*
|
|
15
|
+
* Two rendering models, selected by `profile.colorFormat`:
|
|
16
|
+
* - `'rgb565'` (TFT): a one-row line buffer (no full framebuffer — see
|
|
17
|
+
* AGENTS.md rendering guardrails); fill_rect/draw_rect/draw_text stream
|
|
18
|
+
* each row via display_write.
|
|
19
|
+
* - `'mono'` (OLED, e.g. SSD1306): a full framebuffer — the standard model
|
|
20
|
+
* for page-buffered monochrome panels (the AGENTS.md "no full framebuffer"
|
|
21
|
+
* guardrail targets RGB SPI TFTs, not mono OLEDs). draw ops set bits;
|
|
22
|
+
* display_flush pushes the whole framebuffer. The MONO01 packing is
|
|
23
|
+
* horizontal, MSB-first (Zephyr convention): byte = (y*rowBytes)+(x>>3),
|
|
24
|
+
* bit = 0x80>>(x&7).
|
|
16
25
|
*/
|
|
17
26
|
export declare function buildDisplayRuntime(profile: ZephyrDisplayProfile): DisplayRuntimeResult;
|
package/dist/display/gfx.js
CHANGED
|
@@ -71,18 +71,23 @@ function fontTableCpp() {
|
|
|
71
71
|
return lines.join('\n');
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
|
-
* Build the display runtime C++ for a profile.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* Build the display runtime C++ for a profile.
|
|
75
|
+
*
|
|
76
|
+
* Two rendering models, selected by `profile.colorFormat`:
|
|
77
|
+
* - `'rgb565'` (TFT): a one-row line buffer (no full framebuffer — see
|
|
78
|
+
* AGENTS.md rendering guardrails); fill_rect/draw_rect/draw_text stream
|
|
79
|
+
* each row via display_write.
|
|
80
|
+
* - `'mono'` (OLED, e.g. SSD1306): a full framebuffer — the standard model
|
|
81
|
+
* for page-buffered monochrome panels (the AGENTS.md "no full framebuffer"
|
|
82
|
+
* guardrail targets RGB SPI TFTs, not mono OLEDs). draw ops set bits;
|
|
83
|
+
* display_flush pushes the whole framebuffer. The MONO01 packing is
|
|
84
|
+
* horizontal, MSB-first (Zephyr convention): byte = (y*rowBytes)+(x>>3),
|
|
85
|
+
* bit = 0x80>>(x&7).
|
|
77
86
|
*/
|
|
78
87
|
export function buildDisplayRuntime(profile) {
|
|
79
88
|
const w = profile.width;
|
|
80
|
-
const
|
|
81
|
-
|
|
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
|
-
];
|
|
89
|
+
const h = profile.height;
|
|
90
|
+
const isMono = profile.colorFormat === 'mono';
|
|
86
91
|
// Backlight is optional: the overlay emits the DT alias only when a backlight
|
|
87
92
|
// GPIO is configured. Guard with DT_HAS_ALIAS so this compiles whether or not
|
|
88
93
|
// the alias exists (DT_NODE_HAS_STATUS(DT_ALIAS(...)) is version-dependent
|
|
@@ -90,7 +95,122 @@ export function buildDisplayRuntime(profile) {
|
|
|
90
95
|
const blInit = profile.backlight
|
|
91
96
|
? `#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
97
|
: '';
|
|
93
|
-
const
|
|
98
|
+
const stateLines = isMono
|
|
99
|
+
? [
|
|
100
|
+
'// CUTTLEFISH_DISPLAY_BEGIN',
|
|
101
|
+
`static const struct device* __tc_display = DEVICE_DT_GET(DT_NODELABEL(${profile.dtLabel}));`,
|
|
102
|
+
`// Mono framebuffer (Zephyr MONO01: horizontal, MSB-first). ${(w + 7) >> 3} bytes/row x ${h} rows.`,
|
|
103
|
+
`static uint8_t __tc_display_fb[((${w} * ${h}) + 7) / 8];`,
|
|
104
|
+
'// CUTTLEFISH_DISPLAY_END',
|
|
105
|
+
]
|
|
106
|
+
: [
|
|
107
|
+
'// CUTTLEFISH_DISPLAY_BEGIN',
|
|
108
|
+
`static const struct device* __tc_display = DEVICE_DT_GET(DT_NODELABEL(${profile.dtLabel}));`,
|
|
109
|
+
`static uint16_t __tc_display_line[${w}]; // one-row line buffer (rgb565)`,
|
|
110
|
+
'// CUTTLEFISH_DISPLAY_END',
|
|
111
|
+
];
|
|
112
|
+
const helpers = isMono
|
|
113
|
+
? monoHelpers(w, h, blInit)
|
|
114
|
+
: rgb565Helpers(w, blInit);
|
|
115
|
+
const fontTable = fontTableCpp();
|
|
116
|
+
return {
|
|
117
|
+
includes: ['<zephyr/drivers/display.h>'],
|
|
118
|
+
stateLines,
|
|
119
|
+
helpers,
|
|
120
|
+
fontTable,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Mono (OLED) helpers: a full framebuffer + bit-packing. SSD1306-class panels
|
|
125
|
+
* are page-buffered, so draw ops set bits in the framebuffer and display_flush
|
|
126
|
+
* pushes the whole buffer. color != 0 ⇒ lit.
|
|
127
|
+
*/
|
|
128
|
+
function monoHelpers(w, h, blInit) {
|
|
129
|
+
const rowBytes = (w + 7) >> 3; // bytes per row (w is byte-aligned for 128-wide panels)
|
|
130
|
+
return `
|
|
131
|
+
// MONO01 pixel packing: byte = (y * ${rowBytes}) + (x >> 3), bit = 0x80 >> (x & 7).
|
|
132
|
+
static inline void __tc_set_pixel(uint16_t x, uint16_t y, uint8_t on) {
|
|
133
|
+
if ((x >= ${w}U) || (y >= ${h}U)) { return; }
|
|
134
|
+
uint16_t idx = static_cast<uint16_t>((static_cast<uint32_t>(y) * ${rowBytes}U) + (x >> 3));
|
|
135
|
+
uint8_t mask = static_cast<uint8_t>(0x80U >> (x & 7U));
|
|
136
|
+
if (on != 0U) {
|
|
137
|
+
__tc_display_fb[idx] = static_cast<uint8_t>(__tc_display_fb[idx] | mask);
|
|
138
|
+
} else {
|
|
139
|
+
__tc_display_fb[idx] = static_cast<uint8_t>(__tc_display_fb[idx] & static_cast<uint8_t>(~mask));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static inline void display_init(void) {
|
|
144
|
+
if (!device_is_ready(__tc_display)) { for (;;) { k_msleep(1000); } }
|
|
145
|
+
${blInit}
|
|
146
|
+
for (uint16_t i = 0; i < static_cast<uint16_t>(sizeof(__tc_display_fb)); i++) { __tc_display_fb[i] = 0U; }
|
|
147
|
+
display_blanking_off(__tc_display);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static inline void display_fill_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t rh, uint16_t color) {
|
|
151
|
+
uint8_t on = (color != 0U) ? 1U : 0U;
|
|
152
|
+
for (uint16_t row = 0; row < rh; row++) {
|
|
153
|
+
for (uint16_t i = 0; i < rw; i++) {
|
|
154
|
+
__tc_set_pixel(static_cast<uint16_t>(x + i), static_cast<uint16_t>(y + row), on);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static inline void display_draw_rect(uint16_t x, uint16_t y, uint16_t rw, uint16_t rh, uint16_t color) {
|
|
160
|
+
uint8_t on = (color != 0U) ? 1U : 0U;
|
|
161
|
+
for (uint16_t i = 0; i < rw; i++) {
|
|
162
|
+
__tc_set_pixel(static_cast<uint16_t>(x + i), y, on);
|
|
163
|
+
__tc_set_pixel(static_cast<uint16_t>(x + i), static_cast<uint16_t>(y + rh - 1U), on);
|
|
164
|
+
}
|
|
165
|
+
for (uint16_t row = 1U; row < rh - 1U; row++) {
|
|
166
|
+
__tc_set_pixel(x, static_cast<uint16_t>(y + row), on);
|
|
167
|
+
__tc_set_pixel(static_cast<uint16_t>(x + rw - 1U), static_cast<uint16_t>(y + row), on);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static inline void display_draw_text(uint16_t x, uint16_t y, const char* text, uint16_t color) {
|
|
172
|
+
uint8_t on = (color != 0U) ? 1U : 0U;
|
|
173
|
+
uint16_t cx = x;
|
|
174
|
+
for (const char* p = text; *p != 0; p++) {
|
|
175
|
+
uint8_t uc = static_cast<uint8_t>(*p);
|
|
176
|
+
if (uc >= 128U) { uc = static_cast<uint8_t>(' '); }
|
|
177
|
+
const uint8_t* glyph = &__tc_font5x7[uc][0];
|
|
178
|
+
if (uc >= static_cast<uint8_t>('a') && uc <= static_cast<uint8_t>('z')) {
|
|
179
|
+
glyph = &__tc_font5x7[uc - 32U][0];
|
|
180
|
+
} else if (uc < static_cast<uint8_t>('0')
|
|
181
|
+
|| (uc > static_cast<uint8_t>('9') && uc < static_cast<uint8_t>('A'))
|
|
182
|
+
|| uc > static_cast<uint8_t>('Z')) {
|
|
183
|
+
glyph = &__tc_font5x7[static_cast<uint8_t>(' ')][0];
|
|
184
|
+
}
|
|
185
|
+
for (uint16_t col = 0; col < 5U; col++) {
|
|
186
|
+
uint8_t bits = glyph[col];
|
|
187
|
+
for (uint16_t row = 0; row < 7U; row++) {
|
|
188
|
+
if ((bits & static_cast<uint8_t>(1U << row)) != 0U) {
|
|
189
|
+
__tc_set_pixel(static_cast<uint16_t>(cx + col), static_cast<uint16_t>(y + row), on);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
cx = static_cast<uint16_t>(cx + 6U);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static inline void display_flush(void) {
|
|
198
|
+
struct display_buffer_descriptor __desc;
|
|
199
|
+
__desc.buf_size = sizeof(__tc_display_fb); // ${rowBytes} * ${h} bytes
|
|
200
|
+
__desc.width = ${w}U;
|
|
201
|
+
__desc.height = ${h}U;
|
|
202
|
+
__desc.pitch = ${rowBytes}U; // bytes per row
|
|
203
|
+
__desc.frame_incomplete = false;
|
|
204
|
+
(void)display_write(__tc_display, 0, 0, &__desc, __tc_display_fb);
|
|
205
|
+
}
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* RGB565 (TFT) helpers: a one-row line buffer; each op streams rows via
|
|
210
|
+
* display_write. No full framebuffer (AGENTS.md rendering guardrails).
|
|
211
|
+
*/
|
|
212
|
+
function rgb565Helpers(w, blInit) {
|
|
213
|
+
return `
|
|
94
214
|
// Write a single row of \`rw\` rgb565 pixels at (x,y). Builds the
|
|
95
215
|
// display_buffer_descriptor the Zephyr display_write API requires (rgb565 =
|
|
96
216
|
// 2 bytes/pixel) and pushes the one-row line buffer.
|
|
@@ -161,11 +281,4 @@ static inline void display_flush(void) {
|
|
|
161
281
|
// No-op: writes are immediate via display_write; there is no framebuffer to push.
|
|
162
282
|
}
|
|
163
283
|
`;
|
|
164
|
-
const fontTable = fontTableCpp();
|
|
165
|
-
return {
|
|
166
|
-
includes: ['<zephyr/drivers/display.h>'],
|
|
167
|
-
stateLines,
|
|
168
|
-
helpers,
|
|
169
|
-
fontTable,
|
|
170
|
-
};
|
|
171
284
|
}
|
|
@@ -15,7 +15,32 @@ export interface ZephyrDisplayProfile {
|
|
|
15
15
|
readonly rotation?: number;
|
|
16
16
|
/** DT alias for the backlight GPIO (set high at init), if any. */
|
|
17
17
|
readonly backlight?: string;
|
|
18
|
+
/** Panel controller the direct-drive UI adapter targets. Selects the init
|
|
19
|
+
* sequence + pixel wire format (ST7796S: 18-bit; ILI9341: 16-bit RGB565).
|
|
20
|
+
* Required for rgb565 profiles; mono profiles use the direct-op GFX
|
|
21
|
+
* runtime and ignore it. */
|
|
22
|
+
readonly controller?: ZephyrPanelController;
|
|
23
|
+
/** DT compatible string for the display@0 node. Defaults per controller
|
|
24
|
+
* (see PANEL_CONTROLLER_DEFAULTS) — override only for a panel whose DT
|
|
25
|
+
* binding differs from its controller family. */
|
|
26
|
+
readonly dtCompatible?: string;
|
|
27
|
+
/** SPI controller nodelabel the panel hangs off (and SPI touch, if any).
|
|
28
|
+
* Default 'spi2' — the ESP32-S3 first general-purpose SPI controller. */
|
|
29
|
+
readonly busLabel?: string;
|
|
30
|
+
/** Nodelabel of the MIPI DBI bridge node carrying the dc/reset GPIOs.
|
|
31
|
+
* Default 'mipi_dbi' (the overlay emits the bridge under that label). */
|
|
32
|
+
readonly bridgeLabel?: string;
|
|
18
33
|
}
|
|
34
|
+
/** Panel controllers the direct-drive UI adapter knows how to init. */
|
|
35
|
+
export type ZephyrPanelController = 'st7796s' | 'ili9341';
|
|
36
|
+
/** Per-controller DT + transport defaults, shared by the overlay generator
|
|
37
|
+
* (DT node props) and the UI adapter (init sequence + wire format). */
|
|
38
|
+
export declare const PANEL_CONTROLLER_DEFAULTS: Record<ZephyrPanelController, {
|
|
39
|
+
dtCompatible: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Resolve a profile's panel controller, inferring it from the driver id when
|
|
42
|
+
* the profile doesn't declare one (the '<controller>-zephyr' naming scheme). */
|
|
43
|
+
export declare function panelControllerFor(profile: Pick<ZephyrDisplayProfile, 'driver' | 'controller'>): ZephyrPanelController;
|
|
19
44
|
/**
|
|
20
45
|
* Built-in profile registry. Looked up by driver id. Add a profile here when a
|
|
21
46
|
* new board's display node is wired into its devicetree.
|
package/dist/display/profiles.js
CHANGED
|
@@ -6,6 +6,21 @@
|
|
|
6
6
|
// pin wiring. The GFX runtime (gfx.ts) reads width/height/colorFormat from the
|
|
7
7
|
// active profile to size its line buffer.
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
|
+
/** Per-controller DT + transport defaults, shared by the overlay generator
|
|
10
|
+
* (DT node props) and the UI adapter (init sequence + wire format). */
|
|
11
|
+
export const PANEL_CONTROLLER_DEFAULTS = {
|
|
12
|
+
st7796s: { dtCompatible: 'sitronix,st7796s' },
|
|
13
|
+
ili9341: { dtCompatible: 'ilitek,ili9341' },
|
|
14
|
+
};
|
|
15
|
+
/** Resolve a profile's panel controller, inferring it from the driver id when
|
|
16
|
+
* the profile doesn't declare one (the '<controller>-zephyr' naming scheme). */
|
|
17
|
+
export function panelControllerFor(profile) {
|
|
18
|
+
if (profile.controller)
|
|
19
|
+
return profile.controller;
|
|
20
|
+
if (profile.driver.startsWith('ili9341'))
|
|
21
|
+
return 'ili9341';
|
|
22
|
+
return 'st7796s';
|
|
23
|
+
}
|
|
9
24
|
/**
|
|
10
25
|
* Built-in profile registry. Looked up by driver id. Add a profile here when a
|
|
11
26
|
* new board's display node is wired into its devicetree.
|
|
@@ -17,6 +32,7 @@ export const ZEPHYR_DISPLAY_PROFILES = {
|
|
|
17
32
|
width: 320,
|
|
18
33
|
height: 240,
|
|
19
34
|
colorFormat: 'rgb565',
|
|
35
|
+
controller: 'ili9341',
|
|
20
36
|
rotation: 90,
|
|
21
37
|
backlight: 'backlight',
|
|
22
38
|
},
|
|
@@ -33,9 +49,23 @@ export const ZEPHYR_DISPLAY_PROFILES = {
|
|
|
33
49
|
nativeWidth: 320,
|
|
34
50
|
nativeHeight: 480,
|
|
35
51
|
colorFormat: 'rgb565',
|
|
52
|
+
controller: 'st7796s',
|
|
36
53
|
rotation: 1,
|
|
37
54
|
backlight: 'backlight',
|
|
38
55
|
},
|
|
56
|
+
'ssd1306-zephyr': {
|
|
57
|
+
// Monochrome OLED (SSD1306-class, 128x64, 1bpp). Driven through Zephyr's
|
|
58
|
+
// generic display API (the ssd1306 driver + a DT display node). The GFX
|
|
59
|
+
// runtime (gfx.ts mono branch) keeps a full page-framebuffer and pushes it
|
|
60
|
+
// on display_flush — the standard model for page-buffered OLEDs. Direct
|
|
61
|
+
// display.* ops only (no @typecad/ui CuttlefishGFX rendering on mono).
|
|
62
|
+
driver: 'ssd1306-zephyr',
|
|
63
|
+
dtLabel: 'display0',
|
|
64
|
+
width: 128,
|
|
65
|
+
height: 64,
|
|
66
|
+
colorFormat: 'mono',
|
|
67
|
+
rotation: 0,
|
|
68
|
+
},
|
|
39
69
|
};
|
|
40
70
|
/** The default profile used when resolveDisplayOp is probed without a display.init. */
|
|
41
71
|
export const DEFAULT_ZEPHYR_DISPLAY_PROFILE = ZEPHYR_DISPLAY_PROFILES['ili9341-zephyr'];
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { TouchAdapterCodegen } from "@typecad/cuttlefish/api/shared";
|
|
2
2
|
import type { TouchProfile } from "@typecad/cuttlefish/api/shared";
|
|
3
3
|
/**
|
|
4
|
-
* Generate
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* strategy can decline.
|
|
4
|
+
* Generate a Zephyr touch adapter for the profile's library. Returns
|
|
5
|
+
* undefined for libraries this framework doesn't handle so the strategy can
|
|
6
|
+
* decline and cuttlefish surfaces a clear error.
|
|
8
7
|
*/
|
|
9
8
|
export declare function zephyrTouchAdapter(touch: TouchProfile): TouchAdapterCodegen | undefined;
|
|
@@ -1,29 +1,132 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// Zephyr touch
|
|
2
|
+
// Zephyr touch adapters for the Cuttlefish UI rendering pipeline.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// Two controllers, both driven directly (polling, no in-tree driver):
|
|
5
|
+
// - FT6336U capacitive over I2C (i2c_write_read_dt)
|
|
6
|
+
// - XPT2046 resistive over SPI (spi_transceive_dt) — the Zephyr analog of
|
|
7
|
+
// Arduino's XPT2046_Touchscreen adapter. The DT node uses the in-tree
|
|
8
|
+
// xptek,xpt2046 binding; CONFIG_INPUT stays off, so the in-tree input
|
|
9
|
+
// driver does not build and this adapter owns the chip.
|
|
8
10
|
//
|
|
9
|
-
// The
|
|
10
|
-
//
|
|
11
|
-
// the `focaltech,ft6336u` (or compatible) binding so the device is ready.
|
|
11
|
+
// The adapters emit touch_init / touch_isTouched / touch_readRaw — the three
|
|
12
|
+
// symbols the runtime's ui_poll_touch body calls.
|
|
12
13
|
//
|
|
13
14
|
// EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
|
|
14
15
|
// Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
16
17
|
/**
|
|
17
|
-
* Generate
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* strategy can decline.
|
|
18
|
+
* Generate a Zephyr touch adapter for the profile's library. Returns
|
|
19
|
+
* undefined for libraries this framework doesn't handle so the strategy can
|
|
20
|
+
* decline and cuttlefish surfaces a clear error.
|
|
21
21
|
*/
|
|
22
22
|
export function zephyrTouchAdapter(touch) {
|
|
23
|
-
if (touch.library
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
if (touch.library === "XPT2046_Touchscreen")
|
|
24
|
+
return xpt2046Adapter(touch);
|
|
25
|
+
if (touch.library === "FT6336U")
|
|
26
|
+
return ft6336uAdapter(touch);
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/** XPT2046 control bytes (12-bit differential mode, auto power-down). */
|
|
30
|
+
const XPT2046_CMD_X = 0x90;
|
|
31
|
+
const XPT2046_CMD_Y = 0xd0;
|
|
32
|
+
const XPT2046_CMD_Z1 = 0xb0;
|
|
33
|
+
const hex = (v) => `0x${v.toString(16).toUpperCase()}`;
|
|
34
|
+
function xpt2046Adapter(touch) {
|
|
35
|
+
// Pen-detect Z1 threshold (raw 12-bit counts). Resistive panels need a few
|
|
36
|
+
// hundred counts above noise; the TouchProfile default (10) is far too low
|
|
37
|
+
// for raw Z1, so the adapter defaults to 400 (the Arduino XPT2046
|
|
38
|
+
// library's Z_THRESHOLD) unless the config sets one explicitly.
|
|
39
|
+
const zThreshold = touch.minPressure ?? 400;
|
|
40
|
+
return {
|
|
41
|
+
includes: [
|
|
42
|
+
`#include <zephyr/kernel.h>`,
|
|
43
|
+
`#include <zephyr/drivers/spi.h>`,
|
|
44
|
+
`#include <zephyr/drivers/gpio.h>`,
|
|
45
|
+
],
|
|
46
|
+
declaration: [
|
|
47
|
+
`// Zephyr XPT2046 resistive touch — SPI device resolved via devicetree.`,
|
|
48
|
+
`// The overlay defines the 'xpt2046' nodelabel on the panel's SPI bus`,
|
|
49
|
+
`// (CS index 1) with the xptek,xpt2046 binding; the bus + CS + 2.5MHz`,
|
|
50
|
+
`// ceiling come from that node. Raw X/Y/Z1 values are reported — the`,
|
|
51
|
+
`// runtime applies the profile calibration + rotation.`,
|
|
52
|
+
`static const struct spi_dt_spec __tc_touch =`,
|
|
53
|
+
` SPI_DT_SPEC_GET(DT_NODELABEL(xpt2046), SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0U);`,
|
|
54
|
+
`static int16_t __tc_touch_cached_x = 0;`,
|
|
55
|
+
`static int16_t __tc_touch_cached_y = 0;`,
|
|
56
|
+
`static int16_t __tc_touch_cached_z = 0;`,
|
|
57
|
+
`static uint8_t __tc_touch_cached_valid = 0;`,
|
|
58
|
+
`#if DT_NODE_HAS_PROP(DT_NODELABEL(xpt2046), int_gpios)`,
|
|
59
|
+
`static const struct gpio_dt_spec __tc_touch_irq = GPIO_DT_SPEC_GET(DT_NODELABEL(xpt2046), int_gpios);`,
|
|
60
|
+
`static uint8_t __tc_touch_irq_ready = 0;`,
|
|
61
|
+
`#endif`,
|
|
62
|
+
].join("\n"),
|
|
63
|
+
functions: [
|
|
64
|
+
`// One XPT2046 conversion: clock out a control byte, read the 12-bit`,
|
|
65
|
+
`// result. The rx byte during the command phase is a dummy; the value is`,
|
|
66
|
+
`// MSB-aligned across the following 16 bits ((b1<<8 | b2) >> 3). The`,
|
|
67
|
+
`// control byte's PD bits are 00 (auto power-down between transactions),`,
|
|
68
|
+
`// so the bus stays shareable with the panel. Touch is polled every frame`,
|
|
69
|
+
`// from ui_poll_touch, so a failed transfer is non-fatal — returns 0 and`,
|
|
70
|
+
`// touch_isTouched() reports false.`,
|
|
71
|
+
`static uint16_t __tc_xpt_read(uint8_t cmd) {`,
|
|
72
|
+
` if (!device_is_ready(__tc_touch.bus)) return 0U;`,
|
|
73
|
+
` uint8_t __tx[3] = { cmd, 0U, 0U };`,
|
|
74
|
+
` uint8_t __rx[3] = { 0U, 0U, 0U };`,
|
|
75
|
+
` struct spi_buf __bt = { __tx, sizeof(__tx) };`,
|
|
76
|
+
` struct spi_buf __br = { __rx, sizeof(__rx) };`,
|
|
77
|
+
` struct spi_buf_set __st = { &__bt, 1 };`,
|
|
78
|
+
` struct spi_buf_set __sr = { &__br, 1 };`,
|
|
79
|
+
` if (spi_transceive_dt(&__tc_touch, &__st, &__sr) != 0) return 0U;`,
|
|
80
|
+
` return static_cast<uint16_t>(((static_cast<uint16_t>(__rx[1]) << 8) | __rx[2]) >> 3);`,
|
|
81
|
+
`}`,
|
|
82
|
+
``,
|
|
83
|
+
`static inline void touch_init() {`,
|
|
84
|
+
` // The SPI bus + CS are configured by devicetree. Configure the pen IRQ`,
|
|
85
|
+
` // input (active low) when the overlay wired it — the cheap detect path.`,
|
|
86
|
+
`#if DT_NODE_HAS_PROP(DT_NODELABEL(xpt2046), int_gpios)`,
|
|
87
|
+
` if (device_is_ready(__tc_touch_irq.port)) {`,
|
|
88
|
+
` gpio_pin_configure_dt(&__tc_touch_irq, GPIO_INPUT);`,
|
|
89
|
+
` __tc_touch_irq_ready = 1U;`,
|
|
90
|
+
` }`,
|
|
91
|
+
`#endif`,
|
|
92
|
+
` (void)device_is_ready(__tc_touch.bus);`,
|
|
93
|
+
`}`,
|
|
94
|
+
``,
|
|
95
|
+
`static inline bool touch_isTouched() {`,
|
|
96
|
+
` __tc_touch_cached_valid = 0;`,
|
|
97
|
+
` __tc_touch_cached_z = 0;`,
|
|
98
|
+
`#if DT_NODE_HAS_PROP(DT_NODELABEL(xpt2046), int_gpios)`,
|
|
99
|
+
` if (__tc_touch_irq_ready != 0U) {`,
|
|
100
|
+
` // PENIRQ asserts (active low) while the panel is pressed — no SPI`,
|
|
101
|
+
` // traffic needed to answer the per-frame poll.`,
|
|
102
|
+
` if (gpio_pin_get_dt(&__tc_touch_irq) <= 0) return false;`,
|
|
103
|
+
` }`,
|
|
104
|
+
`#endif`,
|
|
105
|
+
` // No IRQ (or it fired): confirm via Z1 pressure before reading coords.`,
|
|
106
|
+
` uint16_t __z1 = __tc_xpt_read(${hex(XPT2046_CMD_Z1)});`,
|
|
107
|
+
` if (__z1 <= ${zThreshold}) return false;`,
|
|
108
|
+
` __tc_touch_cached_x = static_cast<int16_t>(__tc_xpt_read(${hex(XPT2046_CMD_X)}));`,
|
|
109
|
+
` __tc_touch_cached_y = static_cast<int16_t>(__tc_xpt_read(${hex(XPT2046_CMD_Y)}));`,
|
|
110
|
+
` __tc_touch_cached_z = static_cast<int16_t>(__z1);`,
|
|
111
|
+
` __tc_touch_cached_valid = 1;`,
|
|
112
|
+
` return true;`,
|
|
113
|
+
`}`,
|
|
114
|
+
``,
|
|
115
|
+
`static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
|
|
116
|
+
` // touch_isTouched() is polled each frame by ui_poll_touch; the cached`,
|
|
117
|
+
` // values are fresh. If something calls readRaw before isTouched, probe now.`,
|
|
118
|
+
` if (!__tc_touch_cached_valid) {`,
|
|
119
|
+
` (void)touch_isTouched();`,
|
|
120
|
+
` }`,
|
|
121
|
+
` if (x) *x = __tc_touch_cached_x;`,
|
|
122
|
+
` if (y) *y = __tc_touch_cached_y;`,
|
|
123
|
+
` if (z) *z = __tc_touch_cached_z;`,
|
|
124
|
+
` __tc_touch_cached_valid = 0;`,
|
|
125
|
+
`}`,
|
|
126
|
+
].join("\n"),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function ft6336uAdapter(_touch) {
|
|
27
130
|
return {
|
|
28
131
|
includes: [
|
|
29
132
|
`#include <zephyr/drivers/i2c.h>`,
|