feat: speed up PRTS LCD refresh

This commit is contained in:
TuxMonkey
2026-07-20 23:34:13 +08:00
parent b2169513e1
commit 8671f84139
14 changed files with 2296 additions and 98 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -69,7 +69,7 @@
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
/** Size of memory available for `lv_malloc()` in bytes (>= 2kB) */
#define LV_MEM_SIZE (32 * 1024U) /**< [bytes] */
#define LV_MEM_SIZE (80 * 1024U) /**< [bytes] */
/** Size of the memory expand for `lv_malloc()` in bytes */
#define LV_MEM_POOL_EXPAND_SIZE 0
@@ -107,7 +107,7 @@
* - LV_OS_MQX
* - LV_OS_SDL2
* - LV_OS_CUSTOM */
#define LV_USE_OS LV_OS_NONE
#define LV_USE_OS LV_OS_FREERTOS
#if LV_USE_OS == LV_OS_CUSTOM
#define LV_OS_CUSTOM_INCLUDE <stdint.h>
@@ -203,7 +203,7 @@
/**
* - 0: Use a simple renderer capable of drawing only simple rectangles with gradient, images, text, 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 0
#define LV_DRAW_SW_COMPLEX 1
#if LV_DRAW_SW_COMPLEX == 1
/** Allow buffering some shadow calculation.
@@ -691,10 +691,10 @@
* #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)
* @endcode
*/
#define LV_FONT_CUSTOM_DECLARE
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(prts_font_14)
/** Always set a default font */
#define LV_FONT_DEFAULT &lv_font_montserrat_14
#define LV_FONT_DEFAULT &prts_font_14
/** Enable handling large font and/or fonts with a lot of characters.
* The limit depends on the font size, font face and bpp.
@@ -1096,7 +1096,7 @@
#define LV_USE_SNAPSHOT 0
/** 1: Enable system monitor component */
#define LV_USE_SYSMON 0
#define LV_USE_SYSMON 1
#if LV_USE_SYSMON
/** Get the idle percentage. E.g. uint32_t my_get_idle(void); */
#define LV_SYSMON_GET_IDLE lv_os_get_idle_percent

View File

@@ -7,11 +7,12 @@
#include <stddef.h>
#include <stdint.h>
#define PRTS_LVGL_BUF_LINES 40U
#define PRTS_LVGL_BUF_LINES 64U
#define PRTS_DMA_BUFFER_ATTR __attribute__((section(".dma_buffer"), aligned(32)))
static lv_display_t *prts_display = NULL;
static uint8_t prts_lvgl_buf[TFT_LCD_W * PRTS_LVGL_BUF_LINES * 2U] PRTS_DMA_BUFFER_ATTR;
static volatile uint32_t prts_lvgl_frame_count = 0U;
static uint32_t prts_lvgl_tick_get(void)
{
@@ -30,6 +31,10 @@ static void prts_lvgl_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *
if (area->x2 < 0 || area->y2 < 0 || area->x1 >= (int32_t) TFT_LCD_W || area->y1 >= (int32_t) TFT_LCD_H)
{
if (lv_display_flush_is_last(disp))
{
prts_lvgl_frame_count++;
}
lv_display_flush_ready(disp);
return;
}
@@ -38,6 +43,10 @@ static void prts_lvgl_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *
area_height = lv_area_get_height(area);
if (area_width <= 0 || area_height <= 0)
{
if (lv_display_flush_is_last(disp))
{
prts_lvgl_frame_count++;
}
lv_display_flush_ready(disp);
return;
}
@@ -72,9 +81,18 @@ static void prts_lvgl_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *
}
}
if (lv_display_flush_is_last(disp))
{
prts_lvgl_frame_count++;
}
lv_display_flush_ready(disp);
}
uint32_t PRTS_LVGLPortGetFrameCount(void)
{
return prts_lvgl_frame_count;
}
bool PRTS_LVGLPortInit(void)
{
if (prts_display != NULL)

View File

@@ -2,12 +2,14 @@
#define PRTS_LVGL_PORT_H
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
bool PRTS_LVGLPortInit(void);
uint32_t PRTS_LVGLPortGetFrameCount(void);
#ifdef __cplusplus
}

View File

