#include "lvgl_task.h" #include "lvgl.h" #include "lv_port_disp.h" #include "lv_port_indev.h" #include "monitor_task.h" #include "work_task.h" #include "led_driver.h" #include "touch.h" #include "lcd.h" #include #define LVGL_TASK_PRIORITY (RT_THREAD_PRIORITY_MAX - 2) #define LVGL_TASK_STACK_SIZE 8192 extern Led led0, led1, led2, led3; rt_thread_t lvgl_thread = RT_NULL; static const lv_color_t COLOR_BG = lv_color_hex(0x0D1117); static const lv_color_t COLOR_CARD = lv_color_hex(0x161B22); static const lv_color_t COLOR_HEADER_BG = lv_color_hex(0x1C2541); static const lv_color_t COLOR_ACCENT = lv_color_hex(0x58A6FF); static const lv_color_t COLOR_GREEN = lv_color_hex(0x3FB950); static const lv_color_t COLOR_YELLOW = lv_color_hex(0xD29922); static const lv_color_t COLOR_RED = lv_color_hex(0xF85149); static const lv_color_t COLOR_TEXT = lv_color_hex(0xF0F6FC); static const lv_color_t COLOR_TEXT_DIM = lv_color_hex(0x8B949E); static const lv_color_t COLOR_BUTTON = lv_color_hex(0x21262D); static const lv_color_t COLOR_BTN_ACTIVE = lv_color_hex(0x2EA043); static lv_obj_t *uptime_label; static lv_obj_t *cpu_speed_label; static lv_obj_t *mem_bar; static lv_obj_t *mem_info_label; static lv_obj_t *mem_pct_label; static lv_obj_t *tick_label; static lv_obj_t *job_id_label; static lv_obj_t *work_bar; static lv_obj_t *work_status_label; static lv_obj_t *work_desc_label; static lv_obj_t *led_dots[4]; static lv_obj_t *touch_status_label; static lv_obj_t *frame_counter_label; static lv_obj_t *touch_coord_label; static lv_obj_t *led_buttons[4]; static lv_obj_t *led_btn_labels[4]; static lv_obj_t *start_job_btn; static lv_obj_t *start_job_label; static uint32_t frame_count = 0; static uint8_t led_states[4] = {0, 0, 0, 0}; static lv_obj_t *create_card(lv_obj_t *parent, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h) { lv_obj_t *card = lv_obj_create(parent); lv_obj_remove_style_all(card); lv_obj_set_style_bg_color(card, COLOR_CARD, 0); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, 0); lv_obj_set_style_radius(card, 8, 0); lv_obj_set_style_border_width(card, 1, 0); lv_obj_set_style_border_color(card, lv_color_hex(0x30363D), 0); lv_obj_set_style_pad_all(card, 10, 0); lv_obj_set_size(card, w, h); lv_obj_set_pos(card, x, y); return card; } static void create_card_title(lv_obj_t *card, const char *text) { lv_obj_t *title = lv_label_create(card); lv_label_set_text(title, text); lv_obj_set_style_text_color(title, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(title, &lv_font_montserrat_12, 0); lv_obj_align(title, LV_ALIGN_TOP_LEFT, 0, 0); } static void led_btn_cb(lv_event_t *e) { uint32_t idx = (uint32_t)(uintptr_t)lv_event_get_user_data(e); if (idx >= 4) return; Led *leds[] = {&led0, &led1, &led2, &led3}; leds[idx]->toggle(); led_states[idx] = !led_states[idx]; if (led_states[idx]) { lv_obj_set_style_bg_color(led_buttons[idx], COLOR_GREEN, 0); lv_obj_set_style_border_color(led_buttons[idx], lv_color_hex(0x2EA043), 0); lv_label_set_text(led_btn_labels[idx], "ON"); lv_obj_set_style_text_color(led_btn_labels[idx], COLOR_GREEN, 0); } else { lv_obj_set_style_bg_color(led_buttons[idx], COLOR_BUTTON, 0); lv_obj_set_style_border_color(led_buttons[idx], lv_color_hex(0x30363D), 0); lv_label_set_text(led_btn_labels[idx], "OFF"); lv_obj_set_style_text_color(led_btn_labels[idx], COLOR_TEXT_DIM, 0); } } static void start_job_cb(lv_event_t *e) { (void)e; static uint32_t job_counter = 0; job_counter++; work_task_send(job_counter); printf("[UI] START JOB #%lu dispatched\r\n", (unsigned long)job_counter); lv_obj_set_style_bg_color(start_job_btn, COLOR_GREEN, 0); lv_obj_set_style_border_color(start_job_btn, lv_color_hex(0x2EA043), 0); lv_label_set_text(start_job_label, "RUNNING"); lv_obj_set_style_text_color(start_job_label, COLOR_GREEN, 0); lv_label_set_text(work_desc_label, "Job started..."); } static lv_obj_t *create_led_btn(lv_obj_t *parent, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h, const char *text, lv_event_cb_t cb, uint32_t idx) { lv_obj_t *btn = lv_btn_create(parent); lv_obj_set_size(btn, w, h); lv_obj_set_pos(btn, x, y); lv_obj_set_style_bg_color(btn, COLOR_BUTTON, 0); lv_obj_set_style_bg_grad_color(btn, COLOR_BUTTON, 0); lv_obj_set_style_border_width(btn, 1, 0); lv_obj_set_style_border_color(btn, lv_color_hex(0x30363D), 0); lv_obj_set_style_radius(btn, 6, 0); lv_obj_set_style_shadow_width(btn, 0, 0); lv_obj_set_style_pad_all(btn, 0, 0); lv_obj_set_style_transform_width(btn, 0, LV_STATE_PRESSED); lv_obj_set_style_transform_height(btn, 0, LV_STATE_PRESSED); lv_obj_t *name = lv_label_create(btn); lv_label_set_text(name, text); lv_obj_set_style_text_color(name, COLOR_TEXT, 0); lv_obj_set_style_text_font(name, &lv_font_montserrat_12, 0); lv_obj_align(name, LV_ALIGN_TOP_MID, 0, 4); lv_obj_t *state = lv_label_create(btn); lv_label_set_text(state, "OFF"); lv_obj_set_style_text_color(state, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(state, &lv_font_montserrat_14, 0); lv_obj_align(state, LV_ALIGN_BOTTOM_MID, 0, -4); lv_obj_add_event_cb(btn, cb, LV_EVENT_SHORT_CLICKED, (void *)(uintptr_t)idx); return btn; } static void create_system_monitor_ui(void) { lv_obj_t *scr = lv_scr_act(); lv_obj_set_style_bg_color(scr, COLOR_BG, 0); lv_obj_set_style_bg_opa(scr, LV_OPA_COVER, 0); lv_obj_t *header = lv_obj_create(scr); lv_obj_remove_style_all(header); lv_obj_set_style_bg_color(header, COLOR_HEADER_BG, 0); lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0); lv_obj_set_style_radius(header, 0, 0); lv_obj_set_size(header, 480, 52); lv_obj_set_pos(header, 0, 0); lv_obj_t *title = lv_label_create(header); lv_label_set_text(title, "SYSTEM MONITOR"); lv_obj_set_style_text_color(title, COLOR_TEXT, 0); lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0); lv_obj_align(title, LV_ALIGN_CENTER, 0, -8); uptime_label = lv_label_create(header); lv_label_set_text(uptime_label, "Uptime: 00h 00m 00s"); lv_obj_set_style_text_color(uptime_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(uptime_label, &lv_font_montserrat_12, 0); lv_obj_align(uptime_label, LV_ALIGN_CENTER, 0, 14); lv_obj_t *cpu_card = create_card(scr, 10, 64, 222, 76); create_card_title(cpu_card, "CPU"); cpu_speed_label = lv_label_create(cpu_card); lv_label_set_text(cpu_speed_label, "168 MHz"); lv_obj_set_style_text_color(cpu_speed_label, COLOR_ACCENT, 0); lv_obj_set_style_text_font(cpu_speed_label, &lv_font_montserrat_24, 0); lv_obj_align(cpu_speed_label, LV_ALIGN_LEFT_MID, 0, 0); tick_label = lv_label_create(cpu_card); lv_label_set_text(tick_label, "Ticks: 0"); lv_obj_set_style_text_color(tick_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(tick_label, &lv_font_montserrat_12, 0); lv_obj_align(tick_label, LV_ALIGN_BOTTOM_LEFT, 0, -4); lv_obj_t *mem_card = create_card(scr, 242, 64, 228, 76); create_card_title(mem_card, "MEMORY"); mem_info_label = lv_label_create(mem_card); lv_label_set_text(mem_info_label, "Free: -- KB"); lv_obj_set_style_text_color(mem_info_label, COLOR_GREEN, 0); lv_obj_set_style_text_font(mem_info_label, &lv_font_montserrat_16, 0); lv_obj_align(mem_info_label, LV_ALIGN_LEFT_MID, 0, 0); mem_bar = lv_bar_create(mem_card); lv_obj_set_size(mem_bar, 208, 8); lv_obj_align(mem_bar, LV_ALIGN_BOTTOM_LEFT, 0, -8); lv_obj_set_style_bg_color(mem_bar, lv_color_hex(0x21262D), LV_PART_MAIN); lv_obj_set_style_bg_color(mem_bar, COLOR_GREEN, LV_PART_INDICATOR); lv_obj_set_style_radius(mem_bar, 4, LV_PART_MAIN); lv_obj_set_style_radius(mem_bar, 4, LV_PART_INDICATOR); lv_bar_set_range(mem_bar, 0, 100); lv_bar_set_value(mem_bar, 0, LV_ANIM_OFF); mem_pct_label = lv_label_create(mem_card); lv_label_set_text(mem_pct_label, "0%"); lv_obj_set_style_text_color(mem_pct_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(mem_pct_label, &lv_font_montserrat_12, 0); lv_obj_align(mem_pct_label, LV_ALIGN_RIGHT_MID, -4, 0); lv_obj_t *work_card = create_card(scr, 10, 152, 460, 90); create_card_title(work_card, "WORK TASK"); job_id_label = lv_label_create(work_card); lv_label_set_text(job_id_label, "Idle"); lv_obj_set_style_text_color(job_id_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(job_id_label, &lv_font_montserrat_14, 0); lv_obj_align(job_id_label, LV_ALIGN_TOP_LEFT, 0, 18); work_bar = lv_bar_create(work_card); lv_obj_set_size(work_bar, 440, 14); lv_obj_align(work_bar, LV_ALIGN_LEFT_MID, 0, 6); lv_obj_set_style_bg_color(work_bar, lv_color_hex(0x21262D), LV_PART_MAIN); lv_obj_set_style_bg_color(work_bar, COLOR_ACCENT, LV_PART_INDICATOR); lv_obj_set_style_radius(work_bar, 7, LV_PART_MAIN); lv_obj_set_style_radius(work_bar, 7, LV_PART_INDICATOR); lv_bar_set_range(work_bar, 0, 100); lv_bar_set_value(work_bar, 0, LV_ANIM_OFF); work_status_label = lv_label_create(work_card); lv_label_set_text(work_status_label, "Idle"); lv_obj_set_style_text_color(work_status_label, COLOR_TEXT, 0); lv_obj_set_style_text_font(work_status_label, &lv_font_montserrat_14, 0); lv_obj_align(work_status_label, LV_ALIGN_RIGHT_MID, 0, 6); work_desc_label = lv_label_create(work_card); lv_label_set_text(work_desc_label, "Tap START JOB below"); lv_obj_set_style_text_color(work_desc_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(work_desc_label, &lv_font_montserrat_12, 0); lv_obj_align(work_desc_label, LV_ALIGN_BOTTOM_LEFT, 0, -4); lv_obj_t *ctrl_card = create_card(scr, 10, 254, 460, 260); create_card_title(ctrl_card, "CONTROL"); lv_obj_t *led_hint = lv_label_create(ctrl_card); lv_label_set_text(led_hint, "LED Indicators"); lv_obj_set_style_text_color(led_hint, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(led_hint, &lv_font_montserrat_12, 0); lv_obj_align(led_hint, LV_ALIGN_TOP_LEFT, 0, 16); int dot_x = 0; for (int i = 0; i < 4; i++) { led_dots[i] = lv_obj_create(ctrl_card); lv_obj_remove_style_all(led_dots[i]); lv_obj_set_size(led_dots[i], 10, 10); lv_obj_set_pos(led_dots[i], dot_x, 36); lv_obj_set_style_bg_color(led_dots[i], lv_color_hex(0x21262D), 0); lv_obj_set_style_bg_opa(led_dots[i], LV_OPA_COVER, 0); lv_obj_set_style_radius(led_dots[i], 5, 0); lv_obj_t *ln = lv_label_create(ctrl_card); lv_label_set_text(ln, "OFF"); lv_obj_set_style_text_color(ln, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(ln, &lv_font_montserrat_12, 0); lv_obj_set_pos(ln, dot_x + 14, 34); dot_x += 110; } lv_obj_t *btn_hint = lv_label_create(ctrl_card); lv_label_set_text(btn_hint, "Toggle Switches"); lv_obj_set_style_text_color(btn_hint, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(btn_hint, &lv_font_montserrat_12, 0); lv_obj_align(btn_hint, LV_ALIGN_TOP_LEFT, 0, 56); const char *led_names[] = {"LED0", "LED1", "LED2", "LED3"}; int led_w = 100; int led_gap = 12; int btn_start_x = (460 - (4 * led_w + 3 * led_gap)) / 2; for (int i = 0; i < 4; i++) { int bx = btn_start_x + i * (led_w + led_gap); led_buttons[i] = create_led_btn(ctrl_card, bx, 74, led_w, 40, led_names[i], led_btn_cb, i); led_btn_labels[i] = lv_obj_get_child(led_buttons[i], 1); } lv_obj_t *sep = lv_obj_create(ctrl_card); lv_obj_remove_style_all(sep); lv_obj_set_size(sep, 440, 1); lv_obj_set_style_bg_color(sep, lv_color_hex(0x30363D), 0); lv_obj_set_style_bg_opa(sep, LV_OPA_COVER, 0); lv_obj_align(sep, LV_ALIGN_BOTTOM_LEFT, 0, -86); lv_obj_t *task_hint = lv_label_create(ctrl_card); lv_label_set_text(task_hint, "Task"); lv_obj_set_style_text_color(task_hint, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(task_hint, &lv_font_montserrat_12, 0); lv_obj_align(task_hint, LV_ALIGN_BOTTOM_LEFT, 0, -70); start_job_btn = lv_btn_create(ctrl_card); lv_obj_set_size(start_job_btn, 130, 44); lv_obj_align(start_job_btn, LV_ALIGN_BOTTOM_LEFT, 0, -16); lv_obj_set_style_bg_color(start_job_btn, COLOR_BUTTON, 0); lv_obj_set_style_border_width(start_job_btn, 1, 0); lv_obj_set_style_border_color(start_job_btn, lv_color_hex(0x30363D), 0); lv_obj_set_style_radius(start_job_btn, 6, 0); lv_obj_set_style_shadow_width(start_job_btn, 0, 0); lv_obj_set_style_transform_width(start_job_btn, 0, LV_STATE_PRESSED); lv_obj_set_style_transform_height(start_job_btn, 0, LV_STATE_PRESSED); start_job_label = lv_label_create(start_job_btn); lv_label_set_text(start_job_label, "START JOB"); lv_obj_set_style_text_color(start_job_label, COLOR_TEXT, 0); lv_obj_set_style_text_font(start_job_label, &lv_font_montserrat_14, 0); lv_obj_align(start_job_label, LV_ALIGN_CENTER, 0, 0); lv_obj_add_event_cb(start_job_btn, start_job_cb, LV_EVENT_SHORT_CLICKED, NULL); lv_obj_t *task_info = lv_label_create(ctrl_card); lv_label_set_text(task_info, "Sends async work job"); lv_obj_set_style_text_color(task_info, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(task_info, &lv_font_montserrat_12, 0); lv_obj_align(task_info, LV_ALIGN_BOTTOM_LEFT, 140, -16); lv_obj_t *touch_card = create_card(scr, 10, 526, 460, 50); create_card_title(touch_card, "TOUCH"); touch_status_label = lv_label_create(touch_card); lv_label_set_text(touch_status_label, "Touch: OK"); lv_obj_set_style_text_color(touch_status_label, COLOR_GREEN, 0); lv_obj_set_style_text_font(touch_status_label, &lv_font_montserrat_12, 0); lv_obj_align(touch_status_label, LV_ALIGN_LEFT_MID, 0, 6); touch_coord_label = lv_label_create(touch_card); lv_label_set_text(touch_coord_label, "(0, 0)"); lv_obj_set_style_text_color(touch_coord_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(touch_coord_label, &lv_font_montserrat_12, 0); lv_obj_align(touch_coord_label, LV_ALIGN_RIGHT_MID, -10, 6); lv_obj_t *ver_label = lv_label_create(touch_card); lv_label_set_text(ver_label, "LVGL v8.3"); lv_obj_set_style_text_color(ver_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(ver_label, &lv_font_montserrat_12, 0); lv_obj_align(ver_label, LV_ALIGN_CENTER, 0, 6); frame_counter_label = lv_label_create(scr); lv_label_set_text(frame_counter_label, "Frames: 0"); lv_obj_set_style_text_color(frame_counter_label, COLOR_TEXT_DIM, 0); lv_obj_set_style_text_font(frame_counter_label, &lv_font_montserrat_12, 0); lv_obj_align(frame_counter_label, LV_ALIGN_BOTTOM_MID, 0, -6); } static void update_ui(void) { uint32_t sec = g_system_uptime_sec; uint32_t h = sec / 3600; uint32_t m = (sec % 3600) / 60; uint32_t s = sec % 60; char buf[64]; snprintf(buf, sizeof(buf), "Uptime: %02luh %02lum %02lus", (unsigned long)h, (unsigned long)m, (unsigned long)s); lv_label_set_text(uptime_label, buf); uint32_t free_kb = g_free_heap_size / 1024; rt_size_t total_rt = 0; rt_memory_info(NULL, &total_rt, NULL); uint32_t total_kb = total_rt / 1024; snprintf(buf, sizeof(buf), "Free: %lu / %lu KB", (unsigned long)free_kb, (unsigned long)total_kb); lv_label_set_text(mem_info_label, buf); uint8_t pct = 0; if (total_kb > 0) { uint32_t used_kb = total_kb - (g_free_heap_size / 1024); pct = (uint8_t)((used_kb * 100) / total_kb); if (pct > 100) pct = 100; } lv_bar_set_value(mem_bar, pct, LV_ANIM_OFF); snprintf(buf, sizeof(buf), "%u%%", (unsigned int)pct); lv_label_set_text(mem_pct_label, buf); if (pct > 80) lv_obj_set_style_bg_color(mem_bar, COLOR_RED, LV_PART_INDICATOR); else if (pct > 60) lv_obj_set_style_bg_color(mem_bar, COLOR_YELLOW, LV_PART_INDICATOR); else lv_obj_set_style_bg_color(mem_bar, COLOR_GREEN, LV_PART_INDICATOR); snprintf(buf, sizeof(buf), "Ticks: %lu", (unsigned long)rt_tick_get()); lv_label_set_text(tick_label, buf); if (g_work_progress >= 0 && g_work_progress <= 100) { lv_bar_set_value(work_bar, g_work_progress, LV_ANIM_ON); snprintf(buf, sizeof(buf), "%d%%", g_work_progress); lv_label_set_text(work_status_label, buf); lv_obj_set_style_bg_color(work_bar, COLOR_ACCENT, LV_PART_INDICATOR); lv_label_set_text(work_desc_label, "Processing..."); lv_label_set_text(job_id_label, "Job active"); } else { lv_bar_set_value(work_bar, 0, LV_ANIM_OFF); lv_label_set_text(work_status_label, "Idle"); lv_obj_set_style_bg_color(work_bar, COLOR_TEXT_DIM, LV_PART_INDICATOR); lv_label_set_text(job_id_label, "Idle"); lv_label_set_text(work_desc_label, "Tap START JOB below"); } for (int i = 0; i < 4; i++) { if (led_states[i]) { lv_color_t cols[] = { lv_color_hex(0xFF4444), lv_color_hex(0x44FF44), lv_color_hex(0x4444FF), lv_color_hex(0xFFFF44) }; lv_obj_set_style_bg_color(led_dots[i], cols[i], 0); } else { lv_obj_set_style_bg_color(led_dots[i], lv_color_hex(0x21262D), 0); } } GT1151 &touch = GT1151::instance(); if (touch.isActive(0)) { snprintf(buf, sizeof(buf), "(%d, %d)", touch.x(0), touch.y(0)); lv_label_set_text(touch_coord_label, buf); lv_label_set_text(touch_status_label, "Touch: Touched"); lv_obj_set_style_text_color(touch_status_label, COLOR_ACCENT, 0); } else { lv_label_set_text(touch_status_label, "Touch: OK"); lv_obj_set_style_text_color(touch_status_label, COLOR_GREEN, 0); } if (g_work_progress < 0) { lv_obj_set_style_bg_color(start_job_btn, COLOR_BUTTON, 0); lv_obj_set_style_border_color(start_job_btn, lv_color_hex(0x30363D), 0); lv_label_set_text(start_job_label, "START JOB"); lv_obj_set_style_text_color(start_job_label, COLOR_TEXT, 0); } snprintf(buf, sizeof(buf), "Frames: %lu", (unsigned long)frame_count); lv_label_set_text(frame_counter_label, buf); } static void lvgl_work(void *parameter) { (void)parameter; printf("[LVGL] LCD diagnostic: fill screen RED...\r\n"); Lcd::instance().clear(Lcd::Color::RED); rt_thread_mdelay(500); printf("[LVGL] fill screen GREEN...\r\n"); Lcd::instance().clear(Lcd::Color::GREEN); rt_thread_mdelay(500); printf("[LVGL] fill screen BLUE...\r\n"); Lcd::instance().clear(Lcd::Color::BLUE); rt_thread_mdelay(500); printf("[LVGL] Clear to black before LVGL init...\r\n"); Lcd::instance().clear(0x0000); rt_thread_mdelay(200); printf("[LVGL] Initializing...\r\n"); lv_init(); RetCode ret = lv_port_disp_init(); printf("[LVGL] disp_init: %d\r\n", ret); ret = lv_port_indev_init(); printf("[LVGL] indev_init: %d\r\n", ret); printf("[LVGL] Creating UI...\r\n"); create_system_monitor_ui(); lv_obj_invalidate(lv_scr_act()); for (uint32_t i = 0; i < 50; i++) { lv_timer_handler(); rt_thread_mdelay(10); } printf("[LVGL] System Monitor running!\r\n"); uint32_t update_counter = 0; while (1) { lv_timer_handler(); lv_port_indev_feed(); frame_count++; update_counter++; if ((update_counter % 50) == 0) { update_ui(); lv_refr_now(NULL); } rt_thread_mdelay(5); } } RetCode lvgl_task_init(void) { lvgl_thread = rt_thread_create("lvgl", lvgl_work, RT_NULL, LVGL_TASK_STACK_SIZE, LVGL_TASK_PRIORITY, 10); if (lvgl_thread == RT_NULL) return RET_ERROR; rt_thread_startup(lvgl_thread); return RET_OK; }