#include "prts.h" #include "bsp_adc.h" #include "lvgl.h" #include "main.h" #include "prts_lvgl_port.h" #include "src/debugging/sysmon/lv_sysmon.h" #include #include #include #include #include #define PRTS_SCREEN_W 280 #define PRTS_SCREEN_H 240 #define PRTS_STATUS_H 30 #define PRTS_HINT_H 24 #define PRTS_CONTENT_H (PRTS_SCREEN_H - PRTS_STATUS_H - PRTS_HINT_H) #define PRTS_CARD_W 130 #define PRTS_CARD_H 140 #define PRTS_CARD_GAP 15 #define PRTS_CARD_PAD_HOR 75 #define PRTS_DOT_SIZE_NORMAL 10 #define PRTS_DOT_SIZE_ACTIVE 10 #define PRTS_MENU_CARD_COUNT 3U #define PRTS_STATUS_BRAND_X 18 #define PRTS_STATUS_BRAND_W 54 #define PRTS_STATUS_TITLE_W 130 #define PRTS_VALUE_PANEL_WIDTH 228 #define PRTS_VALUE_PANEL_HEIGHT 34 #define PRTS_VALUE_ACCENT_WIDTH 4 #define PRTS_VALUE_BG_COLOR 0x132530 #define PRTS_JOY_DEBOUNCE_MS 25U #define PRTS_JOY_LONG_PRESS_MS 700U #define PRTS_MONITOR_UPDATE_MS 150U #define PRTS_SCREEN_ANIM_MS 0U typedef enum { PRTS_PAGE_MAIN = 0, PRTS_PAGE_STATUS, PRTS_PAGE_MONITOR, PRTS_PAGE_ASSETS } PRTS_Page_t; typedef enum { PRTS_JOY_NONE = 0, PRTS_JOY_ENTER, PRTS_JOY_UP, PRTS_JOY_DOWN, PRTS_JOY_LEFT, PRTS_JOY_RIGHT } PRTS_JoyKey_t; typedef struct { const char *title; const char *subtitle; const char *hint; uint32_t color; PRTS_Page_t page; } PRTS_MenuCard_t; static const PRTS_MenuCard_t prts_menu_cards_data[PRTS_MENU_CARD_COUNT] = { {"状态", "温度 / 遥控", "STATUS", 0x40C4FF, PRTS_PAGE_STATUS}, {"监视", "摇杆 / 性能", "MONITOR", 0xFFD666, PRTS_PAGE_MONITOR}, {"资源", "字体 / 存储", "ASSETS", 0x25D366, PRTS_PAGE_ASSETS}, }; static bool prts_initialized = false; static bool prts_lvgl_initialized = false; static lv_group_t *prts_key_group = NULL; static lv_indev_t *prts_key_indev = NULL; static lv_style_t prts_style_card_normal; static lv_style_t prts_style_card_focused; static lv_style_t prts_style_title_text; static lv_style_transition_dsc_t prts_card_transition; static bool prts_styles_ready = false; static lv_obj_t *prts_main_screen = NULL; static lv_obj_t *prts_status_screen = NULL; static lv_obj_t *prts_monitor_screen = NULL; static lv_obj_t *prts_assets_screen = NULL; static lv_obj_t *prts_main_status_title = NULL; static lv_obj_t *prts_menu_cards[PRTS_MENU_CARD_COUNT] = {NULL}; static lv_obj_t *prts_menu_dots[PRTS_MENU_CARD_COUNT] = {NULL}; static lv_obj_t *prts_menu_status_value = NULL; static lv_obj_t *prts_menu_monitor_value = NULL; static lv_obj_t *prts_temperature_panel = NULL; static lv_obj_t *prts_temperature_label = NULL; static lv_obj_t *prts_temperature_accent = NULL; static lv_obj_t *prts_mode_panel = NULL; static lv_obj_t *prts_mode_label = NULL; static lv_obj_t *prts_mode_accent = NULL; static lv_obj_t *prts_status_bar = NULL; static lv_obj_t *prts_adc0_label = NULL; static lv_obj_t *prts_adc1_label = NULL; static lv_obj_t *prts_joy12_label = NULL; static lv_obj_t *prts_joy_key_label = NULL; static lv_obj_t *prts_joy_voltage_label = NULL; static uint8_t prts_current_card = 0U; static PRTS_Page_t prts_current_page = PRTS_PAGE_MAIN; static int16_t prts_last_temperature_tenths = INT16_MIN; static RobotMode_t prts_last_mode = (RobotMode_t) 0xFF; static uint32_t prts_last_monitor_update_ms = 0U; static const lv_font_t *prts_font(void) { return &prts_font_14; } static void prts_label_set_text_if_changed(lv_obj_t *label, const char *text) { const char *old_text; if (label == NULL || text == NULL) { return; } old_text = lv_label_get_text(label); if (old_text == NULL || strcmp(old_text, text) != 0) { lv_label_set_text(label, text); } } static int16_t prts_temperature_to_tenths(float temperature_c) { return (int16_t) ((temperature_c * 10.0f) + (temperature_c >= 0.0f ? 0.5f : -0.5f)); } static void prts_format_temperature(char *buf, size_t len, int16_t temperature_tenths) { uint16_t abs_temp; uint16_t integer; uint8_t decimal; if (buf == NULL || len == 0U) { return; } if (temperature_tenths < 0) { abs_temp = (uint16_t) (-temperature_tenths); integer = abs_temp / 10U; decimal = (uint8_t) (abs_temp % 10U); (void) snprintf(buf, len, "-%u.%u C", (unsigned int) integer, (unsigned int) decimal); } else { abs_temp = (uint16_t) temperature_tenths; integer = abs_temp / 10U; decimal = (uint8_t) (abs_temp % 10U); (void) snprintf(buf, len, "%u.%u C", (unsigned int) integer, (unsigned int) decimal); } } static const char *prts_robot_mode_text(RobotMode_t mode) { switch (mode) { case NORMAL_MODE: return "NORMAL"; case SYS_ERROR_OCCURRED: return "ERROR"; case REMOTE_NOT_CONNECTED: return "REMOTE OFF"; case REMOTE_NOT_READY: return "NOT READY"; case REMOTE_READY: return "READY"; case REMOTE_PROTECT: return "PROTECT"; case AUTO_SHOOTING_MODE: return "AUTO SHOOT"; case IMU_CALIBERATION_MODE: return "IMU CAL"; default: return "UNKNOWN"; } } static lv_color_t prts_robot_mode_color(RobotMode_t mode) { switch (mode) { case NORMAL_MODE: return lv_color_hex(0x25D366); case SYS_ERROR_OCCURRED: case REMOTE_NOT_CONNECTED: return lv_color_hex(0xFF4D4F); case REMOTE_NOT_READY: return lv_color_hex(0xEDEDED); case REMOTE_READY: return lv_color_hex(0xC678FF); case REMOTE_PROTECT: return lv_color_hex(0xFFD666); case AUTO_SHOOTING_MODE: return lv_color_hex(0x40C4FF); case IMU_CALIBERATION_MODE: return lv_color_hex(0xC678FF); default: return lv_color_hex(0xD9D9D9); } } static lv_color_t prts_robot_mode_text_color(RobotMode_t mode) { switch (mode) { case SYS_ERROR_OCCURRED: case REMOTE_NOT_CONNECTED: return lv_color_hex(0xFFE2E2); case REMOTE_NOT_READY: return lv_color_hex(0xFFFFFF); case REMOTE_READY: return lv_color_hex(0xF5E6FF); case REMOTE_PROTECT: return lv_color_hex(0xFFF3C4); default: return lv_color_hex(0xFFFFFF); } } static const char *prts_joy_key_text(PRTS_JoyKey_t key) { switch (key) { case PRTS_JOY_ENTER: return "ENTER"; case PRTS_JOY_UP: return "UP"; case PRTS_JOY_DOWN: return "DOWN"; case PRTS_JOY_LEFT: return "LEFT"; case PRTS_JOY_RIGHT: return "RIGHT"; case PRTS_JOY_NONE: default: return "NONE"; } } static PRTS_JoyKey_t prts_joy_from_adc12(uint16_t raw12) { if (raw12 > 3800U) { return PRTS_JOY_NONE; } if (raw12 < 200U) { return PRTS_JOY_ENTER; } if (raw12 > 600U && raw12 < 1050U) { return PRTS_JOY_DOWN; } if (raw12 > 1400U && raw12 < 1850U) { return PRTS_JOY_UP; } if (raw12 > 2200U && raw12 < 2700U) { return PRTS_JOY_LEFT; } if (raw12 > 3000U && raw12 < 3500U) { return PRTS_JOY_RIGHT; } return PRTS_JOY_NONE; } static void prts_keypad_read_cb(lv_indev_t *indev, lv_indev_data_t *data) { static PRTS_JoyKey_t candidate_key = PRTS_JOY_NONE; static PRTS_JoyKey_t stable_key = PRTS_JOY_NONE; static uint32_t candidate_since_ms = 0U; static uint32_t stable_since_ms = 0U; static uint32_t last_lv_key = 0U; uint32_t now_ms = HAL_GetTick(); PRTS_JoyKey_t raw_key = prts_joy_from_adc12(BSP_ADC_GetJoystickRaw12()); (void) indev; if (raw_key != candidate_key) { candidate_key = raw_key; candidate_since_ms = now_ms; } if (stable_key != candidate_key && (now_ms - candidate_since_ms) >= PRTS_JOY_DEBOUNCE_MS) { stable_key = candidate_key; stable_since_ms = now_ms; } if (stable_key == PRTS_JOY_NONE) { data->state = LV_INDEV_STATE_RELEASED; data->key = last_lv_key; return; } switch (stable_key) { case PRTS_JOY_ENTER: last_lv_key = LV_KEY_ENTER; break; case PRTS_JOY_UP: last_lv_key = LV_KEY_UP; break; case PRTS_JOY_DOWN: last_lv_key = LV_KEY_DOWN; break; case PRTS_JOY_LEFT: if (prts_current_page == PRTS_PAGE_MAIN) { last_lv_key = LV_KEY_PREV; } else { last_lv_key = ((now_ms - stable_since_ms) >= PRTS_JOY_LONG_PRESS_MS) ? LV_KEY_ESC : LV_KEY_LEFT; } break; case PRTS_JOY_RIGHT: last_lv_key = (prts_current_page == PRTS_PAGE_MAIN) ? LV_KEY_NEXT : LV_KEY_RIGHT; break; case PRTS_JOY_NONE: default: break; } data->state = LV_INDEV_STATE_PRESSED; data->key = last_lv_key; } static void prts_init_styles(void) { static const lv_style_prop_t card_transition_props[] = { LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR, LV_STYLE_BORDER_WIDTH, 0 }; if (prts_styles_ready) { return; } lv_style_transition_dsc_init(&prts_card_transition, card_transition_props, lv_anim_path_ease_out, 120, 0, NULL); lv_style_init(&prts_style_card_normal); lv_style_set_bg_color(&prts_style_card_normal, lv_color_hex(0x2C2C2E)); lv_style_set_radius(&prts_style_card_normal, 8); lv_style_set_border_width(&prts_style_card_normal, 0); lv_style_set_pad_all(&prts_style_card_normal, 10); lv_style_set_text_font(&prts_style_card_normal, prts_font()); lv_style_set_text_color(&prts_style_card_normal, lv_color_hex(0xEBEBF5)); lv_style_set_transform_width(&prts_style_card_normal, 0); lv_style_set_transform_height(&prts_style_card_normal, 0); lv_style_set_shadow_width(&prts_style_card_normal, 0); lv_style_set_shadow_opa(&prts_style_card_normal, LV_OPA_0); lv_style_set_transition(&prts_style_card_normal, &prts_card_transition); lv_style_init(&prts_style_card_focused); lv_style_set_bg_color(&prts_style_card_focused, lv_color_hex(0x3A3A3C)); lv_style_set_border_color(&prts_style_card_focused, lv_color_hex(0x0A84FF)); lv_style_set_border_width(&prts_style_card_focused, 2); lv_style_set_transform_width(&prts_style_card_focused, 0); lv_style_set_transform_height(&prts_style_card_focused, 0); lv_style_set_shadow_width(&prts_style_card_focused, 0); lv_style_set_shadow_color(&prts_style_card_focused, lv_color_hex(0x0A84FF)); lv_style_set_shadow_opa(&prts_style_card_focused, LV_OPA_0); lv_style_set_transition(&prts_style_card_focused, &prts_card_transition); lv_style_init(&prts_style_title_text); lv_style_set_text_color(&prts_style_title_text, lv_color_hex(0xEBEBF5)); lv_style_set_text_font(&prts_style_title_text, prts_font()); prts_styles_ready = true; } static void prts_create_keypad(void) { if (prts_key_indev != NULL) { return; } prts_key_indev = lv_indev_create(); lv_indev_set_type(prts_key_indev, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(prts_key_indev, prts_keypad_read_cb); prts_key_group = lv_group_create(); lv_indev_set_group(prts_key_indev, prts_key_group); lv_group_set_default(prts_key_group); } static void prts_style_label(lv_obj_t *label, lv_color_t color) { lv_obj_set_style_text_font(label, prts_font(), 0); lv_obj_set_style_text_color(label, color, 0); lv_obj_set_style_text_opa(label, LV_OPA_COVER, 0); lv_obj_set_style_bg_opa(label, LV_OPA_TRANSP, 0); } static lv_obj_t *prts_create_bar(lv_obj_t *parent, const char *hint) { lv_obj_t *bar = lv_obj_create(parent); lv_obj_t *label; lv_obj_set_size(bar, PRTS_SCREEN_W, PRTS_HINT_H); lv_obj_align(bar, LV_ALIGN_BOTTOM_MID, 0, 0); lv_obj_set_style_bg_color(bar, lv_color_hex(0x111111), 0); lv_obj_set_style_border_width(bar, 0, 0); lv_obj_set_style_radius(bar, 0, 0); lv_obj_set_style_pad_all(bar, 0, 0); lv_obj_remove_flag(bar, LV_OBJ_FLAG_SCROLLABLE); label = lv_label_create(bar); lv_label_set_text(label, hint); lv_obj_add_style(label, &prts_style_title_text, 0); lv_obj_center(label); return bar; } static lv_obj_t *prts_create_status_bar(lv_obj_t *parent, const char *title_text) { lv_obj_t *bar = lv_obj_create(parent); lv_obj_t *brand; lv_obj_t *title; lv_obj_set_size(bar, PRTS_SCREEN_W, PRTS_STATUS_H); lv_obj_align(bar, LV_ALIGN_TOP_MID, 0, 0); lv_obj_set_style_bg_color(bar, lv_color_hex(0x111111), 0); lv_obj_set_style_border_width(bar, 0, 0); lv_obj_set_style_radius(bar, 0, 0); lv_obj_set_style_pad_all(bar, 0, 0); lv_obj_remove_flag(bar, LV_OBJ_FLAG_SCROLLABLE); brand = lv_label_create(bar); lv_label_set_text(brand, "PRTS"); prts_style_label(brand, lv_color_hex(0x6EE7F9)); lv_obj_set_pos(brand, PRTS_STATUS_BRAND_X, 6); lv_obj_set_size(brand, PRTS_STATUS_BRAND_W, 18); lv_obj_set_style_text_align(brand, LV_TEXT_ALIGN_CENTER, 0); title = lv_label_create(bar); lv_label_set_text(title, title_text); prts_style_label(title, lv_color_hex(0xEBEBF5)); lv_obj_set_pos(title, (PRTS_SCREEN_W - PRTS_STATUS_TITLE_W) / 2, 6); lv_obj_set_size(title, PRTS_STATUS_TITLE_W, 18); lv_obj_set_style_text_align(title, LV_TEXT_ALIGN_CENTER, 0); if (parent == prts_main_screen) { prts_main_status_title = title; } return bar; } static lv_obj_t *prts_create_value_panel(lv_obj_t *parent, int32_t x, int32_t y) { lv_obj_t *panel = lv_obj_create(parent); lv_obj_remove_flag(panel, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(panel, x, y); lv_obj_set_size(panel, PRTS_VALUE_PANEL_WIDTH, PRTS_VALUE_PANEL_HEIGHT); lv_obj_set_style_radius(panel, 0, 0); lv_obj_set_style_border_width(panel, 0, 0); lv_obj_set_style_pad_all(panel, 0, 0); lv_obj_set_style_bg_color(panel, lv_color_hex(PRTS_VALUE_BG_COLOR), 0); lv_obj_set_style_bg_opa(panel, LV_OPA_COVER, 0); return panel; } static lv_obj_t *prts_create_value_accent(lv_obj_t *parent, lv_color_t color) { lv_obj_t *accent = lv_obj_create(parent); lv_obj_remove_flag(accent, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(accent, 0, 0); lv_obj_set_size(accent, PRTS_VALUE_ACCENT_WIDTH, PRTS_VALUE_PANEL_HEIGHT); lv_obj_set_style_radius(accent, 0, 0); lv_obj_set_style_border_width(accent, 0, 0); lv_obj_set_style_pad_all(accent, 0, 0); lv_obj_set_style_bg_color(accent, color, 0); lv_obj_set_style_bg_opa(accent, LV_OPA_COVER, 0); return accent; } static lv_obj_t *prts_create_card_text(lv_obj_t *parent, const char *text, int32_t y, lv_color_t color) { lv_obj_t *label = lv_label_create(parent); lv_label_set_text(label, text); lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_CLIP); prts_style_label(label, color); lv_obj_set_pos(label, 14, y); lv_obj_set_size(label, PRTS_CARD_W - 30, 20); return label; } static void prts_update_dots(void) { for (uint8_t i = 0U; i < PRTS_MENU_CARD_COUNT; i++) { bool active = (i == prts_current_card); lv_obj_set_size(prts_menu_dots[i], active ? PRTS_DOT_SIZE_ACTIVE : PRTS_DOT_SIZE_NORMAL, active ? PRTS_DOT_SIZE_ACTIVE : PRTS_DOT_SIZE_NORMAL); lv_obj_set_style_bg_color(prts_menu_dots[i], active ? lv_color_hex(0xFFFFFF) : lv_color_hex(0x888888), 0); lv_obj_set_style_radius(prts_menu_dots[i], LV_RADIUS_CIRCLE, 0); } if (prts_main_status_title != NULL) { prts_label_set_text_if_changed(prts_main_status_title, prts_menu_cards_data[prts_current_card].title); } } static void prts_focus_card(uint8_t index, lv_anim_enable_t anim) { if (index >= PRTS_MENU_CARD_COUNT) { return; } prts_current_card = index; if (prts_key_group != NULL) { lv_group_focus_obj(prts_menu_cards[index]); } lv_obj_scroll_to_view(prts_menu_cards[index], anim); prts_update_dots(); } static void prts_show_page(PRTS_Page_t page, lv_screen_load_anim_t anim) { lv_obj_t *screen = prts_main_screen; prts_current_page = page; if (prts_key_group != NULL) { lv_group_remove_all_objs(prts_key_group); } switch (page) { case PRTS_PAGE_STATUS: screen = prts_status_screen; if (prts_key_group != NULL) lv_group_add_obj(prts_key_group, prts_status_screen); break; case PRTS_PAGE_MONITOR: screen = prts_monitor_screen; if (prts_key_group != NULL) lv_group_add_obj(prts_key_group, prts_monitor_screen); break; case PRTS_PAGE_ASSETS: screen = prts_assets_screen; if (prts_key_group != NULL) lv_group_add_obj(prts_key_group, prts_assets_screen); break; case PRTS_PAGE_MAIN: default: screen = prts_main_screen; if (prts_key_group != NULL) { for (uint8_t i = 0U; i < PRTS_MENU_CARD_COUNT; i++) { lv_group_add_obj(prts_key_group, prts_menu_cards[i]); } } break; } if (anim == LV_SCREEN_LOAD_ANIM_NONE || PRTS_SCREEN_ANIM_MS == 0U) { lv_screen_load(screen); } else { lv_screen_load_anim(screen, anim, PRTS_SCREEN_ANIM_MS, 0, false); } if (page == PRTS_PAGE_MAIN) { prts_focus_card(prts_current_card, LV_ANIM_ON); } else if (prts_key_group != NULL) { lv_group_focus_obj(screen); } } static void prts_page_key_event_cb(lv_event_t *e) { if (lv_event_get_code(e) != LV_EVENT_KEY) { return; } if (lv_event_get_key(e) == LV_KEY_ESC) { prts_show_page(PRTS_PAGE_MAIN, LV_SCREEN_LOAD_ANIM_NONE); } } static void prts_card_event_cb(lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t *card = lv_event_get_target_obj(e); uint32_t index = (uint32_t) lv_event_get_user_data(e); if (index >= PRTS_MENU_CARD_COUNT) { return; } if (code == LV_EVENT_FOCUSED) { prts_current_card = (uint8_t) index; lv_obj_scroll_to_view(card, LV_ANIM_ON); prts_update_dots(); } else if (code == LV_EVENT_CLICKED) { prts_current_card = (uint8_t) index; prts_show_page(prts_menu_cards_data[index].page, LV_SCREEN_LOAD_ANIM_NONE); } } static void prts_create_main_screen(void) { lv_obj_t *banner; lv_obj_t *dot_row; prts_main_screen = lv_obj_create(NULL); lv_obj_set_style_bg_color(prts_main_screen, lv_color_hex(0x1A1A1A), 0); lv_obj_remove_flag(prts_main_screen, LV_OBJ_FLAG_SCROLLABLE); prts_create_status_bar(prts_main_screen, "主菜单"); prts_create_bar(prts_main_screen, "LEFT RIGHT ENTER"); banner = lv_obj_create(prts_main_screen); lv_obj_set_size(banner, PRTS_SCREEN_W, PRTS_CONTENT_H); lv_obj_align(banner, LV_ALIGN_TOP_MID, 0, PRTS_STATUS_H); lv_obj_set_style_bg_opa(banner, LV_OPA_TRANSP, 0); lv_obj_set_style_border_width(banner, 0, 0); lv_obj_set_style_pad_hor(banner, PRTS_CARD_PAD_HOR, 0); lv_obj_set_style_pad_column(banner, PRTS_CARD_GAP, 0); lv_obj_set_scrollbar_mode(banner, LV_SCROLLBAR_MODE_OFF); lv_obj_set_scroll_dir(banner, LV_DIR_HOR); lv_obj_set_scroll_snap_x(banner, LV_SCROLL_SNAP_CENTER); lv_obj_set_flex_flow(banner, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(banner, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START); for (uint8_t i = 0U; i < PRTS_MENU_CARD_COUNT; i++) { lv_obj_t *card = lv_button_create(banner); lv_obj_t *accent; lv_obj_set_size(card, PRTS_CARD_W, PRTS_CARD_H); lv_obj_add_style(card, &prts_style_card_normal, 0); lv_obj_add_style(card, &prts_style_card_focused, LV_STATE_FOCUSED); lv_obj_remove_flag(card, LV_OBJ_FLAG_SCROLL_ON_FOCUS); lv_obj_add_event_cb(card, prts_card_event_cb, LV_EVENT_FOCUSED, (void *) (uintptr_t) i); lv_obj_add_event_cb(card, prts_card_event_cb, LV_EVENT_CLICKED, (void *) (uintptr_t) i); accent = lv_obj_create(card); lv_obj_remove_flag(accent, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(accent, 0, 0); lv_obj_set_size(accent, 4, PRTS_CARD_H - 20); lv_obj_set_style_radius(accent, 0, 0); lv_obj_set_style_border_width(accent, 0, 0); lv_obj_set_style_pad_all(accent, 0, 0); lv_obj_set_style_bg_color(accent, lv_color_hex(prts_menu_cards_data[i].color), 0); lv_obj_set_style_bg_opa(accent, LV_OPA_COVER, 0); (void) prts_create_card_text(card, prts_menu_cards_data[i].hint, 8, lv_color_hex(prts_menu_cards_data[i].color)); (void) prts_create_card_text(card, prts_menu_cards_data[i].title, 42, lv_color_hex(0xFFFFFF)); (void) prts_create_card_text(card, prts_menu_cards_data[i].subtitle, 70, lv_color_hex(0xD1D1D6)); if (i == 0U) { prts_menu_status_value = prts_create_card_text(card, "UNKNOWN", 108, lv_color_hex(0xD9D9D9)); } else if (i == 1U) { prts_menu_monitor_value = prts_create_card_text(card, "ADC --", 108, lv_color_hex(0xFFD666)); } prts_menu_cards[i] = card; } dot_row = lv_obj_create(prts_main_screen); lv_obj_remove_style_all(dot_row); lv_obj_set_size(dot_row, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_flex_flow(dot_row, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(dot_row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_align(dot_row, LV_ALIGN_BOTTOM_MID, 0, -PRTS_HINT_H - 2); for (uint8_t i = 0U; i < PRTS_MENU_CARD_COUNT; i++) { prts_menu_dots[i] = lv_obj_create(dot_row); lv_obj_remove_style_all(prts_menu_dots[i]); lv_obj_set_size(prts_menu_dots[i], PRTS_DOT_SIZE_NORMAL, PRTS_DOT_SIZE_NORMAL); lv_obj_set_style_radius(prts_menu_dots[i], LV_RADIUS_CIRCLE, 0); lv_obj_set_style_bg_color(prts_menu_dots[i], lv_color_hex(0x888888), 0); lv_obj_set_style_bg_opa(prts_menu_dots[i], LV_OPA_COVER, 0); if (i > 0U) { lv_obj_set_style_margin_left(prts_menu_dots[i], 10, 0); } } prts_update_dots(); } static lv_obj_t *prts_create_card(lv_obj_t *parent, int32_t x, int32_t y, int32_t w, int32_t h) { lv_obj_t *card = lv_obj_create(parent); lv_obj_remove_flag(card, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(card, x, y); lv_obj_set_size(card, w, h); lv_obj_set_style_radius(card, 6, 0); lv_obj_set_style_border_width(card, 0, 0); lv_obj_set_style_bg_color(card, lv_color_hex(0x20262D), 0); lv_obj_set_style_bg_opa(card, LV_OPA_COVER, 0); lv_obj_set_style_pad_all(card, 10, 0); return card; } static void prts_create_status_screen(void) { lv_obj_t *temp_card; lv_obj_t *mode_card; lv_obj_t *caption; prts_status_screen = lv_obj_create(NULL); lv_obj_set_style_bg_color(prts_status_screen, lv_color_hex(0x1A1A1A), 0); lv_obj_remove_flag(prts_status_screen, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_event_cb(prts_status_screen, prts_page_key_event_cb, LV_EVENT_KEY, NULL); prts_create_status_bar(prts_status_screen, "状态详情"); prts_create_bar(prts_status_screen, "HOLD LEFT 返回"); temp_card = prts_create_card(prts_status_screen, 16, 48, 248, 62); caption = lv_label_create(temp_card); lv_label_set_text(caption, "TEMPERATURE"); prts_style_label(caption, lv_color_hex(0xFFD666)); lv_obj_set_pos(caption, 0, 0); prts_temperature_panel = prts_create_value_panel(temp_card, 0, 23); prts_temperature_accent = prts_create_value_accent(prts_temperature_panel, lv_color_hex(0x40C4FF)); prts_temperature_label = lv_label_create(prts_temperature_panel); lv_label_set_text(prts_temperature_label, "--.- C"); prts_style_label(prts_temperature_label, lv_color_hex(0xFFFFFF)); lv_obj_set_pos(prts_temperature_label, 12, 7); lv_obj_set_size(prts_temperature_label, 210, 18); mode_card = prts_create_card(prts_status_screen, 16, 124, 248, 62); caption = lv_label_create(mode_card); lv_label_set_text(caption, "CURRENT STATE"); prts_style_label(caption, lv_color_hex(0xFFD666)); lv_obj_set_pos(caption, 0, 0); prts_mode_panel = prts_create_value_panel(mode_card, 0, 23); prts_mode_accent = prts_create_value_accent(prts_mode_panel, lv_color_hex(0xD9D9D9)); prts_mode_label = lv_label_create(prts_mode_panel); lv_label_set_text(prts_mode_label, "UNKNOWN"); prts_style_label(prts_mode_label, lv_color_hex(0xD9D9D9)); lv_obj_set_pos(prts_mode_label, 12, 7); lv_obj_set_size(prts_mode_label, 210, 18); prts_status_bar = lv_obj_create(prts_status_screen); lv_obj_remove_flag(prts_status_bar, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_pos(prts_status_bar, 222, 11); lv_obj_set_size(prts_status_bar, 46, 8); lv_obj_set_style_radius(prts_status_bar, 2, 0); lv_obj_set_style_border_width(prts_status_bar, 0, 0); lv_obj_set_style_bg_color(prts_status_bar, lv_color_hex(0xD9D9D9), 0); lv_obj_set_style_bg_opa(prts_status_bar, LV_OPA_COVER, 0); } static lv_obj_t *prts_create_metric_label(lv_obj_t *parent, const char *text, int32_t x, int32_t y) { lv_obj_t *label = lv_label_create(parent); lv_label_set_text(label, text); lv_label_set_long_mode(label, LV_LABEL_LONG_MODE_CLIP); prts_style_label(label, lv_color_hex(0xEBEBF5)); lv_obj_set_pos(label, x, y); lv_obj_set_size(label, 112, 18); return label; } static void prts_create_monitor_screen(void) { lv_obj_t *card; lv_obj_t *title; prts_monitor_screen = lv_obj_create(NULL); lv_obj_set_style_bg_color(prts_monitor_screen, lv_color_hex(0x1A1A1A), 0); lv_obj_remove_flag(prts_monitor_screen, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_event_cb(prts_monitor_screen, prts_page_key_event_cb, LV_EVENT_KEY, NULL); prts_create_status_bar(prts_monitor_screen, "监视器"); prts_create_bar(prts_monitor_screen, "HOLD LEFT 返回"); card = prts_create_card(prts_monitor_screen, 16, 46, 248, 142); title = lv_label_create(card); lv_label_set_text(title, "JOYSTICK ADC"); prts_style_label(title, lv_color_hex(0x6EE7F9)); lv_obj_set_pos(title, 0, 0); prts_adc0_label = prts_create_metric_label(card, "PC4 0", 0, 32); prts_adc1_label = prts_create_metric_label(card, "PA5 0", 118, 32); prts_joy12_label = prts_create_metric_label(card, "12b 0", 0, 66); prts_joy_key_label = prts_create_metric_label(card, "KEY NONE", 118, 66); prts_joy_voltage_label = prts_create_metric_label(card, "JOY 0.000 V", 0, 100); lv_obj_set_size(prts_joy_voltage_label, 224, 18); } static void prts_create_assets_screen(void) { lv_obj_t *card; lv_obj_t *label; prts_assets_screen = lv_obj_create(NULL); lv_obj_set_style_bg_color(prts_assets_screen, lv_color_hex(0x1A1A1A), 0); lv_obj_remove_flag(prts_assets_screen, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_event_cb(prts_assets_screen, prts_page_key_event_cb, LV_EVENT_KEY, NULL); prts_create_status_bar(prts_assets_screen, "资源"); prts_create_bar(prts_assets_screen, "HOLD LEFT 返回"); card = prts_create_card(prts_assets_screen, 16, 46, 248, 142); label = lv_label_create(card); lv_label_set_text(label, "字体: PRTS 子集字库\n位置: 内部 Flash\n外置: W25Q64 接口准备\nOSPI: CubeMX 未生成"); prts_style_label(label, lv_color_hex(0xEBEBF5)); lv_obj_set_pos(label, 0, 0); lv_obj_set_size(label, 228, 120); lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); } static void prts_create_screens(void) { prts_create_main_screen(); prts_create_status_screen(); prts_create_monitor_screen(); prts_create_assets_screen(); prts_show_page(PRTS_PAGE_MAIN, LV_SCREEN_LOAD_ANIM_NONE); } bool PRTS_Init(void) { if (prts_initialized) { return true; } if (!prts_lvgl_initialized) { lv_init(); prts_lvgl_initialized = true; } if (!PRTS_LVGLPortInit()) { return false; } BSP_ADC_Init(); prts_init_styles(); prts_create_keypad(); prts_create_screens(); lv_sysmon_show_performance(lv_display_get_default()); prts_initialized = true; return true; } void PRTS_UpdateStatus(float temperature_c, RobotMode_t mode) { int16_t temperature_tenths; lv_color_t mode_color; char temperature_text[16]; if (!prts_initialized) { return; } temperature_tenths = prts_temperature_to_tenths(temperature_c); if (temperature_tenths != prts_last_temperature_tenths) { bool temperature_warning = temperature_tenths >= 550; prts_format_temperature(temperature_text, sizeof(temperature_text), temperature_tenths); lv_label_set_text(prts_temperature_label, temperature_text); lv_obj_set_style_bg_color(prts_temperature_accent, temperature_warning ? lv_color_hex(0xFF4D4F) : lv_color_hex(0x40C4FF), 0); lv_obj_set_style_text_color(prts_temperature_label, temperature_warning ? lv_color_hex(0xFFE2E2) : lv_color_hex(0xFFFFFF), 0); prts_last_temperature_tenths = temperature_tenths; } if (mode != prts_last_mode) { mode_color = prts_robot_mode_color(mode); prts_label_set_text_if_changed(prts_mode_label, prts_robot_mode_text(mode)); lv_obj_set_style_text_color(prts_mode_label, prts_robot_mode_text_color(mode), 0); lv_obj_set_style_bg_color(prts_mode_accent, mode_color, 0); lv_obj_set_style_bg_color(prts_status_bar, mode_color, 0); prts_label_set_text_if_changed(prts_menu_status_value, prts_robot_mode_text(mode)); lv_obj_set_style_text_color(prts_menu_status_value, mode_color, 0); prts_last_mode = mode; } } static void prts_update_monitor(uint32_t now_ms) { uint16_t adc0; uint16_t adc1; uint16_t raw12; PRTS_JoyKey_t key; char text[32]; if ((now_ms - prts_last_monitor_update_ms) < PRTS_MONITOR_UPDATE_MS) { return; } if (prts_current_page != PRTS_PAGE_MAIN && prts_current_page != PRTS_PAGE_MONITOR) { prts_last_monitor_update_ms = now_ms; return; } prts_last_monitor_update_ms = now_ms; adc0 = BSP_ADC_GetRaw(BSP_ADC_BOARD_VOLTAGE_INDEX); adc1 = BSP_ADC_GetJoystickRaw(); raw12 = (uint16_t) (adc1 >> 4U); key = prts_joy_from_adc12(raw12); (void) snprintf(text, sizeof(text), "PC4 %5u", (unsigned int) adc0); prts_label_set_text_if_changed(prts_adc0_label, text); (void) snprintf(text, sizeof(text), "PA5 %5u", (unsigned int) adc1); prts_label_set_text_if_changed(prts_adc1_label, text); (void) snprintf(text, sizeof(text), "12b %4u", (unsigned int) raw12); prts_label_set_text_if_changed(prts_joy12_label, text); (void) snprintf(text, sizeof(text), "KEY %s", prts_joy_key_text(key)); prts_label_set_text_if_changed(prts_joy_key_label, text); (void) snprintf(text, sizeof(text), "JOY %u.%03u V", (unsigned int) ((((uint32_t) adc1) * 3300U / 65535U) / 1000U), (unsigned int) ((((uint32_t) adc1) * 3300U / 65535U) % 1000U)); prts_label_set_text_if_changed(prts_joy_voltage_label, text); (void) snprintf(text, sizeof(text), "PA5 %4u", (unsigned int) raw12); prts_label_set_text_if_changed(prts_menu_monitor_value, text); } uint32_t PRTS_Task(void) { uint32_t next_ms; if (!prts_initialized) { return 10U; } prts_update_monitor(HAL_GetTick()); next_ms = lv_timer_handler(); if (next_ms == 0U) { next_ms = 1U; } if (next_ms > 20U) { next_ms = 20U; } return next_ms; }