@@ -1,23 +1,96 @@
#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 <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define PRTS_CARD_BG_COLOR 0x0E1B24
#define PRTS_VALUE_BG_COLOR 0x132530
#define PRTS_VALUE_PANEL_WIDTH 228
#define PRTS_VALUE_PANEL_HEIGHT 34
#define PRTS_VALUE_LABEL_WIDTH 210
#define PRTS_VALUE_LABEL_HEIGHT 28
#define PRTS_VALUE_LABEL_X 12
#define PRTS_VALUE_LABEL_Y 4
#define PRTS_VALUE_ACCENT_WIDTH 4
#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 8
#define PRTS_DOT_SIZE_ACTIVE 12
#define PRTS_MENU_CARD_COUNT 3U
#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 140U
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;
@@ -25,8 +98,39 @@ 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)
{
@@ -115,10 +219,250 @@ static lv_color_t prts_robot_mode_text_color(RobotMode_t mode)
}
}
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,
LV_STYLE_TRANSFORM_WIDTH,
LV_STYLE_TRANSFORM_HEIGHT,
LV_STYLE_SHADOW_OPA,
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, 4);
lv_style_set_transform_height(&prts_style_card_focused, 3);
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_bg_opa(label, LV_OPA_TRANSP, 0);
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, 10, 6);
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, 74, 6);
lv_obj_set_size(title, 130, 18);
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)
@@ -149,16 +493,232 @@ static lv_obj_t *prts_create_value_accent(lv_obj_t *parent, lv_color_t color)
return accent;
}
static void prts_style_value_label(lv_obj_t *label, lv_color_t color)
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);
lv_obj_set_size(label, PRTS_VALUE_LABEL_WIDTH, PRTS_VALUE_LABEL_HEIGHT);
lv_obj_set_style_radius(label, 0, 0);
lv_obj_set_style_border_width(label, 0, 0);
lv_obj_set_style_pad_all(label, 0, 0);
lv_obj_set_style_bg_opa(label, LV_OPA_TRANSP, 0);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_style_text_color(label, color, 0);
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_OVER_RIGHT);
}
}
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_OVER_LEFT);
}
}
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)
@@ -167,10 +727,9 @@ static lv_obj_t *prts_create_card(lv_obj_t *parent, int32_t x, int32_t y, int32_
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, 4, 0);
lv_obj_set_style_border_width(card, 1, 0);
lv_obj_set_style_border_color(card, lv_color_hex(0x27475A), 0);
lv_obj_set_style_bg_color(card, lv_color_hex(PRTS_CARD_BG_COLOR), 0);
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;
@@ -178,64 +737,123 @@ static lv_obj_t *prts_create_card(lv_obj_t *parent, int32_t x, int32_t y, int32_
static void prts_create_status_screen(void)
{
lv_obj_t *screen = lv_obj_create(NULL);
lv_obj_t *title;
lv_obj_t *subtitle;
lv_obj_t *temp_card;
lv_obj_t *mode_card;
lv_obj_t *caption;
lv_obj_remove_flag(screen, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(screen, lv_color_hex(0x071016), 0);
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
lv_obj_set_style_pad_all(screen, 0, 0);
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);
title = lv_label_create(screen);
lv_label_set_text(title, "PRTS");
prts_style_label(title, lv_color_hex(0x6EE7F9));
lv_obj_set_pos(title, 16, 12);
prts_create_status_bar(prts_status_screen, "状态详情");
prts_create_bar(prts_status_screen, "HOLD LEFT 返回");
subtitle = lv_label_create(screen);
lv_label_set_text(subtitle, "PORTABLE RM TELEMETRY");
prts_style_label(subtitle, lv_color_hex(0xB5C8D5));
lv_obj_set_pos(subtitle, 16, 34);
prts_status_bar = lv_obj_create(screen);
lv_obj_remove_flag(prts_status_bar, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_pos(prts_status_bar, 190, 16);
lv_obj_set_size(prts_status_bar, 74, 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);
temp_card = prts_create_card(screen, 16, 62, 248, 68);
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, 25);
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_value_label(prts_temperature_label, lv_color_hex(0xFFFFFF));
lv_obj_set_pos(prts_temperature_label, PRTS_VALUE_LABEL_X, PRTS_VALUE_LABEL_Y);
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(screen, 16, 146, 248, 68);
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, 25);
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_value_label(prts_mode_label, lv_color_hex(0xD9D9D9));
lv_obj_set_pos(prts_mode_label, PRTS_VALUE_LABEL_X, PRTS_VALUE_LABEL_Y);
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);
lv_screen_load(screen);
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)
@@ -256,7 +874,12 @@ bool PRTS_Init(void)
return false;
}
prts_create_status_screen();
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;
}
@@ -277,7 +900,6 @@ void PRTS_UpdateStatus(float temperature_c, RobotMode_t mode)
{
bool temperature_warning = temperature_tenths >= 550;
prts_format_temperature(temperature_text, sizeof(temperature_text), temperature_tenths);
lv_obj_invalidate(prts_temperature_panel);
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),
@@ -285,23 +907,65 @@ void PRTS_UpdateStatus(float temperature_c, RobotMode_t mode)
lv_obj_set_style_text_color(prts_temperature_label,
temperature_warning ? lv_color_hex(0xFFE2E2) : lv_color_hex(0xFFFFFF),
0);
lv_obj_invalidate(prts_temperature_panel);
prts_last_temperature_tenths = temperature_tenths;
}
if (mode != prts_last_mode)
{
mode_color = prts_robot_mode_color(mode);
lv_obj_invalidate(prts_mode_panel);
lv_label_set_text(prts_mode_label, prts_robot_mode_text(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);
lv_obj_invalidate(prts_mode_panel);
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;
@@ -311,10 +975,16 @@ uint32_t PRTS_Task(void)
return 10U;
}
prts_update_monitor(HAL_GetTick());
next_ms = lv_timer_handler();
if (next_ms == 0U || next_ms > 10U)
if (next_ms == 0U)
{
next_ms = 10U;
next_ms = 1U;
}
if (next_ms > 20U)
{
next_ms = 20U;
}
return next_ms;
}