From efe148b4ae6ce0a3237f29728d78a3e71f9fd4b5 Mon Sep 17 00:00:00 2001 From: alfred_liang Date: Thu, 20 Nov 2025 17:39:55 +0800 Subject: [PATCH] =?UTF-8?q?openvela:=20=E7=94=B5=E5=AD=90=E6=B2=99?= =?UTF-8?q?=E6=BC=8F=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hourglass/Kconfig | 30 ++++ hourglass/Make.defs | 23 +++ hourglass/Makefile | 33 +++++ hourglass/Readme.md | 79 ++++++++++ hourglass/hourglass_control.c | 265 ++++++++++++++++++++++++++++++++++ hourglass/hourglass_control.h | 9 ++ hourglass/hourglass_main.c | 171 ++++++++++++++++++++++ hourglass/hourglass_page.c | 260 +++++++++++++++++++++++++++++++++ hourglass/hourglass_page.h | 15 ++ 9 files changed, 885 insertions(+) create mode 100644 hourglass/Kconfig create mode 100644 hourglass/Make.defs create mode 100644 hourglass/Makefile create mode 100644 hourglass/Readme.md create mode 100644 hourglass/hourglass_control.c create mode 100644 hourglass/hourglass_control.h create mode 100644 hourglass/hourglass_main.c create mode 100644 hourglass/hourglass_page.c create mode 100644 hourglass/hourglass_page.h diff --git a/hourglass/Kconfig b/hourglass/Kconfig new file mode 100644 index 0000000..bc7741a --- /dev/null +++ b/hourglass/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig LVX_USE_DEMO_HOURGLASS + tristate "Hourglass Demo" + depends on GRAPHICS_LVGL + default n + ---help--- + Enable build the Light and Versatile Graphics Library Demo programs + +if LVX_USE_DEMO_HOURGLASS + +config LVX_USE_DEMO_HOURGLASS_PRIORITY + int "hourglass task priority" + default 100 + +config LVX_USE_DEMO_HOURGLASS_STACKSIZE + int "hourglass stack size" + default 16384 + +config LVX_USE_DEMO_HOURGLASS_DEVPATH + string "Touchscreen device path" + default "/dev/input0" + depends on INPUT_TOUCHSCREEN + ---help--- + The path to the touchscreen device. Default: "/dev/input0" + +endif # LVX_USE_DEMO_HOURGLASS diff --git a/hourglass/Make.defs b/hourglass/Make.defs new file mode 100644 index 0000000..521d384 --- /dev/null +++ b/hourglass/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# packages/demos/hourglass/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_LVX_USE_DEMO_HOURGLASS),) +CONFIGURED_APPS += $(APPDIR)/packages/demos/hourglass +endif diff --git a/hourglass/Makefile b/hourglass/Makefile new file mode 100644 index 0000000..8d7900a --- /dev/null +++ b/hourglass/Makefile @@ -0,0 +1,33 @@ +############################################################################ +# packages/demos/hourglass/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +ifeq ($(CONFIG_LVX_USE_DEMO_HOURGLASS), y) +PROGNAME = hourglass +PRIORITY = $(CONFIG_LVX_USE_DEMO_HOURGLASS_PRIORITY) +STACKSIZE = $(CONFIG_LVX_USE_DEMO_HOURGLASS_STACKSIZE) +MODULE = $(CONFIG_LVX_USE_DEMO_HOURGLASS) + +CSRCS = hourglass_page.c hourglass_control.c +MAINSRC = hourglass_main.c +endif + +include $(APPDIR)/Application.mk diff --git a/hourglass/Readme.md b/hourglass/Readme.md new file mode 100644 index 0000000..7f7da3b --- /dev/null +++ b/hourglass/Readme.md @@ -0,0 +1,79 @@ +电子沙漏 Demo (Hourglass Demo) +项目简介 +这是一个基于openvela系统,在模拟器上运行的电子沙漏演示程序。它使用LVGL图形库创建了一个可视化的沙漏效果,用户可以通过触摸屏控制沙漏的启动和计时时间。 +功能特性 +逼真的沙漏动画:模拟沙粒从上至下的流动效果。 +交互控制: +开始/重置:启动或重置沙漏计时。 +时间调整:增加或减少沙漏的总计时时间。 +模块化设计:UI界面与控制逻辑分离,便于维护和扩展。 +前提条件: +已搭建OpenVela开发环境(参考:[环境搭建](https://gitee.com/open-vela/docs/blob/dev/zh-cn/quickstart/Set_up_the_development_environment_zh-cn.md)) +已获取OpenVela源码(参考:[下载openvela源码](https://gitee.com/open-vela/docs/blob/dev/zh-cn/quickstart/Download_Vela_sources_zh-cn.md)) +快速开始 +1. 配置项目 +### 打开功能选项 +```bash +./build.sh vendor/openvela/boards/vela/configs/qemu-armeabi-v7a-ap/ distclean -j$(nproc) menuconfig +``` +在打开的配置界面中,进行如下操作: +按下 / 键,搜索 LVX_USE_DEMO_HOURGLASS。 +按回车键进入该配置项。 +按下空格键,将选项标记为 [*] (即 =y) 以启用该Demo。 +多次按 Q 键退出,并在提示是否保存时按 Y 键。 +注意:其他相关配置(如设备路径、任务优先级等)已预设好,无需修改,除非有特殊需求。 +2. 编译与构建 +在源码根目录下,依次执行以下命令: +### 构建 +```bash +./build.sh vendor/openvela/boards/vela/configs/qemu-armeabi-v7a-ap/ -j$(nproc) +``` + +### 清理构建产物 +```bash +./build.sh vendor/openvela/boards/vela/configs/qemu-armeabi-v7a-ap/ distclean -j$(nproc) +``` + +3. 运行Demo +### 启动模拟器 +```bash +./emulator.sh vela +``` + +### 启动计算器应用 +```bash +hourglass & +``` + +项目结构 +text +vela/packages/demos/hourglass/ +├── hourglass_page.c/h # UI界面绘制、按钮事件处理 +├── hourglass_control.c/h # 沙漏动画的核心控制逻辑与状态机 +├── hourglass_main.c # 程序入口,任务初始化 +├── Kconfig # 项目配置选项(使能、优先级、设备路径等) +├── Make.defs # 编译系统依赖项定义 +└── Makefile # 编译规则 +核心实现说明 +UI界面 +沙漏主体:由两个旋转45度的正方形容器构成,每个容器内动态创建8x8的红色小方块矩阵来模拟沙粒。 +控制面板:包含Start按钮和+/-按钮,用于控制沙漏运行和调整时间。 +控制逻辑 +状态机:管理沙漏的初始、运行、结束三种状态。 +定时器驱动:核心动画由LVGL定时器周期性触发。 +沙粒移动算法:使用对角线扫描与随机方向策略,每次移动一粒“沙粒”(即改变一个小方块的颜色),模拟自然下落。 +故障排除 +Demo未启动:请确认配置步骤中 LVX_USE_DEMO_HOURGLASS 已正确设置为 y。 +触摸无响应:检查 LVX_USE_DEMO_HOURGLASS_DEVPATH 配置的设备路径是否与系统实际设备节点匹配。 +编译错误:确保开发环境已正确搭建,并执行过彻底的 distclean。 + +--- + + + + + + + + + diff --git a/hourglass/hourglass_control.c b/hourglass/hourglass_control.c new file mode 100644 index 0000000..d1b97de --- /dev/null +++ b/hourglass/hourglass_control.c @@ -0,0 +1,265 @@ +#include "hourglass_control.h" + +#define ROWS 8 +#define CLOUMS 8 +#define MATRIX_SIZE 8 + +static void demo_start(lv_timer_t *timer); + +extern int time_value; +extern struct post1 (*matrix_top_ptr)[CLOUMS]; +extern struct post1 (*matrix_bottom_ptr)[CLOUMS]; + +lv_timer_t *ptimer = NULL; + +static void setLed(int addr, int row, int column, bool state) +{ + if(addr<0 || addr>=2) + return; + if(row<0 || row>7 || column<0 || column>7) + return; + + if (addr == 0) { + if (state) { + lv_obj_set_style_bg_color(matrix_top_ptr[row][column].obj, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN); + matrix_top_ptr[row][column].state = true; + } else { + lv_obj_set_style_bg_color(matrix_top_ptr[row][column].obj, lv_color_white(), LV_PART_MAIN); + matrix_top_ptr[row][column].state = false; + } + } else { + if (state) { + lv_obj_set_style_bg_color(matrix_bottom_ptr[row][column].obj, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN); + matrix_bottom_ptr[row][column].state = true; + LV_LOG_USER("%s %d, bottom led on x %d, y %d", __func__, __LINE__, row, column); + } else { + lv_obj_set_style_bg_color(matrix_bottom_ptr[row][column].obj, lv_color_white(), LV_PART_MAIN); + matrix_bottom_ptr[row][column].state = false; + } + } +} + +static bool getLed(int addr, int row, int column) +{ + if(addr<0 || addr>=2) + return false; + if(row<0 || row>7 || column<0 || column>7) + return false; + + if (addr == 0) { + return matrix_top_ptr[row][column].state; + } + + return matrix_bottom_ptr[row][column].state; +} + +static bool canGoLeft(int addr, int x, int y) +{ + if (x == 0) return false; // not available + + return !getLed(addr, x - 1, y); +} +static bool canGoRight(int addr, int x, int y) +{ + if (y == 7) return false; // not available + + return !getLed(addr, x, y + 1); +} +static bool canGoDown(int addr, int x, int y) +{ + if (y == 7) return false; // not available + if (x == 0) return false; // not available + if (!canGoLeft(addr, x, y)) return false; + if (!canGoRight(addr, x, y)) return false; + + return !getLed(addr, x - 1, y + 1); +} + +static void goDown(int addr, int x, int y) +{ + setLed(addr, x, y, false); + setLed(addr, x - 1, y + 1, true); +} + +static void goLeft(int addr, int x, int y) +{ + setLed(addr, x, y, false); + setLed(addr, x - 1, y, true); +} + +static void goRight(int addr, int x, int y) +{ + setLed(addr, x, y, false); + setLed(addr, x, y + 1, true); +} + +static void checking(int addr) +{ + if (!getLed(addr, 5, 2)) { + setLed(addr, 5, 2, true); + for (int i = 0; i < 100000; i++) { + } + LV_LOG_USER("%s %d, getLed is no", __func__, __LINE__); + setLed(addr, 5, 2, false); + } +} + +static bool moveParticle(int addr, int x, int y) +{ + bool can_GoLeft = canGoLeft(addr, x, y); + bool can_GoRight = canGoRight(addr, x, y); + + if (!can_GoLeft && !can_GoRight) { + return false; + } + + checking(addr); + + bool can_GoDown = canGoDown(addr, x, y); + if (can_GoDown) { + goDown(addr, x, y); + } else if (can_GoLeft && !can_GoRight) { + goLeft(addr, x, y); + } else if (can_GoRight && !can_GoLeft) { + goRight(addr, x, y); + } else if (random() % 2 == 1) { + goLeft(addr, x, y); + } else { + goRight(addr, x, y); + } + return true; +} + +static bool updateMatrix(void) +{ + int n = 8; + bool somethingMoved = false; + int x, y; + bool direction; + + for (int slice = 0; slice < 2 * n - 1; ++slice) { + direction = (random() % 2 == 1); + int z = slice < n ? 0 : slice - n + 1; + + for (int j = z; j <= slice - z; ++j) { + y = direction ? (7 - j) : (7 - (slice - j)); + x = direction ? (slice - j) : j; + if (moveParticle(1, x, y)) { + somethingMoved = true; + goto find; + } else { + LV_LOG_USER("%s %d, moveParticle 1 is no", __func__, __LINE__); + } + } + } + +find: + + return somethingMoved; +} + +// 重置沙漏 +void reset_sand_hourglass(void) +{ + if (ptimer) { + lv_timer_del(ptimer); + ptimer = NULL; + } + + // 重置上方矩阵 (全亮) + for (uint8_t y = 0; y < MATRIX_SIZE; y++) { + for (uint8_t x = 0; x < MATRIX_SIZE; x++) { + if ((x == 0 || x == 1) && (y == 6 || y == 7)) { + lv_obj_set_style_bg_color(matrix_top_ptr[y][x].obj, lv_color_white(), LV_PART_MAIN); + matrix_top_ptr[y][x].state = false; + } else { + lv_obj_set_style_bg_color(matrix_top_ptr[y][x].obj, lv_palette_main(LV_PALETTE_RED), 0); + matrix_top_ptr[y][x].state = true; + } + } + } + + // 重置下方矩阵 (全暗) + for (uint8_t y = 0; y < MATRIX_SIZE; y++) { + for (uint8_t x = 0; x < MATRIX_SIZE; x++) { + lv_obj_set_style_bg_color(matrix_bottom_ptr[y][x].obj, lv_color_white(), 0); + matrix_bottom_ptr[y][x].state = 0; + } + } + + // 重新创建定时器 + ptimer = lv_timer_create(demo_start, time_value * 1000, NULL); +} + +// 尝试移动沙粒(优化后的对角线扫描+随机方向) +static uint8_t move_sand_particle(uint8_t from_top) +{ + uint8_t something_moved = 0; + uint8_t x, y; + uint8_t direction; + LV_LOG_USER("%s %d", __func__, __LINE__); + + // 对角线扫描(类似您提供的算法) + for (uint8_t slice = 0; slice < 2 * ROWS - 1; ++slice) { + direction = (rand() % 2); // 随机扫描方向 + uint8_t z = slice < ROWS ? 0 : slice - ROWS + 1; + + for (uint8_t j = z; j <= slice - z; ++j) { + y = direction ? (ROWS - 1 - j) : (ROWS - 1 - (slice - j)); + x = direction ? (slice - j) : j; + + // 只在上矩阵寻找亮起的元素,或在下矩阵寻找暗的元素 + if ((from_top && matrix_top_ptr[y][x].state) || (!from_top && !matrix_bottom_ptr[y][x].state)) { + // 检查边界 + if (x >= 0 && x < ROWS && y >= 0 && y < ROWS) { + // 上矩阵:尝试移动亮起的元素 + if (from_top && matrix_top_ptr[y][x].state) { + matrix_top_ptr[y][x].state = 0; + lv_obj_set_style_bg_color(matrix_top_ptr[y][x].obj, lv_color_white(), 0); + something_moved = 1; + return something_moved; // 每次只移动一粒沙子 + } + // 下矩阵:尝试填充暗的元素 + else if (!from_top && !matrix_bottom_ptr[y][x].state) { + matrix_bottom_ptr[y][x].state = 1; + lv_obj_set_style_bg_color(matrix_bottom_ptr[y][x].obj, lv_palette_main(LV_PALETTE_RED), 0); + something_moved = 1; + return something_moved; // 每次只填充一粒沙子 + } + } + } + } + } + + return something_moved; +} + +static void demo_start(lv_timer_t *timer) +{ + LV_LOG_USER("%s %d, Clicked", __func__, __LINE__); + + // 1. 从上矩阵移动一粒沙子(随机对角线扫描) + uint8_t moved = move_sand_particle(1); + + // 2. 如果成功移除了沙子,在下矩阵添加一粒沙子 + if (moved) { + updateMatrix(); + } + + // 3. 检查上矩阵是否还有沙子 + uint8_t has_sand = 0; + for (uint8_t y = 0; y < ROWS && !has_sand; y++) { + for (uint8_t x = 0; x < ROWS && !has_sand; x++) { + if (matrix_top_ptr[y][x].state) has_sand = 1; + } + } + + if (!has_sand) { + if (ptimer) { + lv_timer_del(ptimer); + ptimer = NULL; + } + } +} + + diff --git a/hourglass/hourglass_control.h b/hourglass/hourglass_control.h new file mode 100644 index 0000000..3671e6e --- /dev/null +++ b/hourglass/hourglass_control.h @@ -0,0 +1,9 @@ +#ifndef HOURGLASS_CONTROL_H +#define HOURGLASS_CONTROL_H + +#include +#include +#include "lvgl.h" +#include "hourglass_page.h" + +#endif \ No newline at end of file diff --git a/hourglass/hourglass_main.c b/hourglass/hourglass_main.c new file mode 100644 index 0000000..593f49c --- /dev/null +++ b/hourglass/hourglass_main.c @@ -0,0 +1,171 @@ +/**************************************************************************** + * packages/demos/hourglass/hourglass_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#ifdef CONFIG_LV_USE_NUTTX_LIBUV +#include +#endif + +#include "hourglass_page.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Should we perform board-specific driver initialization? There are two + * ways that board initialization can occur: 1) automatically via + * board_late_initialize() during bootupif CONFIG_BOARD_LATE_INITIALIZE + * or 2). + * via a call to boardctl() if the interface is enabled + * (CONFIG_BOARDCTL=y). + * If this task is running as an NSH built-in application, then that + * initialization has probably already been performed otherwise we do it + * here. + */ + +#undef NEED_BOARDINIT + +#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_NSH_ARCHINIT) +# define NEED_BOARDINIT 1 +#endif + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void launcher_create(void) +{ + sandglass_page_create(); +} + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV +static void lv_nuttx_uv_loop(uv_loop_t *loop, lv_nuttx_result_t *result) +{ + lv_nuttx_uv_t uv_info; + void *data; + + uv_loop_init(loop); + + lv_memset(&uv_info, 0, sizeof(uv_info)); + uv_info.loop = loop; + uv_info.disp = result->disp; + uv_info.indev = result->indev; +#ifdef CONFIG_UINPUT_TOUCH + uv_info.uindev = result->utouch_indev; +#endif + + data = lv_nuttx_uv_init(&uv_info); + uv_run(loop, UV_RUN_DEFAULT); + lv_nuttx_uv_deinit(&data); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main or lv_demos_main + * + * Description: + * + * Input Parameters: + * Standard argc and argv + * + * Returned Value: + * Zero on success; a positive, non-zero value on failure. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + lv_nuttx_dsc_t info; + lv_nuttx_result_t result; + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV + uv_loop_t ui_loop; + lv_memzero(&ui_loop, sizeof(ui_loop)); +#endif + + if (lv_is_initialized()) { + LV_LOG_ERROR("LVGL already initialized! aborting."); + return -1; + } + +#ifdef NEED_BOARDINIT + /* Perform board-specific driver initialization */ + boardctl(BOARDIOC_INIT, 0); +#endif + + lv_init(); + lv_nuttx_dsc_init(&info); + +#ifdef CONFIG_LV_USE_NUTTX_LCD + info.fb_path = "/dev/lcd0"; +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN + info.input_path = CONFIG_LVX_USE_DEMO_HOURGLASS_DEVPATH; +#endif + + lv_nuttx_init(&info, &result); + + if (result.disp == NULL) { + LV_LOG_ERROR("lv_demos initialization failure!"); + return 1; + } + + launcher_create(); + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV + lv_nuttx_uv_loop(&ui_loop, &result); +#else + + while (1) { + uint32_t idle; + idle = lv_timer_handler(); + /* Minimum sleep of 1ms */ + idle = idle ? idle : 1; + usleep(idle * 1000); + } +#endif + + lv_nuttx_deinit(&result); + lv_deinit(); + + return 0; +} diff --git a/hourglass/hourglass_page.c b/hourglass/hourglass_page.c new file mode 100644 index 0000000..d1b2219 --- /dev/null +++ b/hourglass/hourglass_page.c @@ -0,0 +1,260 @@ +#include "hourglass_page.h" + +#define PART_WIDTH_HEIGHT 130 +#define CHILD_WIDTH_HEIGHT 16 +#define EXPAND_WIDTH 50 +#define PADDING_WIDTH 5 +#define ROWS_VALUE 8 +#define CLOUMS_VALUE 8 +#define DISP_ROTATION_SUPPORT 1 // 1横屏 0竖屏 + +extern void reset_sand_hourglass(void); +static void SetStyle_PartChildren(lv_style_t *style); +static void SetStyle_Part(lv_style_t *style); +static void Menu_Create( lv_obj_t *parent,lv_obj_t *obj); + +typedef enum { + LV_EVENT_USER_START = 100, +}lv_event_user_code_t; + +static void plus_event_cb(lv_event_t * e); +static void minus_event_cb(lv_event_t * e); +static void start_event_cb(lv_event_t * e); +static void excu_event_cb(lv_event_t * e); + +lv_obj_t *start_btn = NULL; +lv_obj_t *part_parent = NULL; +lv_obj_t *plus_label = NULL; +lv_obj_t *minus_label = NULL; +lv_obj_t *time_label = NULL; + +int time_value = 1; + +Post1_t (*matrix_top_ptr)[CLOUMS_VALUE] = NULL; +Post1_t (*matrix_bottom_ptr)[CLOUMS_VALUE] = NULL; +Post1_t matrix_top[ROWS_VALUE][CLOUMS_VALUE]; +Post1_t matrix_bottom[ROWS_VALUE][CLOUMS_VALUE]; + +void sandglass_page_create(void) +{ + lv_obj_t *menu = NULL; + + static lv_style_t part_style; + static lv_style_t children_style; + SetStyle_Part(&part_style); + SetStyle_PartChildren(&children_style); + lv_obj_t *screen = lv_screen_active(); + + // 创建父对象 + part_parent = lv_obj_create(screen); + lv_obj_clear_flag(part_parent, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_style_opa(part_parent, LV_OPA_COVER, LV_PART_MAIN); +#if DISP_ROTATION_SUPPORT + lv_obj_set_size(part_parent, 272,480); +#else + lv_obj_set_size(part_parent, 480,272); +#endif + lv_obj_align(part_parent, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_radius(part_parent, 30, LV_PART_MAIN); // 四角圆角半径150 + lv_obj_set_style_bg_color(part_parent, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_border_width(part_parent, 0, LV_PART_MAIN); // 父对象无边框 + lv_obj_add_event_cb(part_parent, excu_event_cb, (lv_event_code_t)LV_EVENT_USER_START, NULL); + + lv_obj_t *part_top = lv_obj_create(part_parent); + lv_obj_remove_style_all(part_top); + lv_obj_add_style(part_top, &part_style, LV_PART_MAIN); + lv_obj_set_style_transform_pivot_x(part_top, PART_WIDTH_HEIGHT / 2,LV_PART_MAIN); // 中心点 X 轴(百分比) + lv_obj_set_style_transform_pivot_y(part_top, PART_WIDTH_HEIGHT / 2,LV_PART_MAIN); // 中心点 Y 轴 + #if DISP_ROTATION_SUPPORT + lv_obj_align(part_top,LV_ALIGN_TOP_MID,0,20); + lv_obj_set_style_transform_rotation(part_top, -450,LV_PART_MAIN | LV_STATE_DEFAULT); // 旋转角度 +#else + lv_obj_align(part_top,LV_ALIGN_RIGHT_MID,-20,0); + lv_obj_set_style_transform_rotation(part_top, 450,LV_PART_MAIN | LV_STATE_DEFAULT); // 旋转角度 +#endif + + lv_obj_set_style_clip_corner(part_top,false,0); + lv_obj_clear_flag(part_top,LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_CLICKABLE); + + // 配置子对象统一样式 + // 生成子对象 + for (int i = 0; i < ROWS_VALUE ; i++) { + for (int j = 0; j < CLOUMS_VALUE; j++) { + lv_obj_t* child = lv_obj_create(part_top); + lv_obj_add_style(child, &children_style, LV_PART_MAIN); + lv_obj_set_pos(child,i*CHILD_WIDTH_HEIGHT,j*CHILD_WIDTH_HEIGHT); + + matrix_top[i][j].obj = child; + matrix_top[i][j].state = true; + if ((i == 6 || i == 7) && (j == 0 || j == 1)) { + lv_obj_set_style_bg_color(matrix_top[i][j].obj, lv_color_white(), LV_PART_MAIN); + matrix_top[i][j].state = false; + } + } + } + matrix_top_ptr = matrix_top; + + lv_obj_t *part_bottom = lv_obj_create(part_parent); + lv_obj_remove_style_all(part_bottom); + lv_obj_add_style(part_bottom, &part_style, LV_PART_MAIN); + lv_obj_set_style_transform_pivot_x(part_bottom, PART_WIDTH_HEIGHT / 2,LV_PART_MAIN); // 中心点 X 轴(百分比) + lv_obj_set_style_transform_pivot_y(part_bottom, PART_WIDTH_HEIGHT / 2,LV_PART_MAIN); // 中心点 Y 轴 +#if DISP_ROTATION_SUPPORT + lv_obj_align(part_bottom,LV_ALIGN_BOTTOM_MID,0,-110); + lv_obj_set_style_transform_rotation(part_bottom, -450,LV_PART_MAIN | LV_STATE_DEFAULT); // 旋转角度 +#else + lv_obj_align(part_bottom,LV_ALIGN_LEFT_MID,111,0); + lv_obj_set_style_transform_rotation(part_bottom, 450,LV_PART_MAIN | LV_STATE_DEFAULT); // 旋转角度 +#endif + lv_obj_set_style_clip_corner(part_bottom,false,0); + lv_obj_clear_flag(part_bottom,LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_CLICKABLE); + + // 生成子对象 + for (int i = 0; i < ROWS_VALUE; i++) { + for (int j = 0; j < CLOUMS_VALUE; j++) { + lv_obj_t* child = lv_obj_create(part_bottom); + lv_obj_add_style(child, &children_style, LV_PART_MAIN); + lv_obj_set_pos(child,i*CHILD_WIDTH_HEIGHT,j*CHILD_WIDTH_HEIGHT); + + matrix_bottom[i][j].obj = child; + lv_obj_set_style_bg_color(matrix_bottom[i][j].obj, lv_color_white(), LV_PART_MAIN); + matrix_bottom[i][j].state = false; + } + } + matrix_bottom_ptr = matrix_bottom; + Menu_Create(screen,menu); +} + +static void SetStyle_PartChildren(lv_style_t *style) +{ + lv_style_init(style); + lv_style_set_size(style, CHILD_WIDTH_HEIGHT,CHILD_WIDTH_HEIGHT); + lv_style_set_border_width(style, 1); + lv_style_set_border_color(style, lv_color_black()); + lv_style_set_border_opa(style, LV_OPA_20); + lv_style_set_bg_color(style, lv_palette_main(LV_PALETTE_RED)); + lv_style_set_bg_opa(style, LV_OPA_COVER); + lv_style_set_pad_all(style, 0); // 清除内边距[citation:3] + lv_style_set_radius(style,0); +} + +static void SetStyle_Part(lv_style_t *style) +{ + lv_style_init(style); + lv_style_set_pad_all(style,0); + lv_style_set_width(style,PART_WIDTH_HEIGHT); + lv_style_set_height(style,PART_WIDTH_HEIGHT); + lv_style_set_border_width(style, 1); + lv_style_set_border_color(style, lv_color_black()); + lv_style_set_radius(style,0); +} + +static void Menu_Create( lv_obj_t *parent,lv_obj_t *obj) +{ + obj = lv_obj_create(parent); + lv_obj_set_size(obj, 180, 52); + lv_obj_clear_flag(obj,LV_OBJ_FLAG_SCROLLABLE ); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xffffff), LV_PART_MAIN); +#if DISP_ROTATION_SUPPORT + lv_obj_align(obj,LV_ALIGN_BOTTOM_MID,0,-200); +#else + lv_obj_align(obj,LV_ALIGN_LEFT_MID,-30,0); + lv_obj_set_style_transform_pivot_x(obj, 90,LV_PART_MAIN); // 中心点 X 轴(百分比) + lv_obj_set_style_transform_pivot_y(obj, 25,LV_PART_MAIN); // 中心点 Y 轴 + lv_obj_set_style_transform_rotation(obj, 900,LV_PART_MAIN | LV_STATE_DEFAULT); // 旋转角度 +#endif + start_btn = lv_button_create(obj); + lv_obj_align(start_btn,LV_ALIGN_LEFT_MID,-10,0); + lv_obj_set_size(start_btn, 45, 25); + lv_obj_clear_flag(start_btn,LV_OBJ_FLAG_SCROLLABLE ); + + lv_obj_t * btn_label = lv_label_create(start_btn); + lv_obj_center(btn_label); + lv_label_set_text(btn_label, "Start"); + lv_obj_set_style_bg_color(btn_label, lv_color_hex(0xfe8d00), LV_PART_MAIN | LV_STATE_CHECKED); + + time_label = lv_label_create(obj); + lv_obj_align(time_label,LV_ALIGN_LEFT_MID,75,0); + lv_obj_set_size(time_label, 40,20); + lv_obj_set_style_radius(time_label, 5, LV_PART_MAIN); + lv_obj_set_style_border_width(time_label,1,LV_PART_MAIN); + lv_obj_set_style_border_color(time_label, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_border_opa(time_label, LV_OPA_COVER,LV_PART_MAIN); + lv_obj_set_style_text_font(time_label, &lv_font_montserrat_14,LV_PART_MAIN); + lv_obj_set_style_text_color(time_label, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_text_align(time_label, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); + char str[16] = {0}; + sprintf(str, "%d", time_value); + lv_label_set_text(time_label, str); + + lv_obj_t *plus = lv_button_create(obj); + lv_obj_align_to(plus,time_label,LV_ALIGN_OUT_LEFT_MID,15,0); + lv_obj_set_size(plus, 20, 20); + plus_label = lv_label_create(plus); + lv_obj_align(plus_label,LV_ALIGN_CENTER,0,0); + lv_label_set_text(plus_label, LV_SYMBOL_PLUS); + + lv_obj_t * minus = lv_button_create(obj); + lv_obj_align_to(minus,time_label,LV_ALIGN_OUT_RIGHT_MID,5,0); + lv_obj_set_size(minus, 20, 20); + + minus_label = lv_label_create(minus); + lv_obj_align(minus_label,LV_ALIGN_CENTER,0,0); + lv_label_set_text(minus_label, LV_SYMBOL_MINUS); + + lv_obj_add_event_cb(plus, plus_event_cb, LV_EVENT_SHORT_CLICKED, NULL); + lv_obj_add_event_cb(minus, minus_event_cb, LV_EVENT_SHORT_CLICKED, NULL); + lv_obj_add_event_cb(start_btn, start_event_cb, LV_EVENT_SHORT_CLICKED, NULL); +} + +static void plus_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + + if(code == LV_EVENT_SHORT_CLICKED) { + LV_LOG_USER("Clicked"); + if (time_value >= 20) { + time_value = 20; + } else { + time_value++; + } + char str[16] = {0}; + sprintf(str, "%d", time_value); + lv_label_set_text(time_label, str); + } +} + +static void minus_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + + if(code == LV_EVENT_SHORT_CLICKED) { + LV_LOG_USER("Clicked"); + if (time_value <= 1) { + time_value = 1; + } else { + time_value--; + } + char str[16] = {0}; + sprintf(str, "%d", time_value); + lv_label_set_text(time_label, str); + } +} + +static void start_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + if(code != LV_EVENT_SHORT_CLICKED ) { + return; + } + LV_LOG_USER("Clicked"); + reset_sand_hourglass(); +} + +static void excu_event_cb(lv_event_t * e) +{ + if(LV_EVENT_USER_START != (lv_event_user_code_t)lv_event_get_code(e)){ + return; + } +} + diff --git a/hourglass/hourglass_page.h b/hourglass/hourglass_page.h new file mode 100644 index 0000000..53d6a43 --- /dev/null +++ b/hourglass/hourglass_page.h @@ -0,0 +1,15 @@ +#ifndef HOURGLASS_PAGE_H +#define HOURGLASS_PAGE_H + +#include +#include +#include "lvgl.h" + +typedef struct post1 { + lv_obj_t *obj; + bool state; +}Post1_t; + +void sandglass_page_create(void); + +#endif \ No newline at end of file -- Gitee