Compare commits

..

20 Commits

Author SHA1 Message Date
Adolfo Reyna 68766cdbed feat(files+gb): file browser launches .gb via GameBoy, preserve mDNS 2026-07-20 12:51:19 -04:00
Adolfo Reyna 75178652e2 feat(lvgl+mdns): complete canvas API + fix GB blank render + mDNS browse API
lvgl-module/symbols.c:
- canvas: full LVGL9 docs API (get_buf, get_image, get_px, get_draw_buf,
  set_palette, copy_buf, init_layer, finish_layer, buf_size) – was only 5 symbols
- draw_buf: create/destroy/init/dup/copy/goto/clear/width_to_stride/align/palette/from_image/to_image
- cache: lv_draw_buf_invalidate_cache, flush_cache, lv_image_cache_drop – critical bugfix for
  GameBoy (and any direct framebuffer) where raw buf mutated but image cache stale -> FPS but no image.
- draw layer helpers, image scale/rotation/pivot/offset, transform scales for GB 2x/3x integer scaling

Reason blank screen: lv_canvas in LVGL9 is lv_image backed by draw_buf. Mutating raw pointer
without lv_image_cache_drop + invalidate_cache leaves stale texture. Timer was calling
lv_obj_invalidate only. Now exports needed symbols and app fixed to drop cache each frame.

mDNS (preserve from local WIP, sits on top of 60764979 'kidsOS-XXXX' mdns init):
- Tactility/network/Mdns.h/cpp: C++ browse + resolveHostname wrappers around esp-idf mdns 1.3.2
  (PTR query, txt records, address extraction, POSIX stub)
- TactilityC/tt_mdns.h/cpp: C copy-based API for ELF apps (TT_MDNS_MAX_RESULTS 32,
  capped addresses/txt, hostname->IP resolve)
- TactilityC/tt_init.cpp: export tt_mdns_is_available / browse / resolve_hostname symbols

Test plan:
- firmware build for es3c28p --dev
- release-sdk-current.py
- GameBoy app build against new SDK (0 missing symbols expected)
- push both branches to personal gitea
2026-07-20 12:32:00 -04:00
Adolfo Reyna ff3eeacaf7 feat(lvgl-module): complete canvas API export for GameBoy emulator
- canvas: add missing getters/setters (get_buf, get_image, get_px, get_draw_buf,
  set_palette, copy_buf, init_layer, finish_layer, buf_size)
- draw_buf: export create/destroy/init/dup/copy/goto/width_to_stride/align/palette/from_image/to_image
- draw layer: create + alloc_buf
- image: scale/rotation/pivot/offset for GB integer upscaling
- style: transform_scale/pivot for fallback path

Enables ELF apps to use full LVGL9 canvas docs API:
- 160x144 raw buffer + lv_image_set_scale 2x/3x
- layer init/finish for direct draw calls
- palette for indexed GB modes

Test: GameBoy app builds clean (0 missing symbols) vs 5.5.2 SDK
Fixes missing symbol errors that previously required commented-out scaling workaround
2026-07-20 00:21:01 -04:00
Adolfo Reyna 80bd1c9f20 fix(bluetooth): auto-reconnect to paired devices after reboot
- Auto-set enableOnBoot=true when BT is enabled or device is paired,
  so board restarts with BT on and can reconnect
- Make RADIO_STATE_ON handler non-exclusive: scan for HID Host,
  start HID Device, SPP and MIDI all independently (was else-if)
- Improve HID Host auto-connect for RPA: direct addr match, name-match
  fallback, and direct connect to stored identity using resolving list
- Persist addrType in PairedDevice file for more reliable reconnection
- Add verbose logging for loadAll/save to debug missing files

Fixes issue where board restart did not reconnect to previously
connected BT devices. Also preserves f94cc160 fullscreen flag feature.
2026-07-19 19:31:57 -04:00
Adolfo Reyna f94cc160cf feat(app): allow external apps to hide statusbar via manifest flags
- Add parseAppFlagsString() in AppManifestParsing.cpp to parse comma-separated flags (HideStatusBar, Hidden, None)
- Parse [app]flags in V1 and app.flags in V2 manifests
- Loader already supports HideStatusBar, GuiService hides statusbarWidget when set
- Enables fullscreen for ELF apps (e.g. BibleVerse, BookPlayer) without firmware recompilation of internal manifests
- Add tests for flag parsing (38 tests passing)
- Tested on es3c28p /dev/cu.usbmodem1101 @ 192.168.68.133 with HelloWorld app
2026-07-19 19:25:03 -04:00
Adolfo Reyna 84cd73e503 fix(rlcd): wifi crash - use wifi-pinned disabled like other boards
- Crash: wifi0 as esp32-wifi enabled during kernel init conflicts with
  WebServerService AP mode esp_wifi_init() -> ESP_ERR_INVALID_STATE abort
- Fix: use esp32-wifi-pinned status=disabled pattern like ES3C28P, CYD etc
  (all boards have wifi0 disabled). STA starts later via WifiService
  setEnabled, no longer crashes during kernel init. Log shows:
  'Setting DHCP hostname kidsOS-91EC' + 'WiFi radio on' after boot

- Keeps mDNS AP+STA + hostname kidsOS-XXXX
2026-07-18 23:16:46 -04:00
Adolfo Reyna 6076497997 feat(mdns): restore mDNS + hostname kidsOS-XXXX
- Add espressif/mdns 1.3.2 to idf_component.yml + CMake
- STA path (esp32_wifi.cpp): set DHCP hostname kidsOS-XXXY from MAC,
  mdns_init, hostname, instance, _http:80 + _tactility:6666 services
- AP path (WebServerService.cpp): same hostname + mDNS for AP mode
- Log: mDNS AP/STA initialized hostname 'kidsOS-91ED.local'
- Previous implementation from 300ddb3a lost in driver arch migration
  f9453d89 (#562 #574)

Tested: RLCD 0x2d5470 FLASH_EXIT:0, log shows
  'mDNS AP initialized, hostname kidsOS-91ED.local'
2026-07-18 23:10:48 -04:00
Adolfo Reyna 213ad7ee61 Merge branch 'feature/rlcd-upstream-main' into feature/mcp-rlcd-custom 2026-07-18 22:56:43 -04:00
Adolfo Reyna 1a96307de2 feat(rlcd): Waveshare ESP32-S3-RLCD-4.2 support on latest main
- ST7305 driver with persistent 15KB DMA buffer (fix use-after-free +
  WDT lockup that caused freeze after flash)
- Reset timing fix: 150ms after reset, 50ms after init
- DTS: i2s0 mclk 16, bclk 9, ws 45, out 8, in 10 - fixes GPIO5 conflict
- i2c0 uses esp32-i2c-master, SDMMC 1-bit
- device.properties: colorDepth 16, fontSize 20, DefaultDark
- Amp GPIO46 init in initBoot
- Buttons: two-button control GPIO18 (cycle) + GPIO0 (select)

Base: origin/main f9453d89
2026-07-18 22:44:53 -04:00
Adolfo Reyna c7dc66e04b fix(audio): ES8311 BOTH complementary open + audio-stream close ref-count + mic unmute
- es8311 driver open() now allows OUTPUT->INPUT complementary (promotes to BOTH) when same native rate 44100
- audio-stream close_stream() ref-counts shared BOTH codec (don't close if other direction still open)
- MCP recordVoice + VoiceRecorder app explicitly unmute and set 100% gain

Fixes mic input not working - was returning ERROR_RESOURCE when output already open
2026-07-18 19:55:47 -04:00
Adolfo Reyna 15aad3f585 fix(files): launch mp3 player from file explorer - audio support
- SupportedFiles: add isSupportedAudioFile (.mp3/.wav/.ogg/.flac)
- Files View::viewFile: if audio file, loader->start one.tactility.mp3player with file bundle
- Tactility.cpp: hide mcpoverride (screensaver internal), keep mcpsettings visible
- Fixes MP3 no longer running from file explorer, plus audio fast playback already fixed via audio-stream (Mp3Player/BookPlayer/VoiceRecorder use audio_stream_open_output not direct i2s_controller_write)
- MCP dev coexistence already fixed: DisplayIdle lock inversion 3629ffef, WebServer serverEnabled=ws||mcp, unauth /api/mcp, video 8081/8083 vs dev 6666
2026-07-18 19:18:28 -04:00
Adolfo Reyna 9a307e522d fix(mcp): use audio-stream instead of direct I2S - fixes fast mp3/voice playback
- old optimization bypassed audio-stream resampler (16k direct -> MCLK 4.096MHz vs 11.29MHz native)
- brick game correct because it used audio_stream path
- now MCP uses audio_stream_open_output/input + read/write matching native logger stack
- falls back to direct I2S only if audio-stream device missing
2026-07-18 18:30:19 -04:00
Adolfo Reyna c3abe79c90 fix(mcp): register McpSettings + McpOverride - Settings shows MCP Screen 2026-07-18 18:19:34 -04:00
Adolfo Reyna 05ce4335ff fix(mcp): register McpSettings+McpOverride in Tactility.cpp - show in Settings 2026-07-18 18:12:08 -04:00
Adolfo Reyna ec2a6c0b88 fix(es3c28p): MCP flash - PR#569 CYD+explicit srcs+no HAL, 512K cache, screensaver
- vendor CYD, useDeprecatedHal=false, CMake explicit SRCS TactilityKernel only
- module.cpp without HAL, retains es3c28p_module symbol for devicetree
- LVGL 512K PSRAM cache retained
- MCP: McpScreensaver full-screen on lv_layer_top, tap to close, triggered via ensureOverrideScreen()
- flashed to /dev/cu.usbmodem101 2884320 -> 1672312 hash verified
2026-07-18 18:09:08 -04:00
Adolfo Reyna 0966cd45a4 fix(es3c28p): PR#569 feedback + retain 512K cache - CYD vendor, explicit srcs, no HAL 2026-07-18 18:04:25 -04:00
Adolfo Reyna e3eb3fd415 feat(mcp): restore MCP system with native log.h
- MCP 3.3K LOC: McpSystem 1899 + McpHandler 1029 + McpScreensaver + 2 apps
- Uses native tactility/log.h TAG macros, no old Logger.h
- DisplaySettings McpScreen enum, WebServer coexistence (MCP enabled starts HTTP)
- DisplayIdle: startMcpScreensaver + isDeviceCharging + lock inversion fix 3629ffef
- LVGL 512K PSRAM cache retained from previous perf patch
- Audio kept upstream es8311-module per note we might not need custom
2026-07-18 17:29:37 -04:00
Adolfo Reyna c4adaef0a5 perf(es3c28p): LVGL 512KB PSRAM cache + display charging toggle
- LVGL image cache 512KB + header cache 16 on PSRAM (was PNG bottleneck, c4ec7ead)
- DisplaySettings.disableScreensaverWhenCharging persisted, checked via PowerDevice::IsCharging
- Display app: 'Disable on charging' toggle under Display settings, auto-disabled when timeout off
- DisplayIdle: skip screensaver while charging, auto-stop if plugging in while dimmed
- Keeps upstream full-DTS board clean, no JPEG decoder, no BT HID combo per user
2026-07-18 17:16:18 -04:00
Adolfo Reyna 057daf49a5 perf(es3c28p): enable 512KB LVGL image cache on PSRAM
CONFIG_LV_CACHE_DEF_SIZE=524288
CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT=16
From c4ec7ead perf fix — skip JPEG decoder per user, skip BT HID combo. OCT PSRAM 120M reference.

Build verified IDF 5.5.2 Tactility.bin 0x2b1f20 same as before but with cache enabled at runtime.
2026-07-18 16:52:01 -04:00
Adolfo Reyna d70f474f02 feat(es3c28p): add LCDWIKI ES3C28P pure DTS board
Build Simulator / Linux (push) Has been cancelled
Build Simulator / macOS (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32 id:generic-esp32]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32c6 id:generic-esp32c6]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32p4 id:generic-esp32p4]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32s3 id:generic-esp32s3]) (push) Has been cancelled
Tests / TactilityTests (push) Has been cancelled
Tests / DevicetreeTests (push) Has been cancelled
Build Firmware / GenerateDeviceMatrix (push) Has been cancelled
Build Firmware / BuildFirmware (push) Has been cancelled
Build Firmware / BundleArtifacts (push) Has been cancelled
Build Firmware / PublishFirmwareSnapshot (push) Has been cancelled
Build Firmware / PublishFirmwareStable (push) Has been cancelled
Build Firmware / PublishSdk (push) Has been cancelled
- ESP32-S3 16MB flash OCT PSRAM 120M, esptoolFlashFreq 120M (IDF5.5 MSPI fix)
- No deprecated HAL, pure DTS per maintainer guidance
- Drivers: ili9341-module (swap-xy invert bgr 40MHz DC46 CS10), ft5x06-module (swap-xy mirror-x RST18 INT17), es8311-module (I2S0 BCLK5 WS7 DOUT8 DIN6 MCLK4 addr 0x18, i2s before i2c for phandle order), battery-sense ADC1_CH8 GPIO9 200K/200K, gpio-hog amp GPIO1 low, pwm backlight 45
- module.cpp stub + legacy hardwareConfiguration placeholder (like tdeck)
- Builds with IDF 5.5.2, boots: display success, touch+audio bound, SD mounted, Launcher

