使用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 是一个枚举类型,用于定义屏幕加载时的动画效果。以下是每个枚举值的简要介绍:

枚举类型介绍
  1. LV_SCR_LOAD_ANIM_NONE

  • 描述:不使用任何动画效果。屏幕直接切换到新的内容,没有过渡效果。

  1. LV_SCR_LOAD_ANIM_OVER_LEFT

  • 描述:新的屏幕从左侧滑入,旧的屏幕向右侧滑出。这个效果给人一种新的内容从左侧进入的感觉。

  1. LV_SCR_LOAD_ANIM_OVER_RIGHT

  • 描述:新的屏幕从右侧滑入,旧的屏幕向左侧滑出。这个效果与 OVER_LEFT 相反,新的内容从右侧进入。

  1. LV_SCR_LOAD_ANIM_OVER_TOP

  • 描述:新的屏幕从顶部滑入,旧的屏幕向底部滑出。这个效果使得新的内容从上方进入。

  1. LV_SCR_LOAD_ANIM_OVER_BOTTOM

  • 描述:新的屏幕从底部滑入,旧的屏幕向顶部滑出。这个效果使得新的内容从下方进入。

  1. LV_SCR_LOAD_ANIM_MOVE_LEFT

  • 描述:新的屏幕向左移动,旧的屏幕也向左移动。这个效果给人一种内容平移的感觉。

  1. LV_SCR_LOAD_ANIM_MOVE_RIGHT

  • 描述:新的屏幕向右移动,旧的屏幕也向右移动。与 MOVE_LEFT 相反,内容向右平移。

  1. LV_SCR_LOAD_ANIM_MOVE_TOP

  • 描述:新的屏幕向上移动,旧的屏幕也向上移动。这个效果使内容向上平移。

  1. LV_SCR_LOAD_ANIM_MOVE_BOTTOM

  • 描述:新的屏幕向下移动,旧的屏幕也向下移动。内容向下平移的效果。

  1. 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/!

     祝大家国庆快乐捏              

EEZ Studio中,为了创建一个简单的UI来显示发送和接收到的数据,你可以按照以下步骤进行: 1. **打开GUI编辑器**:通常在项目视图中找到“GUI Designer”或类似的图标,点击进入图形化界面编辑模式。 2. **添加文本框组件**: - 在工具箱中找到“Text”类别,拖拽两个文本框出来,分别命名为"Send Text"和"Receive Text",用于显示发送和接收的数据。 3. **设置属性**: - 对于"Send Text",保持默认或清空内容即可; - 对于"Receive Text",设置初始状态为只读,因为你想在接收到新数据后再更新其内容。 4. **添加LED组件**:同样从工具箱找"LED"类别,添加一个用于指示通信状态的LED。 5. **绑定事件**: - 点击"Send Button"或者其他发送按钮,右键选择"Events",添加一个"Click"事件,然后编写发送数据到串口的函数。 6. **处理接收数据**: - 如果有数据接收,可以在串口事件处理程序中获取数据,然后更新"Receive Text"的值。你可能需要创建一个变量来缓存接收到的内容。 例如,伪代码可能会这样: ```c++ // UI部分 void setup() { // 初始化串口 Serial.begin(9600); // 添加事件监听 attachInterrupt(digitalPinToInterrupt(SerialRxPin), receiveData, RISING); // 假设SerialRxPin是接收中断引脚 // 显示初始化 setTextValue("Send Text", "Send"); setLEDState(LEDConnected, LED_ON); } void receiveData() { String receivedData = Serial.readStringUntil('\n'); // 获取一行数据 setTextValue("Receive Text", receivedData.c_str()); // 更新接收文本框 setLEDState(LEDConnected, LED_BLINKING); // 指示有数据接收 } // 发送部分 (假设sendButtonID为发送按钮的标识符) void sendButtonClick() { // 发送数据... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值