前言
- 上篇博客整合了lvgl到项目中,采用的是自己编写源码的方式,实现了个简单的界面。
- 实际过程中一般情况开发界面都借助设计工具,这里使用的是gui guider来进行示例记录
项目结构(生成代码路径依然放到项目路径下)
CMakeLists配置(改为引用LVGL的源码)
file(GLOB_RECURSE EmWinSrc
EmWin/Source/*.c
EmWin/Resource/*.c
)
file(GLOB_RECURSE LVGL_SRC
LVGL/Code/*.c
)
set(LVGL_INC
LVGL/Code/custom
LVGL/Code/generated
LVGL/Code/generated/guider_fonts
LVGL/Code/generated/guider_customer_fonts
)
target_sources(${PROJECT_NAME}.elf
PRIVATE
${LVGL_SRC}
)
target_include_directories(${PROJECT_NAME}.elf
PUBLIC
${LVGL_INC}
)
LVGL任务线程调整
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-4-20 shchl first version
*/
#include "includes.h"
#include "lv_api_map.h"
#if 1
#define APP_TASK_GUI_LVGL_PRIO 15
#define APP_TASK_GUI_LVGL_STK_SIZE 4096
/*
*******************************************************************************************************
* 外部引入变量
*******************************************************************************************************
*/
/*
*******************************************************************************************************
* 变量
*******************************************************************************************************
*/
TX_THREAD gui_lvgl_thread;
VOID *gui_thread_stack_area;
/*
*********************************************************************************************************
* 静态全局变量
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函数声明
*********************************************************************************************************
*/
static VOID gui_lvgl_thread_entry(ULONG input);
/*
*********************************************************************************************************
* 外部函数
*********************************************************************************************************
*/
/**
* @brief cpu 状态任务
* @param first_thread 第一个启动的任务线程首地址
*/
int tx_task_gui_lvgl_create() {
UINT status;
gui_thread_stack_area = app_malloc(APP_TASK_GUI_LVGL_STK_SIZE);
if(!gui_thread_stack_area){
tx_printf("app malloc error\r\n");
return -1;
}
status = tx_thread_create(&gui_lvgl_thread, /* 任务控制块地址 */
"gui lvgl thread", /* 任务名 */
gui_lvgl_thread_entry, /* 启动任务函数地址 */
0, /* 传递给任务的参数 */
gui_thread_stack_area, /* 堆栈基地址 */
APP_TASK_GUI_LVGL_STK_SIZE, /* 堆栈空间大小 */
APP_TASK_GUI_LVGL_PRIO, /* 任务优先级*/
APP_TASK_GUI_LVGL_PRIO, /* 任务抢占阀值 */
TX_NO_TIME_SLICE, /* 不开启时间片 */
TX_AUTO_START); /* 创建后立即启动 */
if (status) {
tx_printf("create error:%d\r\n",status);
}
return TX_SUCCESS;
}
TX_THREAD_EXPORT(tx_task_gui_lvgl_create);
/*
*********************************************************************************************************
* 内部函数
*********************************************************************************************************
*/
#include "gui_guider.h"
#include "events_init.h"
lv_ui guider_ui;
static VOID gui_lvgl_thread_entry(ULONG input) {
setup_ui(&guider_ui);
events_init(&guider_ui);
while (1) {
lv_task_handler();
tx_thread_sleep(5);
}
}
#endif
测试界面
通过软件添加3个事件
代码事件中添加日志
将移动代码的获取新值的回调函数替换成自己的(原有的基础上加个输出)
/*
* Copyright 2024 NXP
* SPDX-License-Identifier: MIT
* The auto-generated can only be used on NXP devices
*/
#include "events_init.h"
#include <stdio.h>
#include "lvgl.h"
void events_init(lv_ui *ui) {
}
int32_t lv_anim_path_ease_in2(const lv_anim_t *a);
static void screen_1_btn_1_event_handler(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_anim_t screen_1_led_1_anim_x;
lv_anim_t screen_1_led_1_anim_y;
switch (code) {
case LV_EVENT_CLICKED: {
lv_obj_set_style_bg_color(guider_ui.screen_1_spangroup_1, lv_color_make(0xd2, 0x1e, 0x1e), LV_PART_MAIN);
lv_obj_set_pos(guider_ui.screen_1_led_1, 0, 0);
//Write animation: screen_1_led_1move in x direction
lv_anim_init(&screen_1_led_1_anim_x);
lv_anim_set_var(&screen_1_led_1_anim_x, guider_ui.screen_1_led_1);
lv_anim_set_time(&screen_1_led_1_anim_x, 10000);
lv_anim_set_exec_cb(&screen_1_led_1_anim_x, (lv_anim_exec_xcb_t) lv_obj_set_x);
lv_anim_set_values(&screen_1_led_1_anim_x, lv_obj_get_x(guider_ui.screen_1_led_1), 0);
lv_anim_set_path_cb(&screen_1_led_1_anim_x, &lv_anim_path_ease_in2);
lv_anim_start(&screen_1_led_1_anim_x);
//Write animation: screen_1_led_1move in y direction
lv_anim_init(&screen_1_led_1_anim_y);
lv_anim_set_var(&screen_1_led_1_anim_y, guider_ui.screen_1_led_1);
lv_anim_set_time(&screen_1_led_1_anim_y, 10000);
lv_anim_set_exec_cb(&screen_1_led_1_anim_y, (lv_anim_exec_xcb_t) lv_obj_set_y);
lv_anim_set_values(&screen_1_led_1_anim_y, lv_obj_get_y(guider_ui.screen_1_led_1), 0);
lv_anim_set_path_cb(&screen_1_led_1_anim_y, &lv_anim_path_ease_in2);
lv_anim_start(&screen_1_led_1_anim_y);
logInfo("screen_1_btn_1_event_handler");
}
break;
default:
break;
}
}
static void screen_1_btn_2_event_handler(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_anim_t screen_1_led_1_anim_x;
lv_anim_t screen_1_led_1_anim_y;
switch (code) {
case LV_EVENT_CLICKED: {
lv_obj_set_pos(guider_ui.screen_1_led_1, 0, 0);
//Write animation: screen_1_led_1move in x direction
lv_anim_init(&screen_1_led_1_anim_x);
lv_anim_set_var(&screen_1_led_1_anim_x, guider_ui.screen_1_led_1);
lv_anim_set_time(&screen_1_led_1_anim_x, 10000);
lv_anim_set_exec_cb(&screen_1_led_1_anim_x, (lv_anim_exec_xcb_t) lv_obj_set_x);
lv_anim_set_values(&screen_1_led_1_anim_x, lv_obj_get_x(guider_ui.screen_1_led_1), 320);
lv_anim_set_path_cb(&screen_1_led_1_anim_x, &lv_anim_path_ease_in2);
lv_anim_start(&screen_1_led_1_anim_x);
//Write animation: screen_1_led_1move in y direction
lv_anim_init(&screen_1_led_1_anim_y);
lv_anim_set_var(&screen_1_led_1_anim_y, guider_ui.screen_1_led_1);
lv_anim_set_time(&screen_1_led_1_anim_y, 10000);
lv_anim_set_exec_cb(&screen_1_led_1_anim_y, (lv_anim_exec_xcb_t) lv_obj_set_y);
lv_anim_set_values(&screen_1_led_1_anim_y, lv_obj_get_y(guider_ui.screen_1_led_1), 480);
lv_anim_set_path_cb(&screen_1_led_1_anim_y, &lv_anim_path_ease_in2);
lv_anim_start(&screen_1_led_1_anim_y);
tx_printf("screen_1_btn_2_event_handler\r\n");
}
break;
default:
break;
}
}
// 自定义 lv_anim_path_ease_in
int32_t lv_anim_path_ease_in2(const lv_anim_t *a) {
static int cnt = 0;
if (cnt > 20) { // 刷新20次,打印一次,方便调试
lv_coord_t x = lv_obj_get_x(guider_ui.screen_1_led_1);
lv_coord_t y = lv_obj_get_y(guider_ui.screen_1_led_1);
logInfo("(x,y)-->(%d,%d)", x, y);
cnt = 0;
} else {
cnt += 1;
}
/*Calculate the current step*/
uint32_t t = lv_map(a->act_time, 0, a->time, 0, LV_BEZIER_VAL_MAX);
int32_t step = lv_bezier3(t, 0, 50, 100, LV_BEZIER_VAL_MAX);
int32_t new_value;
new_value = step * (a->end_value - a->start_value);
new_value = new_value >> LV_BEZIER_VAL_SHIFT;
new_value += a->start_value;
return new_value;
}
void events_init_screen_1(lv_ui *ui) {
lv_obj_add_event_cb(ui->screen_1_btn_1, screen_1_btn_1_event_handler, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui->screen_1_btn_2, screen_1_btn_2_event_handler, LV_EVENT_ALL, NULL);
}
总结
- gui guider 基本上不做太复杂的逻辑设计,够用了。