Verified: build/Tactility.bin 2.7M flashed to /dev/cu.usbmodem101, boot log OK, mirror-x fix (was mirrored) removed to match old working Display.cpp swap=true mirror=false
2026-07-18 16:25:23 -04:00
598 changed files with 12803 additions and 14138 deletions
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module btt_panda_touch_module = { static error_t start() {
.name = "btt-panda-touch" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module btt_panda_touch_module = {
.name = "btt-panda-touch",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+12
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
static error_t start() {
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
Module cyd_2432s024c_module = { Module cyd_2432s024c_module = {
.name = "cyd-2432s024c", .name = "cyd-2432s024c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_2432s024r_module = { static error_t start() {
.name = "cyd-2432s024r" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_2432s024r_module = {
.name = "cyd-2432s024r",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_2432s028r_module = { static error_t start() {
.name = "cyd-2432s028r" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_2432s028r_module = {
.name = "cyd-2432s028r",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_2432s028rv3_module = { static error_t start() {
.name = "cyd-2432s028rv3" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_2432s028rv3_module = {
.name = "cyd-2432s028rv3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_2432s032c_module = { static error_t start() {
.name = "cyd-2432s032c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_2432s032c_module = {
.name = "cyd-2432s032c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_3248s035c_module = { static error_t start() {
.name = "cyd-3248s035c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_3248s035c_module = {
.name = "cyd-3248s035c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_4848s040c_module = { static error_t start() {
.name = "cyd-4848s040c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_4848s040c_module = {
.name = "cyd-4848s040c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_8048s043c_module = { static error_t start() {
.name = "cyd-8048s043c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_8048s043c_module = {
.name = "cyd-8048s043c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_e32r28t_module = { static error_t start() {
.name = "cyd-e32r28t" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_e32r28t_module = {
.name = "cyd-e32r28t",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module cyd_e32r32p_module = { static error_t start() {
.name = "cyd-e32r32p" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module cyd_e32r32p_module = {
.name = "cyd-e32r32p",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,19 @@
extern "C" { extern "C" {
Module elecrow_crowpanel_advance_28_module = { static error_t start() {
.name = "elecrow-crowpanel-advance-28" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module elecrow_crowpanel_advance_28_module = {
.name = "elecrow-crowpanel-advance-28",
.start = start,
.stop = stop,
.symbols = nullptr
}; };
} }
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
static error_t start() {
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
Module elecrow_crowpanel_advance_35_module = { Module elecrow_crowpanel_advance_35_module = {
.name = "elecrow-crowpanel-advance-35" .name = "elecrow-crowpanel-advance-35",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -86,7 +86,9 @@
vsync-back-porch = <8>; vsync-back-porch = <8>;
vsync-front-porch = <8>; vsync-front-porch = <8>;
pclk-active-neg; pclk-active-neg;
bounce-buffer-size-px = <4000>; num-fbs = <2>;
double-fb;
bounce-buffer-size-px = <8000>;
pin-hsync = <&gpio0 40 GPIO_FLAG_NONE>; pin-hsync = <&gpio0 40 GPIO_FLAG_NONE>;
pin-vsync = <&gpio0 41 GPIO_FLAG_NONE>; pin-vsync = <&gpio0 41 GPIO_FLAG_NONE>;
pin-de = <&gpio0 42 GPIO_FLAG_NONE>; pin-de = <&gpio0 42 GPIO_FLAG_NONE>;
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module elecrow_crowpanel_advance_50_module = { static error_t start() {
.name = "elecrow-crowpanel-advance-50" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module elecrow_crowpanel_advance_50_module = {
.name = "elecrow-crowpanel-advance-50",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module elecrow_crowpanel_basic_28_module = { static error_t start() {
.name = "elecrow-crowpanel-basic-28" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module elecrow_crowpanel_basic_28_module = {
.name = "elecrow-crowpanel-basic-28",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
static error_t start() {
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
Module elecrow_crowpanel_basic_35_module = { Module elecrow_crowpanel_basic_35_module = {
.name = "elecrow-crowpanel-basic-35" .name = "elecrow-crowpanel-basic-35",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -89,7 +89,9 @@
vsync-back-porch = <12>; vsync-back-porch = <12>;
vsync-front-porch = <8>; vsync-front-porch = <8>;
pclk-active-neg; pclk-active-neg;
bounce-buffer-size-px = <4000>; num-fbs = <2>;
double-fb;
bounce-buffer-size-px = <8000>;
pin-hsync = <&gpio0 39 GPIO_FLAG_NONE>; pin-hsync = <&gpio0 39 GPIO_FLAG_NONE>;
pin-vsync = <&gpio0 41 GPIO_FLAG_NONE>; pin-vsync = <&gpio0 41 GPIO_FLAG_NONE>;
pin-de = <&gpio0 40 GPIO_FLAG_NONE>; pin-de = <&gpio0 40 GPIO_FLAG_NONE>;
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module elecrow_crowpanel_basic_50_module = { static error_t start() {
.name = "elecrow-crowpanel-basic-50" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module elecrow_crowpanel_basic_50_module = {
.name = "elecrow-crowpanel-basic-50",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+3
View File
@@ -21,3 +21,6 @@ lvgl.colorDepth=16
storage.userDataLocation=SD storage.userDataLocation=SD
dependencies.useDeprecatedHal=false dependencies.useDeprecatedHal=false
sdkconfig.CONFIG_LV_CACHE_DEF_SIZE=524288
sdkconfig.CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT=16
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module es3c28p_module = { static error_t start() {
.name = "es3c28p" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module es3c28p_module = {
.name = "es3c28p",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
-4
View File
@@ -1,4 +0,0 @@
idf_component_register(
SRCS "source/module.cpp"
REQUIRES TactilityKernel
)
-23
View File
@@ -1,23 +0,0 @@
general.vendor=LCDWIKI/Hosyond
general.name=ES3C35P
apps.launcherAppId=Launcher
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.spiRam=true
hardware.spiRamMode=OCT
hardware.spiRamSpeed=80M
hardware.esptoolFlashFreq=80M
hardware.tinyUsb=true
hardware.bluetooth=true
display.size=3.5"
display.shape=rectangle
display.dpi=165
lvgl.colorDepth=16
storage.userDataLocation=SD
dependencies.useDeprecatedHal=false
-6
View File
@@ -1,6 +0,0 @@
dependencies:
- Platforms/platform-esp32
- Drivers/st77922-module
- Drivers/es8311-module
- Drivers/audio-stream-module
dts: es3c35p.dts
-143
View File
@@ -1,143 +0,0 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/esp32_adc_oneshot.h>
#include <tactility/bindings/esp32_ble.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_pwm_ledc.h>
#include <tactility/bindings/esp32_sdmmc.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_wifi_pinned.h>
#include <tactility/bindings/battery_sense.h>
#include <tactility/bindings/gpio_hog.h>
#include <tactility/bindings/pwm_backlight.h>
#include <bindings/st77922.h>
#include <bindings/st77922_touch.h>
#include <bindings/es8311.h>
/ {
compatible = "root";
model = "LCDWIKI/Hosyond ES3C35P";
wifi0 {
compatible = "espressif,esp32-wifi-pinned";
status = "disabled";
};
ble0 {
compatible = "espressif,esp32-ble";
status = "disabled";
};
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
/* FM8002E speaker amplifier enable, active-low on GPIO1. */
amp_enable {
compatible = "gpio-hog";
pin = <&gpio0 1 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_LOW>;
};
adc0 {
compatible = "espressif,esp32-adc-oneshot";
unit-id = <ADC_UNIT_1>;
clk-src = <ADC_RTC_CLK_SRC_DEFAULT>;
channels = <ADC_CHANNEL_7 ADC_ATTEN_DB_12 ADC_BITWIDTH_DEFAULT>;
};
/* BAT+ through a 200K/200K divider into GPIO8 / ADC1_CH7. */
battery-sense {
compatible = "battery-sense";
io-channel = <&adc0 0>;
reference-voltage-mv = <3300>;
multiplier = <2000>;
};
i2s0 {
compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>;
pin-bclk = <&gpio0 18 GPIO_FLAG_NONE>;
pin-ws = <&gpio0 21 GPIO_FLAG_NONE>;
pin-data-out = <&gpio0 15 GPIO_FLAG_NONE>;
pin-data-in = <&gpio0 16 GPIO_FLAG_NONE>;
pin-mclk = <&gpio0 17 GPIO_FLAG_NONE>;
};
i2c0 {
compatible = "espressif,esp32-i2c";
port = <I2C_NUM_0>;
clock-frequency = <400000>;
pin-sda = <&gpio0 38 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 39 GPIO_FLAG_NONE>;
touch@55 {
compatible = "sitronix,st77922-touch";
reg = <0x55>;
x-max = <320>;
y-max = <480>;
pin-reset = <&gpio0 48 GPIO_FLAG_NONE>;
pin-interrupt = <&gpio0 47 GPIO_FLAG_NONE>;
};
es8311: es8311@18 {
compatible = "everest,es8311";
reg = <0x18>;
i2s = <&i2s0>;
};
};
display_backlight_pwm {
compatible = "espressif,esp32-pwm-ledc";
pin = <&gpio0 41 GPIO_FLAG_NONE>;
period-ns = <200000>;
ledc-timer = <0>;
ledc-channel = <0>;
};
display_backlight {
compatible = "pwm-backlight";
status = "disabled";
pwm = <&display_backlight_pwm>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
pin-sclk = <&gpio0 12 GPIO_FLAG_NONE>;
pin-mosi = <&gpio0 11 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 13 GPIO_FLAG_NONE>;
pin-wp = <&gpio0 14 GPIO_FLAG_NONE>;
pin-hd = <&gpio0 9 GPIO_FLAG_NONE>;
cs-gpios = <&gpio0 10 GPIO_FLAG_NONE>;
/* Accommodate the driver's 1/10-frame DMA staging transfers, matching
* the vendor LVGL port's full-frame refresh path. */
max-transfer-size = <65536>;
display@0 {
compatible = "sitronix,st77922";
horizontal-resolution = <320>;
vertical-resolution = <480>;
/* 80 MHz works in the vendor demo, but produces visible QSPI corruption on
* some modules/cables. The component's 40 MHz default is reliably clean. */
pixel-clock-hz = <40000000>;
backlight = <&display_backlight>;
};
};
sdmmc0 {
compatible = "espressif,esp32-sdmmc";
pin-clk = <&gpio0 5 GPIO_FLAG_NONE>;
pin-cmd = <&gpio0 4 GPIO_FLAG_NONE>;
pin-d0 = <&gpio0 6 GPIO_FLAG_NONE>;
pin-d1 = <&gpio0 7 GPIO_FLAG_NONE>;
pin-d2 = <&gpio0 2 GPIO_FLAG_NONE>;
pin-d3 = <&gpio0 3 GPIO_FLAG_NONE>;
slot = <SDMMC_HOST_SLOT_1>;
bus-width = <4>;
};
};
-9
View File
@@ -1,9 +0,0 @@
#include <tactility/module.h>
extern "C" {
Module es3c35p_module = {
.name = "es3c35p"
};
}
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module generic_esp32_module = { static error_t start() {
.name = "generic-esp32" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module generic_esp32_module = {
.name = "generic-esp32",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module generic_esp32c6_module = { static error_t start() {
.name = "generic-esp32c6" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module generic_esp32c6_module = {
.name = "generic-esp32c6",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module generic_esp32p4_module = { static error_t start() {
.name = "generic-esp32p4" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module generic_esp32p4_module = {
.name = "generic-esp32p4",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module generic_esp32s3_module = { static error_t start() {
.name = "generic-esp32s3" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module generic_esp32s3_module = {
.name = "generic-esp32s3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
static error_t start() {
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
Module guition_jc1060p470ciwy_module = { Module guition_jc1060p470ciwy_module = {
.name = "guition-jc1060p470ciwy" .name = "guition-jc1060p470ciwy",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module guition_jc2432w328c_module = { static error_t start() {
.name = "guition-jc2432w328c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module guition_jc2432w328c_module = {
.name = "guition-jc2432w328c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+14 -2
View File
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module guition_jc8048w550c_module = { static error_t start() {
.name = "guition-jc8048w550c" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module guition_jc8048w550c_module = {
.name = "guition-jc8048w550c",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,20 @@
extern "C" { extern "C" {
Module heltec_wifi_lora_32_v3_module = { static error_t start() {
.name = "heltec-wifi-lora-32-v3" return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module heltec_wifi_lora_32_v3_module = {
.name = "heltec-wifi-lora-32-v3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+1 -1
View File
@@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "source" INCLUDE_DIRS "source"
REQUIRES TactilityKernel REQUIRES Tactility GDEQ031T10 driver
) )
+5 -8
View File
@@ -1,6 +1,6 @@
general.vendor=LilyGO general.vendor=LilyGO
general.name=T-Deck Max general.name=T-Deck Max
# SD card support is not yet working, display driver is untested since refactoring; remove once it is functional. # SD card support is not yet working; remove once it is functional.
general.incubating=true general.incubating=true
apps.launcherAppId=Launcher apps.launcherAppId=Launcher
@@ -18,17 +18,14 @@ hardware.bluetooth=true
# Internal until the SD card is verified working on this board; switch to SD then. # Internal until the SD card is verified working on this board; switch to SD then.
storage.userDataLocation=Internal storage.userDataLocation=Internal
dependencies.useDeprecatedHal=false
display.size=3.1" display.size=3.1"
display.shape=rectangle display.shape=rectangle
display.dpi=128 display.dpi=128
lvgl.colorDepth=8 # The GDEQ031T10 e-paper is monochrome, but its driver renders via
# LV_COLOR_FORMAT_I1 on its own display. The global LVGL color depth must stay
# at 16: esp_lvgl_port refuses to compile with LV_COLOR_DEPTH_1 set.
lvgl.colorDepth=16
# Monochrome theme: the default colour theme renders accent-coloured UI that # Monochrome theme: the default colour theme renders accent-coloured UI that
# thresholds to invisible on a 1bpp panel (enables CONFIG_LV_USE_THEME_MONO). # thresholds to invisible on a 1bpp panel (enables CONFIG_LV_USE_THEME_MONO).
lvgl.theme=Mono lvgl.theme=Mono
cdn.warningMessage=Display not reliably working. Use at your own risk.
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
-1
View File
@@ -2,7 +2,6 @@ dependencies:
- Platforms/platform-esp32 - Platforms/platform-esp32
- Drivers/xl9555-module - Drivers/xl9555-module
- Drivers/cst66xx-module - Drivers/cst66xx-module
- Drivers/gdeq031t10-module
- Drivers/tca8418-module - Drivers/tca8418-module
- Drivers/sy6970-module - Drivers/sy6970-module
- Drivers/bq27220-module - Drivers/bq27220-module
@@ -11,7 +11,6 @@
#include <tactility/bindings/pwm_backlight.h> #include <tactility/bindings/pwm_backlight.h>
#include <bindings/xl9555.h> #include <bindings/xl9555.h>
#include <bindings/cst66xx.h> #include <bindings/cst66xx.h>
#include <bindings/gdeq031t10.h>
#include <bindings/tca8418.h> #include <bindings/tca8418.h>
#include <bindings/sy6970.h> #include <bindings/sy6970.h>
#include <bindings/bq27220.h> #include <bindings/bq27220.h>
@@ -144,17 +143,6 @@
// exceeds the default ~4 KB transfer limit. Raise the bus limit to fit it. // exceeds the default ~4 KB transfer limit. Raise the bus limit to fit it.
max-transfer-size = <65536>; max-transfer-size = <65536>;
display@0 {
compatible = "gooddisplay,gdeq031t10";
pin-dc = <&gpio0 35 GPIO_FLAG_NONE>;
pin-reset = <&gpio0 9 GPIO_FLAG_NONE>;
pin-busy = <&gpio0 37 GPIO_FLAG_NONE>;
clock-speed-hz = <10000000>;
// Default to a fast (~1s) refresh for the automatic ghost-clears so the
// full-screen flash they cause is as short as possible.
refresh-mode = <GDEQ031T10_REFRESH_FAST>;
};
sdcard@1 { sdcard@1 {
compatible = "espressif,esp32-sdspi"; compatible = "espressif,esp32-sdspi";
status = "disabled"; status = "disabled";
@@ -0,0 +1,35 @@
#include "devices/Display.h"
#include <Tactility/hal/Configuration.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/drivers/esp32_spi.h>
using namespace tt::hal;
static bool initBoot() {
// The LoRa and SD chip-selects share the EPD's SPI bus but aren't wired up
// yet. spi0's start() already drives every cs-gpio high during kernel init;
// re-assert deselection before the EPD driver first transacts, as a cheap
// guard against either chip latching stray EPD command bytes (same pattern
// as the esp32_sdspi mount path).
auto* spi0 = device_find_by_name("spi0");
check(spi0 != nullptr);
esp32_spi_deselect_all_cs(spi0);
return true;
}
static DeviceVector createDevices() {
// Touch, keyboard and battery/charging are kernel drivers (cst66xx, tca8418, sy6970,
// bq27220 - see the .dts), started independently of this HAL layer. Only the EPD
// display remains on the deprecated HAL.
return DeviceVector {
createDisplay()
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -0,0 +1,29 @@
#include "Display.h"
#include <Gdeq031t10Display.h>
// Pins from Xinyuan-LilyGO/T-Deck-MAX's lib/TDeckMaxBoard/src/TDeckMaxBoard.h and docs/pinmap.md.
constexpr auto EPD_SPI_HOST = SPI2_HOST;
constexpr auto EPD_PIN_CS = GPIO_NUM_34;
constexpr auto EPD_PIN_DC = GPIO_NUM_35;
constexpr auto EPD_PIN_RST = GPIO_NUM_9;
constexpr auto EPD_PIN_BUSY = GPIO_NUM_37;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
// Touch is a kernel driver (hynitron,cst66xx - see the .dts), discovered and fed to LVGL
// independently of this display, so no touch device is passed into the configuration here.
auto configuration = std::make_unique<Gdeq031t10Display::Configuration>(
EPD_SPI_HOST,
EPD_PIN_CS,
EPD_PIN_DC,
EPD_PIN_RST,
EPD_PIN_BUSY,
nullptr,
10'000'000,
// Default to a fast (~1s) refresh for the automatic ghost-clears so the
// full-screen flash they cause is as short as possible.
Gdeq031t10Display::RefreshMode::Fast
);
return std::make_shared<Gdeq031t10Display>(std::move(configuration));
}
@@ -0,0 +1,5 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
+16 -2
View File
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
Module lilygo_tdeck_max_module = { static error_t start() {
.name = "lilygo-tdeck-max" // Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module lilygo_tdeck_max_module = {
.name = "lilygo-tdeck-max",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -12,8 +12,6 @@ hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M hardware.esptoolFlashFreq=120M
hardware.bluetooth=true hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD storage.userDataLocation=SD
display.size=2.8" display.size=2.8"
@@ -19,6 +19,9 @@
constexpr auto* TAG = "tdeck-plus"; constexpr auto* TAG = "tdeck-plus";
// Legacy placeholder (required until legacy HAL is cleaned up everywhere)
extern const tt::hal::Configuration hardwareConfiguration = {};
extern "C" { extern "C" {
void subscribe_events() { void subscribe_events() {
-7
View File
@@ -1,7 +0,0 @@
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register(
SRCS ${SOURCE_FILES}
INCLUDE_DIRS "source"
REQUIRES TactilityKernel
)
@@ -1,33 +0,0 @@
general.vendor=LilyGO
general.name=T-Deck Pro
# The display driver is not reliable yet
general.incubating=true
apps.launcherAppId=Launcher
hardware.target=ESP32S3
hardware.flashSize=16MB
hardware.flashMode=DIO
hardware.spiRam=true
hardware.spiRamMode=AUTO
hardware.spiRamSpeed=80M
hardware.tinyUsb=true
hardware.esptoolFlashFreq=80M
hardware.bluetooth=true
storage.userDataLocation=SD
dependencies.useDeprecatedHal=false
display.size=3.1"
display.shape=rectangle
display.dpi=128
lvgl.colorDepth=8
# Monochrome theme: the default colour theme renders accent-coloured UI that
# thresholds to invisible on a 1bpp panel (enables CONFIG_LV_USE_THEME_MONO).
lvgl.theme=Mono
cdn.warningMessage=Only for hardware revision 1.0! Display not reliably working. Use at your own risk.
sdkconfig.CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=0
-8
View File
@@ -1,8 +0,0 @@
dependencies:
- Platforms/platform-esp32
- Drivers/cst328-module
- Drivers/gdeq031t10-module
- Drivers/tca8418-module
- Drivers/sy6970-module
- Drivers/bq25896-module
dts: lilygo,tdeck-pro.dts
@@ -1,138 +0,0 @@
/dts-v1/;
#include <tactility/bindings/root.h>
#include <tactility/bindings/esp32_ble.h>
#include <tactility/bindings/esp32_wifi_pinned.h>
#include <tactility/bindings/esp32_gpio.h>
#include <tactility/bindings/esp32_i2c_master.h>
#include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/esp32_pwm_ledc.h>
#include <tactility/bindings/pwm_backlight.h>
#include <bindings/cst328.h>
#include <bindings/gdeq031t10.h>
#include <bindings/tca8418.h>
#include <bindings/bq25896.h>
// Reference for V1.0 (not V1.1!): https://github.com/Xinyuan-LilyGO/T-Deck-Pro/tree/HD-V1-250326
// lib/TDeckMaxBoard/src/TDeckMaxBoard.h, docs/pinmap.md
/ {
compatible = "root";
model = "LilyGO T-Deck Pro";
wifi0 {
compatible = "espressif,esp32-wifi-pinned";
status = "disabled";
};
ble0 {
compatible = "espressif,esp32-ble";
};
gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
i2c0 {
compatible = "espressif,esp32-i2c-master";
port = <I2C_NUM_0>;
clock-frequency = <100000>;
pin-sda = <&gpio0 13 GPIO_FLAG_NONE>;
pin-scl = <&gpio0 14 GPIO_FLAG_NONE>;
touch {
compatible = "hynitron,cst328";
reg = <0x1A>;
pin-reset = <&gpio0 45 GPIO_FLAG_NONE>;
};
// TCA8418 4x10 matrix scanner. Keymaps are written in the vendor's row/column
// orientation (Xinyuan-LilyGO/T-Deck-MAX examples/factory/peri_keypad.cpp); the
// TCA8418 columns are wired in reverse of that order, hence reverse-columns.
// ALT (shift/uppercase) is row 2 col 0; SYM (symbol layer) is row 3 col 8.
keyboard {
compatible = "ti,tca8418";
reg = <0x34>;
rows = <4>;
columns = <10>;
reverse-columns;
keymap-lc = [
113 119 101 114 116 121 117 105 111 112 // q w e r t y u i o p
97 115 100 102 103 104 106 107 108 8 // a s d f g h j k l BACKSPACE
0 122 120 99 118 98 110 109 36 10 // z x c v b n m $ ENTER
0 0 0 0 0 11 48 32 0 9 // PREV 0 SPC NEXT
];
keymap-uc = [
81 87 69 82 84 89 85 73 79 80 // Q W E R T Y U I O P
65 83 68 70 71 72 74 75 76 8 // A S D F G H J K L BACKSPACE
0 90 88 67 86 66 78 77 36 10 // Z X C V B N M $ ENTER
0 0 0 0 0 11 48 32 0 9 // PREV 0 SPC NEXT
];
keymap-sy = [
49 50 51 52 53 54 55 56 57 48 // 1 2 3 4 5 6 7 8 9 0
64 35 43 45 42 47 40 41 95 8 // @ # + - * / ( ) _ BACKSPACE
0 33 63 59 58 39 34 44 46 10 // ! ? ; : ' " , . ENTER
0 0 0 0 0 11 48 32 0 9 // PREV 0 SPC NEXT
];
shift-row = <2>;
shift-col = <0>;
sym-row = <3>;
sym-col = <8>;
};
bq27220 {
compatible = "ti,bq25896";
reg = <0x6B>;
};
};
// Keyboard backlight (LED_PWM), GPIO42. The keypress-triggered flash/decay effect
// from the pre-migration driver isn't reproduced here - kernel drivers don't have a
// home for that kind of UI policy (same reasoning as the touch driver's dropped bezel
// keys). Off by default; app code can drive brightness via the generic pwm-backlight
// API if desired.
keyboard_backlight_pwm {
compatible = "espressif,esp32-pwm-ledc";
pin = <&gpio0 42 GPIO_FLAG_NONE>;
period-ns = <33333>;
ledc-timer = <0>;
ledc-channel = <0>;
};
keyboard_backlight {
compatible = "pwm-backlight";
status = "disabled";
pwm = <&keyboard_backlight_pwm>;
};
spi0 {
compatible = "espressif,esp32-spi";
host = <SPI2_HOST>;
cs-gpios = <&gpio0 34 GPIO_FLAG_NONE>, // 0: EPD display
<&gpio0 48 GPIO_FLAG_NONE>, // 1: SD card
<&gpio0 3 GPIO_FLAG_NONE>; // 2: LoRa radio (SX1262, not wired up yet)
pin-mosi = <&gpio0 33 GPIO_FLAG_NONE>;
pin-miso = <&gpio0 47 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
// The EPD pushes its whole 9600-byte framebuffer in one SPI write, which
// exceeds the default ~4 KB transfer limit. Raise the bus limit to fit it.
max-transfer-size = <65536>;
display@0 {
compatible = "gooddisplay,gdeq031t10";
pin-dc = <&gpio0 35 GPIO_FLAG_NONE>;
pin-busy = <&gpio0 37 GPIO_FLAG_NONE>;
clock-speed-hz = <10000000>;
// Default to a fast (~1s) refresh for the automatic ghost-clears so the
// full-screen flash they cause is as short as possible.
refresh-mode = <GDEQ031T10_REFRESH_FAST>;
};
sdcard@1 {
compatible = "espressif,esp32-sdspi";
status = "disabled";
frequency-khz = <20000>;
};
};
};
@@ -1,9 +0,0 @@
#include <tactility/module.h>
extern "C" {
struct Module lilygo_tdeck_pro_module = {
.name = "lilygo-tdeck-pro"
};
}
-2
View File
@@ -12,8 +12,6 @@ hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M hardware.esptoolFlashFreq=120M
hardware.bluetooth=true hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD storage.userDataLocation=SD
display.size=2.8" display.size=2.8"
+6 -1
View File
@@ -18,6 +18,9 @@
constexpr auto* TAG = "tdeck"; constexpr auto* TAG = "tdeck";
// Legacy placeholder (required until legacy HAL is cleaned up everywhere)
extern const tt::hal::Configuration hardwareConfiguration = {};
extern "C" { extern "C" {
void subscribe_events() { void subscribe_events() {
@@ -60,7 +63,9 @@ static error_t stop() {
Module lilygo_tdeck_module = { Module lilygo_tdeck_module = {
.name = "lilygo-tdeck", .name = "lilygo-tdeck",
.start = start, .start = start,
.stop = stop .stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+5 -6
View File
@@ -30,17 +30,16 @@ static error_t start() {
} }
static error_t stop() { static error_t stop() {
if (gpio_set_level(POWER_ON_PIN, 0) != ESP_OK) { // Empty for now
return ERROR_RESOURCE;
}
return ERROR_NONE; return ERROR_NONE;
} }
Module lilygo_tdisplay_s3_module = { struct Module lilygo_tdisplay_s3_module = {
.name = "lilygo-tdisplay-s3", .name = "lilygo-tdisplay-s3",
.start = start, .start = start,
.stop = stop .stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+16 -2
View File
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
Module lilygo_tdisplay_module = { static error_t start() {
.name = "lilygo-tdisplay" // Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module lilygo_tdisplay_module = {
.name = "lilygo-tdisplay",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+16 -2
View File
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
Module lilygo_tdongle_s3_module = { static error_t start() {
.name = "lilygo-tdongle-s3" // Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module lilygo_tdongle_s3_module = {
.name = "lilygo-tdongle-s3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+16 -2
View File
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
Module lilygo_thmi_module = { static error_t start() {
.name = "lilygo-thmi" // Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module lilygo_thmi_module = {
.name = "lilygo-thmi",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -13,8 +13,6 @@ hardware.tinyUsb=true
hardware.esptoolFlashFreq=40M hardware.esptoolFlashFreq=40M
hardware.bluetooth=true hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD storage.userDataLocation=SD
display.size=2.33" display.size=2.33"
+16 -8
View File
@@ -15,15 +15,18 @@
constexpr auto* TAG = "T-Lora Pager"; constexpr auto* TAG = "T-Lora Pager";
// Legacy placeholder (required until legacy HAL is cleaned up everywhere). All board
// peripherals are kernel drivers now (see the .dts): display (st7796), battery (bq27220),
// charger power-off (bq25896), haptics (drv2605), keyboard (tca8418) and the encoder wheel
// (lilygo,tpager-encoder, bound to LVGL below).
extern const tt::hal::Configuration hardwareConfiguration = {};
extern "C" { extern "C" {
tt::kernel::SystemEventSubscription event_subscription; static void subscribe_events() {
static error_t start() {
LOG_I(TAG, LOG_MESSAGE_POWER_ON_START); LOG_I(TAG, LOG_MESSAGE_POWER_ON_START);
event_subscription = tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootSplash, [](tt::kernel::SystemEvent) { tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootSplash, [](tt::kernel::SystemEvent) {
// The kernel tpager_encoder device is already started by kernel_init(); this just // The kernel tpager_encoder device is already started by kernel_init(); this just
// registers it as an LVGL input device, which requires LVGL to be up first. // registers it as an LVGL input device, which requires LVGL to be up first.
lvgl_lock(); lvgl_lock();
@@ -47,18 +50,23 @@ static error_t start() {
} }
} }
}); });
}
static error_t start() {
subscribe_events();
return ERROR_NONE; return ERROR_NONE;
} }
static error_t stop() { static error_t stop() {
tt::kernel::unsubscribeSystemEvent(event_subscription);
return ERROR_NONE; return ERROR_NONE;
} }
Module lilygo_tlora_pager_module = { struct Module lilygo_tlora_pager_module = {
.name = "lilygo-tlora-pager", .name = "lilygo-tlora-pager",
.start = start, .start = start,
.stop = stop .stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
Module m5stack_cardputer_adv_module = { static error_t start() {
.name = "m5stack-cardputer-adv" // Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module m5stack_cardputer_adv_module = {
.name = "m5stack-cardputer-adv",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+15 -1
View File
@@ -2,8 +2,22 @@
extern "C" { extern "C" {
static error_t start() {
// Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
Module m5stack_cardputer_module = { Module m5stack_cardputer_module = {
.name = "m5stack-cardputer" .name = "m5stack-cardputer",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+1 -1
View File
@@ -2,5 +2,5 @@ file(GLOB_RECURSE SOURCE_FILES source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
REQUIRES TactilityKernel REQUIRES TactilityKernel axp192-module
) )
-2
View File
@@ -21,5 +21,3 @@ display.dpi=200
cdn.warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant cdn.warningMessage=This board implementation concerns the original Core2 hardware and **not** the v1.1 variant
lvgl.colorDepth=16 lvgl.colorDepth=16
sdkconfig.CONFIG_CODEC_DUMMY_SUPPORT=y
+1 -4
View File
@@ -1,11 +1,8 @@
dependencies: dependencies:
- Platforms/platform-esp32 - Platforms/platform-esp32
- Drivers/audio-stream-module
- Drivers/pdm-mic-module
- Drivers/dummy-i2s-amp-module
- Drivers/mpu6886-module - Drivers/mpu6886-module
- Drivers/bm8563-module - Drivers/bm8563-module
- Drivers/axp192-module - Drivers/axp192-module
- Drivers/ft5x06-module - Drivers/ft6x36-module
- Drivers/ili9341-module - Drivers/ili9341-module
dts: m5stack,core2.dts dts: m5stack,core2.dts
+5 -29
View File
@@ -6,16 +6,14 @@
#include <tactility/bindings/esp32_grove.h> #include <tactility/bindings/esp32_grove.h>
#include <tactility/bindings/esp32_i2c.h> #include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_i2s.h> #include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/esp32_spi.h> #include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_uart.h> #include <tactility/bindings/esp32_uart.h>
#include <bindings/mpu6886.h> #include <bindings/mpu6886.h>
#include <bindings/bm8563.h> #include <bindings/bm8563.h>
#include <bindings/axp192.h> #include <bindings/axp192.h>
#include <bindings/ft5x06.h> #include <bindings/ft6x36.h>
#include <bindings/ili9341.h> #include <bindings/ili9341.h>
#include <bindings/dummy_i2s_amp.h> #include <tactility/bindings/esp32_sdspi.h>
#include <bindings/pdm_mic.h>
// Reference: https://docs.m5stack.com/en/core/Core2 // Reference: https://docs.m5stack.com/en/core/Core2
/ { / {
@@ -52,23 +50,10 @@
axp192 { axp192 {
compatible = "x-powers,axp192"; compatible = "x-powers,axp192";
reg = <0x34>; reg = <0x34>;
dcdc1-enable; // ESP32 + peripherals main 3V3 rail - must stay on
dcdc1-voltage = <3300>;
dcdc3-enable; // LCD backlight
dcdc3-voltage = <3300>;
ldo2-enable; // LCD logic + SD card
ldo2-voltage = <3300>;
// ldo3 (vibration motor) intentionally left disabled
exten-enable; // 5V boost (speaker amp etc.)
gpio1-pwm; // GPIO1 as PWM1 output, drives the notification LED
gpio1-pwm1-duty-cycle = <255>; // 255 = LED off
}; };
// FT6x36 driver doesn't reliably work when power cycling,
// or when the driver was powering on with its own power supply (not USB-C).
// The driver would not find the device and init would fail.
touch { touch {
compatible = "focaltech,ft5x06"; compatible = "focaltech,ft6x36";
reg = <0x38>; reg = <0x38>;
x-max = <320>; x-max = <320>;
y-max = <240>; y-max = <240>;
@@ -111,6 +96,8 @@
}; };
}; };
// NS4168: Speaker and microphone
// TODO: Init microphone via I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L391C19-L391C44
i2s0 { i2s0 {
compatible = "espressif,esp32-i2s"; compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>; port = <I2S_NUM_0>;
@@ -119,15 +106,4 @@
pin-data-out = <&gpio0 2 GPIO_FLAG_NONE>; pin-data-out = <&gpio0 2 GPIO_FLAG_NONE>;
pin-data-in = <&gpio0 34 GPIO_FLAG_NONE>; pin-data-in = <&gpio0 34 GPIO_FLAG_NONE>;
}; };
speaker0 {
compatible = "nsiway,ns4168";
i2s = <&i2s0>;
};
mic0 {
compatible = "generic,spm1423";
i2s = <&i2s0>;
channels = <1>;
};
}; };
+42 -5
View File
@@ -1,12 +1,49 @@
#include <drivers/axp192.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/device_listener.h>
#include <tactility/module.h> #include <tactility/module.h>
#include <cstring>
extern "C" { extern "C" {
// AXP192 rail/GPIO1 bring-up is now devicetree-configured (see m5stack,core2.dts's axp192 node static void configure_axp192(Device* axp192) {
// and axp192-module's start()), replacing what used to be done by hand here via a check(axp192_set_rail_voltage(axp192, AXP192_RAIL_LDO2, 3300) == ERROR_NONE); // LCD + SD
// DEVICE_EVENT_STARTED device_listener. check(axp192_set_rail_voltage(axp192, AXP192_RAIL_DCDC3, 3300) == ERROR_NONE); // LCD backlight
Module m5stack_core2_module = {
.name = "m5stack-core2" check(axp192_set_rail_enabled(axp192, AXP192_RAIL_LDO2, true) == ERROR_NONE);
check(axp192_set_rail_enabled(axp192, AXP192_RAIL_LDO3, false) == ERROR_NONE); // VIB_MOTOR stop
check(axp192_set_rail_enabled(axp192, AXP192_RAIL_DCDC3, true) == ERROR_NONE);
check(axp192_set_pwm1_duty_cycle(axp192, 255) == ERROR_NONE); // PWM 255 (LED off)
check(axp192_set_gpio1_pwm1_output(axp192) == ERROR_NONE); // GPIO1: PWM
}
static void on_device_event(Device* device, DeviceEvent event, void* context) {
(void)context;
if (event == DEVICE_EVENT_STARTED && strcmp(device->name, "axp192") == 0) {
configure_axp192(device);
}
}
static error_t start() {
device_listener_add(on_device_event, nullptr);
return ERROR_NONE;
}
static error_t stop() {
device_listener_remove(on_device_event);
return ERROR_NONE;
}
struct Module m5stack_core2_module = {
.name = "m5stack-core2",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+3 -6
View File
@@ -1,12 +1,9 @@
dependencies: dependencies:
- Platforms/platform-esp32 - Platforms/platform-esp32
- Drivers/audio-stream-module
- Drivers/aw88298-module
- Drivers/aw9523b-module
- Drivers/axp2101-module
- Drivers/bm8563-module
- Drivers/bmi270-module - Drivers/bmi270-module
- Drivers/es7210-module - Drivers/bm8563-module
- Drivers/axp2101-module
- Drivers/aw9523b-module
- Drivers/ft5x06-module - Drivers/ft5x06-module
- Drivers/ili9341-module - Drivers/ili9341-module
dts: m5stack,cores3.dts dts: m5stack,cores3.dts
+46 -72
View File
@@ -9,16 +9,15 @@
#include <tactility/bindings/esp32_i2s.h> #include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_spi.h> #include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_uart.h> #include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/gpio_hog.h>
#include <bindings/aw88298.h>
#include <bindings/aw9523b.h>
#include <bindings/axp2101.h>
#include <bindings/axp2101_backlight.h>
#include <bindings/bmi270.h> #include <bindings/bmi270.h>
#include <bindings/bm8563.h> #include <bindings/bm8563.h>
#include <bindings/es7210.h> #include <bindings/axp2101.h>
#include <bindings/axp2101_backlight.h>
#include <bindings/aw9523b.h>
#include <bindings/ft5x06.h> #include <bindings/ft5x06.h>
#include <bindings/ili9341.h> #include <bindings/ili9341.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/gpio_hog.h>
// SD card is not supported because of a hardware problem: // SD card is not supported because of a hardware problem:
// The display uses pin 35 as input (MISO), but the SPI bus (and SD card) uses the same pin as DC (output). // The display uses pin 35 as input (MISO), but the SPI bus (and SD card) uses the same pin as DC (output).
@@ -43,18 +42,6 @@
gpio-count = <49>; gpio-count = <49>;
}; };
i2s0 {
// Note: M5Unified sets the following for speaker: magnification = 2, oversampling = 1
// Note: M5Unified sets the following for microphone: magnification = 4
compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>;
pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>;
pin-ws = <&gpio0 33 GPIO_FLAG_NONE>;
pin-data-out = <&gpio0 13 GPIO_FLAG_NONE>;
pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>;
pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>;
};
i2c_internal { i2c_internal {
compatible = "espressif,esp32-i2c"; compatible = "espressif,esp32-i2c";
port = <I2C_NUM_0>; port = <I2C_NUM_0>;
@@ -74,55 +61,18 @@
// P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset, // P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset,
// P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088) // P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088)
aw9523b { aw9523b: aw9523b {
compatible = "awinic,aw9523b"; compatible = "awinic,aw9523b";
reg = <0x58>; reg = <0x58>;
// Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable
// (SY7088) are AW9523B pins with no owning peripheral driver yet.
aw9523_bus_out_enable {
compatible = "gpio-hog";
pin = <&aw9523b 1 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
aw9523_sdcard_switch {
compatible = "gpio-hog";
pin = <&aw9523b 4 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_INPUT>;
};
aw9523_boost_enable {
compatible = "gpio-hog";
pin = <&aw9523b 15 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
};
aw88298 {
compatible = "awinic,aw88298";
reg = <0x36>;
i2s = <&i2s0>;
pin-reset = <&aw9523b 2 GPIO_FLAG_NONE>;
}; };
axp2101: axp2101 { axp2101: axp2101 {
compatible = "x-powers,axp2101"; compatible = "x-powers,axp2101";
reg = <0x34>; reg = <0x34>;
aldo1-millivolt = <1800>;
aldo1-enabled;
aldo2-millivolt = <3300>;
aldo2-enabled;
aldo3-millivolt = <3300>;
aldo3-enabled;
aldo4-millivolt = <3300>;
aldo4-enabled;
bldo1-enabled;
bldo2-enabled;
// LCD backlight (DLDO1). Raw register codes [20,28] map to [2500,3300]mV; // LCD backlight (DLDO1). Raw register codes [20,28] map to [2500,3300]mV;
// below 2500mV the backlight was considered "too dark" to be useful. // below 2500mV the backlight was considered "too dark" to be useful.
display_backlight { backlight {
compatible = "axp2101-backlight"; compatible = "axp2101-backlight";
ldo = <AXP2101_DLDO1>; ldo = <AXP2101_DLDO1>;
min-millivolt = <2500>; min-millivolt = <2500>;
@@ -137,21 +87,32 @@
y-max = <240>; y-max = <240>;
pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>;
}; };
};
es7210 { // Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable
compatible = "everest,es7210"; // (SY7088) are AW9523B pins with no owning peripheral driver yet.
reg = <0x40>; aw9523_bus_out_enable {
i2s = <&i2s0>; compatible = "gpio-hog";
// Only MIC1/MIC2 are wired to real capsules (see schematic); MIC3/MIC4 pin = <&aw9523b 1 GPIO_FLAG_NONE>;
// slots carry AEC_P/AEC_N reference signal, not microphone audio. mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
mic-mask = <3>; };
// M5Unified applies a 4x post-gain "magnification" for this exact mic
// wiring (see M5Unified.cpp _mic_data_cb_cores3): the ES7210's own aw9523_aw88298_reset {
// hardware ADC gain (already near-max via the default 90% input volume compatible = "gpio-hog";
// setting, ~34dB of its 0-37.5dB range) still isn't enough for these pin = <&aw9523b 2 GPIO_FLAG_NONE>;
// capsules on their own. mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
input-gain-percent = <400>; };
};
aw9523_sdcard_switch {
compatible = "gpio-hog";
pin = <&aw9523b 4 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
aw9523_boost_enable {
compatible = "gpio-hog";
pin = <&aw9523b 15 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
}; };
port_a: grove0 { port_a: grove0 {
@@ -202,7 +163,20 @@
pixel-clock-hz = <40000000>; pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 35 GPIO_FLAG_NONE>; pin-dc = <&gpio0 35 GPIO_FLAG_NONE>;
pin-reset = <&aw9523b 9 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 9 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
}; };
}; };
// TODO: Enable speaker via ES7210 I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L417
// TODO: Enable microphone via ES7210 I2C: https://github.com/m5stack/M5Unified/blob/a6256725481f1bc366655fa48cf03b6095e30ad1/src/M5Unified.cpp#L616
i2s0 {
// Note: M5Unified sets the following for speaker: magnification = 2, oversampling = 1
// Note: M5Unified sets the following for microphone: magnification = 4
compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>;
pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>;
pin-ws = <&gpio0 33 GPIO_FLAG_NONE>;
pin-data-out = <&gpio0 13 GPIO_FLAG_NONE>;
pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>;
pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>;
};
}; };
+48 -2
View File
@@ -1,9 +1,55 @@
#include <drivers/axp2101.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/device_listener.h>
#include <tactility/module.h> #include <tactility/module.h>
#include <cstring>
extern "C" { extern "C" {
Module m5stack_cores3_module = { // M5Stack CoreS3 AXP2101 rail setup, ported from the board's old deprecated-HAL
.name = "m5stack-cores3" // initPowerControl() (source: M5Unified's Power_Class.cpp). ALDO1 powers the AW88298 audio
// amp, ALDO2 the ES7210 microphone ADC, ALDO3 the GC0308 camera, ALDO4 the TF/SD card slot.
// DLDO1 (LCD backlight) is left to the axp2101-backlight child device.
static void configure_axp2101(Device* axp2101) {
check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO1, 1800) == ERROR_NONE);
check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO2, 3300) == ERROR_NONE);
check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO3, 3300) == ERROR_NONE);
check(axp2101_set_ldo_voltage(axp2101, AXP2101_ALDO4, 3300) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO1, true) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO2, true) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO3, true) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_ALDO4, true) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_BLDO1, true) == ERROR_NONE);
check(axp2101_set_ldo_enabled(axp2101, AXP2101_BLDO2, true) == ERROR_NONE);
}
static void on_device_event(Device* device, DeviceEvent event, void* context) {
(void)context;
if (event == DEVICE_EVENT_STARTED && strcmp(device->name, "axp2101") == 0) {
configure_axp2101(device);
}
}
static error_t start() {
device_listener_add(on_device_event, nullptr);
return ERROR_NONE;
}
static error_t stop() {
device_listener_remove(on_device_event);
return ERROR_NONE;
}
struct Module m5stack_cores3_module = {
.name = "m5stack-cores3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
#include "papers3_power.h" #include "papers3_power.h"
#include <tactility/check.h> #include <tactility/check.h>
#include <tactility/driver.h> #include <tactility/driver.h>
#include <tactility/drivers/gpio.h>
#include <tactility/drivers/gpio_controller.h> #include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/power_supply.h> #include <tactility/drivers/power_supply.h>
#include <tactility/drivers/pwm.h> #include <tactility/drivers/pwm.h>
@@ -15,7 +13,7 @@
#include <new> #include <new>
constexpr auto* TAG = "Papers3Power"; #define TAG "Papers3Power"
#define GET_CONFIG(device) (static_cast<const Papers3PowerConfig*>((device)->config)) #define GET_CONFIG(device) (static_cast<const Papers3PowerConfig*>((device)->config))
// Power-off signal timing, ported from the old deprecated-HAL PaperS3Power::powerOff(). // Power-off signal timing, ported from the old deprecated-HAL PaperS3Power::powerOff().
@@ -194,8 +192,17 @@ static void destroy_power_supply_child(Device* child) {
// region Driver lifecycle // region Driver lifecycle
static error_t acquire_input(const GpioPinSpec& pin, GpioDescriptor** out_descriptor) { static error_t acquire_input(const GpioPinSpec& pin, GpioDescriptor** out_descriptor) {
*out_descriptor = gpio_descriptor_acquire(pin.gpio_controller, pin.pin, pin.flags | GPIO_FLAG_DIRECTION_INPUT, GPIO_OWNER_GPIO); auto* descriptor = gpio_descriptor_acquire(pin.gpio_controller, pin.pin, GPIO_OWNER_GPIO);
return (*out_descriptor != nullptr) ? ERROR_NONE : ERROR_RESOURCE; if (descriptor == nullptr) {
return ERROR_RESOURCE;
}
error_t error = gpio_descriptor_set_flags(descriptor, pin.flags | GPIO_FLAG_DIRECTION_INPUT);
if (error != ERROR_NONE) {
gpio_descriptor_release(descriptor);
return error;
}
*out_descriptor = descriptor;
return ERROR_NONE;
} }
static error_t start(Device* device) { static error_t start(Device* device) {
@@ -219,8 +226,13 @@ static error_t start(Device* device) {
return ERROR_RESOURCE; return ERROR_RESOURCE;
} }
internal->power_off_descriptor = gpio_descriptor_acquire(config->pin_power_off.gpio_controller, config->pin_power_off.pin, config->pin_power_off.flags | GPIO_FLAG_DIRECTION_OUTPUT, GPIO_OWNER_GPIO); internal->power_off_descriptor = gpio_descriptor_acquire(config->pin_power_off.gpio_controller, config->pin_power_off.pin, GPIO_OWNER_GPIO);
if (internal->power_off_descriptor == nullptr) { if (internal->power_off_descriptor == nullptr ||
gpio_descriptor_set_flags(internal->power_off_descriptor, config->pin_power_off.flags | GPIO_FLAG_DIRECTION_OUTPUT) != ERROR_NONE) {
LOG_E(TAG, "Failed to configure power-off pin");
if (internal->power_off_descriptor != nullptr) {
gpio_descriptor_release(internal->power_off_descriptor);
}
gpio_descriptor_release(internal->usb_detect_descriptor); gpio_descriptor_release(internal->usb_detect_descriptor);
gpio_descriptor_release(internal->charge_status_descriptor); gpio_descriptor_release(internal->charge_status_descriptor);
delete internal; delete internal;
+19 -7
View File
@@ -1,4 +1,6 @@
#include <tactility/check.h>
#include <tactility/driver.h> #include <tactility/driver.h>
#include <tactility/error.h>
#include <tactility/module.h> #include <tactility/module.h>
extern "C" { extern "C" {
@@ -7,16 +9,26 @@ extern Driver papers3_power_driver;
extern Driver papers3_power_supply_driver; extern Driver papers3_power_supply_driver;
extern Driver papers3_display_driver; extern Driver papers3_display_driver;
static Driver* const papers3_drivers[] = { static error_t start() {
&papers3_display_driver, check(driver_construct_add(&papers3_power_supply_driver) == ERROR_NONE);
&papers3_power_driver, check(driver_construct_add(&papers3_power_driver) == ERROR_NONE);
&papers3_power_supply_driver, check(driver_construct_add(&papers3_display_driver) == ERROR_NONE);
nullptr return ERROR_NONE;
}; }
static error_t stop() {
check(driver_remove_destruct(&papers3_display_driver) == ERROR_NONE);
check(driver_remove_destruct(&papers3_power_driver) == ERROR_NONE);
check(driver_remove_destruct(&papers3_power_supply_driver) == ERROR_NONE);
return ERROR_NONE;
}
Module m5stack_papers3_module = { Module m5stack_papers3_module = {
.name = "m5stack-papers3", .name = "m5stack-papers3",
.drivers = papers3_drivers .start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+3 -3
View File
@@ -1,7 +1,7 @@
file(GLOB_RECURSE SOURCE_FILES source/*.c*) file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "source" INCLUDE_DIRS "Source"
REQUIRES TactilityKernel py32ioexpander-module REQUIRES Tactility esp_lvgl_port ILI934x FT6x36 AXP2101 driver vfs fatfs ina226-module py32ioexpander-module
) )
@@ -0,0 +1,162 @@
#include "devices/Display.h"
#include "devices/Power.h"
#include <tactility/device.h>
#include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <drivers/py32ioexpander.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Tactility/hal/Configuration.h>
#include <Axp2101Power.h>
#include <Axp2101.h>
using namespace tt::hal;
static const auto* TAG = "StackChan";
// ---------------------------------------------------------------------------
// I2C addresses
// ---------------------------------------------------------------------------
static constexpr uint8_t AXP2101_ADDR = 0x34;
// ---------------------------------------------------------------------------
// AW9523B GPIO expander pin map — same wiring as CoreS3.
// AW88298 reset (P0_2) is driven directly by the aw88298-module driver itself
// (see m5stack,stackchan.dts pin-reset property), not from here.
// ---------------------------------------------------------------------------
constexpr auto AW9523B_PIN_TOUCH_RESET = 0; // P0_0
constexpr auto AW9523B_PIN_BUS_OUT_ENABLE = 1; // P0_1
constexpr auto AW9523B_PIN_SD_CARD_SWITCH = 4; // P0_4
constexpr auto AW9523B_PIN_LCD_RESET = 8 + 1; // P1_1
constexpr auto AW9523B_PIN_BOOST_ENABLE = 8 + 7; // P1_7 (SY7088)
static bool initGpioExpander(::Device* aw9523b) {
struct PinInit { uint8_t pin; bool level; };
static constexpr PinInit pins[] = {
{ AW9523B_PIN_TOUCH_RESET, true },
{ AW9523B_PIN_BUS_OUT_ENABLE, true },
{ AW9523B_PIN_SD_CARD_SWITCH, true },
{ AW9523B_PIN_LCD_RESET, true },
{ AW9523B_PIN_BOOST_ENABLE, true },
};
for (const auto& pinInit : pins) {
auto* descriptor = gpio_descriptor_acquire(aw9523b, pinInit.pin, GPIO_OWNER_GPIO);
if (descriptor == nullptr) {
LOG_E(TAG, "AW9523B: Failed to acquire pin %u", pinInit.pin);
return false;
}
error_t error = gpio_descriptor_set_flags(descriptor, GPIO_FLAG_DIRECTION_OUTPUT);
if (error == ERROR_NONE) {
error = gpio_descriptor_set_level(descriptor, pinInit.level);
}
gpio_descriptor_release(descriptor);
if (error != ERROR_NONE) {
LOG_E(TAG, "AW9523B: Failed to configure pin %u", pinInit.pin);
return false;
}
}
return true;
}
// ---------------------------------------------------------------------------
// AXP2101 power management — same voltage rails as CoreS3
// ---------------------------------------------------------------------------
static bool initPowerControl(::Device* i2c) {
// Source: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/Power_Class.cpp#L64
static constexpr uint8_t reg_data[] = {
0x90U, 0xBFU, // LDOS ON/OFF control 0 (backlight)
0x92U, 18U - 5U, // ALDO1 = 1.8V (AW88298)
0x93U, 33U - 5U, // ALDO2 = 3.3V (ES7210)
0x94U, 33U - 5U, // ALDO3 = 3.3V (camera)
0x95U, 33U - 5U, // ALDO4 = 3.3V (TF card)
0x27U, 0x00U, // PowerKey Hold=1sec / PowerOff=4sec
0x69U, 0x11U, // CHGLED setting
0x10U, 0x30U, // PMU common config
0x30U, 0x0FU, // ADC enabled
};
if (i2c_controller_write_register_array(i2c, AXP2101_ADDR, reg_data, sizeof(reg_data), pdMS_TO_TICKS(1000)) != ERROR_NONE) {
LOG_E(TAG, "AXP2101: Failed to set registers");
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// initBoot — called after devicetree devices are constructed/started
// ---------------------------------------------------------------------------
static std::shared_ptr<Axp2101> axp2101;
bool initBoot() {
auto* i2c = device_find_by_name("i2c_internal");
if (i2c == nullptr) {
LOG_E(TAG, "i2c_internal not found");
return false;
}
// Boost enable via AXP2101 before GPIO expander init (same as CoreS3)
if (!initPowerControl(i2c)) {
LOG_E(TAG, "AXP2101 init failed");
return false;
}
auto* aw9523b = device_find_by_name("aw9523b");
if (aw9523b == nullptr) {
LOG_E(TAG, "aw9523b not found");
return false;
}
if (!initGpioExpander(aw9523b)) {
LOG_E(TAG, "AW9523B init failed");
return false;
}
// Boot LED pattern — confirms PY32IOExpander is working.
// PY32 pin 0 = servo VM_EN (output), pin 13 = WS2812C data line.
auto* py32 = device_find_by_name("py32");
if (py32 != nullptr) {
static constexpr uint8_t LED_COUNT = 12;
static constexpr uint8_t COLORS[][3] = {
{ 255, 0, 0 }, // red
{ 0, 255, 0 }, // green
{ 0, 0, 255 }, // blue
};
py32_led_set_count(py32, LED_COUNT);
for (auto& c : COLORS) {
for (uint8_t i = 0; i < LED_COUNT; i++) {
py32_led_set_color(py32, i, c[0], c[1], c[2]);
}
py32_led_refresh(py32);
vTaskDelay(pdMS_TO_TICKS(150));
}
py32_led_disable(py32);
} else {
LOG_W(TAG, "py32 not found — LED boot pattern skipped");
}
// Keep Axp2101 C++ wrapper alive for Axp2101Power (backlight + battery)
axp2101 = std::make_shared<Axp2101>(i2c);
return true;
}
// ---------------------------------------------------------------------------
// Device list
// ---------------------------------------------------------------------------
static DeviceVector createDevices() {
return {
axp2101,
std::make_shared<Axp2101Power>(axp2101),
createPower(),
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -0,0 +1,61 @@
#include "Display.h"
#include <Axp2101.h>
#include <Ft6x36Touch.h>
#include <Ili934xDisplay.h>
#include <tactility/check.h>
#include <tactility/log.h>
constexpr auto* TAG = "StackChanDisplay";
static void setBacklightDuty(uint8_t backlightDuty) {
const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100]
auto controller = device_find_by_name("i2c_internal");
check(controller);
if (i2c_controller_write_register(controller, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000) != ERROR_NONE) { // Sets DLD01
LOG_E(TAG, "Failed to set display backlight voltage");
}
}
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
auto configuration = std::make_unique<Ft6x36Touch::Configuration>(
I2C_NUM_0,
319,//LCD_HORIZONTAL_RESOLUTION,
239,//LCD_VERTICAL_RESOLUTION,
false,
false,
false
);
return std::make_shared<Ft6x36Touch>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
Ili934xDisplay::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 0,
.gapY = 0,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = true,
.swapBytes = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = createTouch(),
.backlightDutyFunction = ::setBacklightDuty,
.resetPin = GPIO_NUM_NC,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_BGR
};
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
}
@@ -0,0 +1,17 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <driver/gpio.h>
#include <driver/spi_common.h>
// Display
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_3;
constexpr auto LCD_PIN_DC = GPIO_NUM_35;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 320;
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
constexpr auto LCD_SPI_TRANSFER_SIZE_LIMIT = LCD_BUFFER_SIZE * LV_COLOR_DEPTH / 8;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
@@ -0,0 +1,86 @@
#include "Power.h"
#include <Tactility/hal/power/PowerDevice.h>
#include <drivers/ina226.h>
#include <tactility/device.h>
#include <tactility/log.h>
using namespace tt::hal::power;
static constexpr auto* TAG = "StackChanPower";
// 1S Li-ion cell (550 mAh): 3.2V (empty) 4.2V (full)
static constexpr float MIN_BATTERY_VOLTAGE_MV = 3200.0f;
static constexpr float MAX_BATTERY_VOLTAGE_MV = 4200.0f;
class StackChanPower final : public PowerDevice {
public:
explicit StackChanPower(::Device* ina226Device) : ina226(ina226Device) {}
std::string getName() const override { return "M5Stack StackChan Power"; }
std::string getDescription() const override { return "Battery monitoring via INA226 over I2C"; }
bool supportsMetric(MetricType type) const override {
switch (type) {
using enum MetricType;
case BatteryVoltage:
case ChargeLevel:
case Current:
return ina226 != nullptr;
default:
return false;
}
}
bool getMetric(MetricType type, MetricData& data) override {
switch (type) {
using enum MetricType;
case BatteryVoltage: {
if (ina226 == nullptr) return false;
float volts = 0.0f;
if (ina226_read_bus_voltage(ina226, &volts) != ERROR_NONE) return false;
data.valueAsUint32 = static_cast<uint32_t>(volts * 1000.0f);
return true;
}
case ChargeLevel: {
if (ina226 == nullptr) return false;
float volts = 0.0f;
if (ina226_read_bus_voltage(ina226, &volts) != ERROR_NONE) return false;
float voltage_mv = volts * 1000.0f;
if (voltage_mv >= MAX_BATTERY_VOLTAGE_MV) {
data.valueAsUint8 = 100;
} else if (voltage_mv <= MIN_BATTERY_VOLTAGE_MV) {
data.valueAsUint8 = 0;
} else {
float factor = (voltage_mv - MIN_BATTERY_VOLTAGE_MV) / (MAX_BATTERY_VOLTAGE_MV - MIN_BATTERY_VOLTAGE_MV);
data.valueAsUint8 = static_cast<uint8_t>(factor * 100.0f);
}
return true;
}
case Current: {
if (ina226 == nullptr) return false;
float amps = 0.0f;
if (ina226_read_shunt_current(ina226, &amps) != ERROR_NONE) return false;
data.valueAsInt32 = static_cast<int32_t>(amps * 1000.0f);
return true;
}
default:
return false;
}
}
private:
::Device* ina226;
};
std::shared_ptr<PowerDevice> createPower() {
auto* ina226 = device_find_by_name("ina226");
if (ina226 == nullptr) {
LOG_E(TAG, "ina226 device not found");
}
return std::make_shared<StackChanPower>(ina226);
}
@@ -0,0 +1,6 @@
#pragma once
#include <memory>
#include <Tactility/hal/power/PowerDevice.h>
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
@@ -0,0 +1,21 @@
#include <tactility/module.h>
extern "C" {
static error_t start() {
return ERROR_NONE;
}
static error_t stop() {
return ERROR_NONE;
}
struct Module m5stack_stackchan_module = {
.name = "m5stack-stackchan",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}
@@ -12,8 +12,6 @@ hardware.tinyUsb=true
hardware.esptoolFlashFreq=120M hardware.esptoolFlashFreq=120M
hardware.bluetooth=true hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=SD storage.userDataLocation=SD
display.size=2" display.size=2"
+5 -8
View File
@@ -1,14 +1,11 @@
dependencies: dependencies:
- Platforms/platform-esp32 - Platforms/platform-esp32
- Drivers/audio-stream-module
- Drivers/aw88298-module
- Drivers/aw9523b-module
- Drivers/axp2101-module
- Drivers/bm8563-module
- Drivers/bmi270-module - Drivers/bmi270-module
- Drivers/es7210-module - Drivers/bm8563-module
- Drivers/ft6x36-module
- Drivers/ili9341-module
- Drivers/ina226-module - Drivers/ina226-module
- Drivers/py32ioexpander-module - Drivers/py32ioexpander-module
- Drivers/aw9523b-module
- Drivers/aw88298-module
- Drivers/es7210-module
- Drivers/audio-stream-module
dts: m5stack,stackchan.dts dts: m5stack,stackchan.dts
+13 -75
View File
@@ -7,21 +7,17 @@
#include <tactility/bindings/esp32_grove.h> #include <tactility/bindings/esp32_grove.h>
#include <tactility/bindings/esp32_i2c.h> #include <tactility/bindings/esp32_i2c.h>
#include <tactility/bindings/esp32_i2s.h> #include <tactility/bindings/esp32_i2s.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/esp32_spi.h> #include <tactility/bindings/esp32_spi.h>
#include <tactility/bindings/esp32_uart.h> #include <tactility/bindings/esp32_uart.h>
#include <tactility/bindings/gpio_hog.h>
#include <bindings/aw88298.h>
#include <bindings/aw9523b.h>
#include <bindings/axp2101.h>
#include <bindings/axp2101_backlight.h>
#include <bindings/bm8563.h>
#include <bindings/bmi270.h> #include <bindings/bmi270.h>
#include <bindings/es7210.h> #include <bindings/bm8563.h>
#include <bindings/ft6x36.h>
#include <bindings/ili9341.h>
#include <bindings/ina226.h> #include <bindings/ina226.h>
#include <bindings/py32ioexpander.h> #include <bindings/py32ioexpander.h>
#include <bindings/aw9523b.h>
#include <bindings/aw88298.h>
#include <bindings/es7210.h>
#include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/display_placeholder.h>
// Reference: https://docs.m5stack.com/en/StackChan // Reference: https://docs.m5stack.com/en/StackChan
/ { / {
@@ -44,7 +40,7 @@
}; };
// AW88298 speaker + ES7210 microphone // AW88298 speaker + ES7210 microphone
i2s0 { i2s0: i2s0 {
compatible = "espressif,esp32-i2s"; compatible = "espressif,esp32-i2s";
port = <I2S_NUM_0>; port = <I2S_NUM_0>;
pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>; pin-bclk = <&gpio0 34 GPIO_FLAG_NONE>;
@@ -81,34 +77,14 @@
compatible = "ti,ina226"; compatible = "ti,ina226";
reg = <0x41>; reg = <0x41>;
shunt-milliohms = <10>; shunt-milliohms = <10>;
power-supply;
}; };
// AW9523B pin map (same wiring as CoreS3):
// P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset, // P0_0 = touch reset, P0_1 = bus-out enable, P0_2 = AW88298 reset,
// P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088) // P0_4 = SD card switch, P1_1 = LCD reset, P1_7 = boost enable (SY7088)
aw9523b { aw9523b: aw9523b {
compatible = "awinic,aw9523b"; compatible = "awinic,aw9523b";
reg = <0x58>; reg = <0x58>;
// Bus-out enable, AW88298 (audio amp) reset, SD card power switch and boost enable
// (SY7088) are AW9523B pins with no owning peripheral driver yet.
aw9523_bus_out_enable {
compatible = "gpio-hog";
pin = <&aw9523b 1 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
aw9523_sdcard_switch {
compatible = "gpio-hog";
pin = <&aw9523b 4 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_INPUT>;
};
aw9523_boost_enable {
compatible = "gpio-hog";
pin = <&aw9523b 15 GPIO_FLAG_NONE>;
mode = <GPIO_HOG_MODE_OUTPUT_HIGH>;
};
}; };
aw88298 { aw88298 {
@@ -118,39 +94,6 @@
pin-reset = <&aw9523b 2 GPIO_FLAG_NONE>; pin-reset = <&aw9523b 2 GPIO_FLAG_NONE>;
}; };
// Same rail assignment as CoreS3: ALDO1=AW88298, ALDO2=ES7210, ALDO3=camera
// (not yet implemented, but harmless to power), ALDO4=TF/SD card. BLDO1/BLDO2 are
// enabled with no specific voltage requirement.
axp2101 {
compatible = "x-powers,axp2101";
reg = <0x34>;
aldo1-millivolt = <1800>;
aldo1-enabled;
aldo2-millivolt = <3300>;
aldo2-enabled;
aldo3-millivolt = <3300>;
aldo3-enabled;
aldo4-millivolt = <3300>;
aldo4-enabled;
bldo1-enabled;
bldo2-enabled;
display_backlight {
compatible = "axp2101-backlight";
ldo = <AXP2101_DLDO1>;
min-millivolt = <2500>;
max-millivolt = <3300>;
};
};
touch {
compatible = "focaltech,ft6x36";
reg = <0x38>;
x-max = <319>;
y-max = <239>;
pin-reset = <&aw9523b 0 GPIO_FLAG_NONE>;
};
es7210 { es7210 {
compatible = "everest,es7210"; compatible = "everest,es7210";
reg = <0x40>; reg = <0x40>;
@@ -166,6 +109,9 @@
input-gain-percent = <400>; input-gain-percent = <400>;
}; };
// AXP2101 PMIC @ 0x34 — initialized manually in initBoot()
// FT6336U capacitive touch @ 0x38 — used by Display driver (FT6x36 library)
// TODO: Si12T 3-zone head touch @ 0x68 — INT active-low, 10kΩ pull-up to 3.3V; driver not yet implemented // TODO: Si12T 3-zone head touch @ 0x68 — INT active-low, 10kΩ pull-up to 3.3V; driver not yet implemented
// TODO: GC0308 camera @ 0x21 — requires i2c_master driver, not yet available // TODO: GC0308 camera @ 0x21 — requires i2c_master driver, not yet available
// TODO: LTR-553ALS-WA proximity/light @ 0x23 — no driver yet // TODO: LTR-553ALS-WA proximity/light @ 0x23 — no driver yet
@@ -221,15 +167,7 @@
pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>; pin-sclk = <&gpio0 36 GPIO_FLAG_NONE>;
display@0 { display@0 {
compatible = "ilitek,ili9341"; compatible = "display-placeholder";
horizontal-resolution = <320>;
vertical-resolution = <240>;
invert-color;
bgr-order;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 35 GPIO_FLAG_NONE>;
pin-reset = <&aw9523b 9 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
}; };
sdcard@1 { sdcard@1 {
@@ -1,62 +0,0 @@
#include <drivers/py32ioexpander.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/device_listener.h>
#include <tactility/module.h>
#include <cstring>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
extern "C" {
// Boot LED pattern (red/green/blue sweep across the 12-LED WS2812C ring).
// Confirms the PY32L020 body IO expander is working.
static void run_led_boot_pattern(Device* py32) {
static constexpr uint8_t LED_COUNT = 12;
static constexpr uint8_t COLORS[][3] = {
{ 255, 0, 0 }, // red
{ 0, 255, 0 }, // green
{ 0, 0, 255 }, // blue
};
py32_led_set_count(py32, LED_COUNT);
for (const auto& color : COLORS) {
for (uint8_t i = 0; i < LED_COUNT; i++) {
py32_led_set_color(py32, i, color[0], color[1], color[2]);
}
py32_led_refresh(py32);
vTaskDelay(pdMS_TO_TICKS(150));
}
py32_led_disable(py32);
}
static void on_device_event(Device* device, DeviceEvent event, void* context) {
(void)context;
if (event != DEVICE_EVENT_STARTED) {
return;
}
if (strcmp(device->name, "py32") == 0) {
run_led_boot_pattern(device);
}
}
static error_t start() {
device_listener_add(on_device_event, nullptr);
return ERROR_NONE;
}
static error_t stop() {
device_listener_remove(on_device_event);
return ERROR_NONE;
}
Module m5stack_stackchan_module = {
.name = "m5stack-stackchan",
.start = start,
.stop = stop
};
}
+15 -2
View File
@@ -7,7 +7,7 @@ constexpr auto* TAG = "StickCPlus2";
extern "C" { extern "C" {
static error_t init_power() { static bool init_power() {
// CH552 applies 4 V to GPIO 0, which reduces Wi-Fi sensitivity. // CH552 applies 4 V to GPIO 0, which reduces Wi-Fi sensitivity.
// Setting output to high adds a bias of 3.3 V and suppresses over-voltage: // Setting output to high adds a bias of 3.3 V and suppresses over-voltage:
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT); gpio_set_direction(GPIO_NUM_0, GPIO_MODE_OUTPUT);
@@ -17,12 +17,25 @@ static error_t init_power() {
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT); gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT);
gpio_set_level(GPIO_NUM_4, 1); gpio_set_level(GPIO_NUM_4, 1);
return true;
}
static error_t start() {
init_power();
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE; return ERROR_NONE;
} }
Module m5stack_stickc_plus2_module = { Module m5stack_stickc_plus2_module = {
.name = "m5stack-stickc-plus2", .name = "m5stack-stickc-plus2",
.start = init_power .start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
}; };
} }
+3 -3
View File
@@ -1,7 +1,7 @@
file(GLOB_RECURSE SOURCE_FILES source/*.c*) file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "source" INCLUDE_DIRS "Source"
REQUIRES TactilityKernel REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 PwmBacklight ButtonControl vfs fatfs m5pm1-module
) )
@@ -0,0 +1,48 @@
#include "devices/Display.h"
#include "devices/Power.h"
#include <tactility/device.h>
#include <drivers/m5pm1.h>
#include <Tactility/hal/Configuration.h>
#include <ButtonControl.h>
#include <PwmBacklight.h>
using namespace tt::hal;
static constexpr auto* TAG = "StickS3";
// Audio codec register programming is owned by the es8311-module driver (registered as an
// AUDIO_CODEC_TYPE device, see m5stack,sticks3.dts). This board still needs to drive the
// AW8737 speaker amplifier's enable line via m5pm1 (PM1_G3), which is board wiring glue
// rather than codec configuration, so it stays here.
static void enableSpeakerAmplifier() {
auto* m5pm1 = device_find_by_name("m5pm1");
if (m5pm1 == nullptr) {
LOG_W(TAG, "m5pm1 not found -- speaker amp not enabled");
return;
}
error_t error = m5pm1_set_speaker_enable(m5pm1, true);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to enable speaker amplifier: %s", error_to_string(error));
}
}
bool initBoot() {
enableSpeakerAmplifier();
return driver::pwmbacklight::init(GPIO_NUM_38, 512);
}
static DeviceVector createDevices() {
return {
createPower(),
ButtonControl::createTwoButtonControl(11, 12), // top button, side button
createDisplay(),
};
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -0,0 +1,32 @@
#include "Display.h"
#include <PwmBacklight.h>
#include <St7789Display.h>
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
St7789Display::Configuration panel_configuration = {
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
.verticalResolution = LCD_VERTICAL_RESOLUTION,
.gapX = 52,
.gapY = 40,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = true,
.bufferSize = LCD_BUFFER_SIZE,
.touch = nullptr,
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = LCD_PIN_RESET,
.lvglSwapBytes = false
};
auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration {
.spiHostDevice = LCD_SPI_HOST,
.csPin = LCD_PIN_CS,
.dcPin = LCD_PIN_DC,
.pixelClockFrequency = 40'000'000,
.transactionQueueDepth = 10
});
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
}
@@ -0,0 +1,17 @@
#pragma once
#include "Tactility/hal/display/DisplayDevice.h"
#include <memory>
#include <driver/gpio.h>
#include <driver/spi_common.h>
constexpr auto LCD_SPI_HOST = SPI2_HOST;
constexpr auto LCD_PIN_CS = GPIO_NUM_41;
constexpr auto LCD_PIN_DC = GPIO_NUM_45;
constexpr auto LCD_PIN_RESET = GPIO_NUM_21;
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
@@ -0,0 +1,94 @@
#include "Power.h"
#include <Tactility/hal/power/PowerDevice.h>
#include <drivers/m5pm1.h>
#include <tactility/device.h>
#include <tactility/log.h>
using namespace tt::hal::power;
static constexpr auto* TAG = "StickS3Power";
static constexpr float MIN_BATTERY_VOLTAGE_MV = 3300.0f;
static constexpr float MAX_BATTERY_VOLTAGE_MV = 4200.0f;
class StickS3Power final : public PowerDevice {
public:
explicit StickS3Power(::Device* m5pm1Device) : m5pm1(m5pm1Device) {}
std::string getName() const override { return "M5Stack StickS3 Power"; }
std::string getDescription() const override { return "Battery monitoring via M5PM1 over I2C"; }
bool supportsMetric(MetricType type) const override {
switch (type) {
using enum MetricType;
case BatteryVoltage:
case ChargeLevel:
case IsCharging:
return true;
default:
return false;
}
}
bool getMetric(MetricType type, MetricData& data) override {
switch (type) {
using enum MetricType;
case BatteryVoltage: {
uint16_t mv = 0;
if (m5pm1_get_battery_voltage(m5pm1, &mv) != ERROR_NONE) return false;
data.valueAsUint32 = mv;
return true;
}
case ChargeLevel: {
uint16_t mv = 0;
if (m5pm1_get_battery_voltage(m5pm1, &mv) != ERROR_NONE) return false;
float voltage = static_cast<float>(mv);
if (voltage >= MAX_BATTERY_VOLTAGE_MV) {
data.valueAsUint8 = 100;
} else if (voltage <= MIN_BATTERY_VOLTAGE_MV) {
data.valueAsUint8 = 0;
} else {
float factor = (voltage - MIN_BATTERY_VOLTAGE_MV) / (MAX_BATTERY_VOLTAGE_MV - MIN_BATTERY_VOLTAGE_MV);
data.valueAsUint8 = static_cast<uint8_t>(factor * 100.0f);
}
return true;
}
case IsCharging: {
bool charging = false;
if (m5pm1_is_charging(m5pm1, &charging) != ERROR_NONE) {
LOG_W(TAG, "Failed to read charging status");
return false;
}
data.valueAsBool = charging;
return true;
}
default:
return false;
}
}
bool supportsPowerOff() const override { return true; }
void powerOff() override {
LOG_W(TAG, "Powering off via M5PM1");
if (m5pm1_shutdown(m5pm1) != ERROR_NONE) {
LOG_E(TAG, "Failed to send power-off command");
}
}
private:
::Device* m5pm1;
};
std::shared_ptr<PowerDevice> createPower() {
auto* m5pm1 = device_find_by_name("m5pm1");
if (m5pm1 == nullptr) {
LOG_E(TAG, "m5pm1 device not found");
}
return std::make_shared<StickS3Power>(m5pm1);
}
@@ -0,0 +1,6 @@
#pragma once
#include <memory>
#include <Tactility/hal/power/PowerDevice.h>
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
+23
View File
@@ -0,0 +1,23 @@
#include <tactility/module.h>
extern "C" {
static error_t start() {
// Empty for now
return ERROR_NONE;
}
static error_t stop() {
// Empty for now
return ERROR_NONE;
}
struct Module m5stack_sticks3_module = {
.name = "m5stack-sticks3",
.start = start,
.stop = stop,
.symbols = nullptr,
.internal = nullptr
};
}
@@ -12,8 +12,6 @@ hardware.esptoolFlashFreq=80M
hardware.tinyUsb=true hardware.tinyUsb=true
hardware.bluetooth=true hardware.bluetooth=true
dependencies.useDeprecatedHal=false
storage.userDataLocation=Internal storage.userDataLocation=Internal
display.size=1.14" display.size=1.14"
-2
View File
@@ -4,6 +4,4 @@ dependencies:
- Drivers/m5pm1-module - Drivers/m5pm1-module
- Drivers/es8311-module - Drivers/es8311-module
- Drivers/audio-stream-module - Drivers/audio-stream-module
- Drivers/st7789-module
- Drivers/button-control-module
dts: m5stack,sticks3.dts dts: m5stack,sticks3.dts
+3 -40
View File
@@ -11,11 +11,7 @@
#include <bindings/m5pm1.h> #include <bindings/m5pm1.h>
#include <bindings/es8311.h> #include <bindings/es8311.h>
#include <tactility/bindings/esp32_sdspi.h> #include <tactility/bindings/esp32_sdspi.h>
#include <tactility/bindings/esp32_pwm_ledc.h> #include <tactility/bindings/display_placeholder.h>
#include <tactility/bindings/pwm_backlight.h>
#include <bindings/st7789.h>
#include <bindings/button_control.h>
/ { / {
compatible = "root"; compatible = "root";
@@ -57,8 +53,6 @@
m5pm1 { m5pm1 {
compatible = "m5stack,m5pm1"; compatible = "m5stack,m5pm1";
reg = <0x6E>; reg = <0x6E>;
power-supply-reference-voltage-mv = <4200>;
speaker-amp-enable-at-boot;
}; };
bmi270 { bmi270 {
@@ -81,22 +75,6 @@
pin-scl = <&gpio0 10 GPIO_FLAG_NONE>; pin-scl = <&gpio0 10 GPIO_FLAG_NONE>;
}; };
display_backlight_pwm {
compatible = "espressif,esp32-pwm-ledc";
pin = <&gpio0 38 GPIO_FLAG_NONE>;
period-ns = <1953125>;
ledc-timer = <0>;
ledc-channel = <0>;
};
display_backlight {
compatible = "pwm-backlight";
// Off by default so display power-on won't show the screen from before the last power loss.
// The display backlight is turned on during the boot process.
status = "disabled";
pwm = <&display_backlight_pwm>;
};
spi0 { spi0 {
compatible = "espressif,esp32-spi"; compatible = "espressif,esp32-spi";
host = <SPI2_HOST>; host = <SPI2_HOST>;
@@ -104,26 +82,11 @@
pin-mosi = <&gpio0 39 GPIO_FLAG_NONE>; pin-mosi = <&gpio0 39 GPIO_FLAG_NONE>;
pin-sclk = <&gpio0 40 GPIO_FLAG_NONE>; pin-sclk = <&gpio0 40 GPIO_FLAG_NONE>;
display@0 { display {
compatible = "sitronix,st7789"; compatible = "display-placeholder";
horizontal-resolution = <135>;
vertical-resolution = <240>;
gap-x = <52>;
gap-y = <40>;
invert-color;
pixel-clock-hz = <40000000>;
pin-dc = <&gpio0 45 GPIO_FLAG_NONE>;
pin-reset = <&gpio0 21 GPIO_FLAG_NONE>;
backlight = <&display_backlight>;
}; };
}; };
buttons {
compatible = "tactility,button-control";
pin-primary = <&gpio0 11 GPIO_FLAG_NONE>;
pin-secondary = <&gpio0 12 GPIO_FLAG_NONE>;
};
uart_grove: uart1 { uart_grove: uart1 {
compatible = "espressif,esp32-uart"; compatible = "espressif,esp32-uart";
status = "disabled"; status = "disabled";
@@ -1,9 +0,0 @@
#include <tactility/module.h>
extern "C" {
Module m5stack_sticks3_module = {
.name = "m5stack-sticks3"
};
}
+1 -1
View File
@@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Source" INCLUDE_DIRS "Source"
REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9881c esp_lcd_st7123 esp_lcd_touch_st7123 esp_lcd_touch_gt911 driver esp_driver_i2c vfs fatfs ina226-module ili9881c-module st7123-module gt911-module REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c esp_lcd_st7123 esp_lcd_touch_st7123 GT911 PwmBacklight driver esp_driver_i2c vfs fatfs ina226-module
) )
@@ -0,0 +1,205 @@
#include "devices/Display.h"
#include "devices/Power.h"
#include "devices/Tab5Keyboard.h"
#include <tactility/drivers/gpio_controller.h>
#include <tactility/drivers/i2c_controller.h>
#include <Tactility/hal/Configuration.h>
#include <esp_clock_output.h>
using namespace tt::hal;
static constexpr auto* TAG = "Tab5";
static DeviceVector createDevices() {
auto* i2c2 = device_find_by_name("i2c2");
check(i2c2, "i2c2 not found");
return {
createPower(),
createDisplay(),
std::make_shared<Tab5Keyboard>(i2c2)
};
}
/*
PI4IOE5V6408-0 (0x43)
- Bit 0: RF internal/external switch
- Bit 1: Speaker enable
- Bit 2: External 5V bus enable
- Bit 3: /
- Bit 4: LCD reset
- Bit 5: Touch reset
- Bit 6: Camera reset
- Bit 7: Headphone detect
*/
constexpr auto GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL = 0;
constexpr auto GPIO_EXP0_PIN_SPEAKER_ENABLE = 1;
constexpr auto GPIO_EXP0_PIN_EXTERNAL_5V_BUS_ENABLE = 2;
constexpr auto GPIO_EXP0_PIN_LCD_RESET = 4;
constexpr auto GPIO_EXP0_PIN_TOUCH_RESET = 5;
constexpr auto GPIO_EXP0_PIN_CAMERA_RESET = 6;
constexpr auto GPIO_EXP0_PIN_HEADPHONE_DETECT = 7;
/*
PI4IOE5V6408-1 (0x44)
- Bit 0: C6 WLAN enable
- Bit 1: /
- Bit 2: /
- Bit 3: USB-A 5V enable
- Bit 4: Device power: PWROFF_PLUSE
- Bit 5: IP2326: nCHG_QC_EN
- Bit 6: IP2326: CHG_STAT_LED
- Bit 7: IP2326: CHG_EN
*/
constexpr auto GPIO_EXP1_PIN_C6_WLAN_ENABLE = 0;
constexpr auto GPIO_EXP1_PIN_USB_A_5V_ENABLE = 3;
constexpr auto GPIO_EXP1_PIN_DEVICE_POWER = 4;
constexpr auto GPIO_EXP1_PIN_IP2326_NCHG_QC_EN = 5;
constexpr auto GPIO_EXP1_PIN_IP2326_CHG_STAT_LED = 6;
constexpr auto GPIO_EXP1_PIN_IP2326_CHG_EN = 7;
static void initExpander0(::Device* io_expander0) {
auto* rf_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_RF_INTERNAL_EXTERNAL, GPIO_OWNER_GPIO);
check(rf_pin);
auto* speaker_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_SPEAKER_ENABLE, GPIO_OWNER_GPIO);
check(speaker_enable_pin);
auto* external_5v_bus_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_EXTERNAL_5V_BUS_ENABLE, GPIO_OWNER_GPIO);
check(external_5v_bus_enable_pin);
auto* lcd_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_LCD_RESET, GPIO_OWNER_GPIO);
check(lcd_reset_pin);
auto* touch_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_TOUCH_RESET, GPIO_OWNER_GPIO);
check(touch_reset_pin);
auto* camera_reset_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_CAMERA_RESET, GPIO_OWNER_GPIO);
check(camera_reset_pin);
auto* headphone_detect_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_HEADPHONE_DETECT, GPIO_OWNER_GPIO);
check(headphone_detect_pin);
gpio_descriptor_set_flags(rf_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(speaker_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(external_5v_bus_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(lcd_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(touch_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(camera_reset_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(headphone_detect_pin, GPIO_FLAG_DIRECTION_INPUT);
gpio_descriptor_set_level(rf_pin, false);
gpio_descriptor_set_level(speaker_enable_pin, false);
gpio_descriptor_set_level(external_5v_bus_enable_pin, true);
gpio_descriptor_set_level(lcd_reset_pin, false);
gpio_descriptor_set_level(touch_reset_pin, false);
gpio_descriptor_set_level(camera_reset_pin, true);
vTaskDelay(pdMS_TO_TICKS(10));
// Enable touch and lcd, but not the camera
gpio_descriptor_set_level(lcd_reset_pin, true);
gpio_descriptor_set_level(touch_reset_pin, true);
gpio_descriptor_release(rf_pin);
gpio_descriptor_release(speaker_enable_pin);
gpio_descriptor_release(external_5v_bus_enable_pin);
gpio_descriptor_release(lcd_reset_pin);
gpio_descriptor_release(touch_reset_pin);
gpio_descriptor_release(camera_reset_pin);
gpio_descriptor_release(headphone_detect_pin);
}
static void initExpander1(::Device* io_expander1) {
auto* c6_wlan_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_C6_WLAN_ENABLE, GPIO_OWNER_GPIO);
check(c6_wlan_enable_pin);
auto* usb_a_5v_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_USB_A_5V_ENABLE, GPIO_OWNER_GPIO);
check(usb_a_5v_enable_pin);
auto* device_power_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_DEVICE_POWER, GPIO_OWNER_GPIO);
check(device_power_pin);
auto* ip2326_ncharge_qc_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_NCHG_QC_EN, GPIO_OWNER_GPIO);
check(ip2326_ncharge_qc_enable_pin);
auto* ip2326_charge_state_led_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_STAT_LED, GPIO_OWNER_GPIO);
check(ip2326_charge_state_led_pin);
auto* ip2326_charge_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_EN, GPIO_OWNER_GPIO);
check(ip2326_charge_enable_pin);
gpio_descriptor_set_flags(c6_wlan_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(usb_a_5v_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(device_power_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_ncharge_qc_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_charge_state_led_pin, GPIO_FLAG_DIRECTION_OUTPUT);
gpio_descriptor_set_flags(ip2326_charge_enable_pin, GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP);
gpio_descriptor_set_level(c6_wlan_enable_pin, true);
gpio_descriptor_set_level(usb_a_5v_enable_pin, true);
gpio_descriptor_set_level(device_power_pin, false);
gpio_descriptor_set_level(ip2326_ncharge_qc_enable_pin, false);
gpio_descriptor_set_level(ip2326_charge_state_led_pin, false);
gpio_descriptor_release(c6_wlan_enable_pin);
gpio_descriptor_release(usb_a_5v_enable_pin);
gpio_descriptor_release(device_power_pin);
gpio_descriptor_release(ip2326_ncharge_qc_enable_pin);
gpio_descriptor_release(ip2326_charge_state_led_pin);
gpio_descriptor_release(ip2326_charge_enable_pin);
}
// Audio codec register programming is owned by the es8388-module/es7210-module drivers
// (registered as AUDIO_CODEC_TYPE devices, see m5stack,tab5.dts). This board still needs
// to drive the external speaker amplifier's enable line via io_expander0, which is board
// wiring glue rather than codec configuration, so it stays here.
static error_t enableSpeakerAmplifier(::Device* io_expander0) {
auto* speaker_enable_pin = gpio_descriptor_acquire(io_expander0, GPIO_EXP0_PIN_SPEAKER_ENABLE, GPIO_OWNER_GPIO);
check(speaker_enable_pin, "Failed to acquire speaker enable pin");
error_t error = gpio_descriptor_set_level(speaker_enable_pin, true);
gpio_descriptor_release(speaker_enable_pin);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to enable amplifier: %s", error_to_string(error));
return ERROR_RESOURCE;
}
return ERROR_NONE;
}
static esp_clock_output_mapping_handle_t camera_osc_handle = nullptr;
static void initCameraOsc() {
if (camera_osc_handle != nullptr) {
return;
}
// 24 MHz clock on GPIO 36 for the SC2356 MIPI CSI sensor, required before esp_video_init.
// Uses SPLL (480 MHz) via esp_clock_output with divider 20 = 24 MHz exactly.
// This avoids any LEDC clock source conflict with the display backlight.
if (esp_clock_output_start(CLKOUT_SIG_SPLL, GPIO_NUM_36, &camera_osc_handle) != ESP_OK) {
LOG_E(TAG, "Camera OSC clock output start failed");
return;
}
if (esp_clock_output_set_divider(camera_osc_handle, 20) != ESP_OK) {
LOG_E(TAG, "Camera OSC clock divider set failed");
esp_clock_output_stop(camera_osc_handle);
camera_osc_handle = nullptr;
return;
}
LOG_I(TAG, "Camera OSC 24MHz started on GPIO 36 (SPLL/20)");
}
static bool initBoot() {
auto* io_expander0 = device_find_by_name("io_expander0");
check(io_expander0, "io_expander0 not found");
auto* io_expander1 = device_find_by_name("io_expander1");
check(io_expander1, "io_expander1 not found");
initCameraOsc();
initExpander0(io_expander0);
initExpander1(io_expander1);
error_t error = enableSpeakerAmplifier(io_expander0);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to enable speaker amplifier");
}
return true;
}
extern const Configuration hardwareConfiguration = {
.initBoot = initBoot,
.createDevices = createDevices
};
@@ -0,0 +1,45 @@
#include "Detect.h"
#include <tactility/device.h>
#include <tactility/drivers/i2c_controller.h>
#include <tactility/log.h>
#include <esp_lcd_touch_gt911.h>
#include <esp_lcd_touch_st7123.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
constexpr auto* TAG = "Tab5Detect";
Tab5Variant detectVariant() {
// Allow time for touch IC to fully boot after expander reset in initBoot().
// 100ms is enough for I2C ACK (probe) but cold power-on needs ~300ms before
// register reads (read_fw_info) succeed reliably.
vTaskDelay(pdMS_TO_TICKS(300));
auto* i2c0 = device_find_by_name("i2c0");
check(i2c0);
constexpr auto PROBE_TIMEOUT = pdMS_TO_TICKS(50);
for (int attempt = 0; attempt < 3; ++attempt) {
// GT911 address depends on INT pin state during reset:
// GPIO 23 has a pull-up resistor to 3V3, so INT is high at reset → GT911 uses 0x5D (primary)
// It may also appear at 0x14 (backup) if the pin happened to be driven low
if (i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, PROBE_TIMEOUT) == ERROR_NONE ||
i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, PROBE_TIMEOUT) == ERROR_NONE) {
LOG_I(TAG, "Detected GT911 touch — using ILI9881C display");
return Tab5Variant::Ili9881c_Gt911;
}
// Probe for ST7123 touch (new variant)
if (i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_ST7123_ADDRESS, PROBE_TIMEOUT) == ERROR_NONE) {
LOG_I(TAG, "Detected ST7123 touch — using ST7123 display");
return Tab5Variant::St7123;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
LOG_W(TAG, "No known touch controller detected, defaulting to ST7123");
return Tab5Variant::St7123;
}
@@ -0,0 +1,8 @@
#pragma once
enum class Tab5Variant {
Ili9881c_Gt911, // Older variant
St7123, // Newer variant (default)
};
[[nodiscard]] Tab5Variant detectVariant();
@@ -0,0 +1,108 @@
#include "Detect.h"
#include "Display.h"
#include "Ili9881cDisplay.h"
#include "St7123Display.h"
#include "St7123Touch.h"
#include <Gt911Touch.h>
#include <PwmBacklight.h>
#include <Tactility/hal/gpio/Gpio.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
constexpr auto* TAG = "Tab5Display";
// LCD reset is wired to the PI4IOE5V6408 IO expander (io_expander0, bit 4), pulsed in
// Configuration.cpp's initExpander0() before display creation - not a direct SoC GPIO.
constexpr auto LCD_PIN_RESET = GPIO_NUM_NC;
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_22;
static std::shared_ptr<tt::hal::touch::TouchDevice> createGt911Touch() {
auto* i2c = device_find_by_name("i2c0");
check(i2c);
auto configuration = std::make_unique<Gt911Touch::Configuration>(
i2c,
720,
1280,
false, // swapXY
false, // mirrorX
false, // mirrorY
GPIO_NUM_NC, // reset pin
GPIO_NUM_NC // "GPIO_NUM_23 cannot be used due to resistor to 3V3"
// https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L76234
);
return std::make_shared<Gt911Touch>(std::move(configuration));
}
static std::shared_ptr<tt::hal::touch::TouchDevice> createSt7123Touch() {
auto* i2c = device_find_by_name("i2c0");
check(i2c, "i2c0 not found");
auto configuration = std::make_unique<St7123Touch::Configuration>(
i2c,
720,
1280,
false, // swapXY
false, // mirrorX
false, // mirrorY
GPIO_NUM_23 // interrupt pin
);
return std::make_shared<St7123Touch>(std::move(configuration));
}
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
// Initialize PWM backlight
if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 5000, LEDC_TIMER_1, LEDC_CHANNEL_0)) {
LOG_W(TAG, "Failed to initialize backlight");
}
Tab5Variant variant = detectVariant();
std::shared_ptr<tt::hal::touch::TouchDevice> touch;
if (variant == Tab5Variant::St7123) {
touch = createSt7123Touch();
} else {
touch = createGt911Touch();
// Work-around to init GT911 touch: interrupt pin must be set to low
// Note: There is a resistor to 3V3 on interrupt pin which is blocking GT911 touch
// See https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L777
tt::hal::gpio::configure(23, tt::hal::gpio::Mode::Output, true, false);
tt::hal::gpio::setLevel(23, false);
}
auto configuration = std::make_shared<EspLcdConfiguration>(EspLcdConfiguration {
.horizontalResolution = 720,
.verticalResolution = 1280,
.gapX = 0,
.gapY = 0,
.monochrome = false,
.swapXY = false,
.mirrorX = false,
.mirrorY = false,
.invertColor = false,
.bufferSize = 0, // 0 = default (1/10 of screen)
.swRotate = true,
.buffSpiram = true,
.touch = touch,
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
.resetPin = LCD_PIN_RESET,
.lvglColorFormat = LV_COLOR_FORMAT_RGB565,
.lvglSwapBytes = false,
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB,
.bitsPerPixel = 16
});
if (variant == Tab5Variant::St7123) {
return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
std::make_shared<St7123Display>(configuration)
);
} else {
return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
std::make_shared<Ili9881cDisplay>(configuration)
);
}
}
@@ -0,0 +1,6 @@
#pragma once
#include <Tactility/hal/display/DisplayDevice.h>
#include <memory>
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
@@ -0,0 +1,154 @@
#include "Ili9881cDisplay.h"
#include "ili9881_init_data.h"
#include <esp_lcd_ili9881c.h>
#include <tactility/log.h>
constexpr auto* TAG = "ILI9881C";
Ili9881cDisplay::~Ili9881cDisplay() {
// TODO: This should happen during ::stop(), but this isn't currently exposed
if (mipiDsiBus != nullptr) {
esp_lcd_del_dsi_bus(mipiDsiBus);
mipiDsiBus = nullptr;
}
if (ldoChannel != nullptr) {
esp_ldo_release_channel(ldoChannel);
ldoChannel = nullptr;
}
}
bool Ili9881cDisplay::createMipiDsiBus() {
esp_ldo_channel_config_t ldo_mipi_phy_config = {
.chan_id = 3,
.voltage_mv = 2500,
.flags = {
.adjustable = 0,
.owned_by_hw = 0,
.bypass = 0
}
};
if (esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldoChannel) != ESP_OK) {
LOG_E(TAG, "Failed to acquire LDO channel for MIPI DSI PHY");
return false;
}
LOG_I(TAG, "Powered on");
// Create bus
// TODO: use MIPI_DSI_PHY_CLK_SRC_DEFAULT() in future ESP-IDF 6.0.0 update with esp_lcd_jd9165 library version 2.x
const esp_lcd_dsi_bus_config_t bus_config = {
.bus_id = 0,
.num_data_lanes = 2,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = 1000
};
if (esp_lcd_new_dsi_bus(&bus_config, &mipiDsiBus) != ESP_OK) {
LOG_E(TAG, "Failed to create bus");
esp_ldo_release_channel(ldoChannel);
ldoChannel = nullptr;
return false;
}
LOG_I(TAG, "Bus created");
return true;
}
bool Ili9881cDisplay::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) {
// Initialize MIPI DSI bus if not already done
if (mipiDsiBus == nullptr) {
if (!createMipiDsiBus()) {
return false;
}
}
// Use DBI interface to send LCD commands and parameters
esp_lcd_dbi_io_config_t dbi_config = ILI9881C_PANEL_IO_DBI_CONFIG();
if (esp_lcd_new_panel_io_dbi(mipiDsiBus, &dbi_config, &ioHandle) != ESP_OK) {
LOG_E(TAG, "Failed to create panel IO");
esp_lcd_del_dsi_bus(mipiDsiBus);
mipiDsiBus = nullptr;
esp_ldo_release_channel(ldoChannel);
ldoChannel = nullptr;
return false;
}
return true;
}
esp_lcd_panel_dev_config_t Ili9881cDisplay::createPanelConfig(std::shared_ptr<EspLcdConfiguration> espLcdConfiguration, gpio_num_t resetPin) {
return {
.reset_gpio_num = resetPin,
.rgb_ele_order = espLcdConfiguration->rgbElementOrder,
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
.bits_per_pixel = static_cast<uint8_t>(espLcdConfiguration->bitsPerPixel),
.flags = {
.reset_active_high = 0
},
.vendor_config = nullptr // Will be set in createPanelHandle
};
}
bool Ili9881cDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) {
// Based on BSP: https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_tab5/README.md
// TODO: undo static
esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 60,
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
.in_color_format = LCD_COLOR_FMT_RGB565,
.out_color_format = LCD_COLOR_FMT_RGB565,
.num_fbs = 2,
.video_timing =
{
.h_size = 720,
.v_size = 1280,
.hsync_pulse_width = 40,
.hsync_back_porch = 140,
.hsync_front_porch = 40,
.vsync_pulse_width = 4,
.vsync_back_porch = 20,
.vsync_front_porch = 20,
},
.flags = {
.use_dma2d = 1, // TODO: true?
.disable_lp = 0,
}
};
ili9881c_vendor_config_t vendor_config = {
.init_cmds = ili9881_init_data,
.init_cmds_size = std::size(ili9881_init_data),
.mipi_config = {
.dsi_bus = mipiDsiBus,
.dpi_config = &dpi_config,
.lane_num = 2,
},
};
// Create a mutable copy of panelConfig to set vendor_config
esp_lcd_panel_dev_config_t mutable_panel_config = panelConfig;
mutable_panel_config.vendor_config = &vendor_config;
if (esp_lcd_new_panel_ili9881c(ioHandle, &mutable_panel_config, &panelHandle) != ESP_OK) {
LOG_E(TAG, "Failed to create panel");
return false;
}
LOG_I(TAG, "Panel created successfully");
// Defer reset/init to base class applyConfiguration to avoid double initialization
return true;
}
lvgl_port_display_dsi_cfg_t Ili9881cDisplay::getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) {
// Disable avoid_tearing to prevent stalls/blank flashes when other tasks (e.g. flash writes) block timing
return lvgl_port_display_dsi_cfg_t{
.flags = {
.avoid_tearing = 0,
},
};
}
@@ -0,0 +1,38 @@
#pragma once
#include <EspLcdDisplayV2.h>
#include <esp_lcd_mipi_dsi.h>
#include <esp_ldo_regulator.h>
class Ili9881cDisplay final : public EspLcdDisplayV2 {
esp_lcd_dsi_bus_handle_t mipiDsiBus = nullptr;
esp_ldo_channel_handle_t ldoChannel = nullptr;
bool createMipiDsiBus();
protected:
bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override;
esp_lcd_panel_dev_config_t createPanelConfig(std::shared_ptr<EspLcdConfiguration> espLcdConfiguration, gpio_num_t resetPin) override;
bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) override;
bool useDsiPanel() const override { return true; }
lvgl_port_display_dsi_cfg_t getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) override;
public:
Ili9881cDisplay(
const std::shared_ptr<EspLcdConfiguration>& configuration
) : EspLcdDisplayV2(configuration) {}
~Ili9881cDisplay() override;
std::string getName() const override { return "ILI9881C"; }
std::string getDescription() const override { return "ILI9881C MIPI-DSI display"; }
};

Some files were not shown because too many files have changed in this diff Show More