commit 9576c5f7d2ca384c95f2da17e9316694c76f06ac Author: DESKTOP-PUHFO60\dsx2016 Date: Fri Oct 23 14:08:51 2026 +0800 Add HW-069 v1.1 4-digit TM1637 test with SoftSPI touch and LVGL. CLK on GPIO22, DIO on GPIO27; value/brightness/colon controls and flashable firmware/cyd package. Co-authored-by: Cursor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ab5fef --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.pio/ +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +*.pyc +__pycache__/ +.DS_Store +Thumbs.db +assets/ +*.elf +# 构建目录里的 bin 不入库;firmware/ 预编译包要入库 +!firmware/**/*.bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..8fb6ef7 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# HW-069 四位数显(CYD) + +触控 + LVGL 参考 `tftest` / `hw269test`。HW-069 v1.1(TM1637)两线驱动:调数值、亮度、冒号、开关。 + +## 硬件 + +| 项目 | 说明 | +|------|------| +| 板子 | CYD ESP32-2432S028R | +| 模块 | HW-069 v1.1 四位 0.36" 数码管(TM1637) | +| CLK | **GPIO22** | +| DIO | **GPIO27** | + +接线: + +| HW-069 | CYD | +|--------|-----| +| VCC | 3.3V 或 5V | +| GND | GND | +| CLK | GPIO22 | +| DIO | GPIO27 | + +## 操作 + +- 大字:当前显示值(冒号开时显示 `mm:ss` 样式) +- **-100 / -1 / +1 / +100**:改数值(0–9999) +- 滑条:亮度 0–100%(映射 TM1637 8 档) +- **Colon**:中间冒号开关 +- **Display ON/OFF**:熄灭 / 点亮数显 + +## 构建 / 烧录 + +```powershell +pio run -e cyd -t upload +``` + +## 预编译固件 + +```powershell +cd firmware\cyd +.\flash.ps1 +# 或 .\flash.ps1 -Port COMx +``` + +重新导出: + +```powershell +.\firmware\export_from_build.ps1 +``` diff --git a/copy_lv_conf.py b/copy_lv_conf.py new file mode 100644 index 0000000..ca4f24c --- /dev/null +++ b/copy_lv_conf.py @@ -0,0 +1,13 @@ +Import("env") # noqa: F821 +from pathlib import Path + +src = Path(env["PROJECT_DIR"]) / "include" / "lv_conf.h" # noqa: F821 +dst_dir = Path(env["PROJECT_LIBDEPS_DIR"]) / env["PIOENV"] # noqa: F821 +dst_dir.mkdir(parents=True, exist_ok=True) +dst = dst_dir / "lv_conf.h" +if src.exists(): + dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8") + lvgl = dst_dir / "lvgl" / "lv_conf.h" + if (dst_dir / "lvgl").exists(): + lvgl.write_text(src.read_text(encoding="utf-8"), encoding="utf-8") + print(f"copy_lv_conf: synced -> {dst}") diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 0000000..c30a005 --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,21 @@ +# 预编译固件 + +目录:[`cyd/`](cyd/) + +板子:**CYD ESP32-2432S028R**(ILI9341) +功能:HW-069 v1.1 四位数显(CLK=GPIO22,DIO=GPIO27,TM1637)+ SoftSPI 触控 + LVGL + +## 刷写 + +```powershell +cd firmware\cyd +.\flash.ps1 +# 或 .\flash.ps1 -Port COM3 +``` + +## 重新打包 + +```powershell +.\firmware\export_from_build.ps1 +.\firmware\export_from_build.ps1 -SkipBuild +``` diff --git a/firmware/cyd/VERSION.txt b/firmware/cyd/VERSION.txt new file mode 100644 index 0000000..402b3ba --- /dev/null +++ b/firmware/cyd/VERSION.txt @@ -0,0 +1,16 @@ +name: CYD HW069 Digit Display (cyd) +git: unknown +date: 2026-10-23 +board: ESP32-2432S028R (ILI9341) +features: SoftSPI touch, LVGL, HW069 TM1637 CLK=GPIO22 DIO=GPIO27 +flash_size: 4MB +offsets: + 0x1000 bootloader.bin + 0x8000 partitions.bin + 0xe000 boot_app0.bin + 0x10000 firmware.bin +sha256: + bootloader.bin 3D234A7471F67B013686DABD4DEE7C1FA915C9928463616A94BC9297ACF1ABF8 + boot_app0.bin F94C5D786A7A8FAB06AC5D10E33BF37711A6697636DC037559EA19CC410A17F0 + firmware.bin C6E7F78925D0BC248734601D6AC8D8A0F1712798857162A1A8A047E5C710A6E9 + partitions.bin 148B959CBFF1C38AA8E1D5C0BA9D612C54997B945E56A63F41223EEF650653A1 diff --git a/firmware/cyd/boot_app0.bin b/firmware/cyd/boot_app0.bin new file mode 100644 index 0000000..13562ca Binary files /dev/null and b/firmware/cyd/boot_app0.bin differ diff --git a/firmware/cyd/bootloader.bin b/firmware/cyd/bootloader.bin new file mode 100644 index 0000000..d592359 Binary files /dev/null and b/firmware/cyd/bootloader.bin differ diff --git a/firmware/cyd/firmware.bin b/firmware/cyd/firmware.bin new file mode 100644 index 0000000..4509ecf Binary files /dev/null and b/firmware/cyd/firmware.bin differ diff --git a/firmware/cyd/flash.bat b/firmware/cyd/flash.bat new file mode 100644 index 0000000..bf983b3 --- /dev/null +++ b/firmware/cyd/flash.bat @@ -0,0 +1,5 @@ +@echo off +setlocal +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0flash.ps1" %* +exit /b %ERRORLEVEL% diff --git a/firmware/cyd/flash.ps1 b/firmware/cyd/flash.ps1 new file mode 100644 index 0000000..c80479b --- /dev/null +++ b/firmware/cyd/flash.ps1 @@ -0,0 +1,98 @@ +# Flash archived CYD TF+Speaker firmware (no rebuild needed) +# Usage: +# .\flash.ps1 +# .\flash.ps1 -Port COM5 +# .\flash.ps1 -Port COM5 -Baud 460800 + +param( + [string]$Port = "", + [int]$Baud = 921600 +) + +$ErrorActionPreference = "Stop" +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $here + +function Find-Esptool { + $pio = Get-Command pio -ErrorAction SilentlyContinue + if (-not $pio) { + $pioPath = Join-Path $env:USERPROFILE "AppData\Local\Programs\Python\Python312\Scripts\pio.exe" + if (Test-Path $pioPath) { + $env:Path = "$(Split-Path $pioPath);$env:Path" + $pio = Get-Command pio -ErrorAction SilentlyContinue + } + } + if ($pio) { + $pkg = Join-Path $env:USERPROFILE ".platformio\packages\tool-esptoolpy\esptool.py" + if (Test-Path $pkg) { return @{ Kind = "py"; Path = $pkg } } + } + $py = Get-Command python -ErrorAction SilentlyContinue + if ($py) { + try { + & python -c "import esptool" 2>$null + if ($LASTEXITCODE -eq 0) { return @{ Kind = "mod"; Path = "esptool" } } + } catch {} + } + $esp = Get-Command esptool.py -ErrorAction SilentlyContinue + if ($esp) { return @{ Kind = "py"; Path = $esp.Source } } + return $null +} + +if (-not $Port) { + $ports = @() + try { + Add-Type -AssemblyName System.IO.Ports -ErrorAction SilentlyContinue | Out-Null + $ports = [System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object + } catch {} + if (-not $ports -or $ports.Count -eq 0) { + $ports = Get-CimInstance Win32_SerialPort -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty DeviceID | Sort-Object + } + if (-not $ports -or $ports.Count -eq 0) { + Write-Error "No serial ports found. Plug in the CYD USB cable, then retry with -Port COMx" + } + if ($ports.Count -eq 1) { + $Port = $ports[0] + } else { + Write-Host "Available ports: $($ports -join ', ')" + $Port = $ports[-1] + Write-Host "Using last port: $Port (override with -Port)" + } +} + +$tool = Find-Esptool +if (-not $tool) { + Write-Error "esptool not found. Install PlatformIO (`pio`) or: pip install esptool" +} + +$needed = @("bootloader.bin", "partitions.bin", "boot_app0.bin", "firmware.bin") +foreach ($f in $needed) { + if (-not (Test-Path (Join-Path $here $f))) { + Write-Error "Missing $f — run ..\export_from_build.ps1 from repo root first" + } +} + +$argsCommon = @( + "--chip", "esp32", + "--port", $Port, + "--baud", "$Baud", + "--before", "default_reset", + "--after", "hard_reset", + "write_flash", "-z", + "--flash_mode", "dio", + "--flash_freq", "40m", + "--flash_size", "4MB", + "0x1000", "bootloader.bin", + "0x8000", "partitions.bin", + "0xe000", "boot_app0.bin", + "0x10000", "firmware.bin" +) + +Write-Host "Flashing $here -> $Port @ $Baud" +if ($tool.Kind -eq "mod") { + & python -m esptool @argsCommon +} else { + & python $tool.Path @argsCommon +} +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +Write-Host "Done. Open serial monitor at 115200 if needed." diff --git a/firmware/cyd/meta.json b/firmware/cyd/meta.json new file mode 100644 index 0000000..f78fd1c --- /dev/null +++ b/firmware/cyd/meta.json @@ -0,0 +1,7 @@ +{ + "boardId": "esp32-2432s028", + "name": "CYD HW-069 Digit Display", + "summary": "HW-069 v1.1 TM1637 4-digit: CLK GPIO22, DIO GPIO27 — value / brightness / colon / on-off", + "description": "SoftSPI touch + LVGL. HW-069 4-digit 7-segment (TM1637) on CLK=22 DIO=27.", + "tags": ["hw069", "tm1637", "digit", "display", "touch", "lvgl"] +} diff --git a/firmware/cyd/partitions.bin b/firmware/cyd/partitions.bin new file mode 100644 index 0000000..2108af9 Binary files /dev/null and b/firmware/cyd/partitions.bin differ diff --git a/firmware/export_from_build.ps1 b/firmware/export_from_build.ps1 new file mode 100644 index 0000000..fb5e520 --- /dev/null +++ b/firmware/export_from_build.ps1 @@ -0,0 +1,57 @@ +# Rebuild (optional) and refresh firmware/cyd from PlatformIO build output. +# Usage: +# .\firmware\export_from_build.ps1 +# .\firmware\export_from_build.ps1 -SkipBuild + +param([switch]$SkipBuild) + +$ErrorActionPreference = "Stop" +$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) +Set-Location $root + +$env:Path = "C:\Users\dsx2016\AppData\Local\Programs\Python\Python312\Scripts;" + $env:Path + +if (-not $SkipBuild) { + pio run -e cyd + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} + +$src = Join-Path $root ".pio\build\cyd" +$dst = Join-Path $root "firmware\cyd" +New-Item -ItemType Directory -Force -Path $dst | Out-Null + +foreach ($f in @("bootloader.bin", "partitions.bin", "firmware.bin")) { + $p = Join-Path $src $f + if (-not (Test-Path $p)) { Write-Error "Missing build artifact: $p" } + Copy-Item $p $dst -Force +} + +$bootApp = Join-Path $env:USERPROFILE ".platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin" +if (-not (Test-Path $bootApp)) { + Write-Error "boot_app0.bin not found under PlatformIO packages" +} +Copy-Item $bootApp (Join-Path $dst "boot_app0.bin") -Force + +$git = "" +try { $git = (git -C $root rev-parse --short HEAD).Trim() } catch { $git = "unknown" } +$date = Get-Date -Format "yyyy-MM-dd" +$hashes = Get-ChildItem (Join-Path $dst "*.bin") | Get-FileHash -Algorithm SHA256 +$lines = @( + "name: CYD HW069 Digit Display (cyd)", + "git: $git", + "date: $date", + "board: ESP32-2432S028R (ILI9341)", + "features: SoftSPI touch, LVGL, HW069 TM1637 CLK=GPIO22 DIO=GPIO27", + "flash_size: 4MB", + "offsets:", + " 0x1000 bootloader.bin", + " 0x8000 partitions.bin", + " 0xe000 boot_app0.bin", + " 0x10000 firmware.bin", + "sha256:" +) +foreach ($h in $hashes) { + $lines += (" {0,-15} {1}" -f (Split-Path $h.Path -Leaf), $h.Hash) +} +$lines | Set-Content -Encoding utf8 (Join-Path $dst "VERSION.txt") +Write-Host "Exported -> $dst (git $git)" diff --git a/include/digit.h b/include/digit.h new file mode 100644 index 0000000..1cd2dbf --- /dev/null +++ b/include/digit.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +void digit_init(); + +void digit_set_on(bool on); +bool digit_is_on(); + +/** 显示数值 0..9999(超出钳位) */ +void digit_set_value(int value); +int digit_get_value(); + +/** 亮度 0..100,映射到 TM1637 8 档 */ +void digit_set_brightness(uint8_t percent); +uint8_t digit_brightness(); + +void digit_set_colon(bool on); +bool digit_colon(); + +void digit_clear(); diff --git a/include/lv_conf.h b/include/lv_conf.h new file mode 100644 index 0000000..94de0de --- /dev/null +++ b/include/lv_conf.h @@ -0,0 +1,1127 @@ +/** + * @file lv_conf.h + * Configuration file for v9.2.2 + */ + +/* + * Copy this file as `lv_conf.h` + * 1. simply next to the `lvgl` folder + * 2. or any other places and + * - define `LV_CONF_INCLUDE_SIMPLE` + * - add the path as include path + */ + +/* clang-format off */ +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H + +/*If you need to include anything here, do it inside the `__ASSEMBLY__` guard */ +#if 0 && defined(__ASSEMBLY__) +#include "my_include.h" +#endif + +/*==================== + COLOR SETTINGS + *====================*/ + +/*Color depth: 1 (I1), 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ +#define LV_COLOR_DEPTH 16 + +/*========================= + STDLIB WRAPPER SETTINGS + *=========================*/ + +/* Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN + +#define LV_STDINT_INCLUDE +#define LV_STDDEF_INCLUDE +#define LV_STDBOOL_INCLUDE +#define LV_INTTYPES_INCLUDE +#define LV_LIMITS_INCLUDE +#define LV_STDARG_INCLUDE + +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + /*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/ + #define LV_MEM_SIZE (64 * 1024U) /*[bytes]*/ + + /*Size of the memory expand for `lv_malloc()` in bytes*/ + #define LV_MEM_POOL_EXPAND_SIZE 0 + + /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ + #define LV_MEM_ADR 0 /*0: unused*/ + /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ + #if LV_MEM_ADR == 0 + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC + #endif +#endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/ + +/*==================== + HAL SETTINGS + *====================*/ + +/*Default display refresh, input device read and animation step period.*/ +#define LV_DEF_REFR_PERIOD 33 /*[ms]*/ + +/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + *(Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI_DEF 130 /*[px/inch]*/ + +/*================= + * OPERATING SYSTEM + *=================*/ +/*Select an operating system to use. Possible options: + * - LV_OS_NONE + * - LV_OS_PTHREAD + * - LV_OS_FREERTOS + * - LV_OS_CMSIS_RTOS2 + * - LV_OS_RTTHREAD + * - LV_OS_WINDOWS + * - LV_OS_MQX + * - LV_OS_CUSTOM */ +#define LV_USE_OS LV_OS_NONE + +#if LV_USE_OS == LV_OS_CUSTOM + #define LV_OS_CUSTOM_INCLUDE +#endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #define LV_USE_FREERTOS_TASK_NOTIFY 1 +#endif + +/*======================== + * RENDERING CONFIGURATION + *========================*/ + +/*Align the stride of all layers and images to this bytes*/ +#define LV_DRAW_BUF_STRIDE_ALIGN 1 + +/*Align the start address of draw_buf addresses to this bytes*/ +#define LV_DRAW_BUF_ALIGN 4 + +/*Using matrix for transformations. + *Requirements: + `LV_USE_MATRIX = 1`. + The rendering engine needs to support 3x3 matrix transformations.*/ +#define LV_DRAW_TRANSFORM_USE_MATRIX 0 + +/* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode + * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. + * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers + * and can't be drawn in chunks. */ + +/*The target buffer size for simple layer chunks.*/ +#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /*[bytes]*/ + +/* The stack size of the drawing thread. + * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. + */ +#define LV_DRAW_THREAD_STACK_SIZE (8 * 1024) /*[bytes]*/ + +#define LV_USE_DRAW_SW 1 +#if LV_USE_DRAW_SW == 1 + + /* + * Selectively disable color format support in order to reduce code size. + * NOTE: some features use certain color formats internally, e.g. + * - gradients use RGB888 + * - bitmaps with transparency may use ARGB8888 + */ + + #define LV_DRAW_SW_SUPPORT_RGB565 1 + #define LV_DRAW_SW_SUPPORT_RGB565A8 1 + #define LV_DRAW_SW_SUPPORT_RGB888 1 + #define LV_DRAW_SW_SUPPORT_XRGB8888 1 + #define LV_DRAW_SW_SUPPORT_ARGB8888 1 + #define LV_DRAW_SW_SUPPORT_L8 1 + #define LV_DRAW_SW_SUPPORT_AL88 1 + #define LV_DRAW_SW_SUPPORT_A8 1 + #define LV_DRAW_SW_SUPPORT_I1 1 + + /* Set the number of draw unit. + * > 1 requires an operating system enabled in `LV_USE_OS` + * > 1 means multiple threads will render the screen in parallel */ + #define LV_DRAW_SW_DRAW_UNIT_CNT 1 + + /* Use Arm-2D to accelerate the sw render */ + #define LV_USE_DRAW_ARM2D_SYNC 0 + + /* Enable native helium assembly to be compiled */ + #define LV_USE_NATIVE_HELIUM_ASM 0 + + /* 0: use a simple renderer capable of drawing only simple rectangles with gradient, images, texts, and straight lines only + * 1: use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too */ + #define LV_DRAW_SW_COMPLEX 1 + + #if LV_DRAW_SW_COMPLEX == 1 + /*Allow buffering some shadow calculation. + *LV_DRAW_SW_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` + *Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost*/ + #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 + + /* Set number of maximally cached circle data. + * The circumference of 1/4 circle are saved for anti-aliasing + * radius * 4 bytes are used per circle (the most often used radiuses are saved) + * 0: to disable caching */ + #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 + #endif + + #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE + + #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM + #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" + #endif + + /* Enable drawing complex gradients in software: linear at an angle, radial or conical */ + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 +#endif + +/* Use NXP's VG-Lite GPU on iMX RTxxx platforms. */ +#define LV_USE_DRAW_VGLITE 0 + +#if LV_USE_DRAW_VGLITE + /* Enable blit quality degradation workaround recommended for screen's dimension > 352 pixels. */ + #define LV_USE_VGLITE_BLIT_SPLIT 0 + + #if LV_USE_OS + /* Use additional draw thread for VG-Lite processing.*/ + #define LV_USE_VGLITE_DRAW_THREAD 1 + + #if LV_USE_VGLITE_DRAW_THREAD + /* Enable VGLite draw async. Queue multiple tasks and flash them once to the GPU. */ + #define LV_USE_VGLITE_DRAW_ASYNC 1 + #endif + #endif + + /* Enable VGLite asserts. */ + #define LV_USE_VGLITE_ASSERT 0 +#endif + +/* Use NXP's PXP on iMX RTxxx platforms. */ +#define LV_USE_PXP 0 + +#if LV_USE_PXP + /* Use PXP for drawing.*/ + #define LV_USE_DRAW_PXP 1 + + /* Use PXP to rotate display.*/ + #define LV_USE_ROTATE_PXP 0 + + #if LV_USE_DRAW_PXP && LV_USE_OS + /* Use additional draw thread for PXP processing.*/ + #define LV_USE_PXP_DRAW_THREAD 1 + #endif + + /* Enable PXP asserts. */ + #define LV_USE_PXP_ASSERT 0 +#endif + +/* Use Renesas Dave2D on RA platforms. */ +#define LV_USE_DRAW_DAVE2D 0 + +/* Draw using cached SDL textures*/ +#define LV_USE_DRAW_SDL 0 + +/* Use VG-Lite GPU. */ +#define LV_USE_DRAW_VG_LITE 0 + +#if LV_USE_DRAW_VG_LITE + /* Enable VG-Lite custom external 'gpu_init()' function */ + #define LV_VG_LITE_USE_GPU_INIT 0 + + /* Enable VG-Lite assert. */ + #define LV_VG_LITE_USE_ASSERT 0 + + /* VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */ + #define LV_VG_LITE_FLUSH_MAX_COUNT 8 + + /* Enable border to simulate shadow + * NOTE: which usually improves performance, + * but does not guarantee the same rendering quality as the software. */ + #define LV_VG_LITE_USE_BOX_SHADOW 0 + + /* VG-Lite gradient maximum cache number. + * NOTE: The memory usage of a single gradient image is 4K bytes. + */ + #define LV_VG_LITE_GRAD_CACHE_CNT 32 + + /* VG-Lite stroke maximum cache number. + */ + #define LV_VG_LITE_STROKE_CACHE_CNT 32 + +#endif + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Logging + *-----------*/ + +/*Enable the log module*/ +#define LV_USE_LOG 0 +#if LV_USE_LOG + + /*How important log should be added: + *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + *LV_LOG_LEVEL_INFO Log important events + *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + *LV_LOG_LEVEL_USER Only logs added by the user + *LV_LOG_LEVEL_NONE Do not log anything*/ + #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + + /*1: Print the log with 'printf'; + *0: User need to register a callback with `lv_log_register_print_cb()`*/ + #define LV_LOG_PRINTF 0 + + /*Set callback to print the logs. + *E.g `my_print`. The prototype should be `void my_print(lv_log_level_t level, const char * buf)` + *Can be overwritten by `lv_log_register_print_cb`*/ + //#define LV_LOG_PRINT_CB + + /*1: Enable print timestamp; + *0: Disable print timestamp*/ + #define LV_LOG_USE_TIMESTAMP 1 + + /*1: Print file and line number of the log; + *0: Do not print file and line number of the log*/ + #define LV_LOG_USE_FILE_LINE 1 + + + /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ + #define LV_LOG_TRACE_MEM 1 + #define LV_LOG_TRACE_TIMER 1 + #define LV_LOG_TRACE_INDEV 1 + #define LV_LOG_TRACE_DISP_REFR 1 + #define LV_LOG_TRACE_EVENT 1 + #define LV_LOG_TRACE_OBJ_CREATE 1 + #define LV_LOG_TRACE_LAYOUT 1 + #define LV_LOG_TRACE_ANIM 1 + #define LV_LOG_TRACE_CACHE 1 + +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/*Enable asserts if an operation is failed or an invalid data is found. + *If LV_USE_LOG is enabled an error message will be printed on failure*/ +#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ +#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ +#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ +#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + +/*Add a custom handler when assert happens e.g. to restart the MCU*/ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + +/*------------- + * Debug + *-----------*/ + +/*1: Draw random colored rectangles over the redrawn areas*/ +#define LV_USE_REFR_DEBUG 0 + +/*1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/ +#define LV_USE_LAYER_DEBUG 0 + +/*1: Draw overlays with different colors for each draw_unit's tasks. + *Also add the index number of the draw unit on white background. + *For layers add the index number of the draw unit on black background.*/ +#define LV_USE_PARALLEL_DRAW_DEBUG 0 + +/*------------- + * Others + *-----------*/ + +#define LV_ENABLE_GLOBAL_CUSTOM 0 +#if LV_ENABLE_GLOBAL_CUSTOM + /*Header to include for the custom 'lv_global' function"*/ + #define LV_GLOBAL_CUSTOM_INCLUDE +#endif + +/*Default cache size in bytes. + *Used by image decoders such as `lv_lodepng` to keep the decoded image in the memory. + *If size is not set to 0, the decoder will fail to decode when the cache is full. + *If size is 0, the cache function is not enabled and the decoded mem will be released immediately after use.*/ +#define LV_CACHE_DEF_SIZE 0 + +/*Default number of image header cache entries. The cache is used to store the headers of images + *The main logic is like `LV_CACHE_DEF_SIZE` but for image headers.*/ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + +/*Number of stops allowed per gradient. Increase this to allow more stops. + *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +#define LV_GRADIENT_MAX_STOPS 2 + +/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. + * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ +#define LV_COLOR_MIX_ROUND_OFS 0 + +/* Add 2 x 32 bit variables to each lv_obj_t to speed up getting style properties */ +#define LV_OBJ_STYLE_CACHE 0 + +/* Add `id` field to `lv_obj_t` */ +#define LV_USE_OBJ_ID 0 + +/* Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN LV_USE_OBJ_ID + +/*Use the builtin obj ID handler functions: +* - lv_obj_assign_id: Called when a widget is created. Use a separate counter for each widget class as an ID. +* - lv_obj_id_compare: Compare the ID to decide if it matches with a requested value. +* - lv_obj_stringify_id: Return e.g. "button3" +* - lv_obj_free_id: Does nothing, as there is no memory allocation for the ID. +* When disabled these functions needs to be implemented by the user.*/ +#define LV_USE_OBJ_ID_BUILTIN 1 + +/*Use obj property set/get API*/ +#define LV_USE_OBJ_PROPERTY 0 + +/*Enable property name support*/ +#define LV_USE_OBJ_PROPERTY_NAME 1 + +/* VG-Lite Simulator */ +/*Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */ +#define LV_USE_VG_LITE_THORVG 0 + +#if LV_USE_VG_LITE_THORVG + + /*Enable LVGL's blend mode support*/ + #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0 + + /*Enable YUV color format support*/ + #define LV_VG_LITE_THORVG_YUV_SUPPORT 0 + + /*Enable Linear gradient extension support*/ + #define LV_VG_LITE_THORVG_LINEAR_GRADIENT_EXT_SUPPORT 0 + + /*Enable 16 pixels alignment*/ + #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1 + + /*Buffer address alignment*/ + #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64 + + /*Enable multi-thread render*/ + #define LV_VG_LITE_THORVG_THREAD_RENDER 0 + +#endif + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/*For big endian systems set to 1*/ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/*Define a custom attribute to `lv_tick_inc` function*/ +#define LV_ATTRIBUTE_TICK_INC + +/*Define a custom attribute to `lv_timer_handler` function*/ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/*Define a custom attribute to `lv_display_flush_ready` function*/ +#define LV_ATTRIBUTE_FLUSH_READY + +/*Required alignment size for buffers*/ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + +/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/*Attribute to mark large constant arrays for example font's bitmaps*/ +#define LV_ATTRIBUTE_LARGE_CONST + +/*Compiler prefix for a big array declaration in RAM*/ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/*Place performance critical functions into a faster memory (e.g RAM)*/ +#define LV_ATTRIBUTE_FAST_MEM + +/*Export integer constant to binding. This macro is used with constants in the form of LV_ that + *should also appear on LVGL binding API such as MicroPython.*/ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + +/*Prefix all global extern data with this*/ +#define LV_ATTRIBUTE_EXTERN_DATA + +/* Use `float` as `lv_value_precise_t` */ +#define LV_USE_FLOAT 0 + +/*Enable matrix support + *Requires `LV_USE_FLOAT = 1`*/ +#define LV_USE_MATRIX 0 + +/*Include `lvgl_private.h` in `lvgl.h` to access internal data and functions by default*/ +#define LV_USE_PRIVATE_API 0 + +/*================== + * FONT USAGE + *===================*/ + +/*Montserrat fonts with ASCII range and some symbols using bpp = 4 + *https://fonts.google.com/specimen/Montserrat*/ +#define LV_FONT_MONTSERRAT_8 0 +#define LV_FONT_MONTSERRAT_10 0 +#define LV_FONT_MONTSERRAT_12 0 +#define LV_FONT_MONTSERRAT_14 1 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 1 +#define LV_FONT_MONTSERRAT_20 1 +#define LV_FONT_MONTSERRAT_22 0 +#define LV_FONT_MONTSERRAT_24 0 +#define LV_FONT_MONTSERRAT_26 0 +#define LV_FONT_MONTSERRAT_28 0 +#define LV_FONT_MONTSERRAT_30 0 +#define LV_FONT_MONTSERRAT_32 0 +#define LV_FONT_MONTSERRAT_34 0 +#define LV_FONT_MONTSERRAT_36 0 +#define LV_FONT_MONTSERRAT_38 0 +#define LV_FONT_MONTSERRAT_40 0 +#define LV_FONT_MONTSERRAT_42 0 +#define LV_FONT_MONTSERRAT_44 0 +#define LV_FONT_MONTSERRAT_46 0 +#define LV_FONT_MONTSERRAT_48 0 + +/*Demonstrate special features*/ +#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ +#define LV_FONT_SIMSUN_14_CJK 0 /*1000 most common CJK radicals*/ +#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ + +/*Pixel perfect monospace fonts*/ +#define LV_FONT_UNSCII_8 0 +#define LV_FONT_UNSCII_16 0 + +/*Optionally declare custom fonts here. + *You can use these fonts as default font too and they will be available globally. + *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font*/ +#define LV_FONT_DEFAULT &lv_font_montserrat_14 + +/*Enable handling large font and/or fonts with a lot of characters. + *The limit depends on the font size, font face and bpp. + *Compiler error will be triggered if a font needs it.*/ +#define LV_FONT_FMT_TXT_LARGE 0 + +/*Enables/disables support for compressed fonts.*/ +#define LV_USE_FONT_COMPRESSED 0 + +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + +/*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_)]}" + +/*If a word is at least this long, will break wherever "prettiest" + *To disable, set to a value <= 0*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/*Minimum number of characters in a long word to put on a line before a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/*Minimum number of characters in a long word to put on a line after a break. + *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. + *The direction will be processed according to the Unicode Bidirectional Algorithm: + *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ +#define LV_USE_BIDI 0 +#if LV_USE_BIDI + /*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect texts base direction*/ + #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/*Enable Arabic/Persian processing + *In these languages characters should be replaced with another form based on their position in the text*/ +#define LV_USE_ARABIC_PERSIAN_CHARS 0 + +/*================== + * WIDGETS + *================*/ + +/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ + +#define LV_WIDGETS_HAS_DEFAULT_VALUE 1 + +#define LV_USE_ANIMIMG 1 + +#define LV_USE_ARC 1 + +#define LV_USE_BAR 1 + +#define LV_USE_BUTTON 1 + +#define LV_USE_BUTTONMATRIX 1 + +#define LV_USE_CALENDAR 1 +#if LV_USE_CALENDAR + #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 + #if LV_CALENDAR_WEEK_STARTS_MONDAY + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} + #endif + + #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} + #define LV_USE_CALENDAR_HEADER_ARROW 1 + #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 + #define LV_USE_CALENDAR_CHINESE 0 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CANVAS 1 + +#define LV_USE_CHART 1 + +#define LV_USE_CHECKBOX 1 + +#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ + +#define LV_USE_IMAGE 1 /*Requires: lv_label*/ + +#define LV_USE_IMAGEBUTTON 1 + +#define LV_USE_KEYBOARD 1 + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL + #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ + #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ + #define LV_LABEL_WAIT_CHAR_COUNT 3 /*The count of wait chart*/ +#endif + +#define LV_USE_LED 1 + +#define LV_USE_LINE 1 + +#define LV_USE_LIST 1 + +#define LV_USE_LOTTIE 0 /*Requires: lv_canvas, thorvg */ + +#define LV_USE_MENU 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_ROLLER 1 /*Requires: lv_label*/ + +#define LV_USE_SCALE 1 + +#define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + +#define LV_USE_SPAN 1 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 1 + +#define LV_USE_SWITCH 1 + +#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ +#if LV_USE_TEXTAREA != 0 + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_USE_TABLE 1 + +#define LV_USE_TABVIEW 1 + +#define LV_USE_TILEVIEW 1 + +#define LV_USE_WIN 1 + +/*================== + * THEMES + *==================*/ + +/*A simple, impressive and very complete theme*/ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + + /*0: Light mode; 1: Dark mode*/ + #define LV_THEME_DEFAULT_DARK 0 + + /*1: Enable grow on press*/ + #define LV_THEME_DEFAULT_GROW 1 + + /*Default transition time in [ms]*/ + #define LV_THEME_DEFAULT_TRANSITION_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/*A very simple theme that is a good starting point for a custom theme*/ +#define LV_USE_THEME_SIMPLE 1 + +/*A theme designed for monochrome displays*/ +#define LV_USE_THEME_MONO 1 + +/*================== + * LAYOUTS + *==================*/ + +/*A layout similar to Flexbox in CSS.*/ +#define LV_USE_FLEX 1 + +/*A layout similar to Grid in CSS.*/ +#define LV_USE_GRID 1 + +/*==================== + * 3RD PARTS LIBRARIES + *====================*/ + +/*File system interfaces for common APIs */ + +/*Setting a default driver letter allows skipping the driver prefix in filepaths*/ +#define LV_FS_DEFAULT_DRIVE_LETTER '\0' + +/*API for fopen, fread, etc*/ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for CreateFile, ReadFile, etc*/ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for memory-mapped file access. */ +#define LV_USE_FS_MEMFS 0 +#if LV_USE_FS_MEMFS + #define LV_FS_MEMFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for LittleFs. */ +#define LV_USE_FS_LITTLEFS 0 +#if LV_USE_FS_LITTLEFS + #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for Arduino LittleFs. */ +#define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0 +#if LV_USE_FS_ARDUINO_ESP_LITTLEFS + #define LV_FS_ARDUINO_ESP_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*API for Arduino Sd. */ +#define LV_USE_FS_ARDUINO_SD 0 +#if LV_USE_FS_ARDUINO_SD + #define LV_FS_ARDUINO_SD_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + +/*LODEPNG decoder library*/ +#define LV_USE_LODEPNG 0 + +/*PNG decoder(libpng) library*/ +#define LV_USE_LIBPNG 0 + +/*BMP decoder library*/ +#define LV_USE_BMP 0 + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_TJPGD 0 + +/* libjpeg-turbo decoder library. + * Supports complete JPEG specifications and high-performance JPEG decoding. */ +#define LV_USE_LIBJPEG_TURBO 0 + +/*GIF decoder library*/ +#define LV_USE_GIF 0 +#if LV_USE_GIF + /*GIF decoder accelerate*/ + #define LV_GIF_CACHE_DECODE_DATA 0 +#endif + + +/*Decode bin images to RAM*/ +#define LV_BIN_DECODER_RAM_LOAD 0 + +/*RLE decompress library*/ +#define LV_USE_RLE 0 + +/*QR code library*/ +#define LV_USE_QRCODE 0 + +/*Barcode code library*/ +#define LV_USE_BARCODE 0 + +/*FreeType library*/ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE + /*Let FreeType to use LVGL memory and file porting*/ + #define LV_FREETYPE_USE_LVGL_PORT 0 + + /*Cache count of the glyphs in FreeType. It means the number of glyphs that can be cached. + *The higher the value, the more memory will be used.*/ + #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256 +#endif + +/* Built-in TTF decoder */ +#define LV_USE_TINY_TTF 0 +#if LV_USE_TINY_TTF + /* Enable loading TTF data from files */ + #define LV_TINY_TTF_FILE_SUPPORT 0 + #define LV_TINY_TTF_CACHE_GLYPH_CNT 256 +#endif + +/*Rlottie library*/ +#define LV_USE_RLOTTIE 0 + +/*Enable Vector Graphic APIs + *Requires `LV_USE_MATRIX = 1`*/ +#define LV_USE_VECTOR_GRAPHIC 0 + +/* Enable ThorVG (vector graphics library) from the src/libs folder */ +#define LV_USE_THORVG_INTERNAL 0 + +/* Enable ThorVG by assuming that its installed and linked to the project */ +#define LV_USE_THORVG_EXTERNAL 0 + +/*Use lvgl built-in LZ4 lib*/ +#define LV_USE_LZ4_INTERNAL 0 + +/*Use external LZ4 library*/ +#define LV_USE_LZ4_EXTERNAL 0 + +/*FFmpeg library for image decoding and playing videos + *Supports all major image formats so do not enable other image decoder with it*/ +#define LV_USE_FFMPEG 0 +#if LV_USE_FFMPEG + /*Dump input information to stderr*/ + #define LV_FFMPEG_DUMP_FORMAT 0 +#endif + +/*================== + * OTHERS + *==================*/ + +/*1: Enable API to take snapshot for object*/ +#define LV_USE_SNAPSHOT 0 + +/*1: Enable system monitor component*/ +#define LV_USE_SYSMON 0 +#if LV_USE_SYSMON + /*Get the idle percentage. E.g. uint32_t my_get_idle(void);*/ + #define LV_SYSMON_GET_IDLE lv_timer_get_idle + + /*1: Show CPU usage and FPS count + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_PERF_MONITOR 0 + #if LV_USE_PERF_MONITOR + #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT + + /*0: Displays performance data on the screen, 1: Prints performance data using log.*/ + #define LV_USE_PERF_MONITOR_LOG_MODE 0 + #endif + + /*1: Show the used memory and the memory fragmentation + * Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` + * Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_MEM_MONITOR 0 + #if LV_USE_MEM_MONITOR + #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT + #endif + +#endif /*LV_USE_SYSMON*/ + +/*1: Enable the runtime performance profiler*/ +#define LV_USE_PROFILER 0 +#if LV_USE_PROFILER + /*1: Enable the built-in profiler*/ + #define LV_USE_PROFILER_BUILTIN 1 + #if LV_USE_PROFILER_BUILTIN + /*Default profiler trace buffer size*/ + #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024) /*[bytes]*/ + #endif + + /*Header to include for the profiler*/ + #define LV_PROFILER_INCLUDE "lvgl/src/misc/lv_profiler_builtin.h" + + /*Profiler start point function*/ + #define LV_PROFILER_BEGIN LV_PROFILER_BUILTIN_BEGIN + + /*Profiler end point function*/ + #define LV_PROFILER_END LV_PROFILER_BUILTIN_END + + /*Profiler start point function with custom tag*/ + #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG + + /*Profiler end point function with custom tag*/ + #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG +#endif + +/*1: Enable Monkey test*/ +#define LV_USE_MONKEY 0 + +/*1: Enable grid navigation*/ +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable an observer pattern implementation*/ +#define LV_USE_OBSERVER 1 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesaurus*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif /*LV_IME_PINYIN_USE_K9_MODE*/ +#endif + +/*1: Enable file explorer*/ +/*Requires: lv_table*/ +#define LV_USE_FILE_EXPLORER 0 +#if LV_USE_FILE_EXPLORER + /*Maximum length of path*/ + #define LV_FILE_EXPLORER_PATH_MAX_LEN (128) + /*Quick access bar, 1:use, 0:not use*/ + /*Requires: lv_list*/ + #define LV_FILE_EXPLORER_QUICK_ACCESS 1 +#endif + +/*================== + * DEVICES + *==================*/ + +/*Use SDL to open window on PC and handle mouse and keyboard*/ +#define LV_USE_SDL 0 +#if LV_USE_SDL + #define LV_SDL_INCLUDE_PATH + #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /*LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance*/ + #define LV_SDL_BUF_COUNT 1 /*1 or 2*/ + #define LV_SDL_ACCELERATED 1 /*1: Use hardware acceleration*/ + #define LV_SDL_FULLSCREEN 0 /*1: Make the window full screen by default*/ + #define LV_SDL_DIRECT_EXIT 1 /*1: Exit the application when all SDL windows are closed*/ + #define LV_SDL_MOUSEWHEEL_MODE LV_SDL_MOUSEWHEEL_MODE_ENCODER /*LV_SDL_MOUSEWHEEL_MODE_ENCODER/CROWN*/ +#endif + +/*Use X11 to open window on Linux desktop and handle mouse and keyboard*/ +#define LV_USE_X11 0 +#if LV_USE_X11 + #define LV_X11_DIRECT_EXIT 1 /*Exit the application when all X11 windows have been closed*/ + #define LV_X11_DOUBLE_BUFFER 1 /*Use double buffers for rendering*/ + /*select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!)*/ + #define LV_X11_RENDER_MODE_PARTIAL 1 /*Partial render mode (preferred)*/ + #define LV_X11_RENDER_MODE_DIRECT 0 /*direct render mode*/ + #define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/ +#endif + +/*Use Wayland to open a window and handle input on Linux or BSD desktops */ +#define LV_USE_WAYLAND 0 +#if LV_USE_WAYLAND + #define LV_WAYLAND_WINDOW_DECORATIONS 0 /*Draw client side window decorations only necessary on Mutter/GNOME*/ + #define LV_WAYLAND_WL_SHELL 0 /*Use the legacy wl_shell protocol instead of the default XDG shell*/ +#endif + +/*Driver for /dev/fb*/ +#define LV_USE_LINUX_FBDEV 0 +#if LV_USE_LINUX_FBDEV + #define LV_LINUX_FBDEV_BSD 0 + #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL + #define LV_LINUX_FBDEV_BUFFER_COUNT 0 + #define LV_LINUX_FBDEV_BUFFER_SIZE 60 +#endif + +/*Use Nuttx to open window and handle touchscreen*/ +#define LV_USE_NUTTX 0 + +#if LV_USE_NUTTX + #define LV_USE_NUTTX_LIBUV 0 + + /*Use Nuttx custom init API to open window and handle touchscreen*/ + #define LV_USE_NUTTX_CUSTOM_INIT 0 + + /*Driver for /dev/lcd*/ + #define LV_USE_NUTTX_LCD 0 + #if LV_USE_NUTTX_LCD + #define LV_NUTTX_LCD_BUFFER_COUNT 0 + #define LV_NUTTX_LCD_BUFFER_SIZE 60 + #endif + + /*Driver for /dev/input*/ + #define LV_USE_NUTTX_TOUCHSCREEN 0 + +#endif + +/*Driver for /dev/dri/card*/ +#define LV_USE_LINUX_DRM 0 + +/*Interface for TFT_eSPI*/ +#define LV_USE_TFT_ESPI 1 + +/*Driver for evdev input devices*/ +#define LV_USE_EVDEV 0 + +/*Driver for libinput input devices*/ +#define LV_USE_LIBINPUT 0 + +#if LV_USE_LIBINPUT + #define LV_LIBINPUT_BSD 0 + + /*Full keyboard support*/ + #define LV_LIBINPUT_XKB 0 + #if LV_LIBINPUT_XKB + /*"setxkbmap -query" can help find the right values for your keyboard*/ + #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL } + #endif +#endif + +/*Drivers for LCD devices connected via SPI/parallel port*/ +#define LV_USE_ST7735 0 +#define LV_USE_ST7789 0 +#define LV_USE_ST7796 0 +#define LV_USE_ILI9341 0 + +#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) + +/*Driver for Renesas GLCD*/ +#define LV_USE_RENESAS_GLCDC 0 + +/* LVGL Windows backend */ +#define LV_USE_WINDOWS 0 + +/* Use OpenGL to open window on PC and handle mouse and keyboard */ +#define LV_USE_OPENGLES 0 +#if LV_USE_OPENGLES + #define LV_USE_OPENGLES_DEBUG 1 /* Enable or disable debug for opengles */ +#endif + +/* QNX Screen display and input drivers */ +#define LV_USE_QNX 0 +#if LV_USE_QNX + #define LV_QNX_BUF_COUNT 1 /*1 or 2*/ +#endif + +/*================== +* EXAMPLES +*==================*/ + +/*Enable the examples to be built with the library*/ +#define LV_BUILD_EXAMPLES 1 + +/*=================== + * DEMO USAGE + ====================*/ + +/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ +#define LV_USE_DEMO_WIDGETS 0 + +/*Demonstrate the usage of encoder and keyboard*/ +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + +/*Benchmark your system*/ +#define LV_USE_DEMO_BENCHMARK 0 + +/*Render test for each primitives. Requires at least 480x272 display*/ +#define LV_USE_DEMO_RENDER 0 + +/*Stress test for LVGL*/ +#define LV_USE_DEMO_STRESS 0 + +/*Music player demo*/ +#define LV_USE_DEMO_MUSIC 0 +#if LV_USE_DEMO_MUSIC + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 +#endif + +/*Flex layout demo*/ +#define LV_USE_DEMO_FLEX_LAYOUT 0 + +/*Smart-phone like multi-language demo*/ +#define LV_USE_DEMO_MULTILANG 0 + +/*Widget transformation demo*/ +#define LV_USE_DEMO_TRANSFORM 0 + +/*Demonstrate scroll settings*/ +#define LV_USE_DEMO_SCROLL 0 + +/*Vector graphic demo*/ +#define LV_USE_DEMO_VECTOR_GRAPHIC 0 + +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/include/pins.h b/include/pins.h new file mode 100644 index 0000000..2a0c603 --- /dev/null +++ b/include/pins.h @@ -0,0 +1,20 @@ +#pragma once + +// CYD ESP32-2432S028R — 触控/屏脚与 tftest 一致 +// HW-069 v1.1 四位数显(TM1637):CLK=GPIO22,DIO=GPIO27 + +#define TFT_BL_PIN 21 + +#define CYD_TOUCH_IRQ 36 +#define CYD_TOUCH_MOSI 32 +#define CYD_TOUCH_MISO 39 +#define CYD_TOUCH_CLK 25 +#define CYD_TOUCH_CS 33 + +#define HW069_CLK_PIN 22 +#define HW069_DIO_PIN 27 + +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 320 +#define UI_HOR_RES 320 +#define UI_VER_RES 240 diff --git a/include/touch.h b/include/touch.h new file mode 100644 index 0000000..86e00fe --- /dev/null +++ b/include/touch.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +void touch_init(); +void touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data); + +// 调试:最近一次触点(物理 / LVGL 旋转后逻辑) +extern int16_t g_dbg_phys_x, g_dbg_phys_y; +extern int16_t g_dbg_logic_x, g_dbg_logic_y; +extern bool g_dbg_touch_down; diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..41e2892 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,4 @@ +#pragma once + +void ui_create(); +void ui_refresh_digit(); diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..e56e770 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,56 @@ +; CYD ESP32-2432S028 — HW069 四位数显(CLK22/DIO27) + SoftSPI 触控 + LVGL +; 触控/屏配置参考 tftest / hw269test +[env:cyd] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 460800 +upload_speed = 460800 +board_build.partitions = default.csv + +lib_deps = + bodmer/TFT_eSPI@^2.5.43 + lvgl/lvgl@9.2.2 + +build_flags = + -DCORE_DEBUG_LEVEL=0 + -DUSER_SETUP_LOADED=1 + -DILI9341_2_DRIVER=1 + -DTFT_WIDTH=240 + -DTFT_HEIGHT=320 + -DTFT_MISO=12 + -DTFT_MOSI=13 + -DTFT_SCLK=14 + -DTFT_CS=15 + -DTFT_DC=2 + -DTFT_RST=-1 + -DTFT_BL=21 + -DTFT_BACKLIGHT_ON=HIGH + -DTOUCH_CS=-1 + -DLOAD_GLCD=1 + -DLOAD_FONT2=1 + -DLOAD_FONT4=1 + -DLOAD_FONT6=1 + -DLOAD_FONT7=1 + -DLOAD_FONT8=1 + -DLOAD_GFXFF=1 + -DSMOOTH_FONT=1 + -DSPI_FREQUENCY=40000000 + -DSPI_READ_FREQUENCY=20000000 + -DLV_CONF_SKIP + -DLV_COLOR_DEPTH=16 + -DLV_MEM_SIZE=49152 + -DLV_FONT_MONTSERRAT_14=1 + -DLV_FONT_MONTSERRAT_16=1 + -DLV_FONT_MONTSERRAT_18=1 + -DLV_FONT_MONTSERRAT_20=1 + -DLV_FONT_MONTSERRAT_28=1 + -DLV_USE_TFT_ESPI=1 + -DLV_USE_FLEX=1 + -DLV_USE_BUTTON=1 + -DLV_USE_LABEL=1 + -DLV_USE_BAR=1 + -DLV_USE_SLIDER=1 + -I${PROJECT_DIR}/include + +extra_scripts = pre:copy_lv_conf.py diff --git a/src/digit.cpp b/src/digit.cpp new file mode 100644 index 0000000..0aed2e4 --- /dev/null +++ b/src/digit.cpp @@ -0,0 +1,178 @@ +#include "digit.h" +#include "pins.h" + +#include + +// HW-069 v1.1:TM1637 两线协议(非 I2C) +static constexpr uint8_t CMD_DATA_AUTO = 0x40; +static constexpr uint8_t CMD_ADDR_BASE = 0xC0; +static constexpr uint8_t CMD_DISP_OFF = 0x80; +static constexpr uint8_t CMD_DISP_ON = 0x88; // | brightness 0..7 + +static const uint8_t SEG_DIGIT[10] = { + 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, +}; + +static bool g_on = true; +static bool g_colon = false; +static uint8_t g_pct = 60; +static int g_value = 1234; +static uint8_t g_raw[4] = {0, 0, 0, 0}; + +static void bit_delay() { delayMicroseconds(50); } + +static void start() { + digitalWrite(HW069_DIO_PIN, HIGH); + digitalWrite(HW069_CLK_PIN, HIGH); + bit_delay(); + digitalWrite(HW069_DIO_PIN, LOW); + bit_delay(); + digitalWrite(HW069_CLK_PIN, LOW); + bit_delay(); +} + +static void stop() { + digitalWrite(HW069_CLK_PIN, LOW); + digitalWrite(HW069_DIO_PIN, LOW); + bit_delay(); + digitalWrite(HW069_CLK_PIN, HIGH); + bit_delay(); + digitalWrite(HW069_DIO_PIN, HIGH); + bit_delay(); +} + +static void write_byte(uint8_t b) { + for (uint8_t i = 0; i < 8; ++i) { + digitalWrite(HW069_CLK_PIN, LOW); + bit_delay(); + digitalWrite(HW069_DIO_PIN, (b & 0x01) ? HIGH : LOW); + bit_delay(); + digitalWrite(HW069_CLK_PIN, HIGH); + bit_delay(); + b >>= 1; + } + // ACK + digitalWrite(HW069_CLK_PIN, LOW); + pinMode(HW069_DIO_PIN, INPUT_PULLUP); + bit_delay(); + digitalWrite(HW069_CLK_PIN, HIGH); + bit_delay(); + digitalWrite(HW069_CLK_PIN, LOW); + pinMode(HW069_DIO_PIN, OUTPUT); + bit_delay(); +} + +static uint8_t level_from_pct(uint8_t pct) { + if (pct > 100) { + pct = 100; + } + // 0% -> 0, 100% -> 7 + return (uint8_t)((pct * 7U) / 100U); +} + +static void encode_value() { + int v = g_value; + if (v < 0) { + v = 0; + } + if (v > 9999) { + v = 9999; + } + g_raw[0] = SEG_DIGIT[(v / 1000) % 10]; + g_raw[1] = SEG_DIGIT[(v / 100) % 10]; + g_raw[2] = SEG_DIGIT[(v / 10) % 10]; + g_raw[3] = SEG_DIGIT[v % 10]; + // 时钟型模块:中间冒号挂在第 2 位的 bit7 + if (g_colon) { + g_raw[1] |= 0x80; + } +} + +static void apply() { + start(); + write_byte(CMD_DATA_AUTO); + stop(); + + start(); + write_byte(CMD_ADDR_BASE); + for (uint8_t i = 0; i < 4; ++i) { + write_byte(g_on ? g_raw[i] : 0x00); + } + stop(); + + start(); + if (g_on) { + write_byte((uint8_t)(CMD_DISP_ON | level_from_pct(g_pct))); + } else { + write_byte(CMD_DISP_OFF); + } + stop(); +} + +void digit_init() { + pinMode(HW069_CLK_PIN, OUTPUT); + pinMode(HW069_DIO_PIN, OUTPUT); + digitalWrite(HW069_CLK_PIN, HIGH); + digitalWrite(HW069_DIO_PIN, HIGH); + + g_on = true; + g_colon = false; + g_pct = 60; + g_value = 1234; + encode_value(); + apply(); + Serial.printf("HW-069 TM1637 CLK=GPIO%d DIO=GPIO%d value=%d bright=%u%%\n", + HW069_CLK_PIN, HW069_DIO_PIN, g_value, (unsigned)g_pct); +} + +void digit_set_on(bool on) { + g_on = on; + apply(); + Serial.printf("Digit %s\n", g_on ? "ON" : "OFF"); +} + +bool digit_is_on() { return g_on; } + +void digit_set_value(int value) { + if (value < 0) { + value = 0; + } + if (value > 9999) { + value = 9999; + } + g_value = value; + encode_value(); + apply(); + Serial.printf("Digit value %d\n", g_value); +} + +int digit_get_value() { return g_value; } + +void digit_set_brightness(uint8_t percent) { + if (percent > 100) { + percent = 100; + } + g_pct = percent; + apply(); + Serial.printf("Digit bright %u%% (lvl %u)\n", (unsigned)g_pct, + (unsigned)level_from_pct(g_pct)); +} + +uint8_t digit_brightness() { return g_pct; } + +void digit_set_colon(bool on) { + g_colon = on; + encode_value(); + apply(); + Serial.printf("Digit colon %s\n", g_colon ? "ON" : "OFF"); +} + +bool digit_colon() { return g_colon; } + +void digit_clear() { + g_value = 0; + g_colon = false; + encode_value(); + apply(); + Serial.println("Digit clear"); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2aaa10b --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include "digit.h" +#include "pins.h" +#include "touch.h" +#include "ui.h" + +static constexpr uint32_t DRAW_BUF_SIZE = + (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8)); +static uint32_t draw_buf[DRAW_BUF_SIZE / 4]; + +void setup() { + Serial.begin(460800); + delay(200); + Serial.println(); + Serial.printf("LVGL %d.%d.%d | HW-069 Digit Display v1.1\n", lv_version_major(), + lv_version_minor(), lv_version_patch()); + Serial.println("BOOT_OK"); + + pinMode(TFT_BL_PIN, OUTPUT); + digitalWrite(TFT_BL_PIN, HIGH); + + touch_init(); + digit_init(); + + lv_init(); + + lv_display_t *disp = + lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf)); + lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270); + + lv_indev_t *indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, touch_read_cb); + lv_indev_set_display(indev, disp); + + ui_create(); +} + +void loop() { + lv_timer_handler(); + lv_tick_inc(5); + delay(5); +} diff --git a/src/touch.cpp b/src/touch.cpp new file mode 100644 index 0000000..d43b68a --- /dev/null +++ b/src/touch.cpp @@ -0,0 +1,125 @@ +#include "touch.h" +#include "pins.h" + +#include + +/** + * SoftSPI XPT2046 + * BL↔TR 是矩形主对角线镜像(XY 对调),不是 180°(180° 会搞成 TL↔BR)。 + * 先按 rot2 得到旧 UI,再做对角修正,反变换喂给 LVGL 270。 + */ + +int16_t g_dbg_phys_x = -1; +int16_t g_dbg_phys_y = -1; +int16_t g_dbg_logic_x = -1; +int16_t g_dbg_logic_y = -1; +bool g_dbg_touch_down = false; + +static void soft_write(uint8_t data) { + for (int i = 7; i >= 0; --i) { + digitalWrite(CYD_TOUCH_MOSI, (data >> i) & 1); + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + } +} + +static uint16_t soft_read12() { + uint16_t v = 0; + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + for (int i = 0; i < 12; ++i) { + digitalWrite(CYD_TOUCH_CLK, HIGH); + delayMicroseconds(2); + v = (uint16_t)((v << 1) | (digitalRead(CYD_TOUCH_MISO) ? 1u : 0u)); + digitalWrite(CYD_TOUCH_CLK, LOW); + delayMicroseconds(2); + } + return v; +} + +static uint16_t xpt_read(uint8_t cmd) { + digitalWrite(CYD_TOUCH_CS, LOW); + soft_write(cmd); + uint16_t v = soft_read12(); + digitalWrite(CYD_TOUCH_CS, HIGH); + return v; +} + +static int16_t clampi(int32_t v, int32_t lo, int32_t hi) { + if (v < lo) { + return (int16_t)lo; + } + if (v > hi) { + return (int16_t)hi; + } + return (int16_t)v; +} + +void touch_init() { + pinMode(CYD_TOUCH_CS, OUTPUT); + pinMode(CYD_TOUCH_CLK, OUTPUT); + pinMode(CYD_TOUCH_MOSI, OUTPUT); + pinMode(CYD_TOUCH_MISO, INPUT); + pinMode(CYD_TOUCH_IRQ, INPUT); + digitalWrite(CYD_TOUCH_CS, HIGH); + digitalWrite(CYD_TOUCH_CLK, LOW); + Serial.println("Touch: SoftSPI + diagonal fix (BL/TR)"); +} + +void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) { + uint16_t adc_x = xpt_read(0x90); + uint16_t adc_y = xpt_read(0xD0); + adc_x = (uint16_t)((adc_x + xpt_read(0x90)) / 2); + adc_y = (uint16_t)((adc_y + xpt_read(0xD0)) / 2); + + const bool irq = (digitalRead(CYD_TOUCH_IRQ) == LOW); + const bool in_range = + (adc_x > 80 && adc_x < 4000 && adc_y > 80 && adc_y < 4000); + + if (!in_range || (!irq && (adc_x < 200 || adc_y < 200))) { + data->state = LV_INDEV_STATE_RELEASED; + g_dbg_touch_down = false; + return; + } + + const int16_t p_x = (int16_t)adc_x; + const int16_t p_y = (int16_t)(4095 - adc_y); + + int16_t x = map(p_x, 200, 3700, 1, SCREEN_WIDTH); + int16_t y = map(p_y, 240, 3800, 1, SCREEN_HEIGHT); + x = clampi(x, 0, SCREEN_WIDTH - 1); + y = clampi(y, 0, SCREEN_HEIGHT - 1); + + // 旧 UI(LVGL270):(y, 239-x) — 该空间里 BL↔TR,做主对角线镜像 + // (ux,uy) -> (uy*(319/239), ux*(239/319)) + const int32_t old_ux = y; + const int32_t old_uy = (SCREEN_WIDTH - 1) - x; + int16_t ui_x = clampi((old_uy * (UI_HOR_RES - 1)) / (UI_VER_RES - 1), 0, + UI_HOR_RES - 1); + int16_t ui_y = clampi((old_ux * (UI_VER_RES - 1)) / (UI_HOR_RES - 1), 0, + UI_VER_RES - 1); + + // 反变换喂 LVGL,使最终落在 (ui_x, ui_y) + const int16_t feed_x = (int16_t)((SCREEN_WIDTH - 1) - ui_y); + const int16_t feed_y = ui_x; + + g_dbg_phys_x = x; + g_dbg_phys_y = y; + g_dbg_logic_x = ui_x; + g_dbg_logic_y = ui_y; + g_dbg_touch_down = true; + + static uint32_t last_log = 0; + if (millis() - last_log > 80) { + last_log = millis(); + Serial.printf("Touch UI(%d,%d)\n", ui_x, ui_y); + } + + data->point.x = feed_x; + data->point.y = feed_y; + data->state = LV_INDEV_STATE_PRESSED; +} diff --git a/src/ui.cpp b/src/ui.cpp new file mode 100644 index 0000000..800e485 --- /dev/null +++ b/src/ui.cpp @@ -0,0 +1,224 @@ +#include "ui.h" +#include "digit.h" +#include "pins.h" + +#include +#include +#include + +static constexpr uint32_t COL_BG = 0x0A1016; +static constexpr uint32_t COL_INK = 0xEAF0F6; +static constexpr uint32_t COL_MUTED = 0x7A8B9C; +static constexpr uint32_t COL_BTN = 0x243140; +static constexpr uint32_t COL_ON = 0x1B5E4A; +static constexpr uint32_t COL_ACCENT = 0xFFB74D; +static constexpr uint32_t COL_DIM = 0x3A4656; + +static lv_obj_t *lbl_title; +static lv_obj_t *lbl_pin; +static lv_obj_t *lbl_value; +static lv_obj_t *lbl_bright; +static lv_obj_t *slider; +static lv_obj_t *btn_power; +static lv_obj_t *lbl_power; +static lv_obj_t *btn_colon; +static lv_obj_t *lbl_colon; +static lv_obj_t *btn_m100; +static lv_obj_t *btn_m1; +static lv_obj_t *btn_p1; +static lv_obj_t *btn_p100; + +static void style_btn(lv_obj_t *btn, uint32_t bg) { + lv_obj_set_style_radius(btn, 10, 0); + lv_obj_set_style_bg_color(btn, lv_color_hex(bg), 0); + lv_obj_set_style_bg_opa(btn, LV_OPA_COVER, 0); + lv_obj_set_style_shadow_width(btn, 0, 0); + lv_obj_set_style_border_width(btn, 0, 0); + lv_obj_set_style_pad_all(btn, 0, 0); +} + +static lv_obj_t *make_step_btn(lv_obj_t *parent, const char *txt, int16_t x, + int16_t y, int16_t w, int16_t h, + lv_event_cb_t cb, void *user) { + lv_obj_t *btn = lv_button_create(parent); + lv_obj_set_size(btn, w, h); + lv_obj_align(btn, LV_ALIGN_TOP_LEFT, x, y); + style_btn(btn, COL_BTN); + lv_obj_add_event_cb(btn, cb, LV_EVENT_CLICKED, user); + lv_obj_t *lbl = lv_label_create(btn); + lv_label_set_text(lbl, txt); + lv_obj_set_style_text_color(lbl, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl, &lv_font_montserrat_18, 0); + lv_obj_center(lbl); + return btn; +} + +void ui_refresh_digit() { + const bool on = digit_is_on(); + const bool colon = digit_colon(); + const int value = digit_get_value(); + const uint8_t pct = digit_brightness(); + + if (lbl_value) { + if (colon) { + lv_label_set_text_fmt(lbl_value, "%02d:%02d", value / 100, value % 100); + } else { + lv_label_set_text_fmt(lbl_value, "%04d", value); + } + lv_obj_set_style_text_color(lbl_value, + lv_color_hex(on ? COL_ACCENT : COL_MUTED), 0); + } + if (lbl_bright) { + lv_label_set_text_fmt(lbl_bright, "Brightness %u%%", (unsigned)pct); + } + if (slider) { + lv_slider_set_value(slider, pct, LV_ANIM_OFF); + lv_obj_set_style_bg_opa(slider, on ? LV_OPA_COVER : LV_OPA_50, LV_PART_MAIN); + } + if (lbl_power) { + lv_label_set_text(lbl_power, on ? "Display OFF" : "Display ON"); + } + if (btn_power) { + style_btn(btn_power, on ? COL_ON : COL_BTN); + } + if (lbl_colon) { + lv_label_set_text(lbl_colon, colon ? "Colon ON" : "Colon OFF"); + } + if (btn_colon) { + style_btn(btn_colon, colon ? COL_ON : COL_DIM); + } +} + +static void on_power(lv_event_t *e) { + if (lv_event_get_code(e) != LV_EVENT_CLICKED) { + return; + } + digit_set_on(!digit_is_on()); + ui_refresh_digit(); +} + +static void on_colon(lv_event_t *e) { + if (lv_event_get_code(e) != LV_EVENT_CLICKED) { + return; + } + digit_set_colon(!digit_colon()); + ui_refresh_digit(); +} + +static void on_step(lv_event_t *e) { + if (lv_event_get_code(e) != LV_EVENT_CLICKED) { + return; + } + const intptr_t delta = (intptr_t)lv_event_get_user_data(e); + int v = digit_get_value() + (int)delta; + if (v < 0) { + v = 0; + } + if (v > 9999) { + v = 9999; + } + digit_set_value(v); + if (!digit_is_on()) { + digit_set_on(true); + } + ui_refresh_digit(); +} + +static void on_slider(lv_event_t *e) { + const lv_event_code_t code = lv_event_get_code(e); + if (code != LV_EVENT_VALUE_CHANGED && code != LV_EVENT_RELEASED) { + return; + } + lv_obj_t *obj = (lv_obj_t *)lv_event_get_target(e); + const int32_t v = lv_slider_get_value(obj); + digit_set_brightness((uint8_t)v); + if (!digit_is_on() && v > 0) { + digit_set_on(true); + } + ui_refresh_digit(); +} + +void ui_create() { + lv_obj_t *scr = lv_screen_active(); + lv_obj_set_style_bg_color(scr, lv_color_hex(COL_BG), 0); + lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); + + lbl_title = lv_label_create(scr); + lv_label_set_text(lbl_title, "HW-069"); + lv_obj_set_style_text_color(lbl_title, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_28, 0); + lv_obj_align(lbl_title, LV_ALIGN_TOP_MID, 0, 4); + + lbl_pin = lv_label_create(scr); + lv_label_set_text_fmt(lbl_pin, "v1.1 CLK%d DIO%d TM1637", HW069_CLK_PIN, + HW069_DIO_PIN); + lv_obj_set_style_text_color(lbl_pin, lv_color_hex(COL_MUTED), 0); + lv_obj_set_style_text_font(lbl_pin, &lv_font_montserrat_14, 0); + lv_obj_align(lbl_pin, LV_ALIGN_TOP_MID, 0, 36); + + lbl_value = lv_label_create(scr); + lv_label_set_text(lbl_value, "1234"); + lv_obj_set_style_text_color(lbl_value, lv_color_hex(COL_ACCENT), 0); + lv_obj_set_style_text_font(lbl_value, &lv_font_montserrat_28, 0); + lv_obj_align(lbl_value, LV_ALIGN_TOP_MID, 0, 56); + + const int16_t row_y = 92; + const int16_t gap = 8; + const int16_t bw = (UI_HOR_RES - 48 - 3 * gap) / 4; + const int16_t bx0 = 24; + btn_m100 = make_step_btn(scr, "-100", bx0, row_y, bw, 40, on_step, + (void *)(intptr_t)-100); + btn_m1 = make_step_btn(scr, "-1", bx0 + (bw + gap), row_y, bw, 40, on_step, + (void *)(intptr_t)-1); + btn_p1 = make_step_btn(scr, "+1", bx0 + 2 * (bw + gap), row_y, bw, 40, on_step, + (void *)(intptr_t)1); + btn_p100 = make_step_btn(scr, "+100", bx0 + 3 * (bw + gap), row_y, bw, 40, + on_step, (void *)(intptr_t)100); + (void)btn_m100; + (void)btn_m1; + (void)btn_p1; + (void)btn_p100; + + lbl_bright = lv_label_create(scr); + lv_label_set_text(lbl_bright, "Brightness 60%"); + lv_obj_set_style_text_color(lbl_bright, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl_bright, &lv_font_montserrat_14, 0); + lv_obj_align(lbl_bright, LV_ALIGN_TOP_MID, 0, 140); + + slider = lv_slider_create(scr); + lv_obj_set_size(slider, UI_HOR_RES - 48, 24); + lv_slider_set_range(slider, 0, 100); + lv_slider_set_value(slider, 60, LV_ANIM_OFF); + lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 162); + lv_obj_set_style_bg_color(slider, lv_color_hex(COL_BTN), LV_PART_MAIN); + lv_obj_set_style_bg_color(slider, lv_color_hex(COL_ACCENT), LV_PART_INDICATOR); + lv_obj_set_style_bg_color(slider, lv_color_hex(COL_INK), LV_PART_KNOB); + lv_obj_set_ext_click_area(slider, 12); + lv_obj_add_event_cb(slider, on_slider, LV_EVENT_VALUE_CHANGED, nullptr); + lv_obj_add_event_cb(slider, on_slider, LV_EVENT_RELEASED, nullptr); + + const int16_t btn_w = (UI_HOR_RES - 48 - gap) / 2; + btn_colon = lv_button_create(scr); + lv_obj_set_size(btn_colon, btn_w, 40); + lv_obj_align(btn_colon, LV_ALIGN_TOP_LEFT, 24, 196); + style_btn(btn_colon, COL_DIM); + lv_obj_add_event_cb(btn_colon, on_colon, LV_EVENT_CLICKED, nullptr); + lbl_colon = lv_label_create(btn_colon); + lv_label_set_text(lbl_colon, "Colon OFF"); + lv_obj_set_style_text_color(lbl_colon, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl_colon, &lv_font_montserrat_16, 0); + lv_obj_center(lbl_colon); + + btn_power = lv_button_create(scr); + lv_obj_set_size(btn_power, btn_w, 40); + lv_obj_align(btn_power, LV_ALIGN_TOP_LEFT, 24 + btn_w + gap, 196); + style_btn(btn_power, COL_ON); + lv_obj_add_event_cb(btn_power, on_power, LV_EVENT_CLICKED, nullptr); + lbl_power = lv_label_create(btn_power); + lv_label_set_text(lbl_power, "Display OFF"); + lv_obj_set_style_text_color(lbl_power, lv_color_hex(COL_INK), 0); + lv_obj_set_style_text_font(lbl_power, &lv_font_montserrat_16, 0); + lv_obj_center(lbl_power); + + ui_refresh_digit(); +}