使用EEZ Studio移植lvgl项目到stm32-移植ui(noflow)到codeblock(二)
前言
这些日子试着用eez的flow功能,已经用得很熟了,但是在移植到stm32mdk上很糟糕,因为官方的flow文件是c++文件,因此需要mdk的版本到6,并且同时支持c++11标准,也是一通捣鼓把版本升级到6,但是出现了很难解决的问题使得笔者最后放弃了使用flow。
其一出现了我至今没遇到的问题,关于正点原子usart文件中不使用半主机模式,但是出现了flow使用半主机模式,这两者矛盾并且笔者翻了很多资料都没有找到好的解决方法
其二是flow中貌似使用了一个tick和原本lvgl中的tick定时器冲突了,时序冲突导致会卡白屏,并且这是在把usart这个半主机模式问题屏蔽的情况下出现的,所以笔者改用不使用其特有的flow。虽然还是有些小缺陷,目前也是大抵上完成了功能的实现。
使用EEZ Studio移植lvgl项目到stm32-移植ui到codeblock(一)往期教程
开始
首先笔者使用的是正点原子的lvgl的模拟器实验例程,只要准备好就可以了。
下面是将ui文件传入进去,注意这里是未使用flow的,笔者同时准备好了ui文件,eez原例程,包含flow和noflow其中更加详细的写进了readme。
添加好ui文件后
这是移植ui文件后的界面,但是你会发现eez中没有Call_function和event文件,这无上大雅,因为笔者这里写Call_function文件只是起到了一个提醒作用,文件内容全是注释的。
请不要使用eez本身的event功能,因为没有使用flow功能,你只能自己来定义,并且左侧的声明变量也会因为没有使用flow导致未声明函数,当然你可以将未声明的函数定义为空,函数只是定义变量,所以只是个冗余操作,如图下面的testarea部件有Variable选项,请不要使用,会出现未定义错误。
笔者将事件全部添加到event文件中,以下是代码,只需要简单掠过即可,后面展开讲。
#include "EVENT.h"
//v1.01因为eez总是会覆盖原有文件,因此需要将事件封装起来,但是目前没有好的方法可以一次性在screen文件中添加事件函数
//很糟糕不能够用函数封装后更加简洁代码,每次大改动都需要添加一次事件函数,建议增加一个readme文件。目前还没想到什么好方法
//不过既然不用封装也意味着运行调取速度会更快些,后续学习freertos及其他内存管理再来优化
//加入remember me可以用at24c02或者w25qxx来存储数据,其他用法后续再做
/Variable///
static lv_obj_t *current_textarea = NULL; // 当前焦点的文本区域
typedef struct {
char user[15];
char password[15];
} UserCredentials;//用户名和密码分别不超过15位
static UserCredentials credentials;
/Variable///
/事件函数//
//键盘关联testarea事件函数
void textarea_event_handler(lv_event_t *e) {
lv_obj_t *textarea = lv_event_get_target(e);
current_textarea = textarea; // 设置当前焦点文本区域
// 将键盘与当前文本区域关联
lv_keyboard_set_textarea(objects.obj0, current_textarea);
lv_obj_clear_flag(objects.obj0, LV_OBJ_FLAG_HIDDEN);
}
//点击空白部分将键盘隐藏起来
void wallpaper_event_handler(lv_event_t *e) {
// 隐藏键盘
lv_obj_add_flag(objects.obj0, LV_OBJ_FLAG_HIDDEN);
}
// user输入判断事件
void U_check_event_handler(lv_event_t *e) {
lv_obj_t *us = lv_event_get_target(e);
strcpy(credentials.user, lv_textarea_get_text(us));
// 检查输入内容
if (strcmp(credentials.user, REQUIRED_INPUT_USER) == 0 && strcmp(credentials.password, REQUIRED_INPUT_PSWD) == 0) {
lv_obj_clear_state(objects.sign_in, LV_STATE_DISABLED);
} else {
lv_obj_add_state(objects.sign_in, LV_STATE_DISABLED);
}
}
// password输入判断事件
void P_check_event_handler(lv_event_t *e) {
lv_obj_t *ps = lv_event_get_target(e);
strcpy(credentials.password, lv_textarea_get_text(ps));
if (strcmp(credentials.user, REQUIRED_INPUT_USER) == 0 && strcmp(credentials.password, REQUIRED_INPUT_PSWD) == 0) {
lv_obj_clear_state(objects.sign_in, LV_STATE_DISABLED);
} else {
lv_obj_add_state(objects.sign_in, LV_STATE_DISABLED);
}
}
void signin_event_handler(lv_event_t *e){
// lv_obj_t *sign = lv_event_get_target(e);
loadScreen(SCREEN_ID_MUSIC);
}
/事件函数//
注意
使用noflow需要对于lvgl各个部件有大概了解,不然添加事件会很卡壳,同时也要多看看手册文档。
笔者实现的是和之前一样的功能—-输入用户名和密码进入次界面,然后在其填充其他功能,笔者在这里快速介绍下右侧的编辑栏,如果不需要这里的介绍请跳过。
介绍
每个部件都可能会有其需要特殊设置的部分,比如文本区域的testarea,会有密码模式和占位符
而后就是最重要的样式部分,可以设置各部分和不同状态时的样式属性
详细关于lvgl的样式请参考网上的视频教程。
event的使用,如果未使用flow功能,那么请忽略这里。
flags是设置可点击和隐藏选项,如果你想要让图片点击,而不是使用图片按钮部件,比如笔者是让点击图片其他空白部分隐藏键盘,聚焦文本区域可显示键盘,用法还很多,多多善用想象力~~。
下面那堆选项一般默认即可,根据需要使用。
最后是states部分,这里你想让部件在初始是什么状态即可在这里添加,noflow请不要用变量选项。
加入事件函数
lv_obj_add_event_cb(obj, textarea_event_handler, LV_EVENT_FOCUSED, NULL);
lv_obj_add_event_cb(obj, U_check_event_handler, LV_EVENT_VALUE_CHANGED, NULL);
添加在不同状态下触发事件,做到这步,其实重心都是在如何填加事件函数了,可参考笔者的ui文件中的EVENT文件。
EVENT文件
更加详细的EVENT介绍请参照笔者github中的readme文件
这里就不得不提eez-noflow的使用,因为每次添加组件或者添加样式都会重新生成文件,将你之前的内容覆盖掉,这意味着你不能把event函数写到screens文件中。
如果你想要添加新组件把ui文件粘贴到mdk工程,都会导致添加事件函数消失,这点笔者没找到好方法,所以只能麻烦点。
每次将ui粘贴到工程都会出一个错误。
EEZ文件生成错误
关于eez生成的error: 'LV_SCR_LOAD_ANIM_FADE_IN' undeclared (first use in this function); did you mean 'LV_SCR_LOAD_ANIM_FADE_ON'?|错误
这是一个关于动画效果的枚举,不包含它自行生成的LV_SCR_LOAD_ANIM_FADE_IN,只需要添加以下枚举中的一个即可,介绍如下
在 LVGL 中,lv_scr_load_anim_t
是一个枚举类型,用于定义屏幕加载时的动画效果。以下是每个枚举值的简要介绍:
枚举类型介绍
-
LV_SCR_LOAD_ANIM_NONE
描述:不使用任何动画效果。屏幕直接切换到新的内容,没有过渡效果。
-
LV_SCR_LOAD_ANIM_OVER_LEFT
描述:新的屏幕从左侧滑入,旧的屏幕向右侧滑出。这个效果给人一种新的内容从左侧进入的感觉。
-
LV_SCR_LOAD_ANIM_OVER_RIGHT
描述:新的屏幕从右侧滑入,旧的屏幕向左侧滑出。这个效果与 OVER_LEFT
相反,新的内容从右侧进入。
-
LV_SCR_LOAD_ANIM_OVER_TOP
描述:新的屏幕从顶部滑入,旧的屏幕向底部滑出。这个效果使得新的内容从上方进入。
-
LV_SCR_LOAD_ANIM_OVER_BOTTOM
描述:新的屏幕从底部滑入,旧的屏幕向顶部滑出。这个效果使得新的内容从下方进入。
-
LV_SCR_LOAD_ANIM_MOVE_LEFT
描述:新的屏幕向左移动,旧的屏幕也向左移动。这个效果给人一种内容平移的感觉。
-
LV_SCR_LOAD_ANIM_MOVE_RIGHT
描述:新的屏幕向右移动,旧的屏幕也向右移动。与 MOVE_LEFT
相反,内容向右平移。
-
LV_SCR_LOAD_ANIM_MOVE_TOP
描述:新的屏幕向上移动,旧的屏幕也向上移动。这个效果使内容向上平移。
-
LV_SCR_LOAD_ANIM_MOVE_BOTTOM
描述:新的屏幕向下移动,旧的屏幕也向下移动。内容向下平移的效果。
-
LV_SCR_LOAD_ANIM_FADE_ON
描述:新的屏幕逐渐淡入,旧的屏幕逐渐淡出。这个效果使得内容切换更加平滑,适合需要柔和过渡的场景。
ui.c文件中有设置动画效果,请根据自己的需要选择动画过渡。
界面切换
笔者一开始觉得是非常复杂,但是eez已经用一个枚举和函数封装好了,只要用事件函数触发即可
void signin_event_handler(lv_event_t *e){
// lv_obj_t *sign = lv_event_get_target(e);
loadScreen(SCREEN_ID_MUSIC);
}
{
// SIGN_IN
lv_obj_t *obj = lv_btn_create(parent_obj);
objects.sign_in = obj;
lv_obj_set_pos(obj, 535, 199);
lv_obj_set_size(obj, 100, 50);
lv_obj_add_state(obj, LV_STATE_DISABLED);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xff62666b), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 160, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_opa(obj, 190, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_add_event_cb(obj, signin_event_handler, LV_EVENT_PRESSED, NULL);
{
lv_obj_t *parent_obj = obj;
{
lv_obj_t *obj = lv_label_create(parent_obj);
lv_obj_set_pos(obj, 0, 0);
lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_label_set_text(obj, "SIGN IN");
lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_text_font(obj, &lv_font_montserrat_16, LV_PART_MAIN | LV_STATE_DEFAULT);
}
}
}
在按钮部件中插入事件,调取loadScreen(SCREEN_ID_MUSIC);函数,参数为枚举变量
enum ScreensEnum {
SCREEN_ID_MAIN = 1,
SCREEN_ID_MUSIC = 2,
};
在初始化函数中有调取这个函数进入对应的界面,这里可以自行发挥。
关于stm32的移植笔者试过了未用到flow可以很完美的移植到mdk中,不过板子现在不在手边,后面几天更新出教程, 其实只需要将ui_tick(); ui_init();移植进去即可,不会出现时序冲突。
主函数调用和上期一样。
/**
* @file main
*
*/
/*********************
* INCLUDES
*********************/
#include <stdlib.h>
#include <unistd.h>
#include "lvgl/lvgl.h"
#include "lv_demos/src/lv_demo_widgets/lv_demo_widgets.h"
#include "lv_drivers/win32drv/win32drv.h"
#include "lv_demos/src/lv_demo_base/lv_demo_base.h"
#include <windows.h>
#include "my_gui.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void hal_init(void);
static int tick_thread(void *data);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{
/*Initialize LittlevGL*/
lv_init();
/*Initialize the HAL for LittlevGL*/
lv_win32_init(hInstance, SW_SHOWNORMAL, 800, 480, NULL);
/*Output prompt information to the console, you can also use printf() to print directly*/
LV_LOG_USER("LVGL initialization completed!");
ui_init();
/*Run the demo*/
//lv_demo_widgets();
// lv_mainstart();
while(!lv_win32_quit_signal) {
/* Periodically call the lv_task handler.
* It could be done in a timer interrupt or an OS task too.*/
ui_tick();
lv_task_handler();
usleep(10000); /*Just to let the system breath*/
}
return 0;
}
最终效果
输入正确按钮可点击
正常切换至对应界面
如果这篇文章对你有帮助,那么请点个赞再走吧q3q/!
祝大家国庆快乐捏
作者:梦与青与浊