[笔记] 基于esp32s3用GUI-Guider-1.9.1-GA开发LVGL界面

基于esp32s3用GUI-Guider-1.9.1-GA开发LVGL界面用于485通讯

1. 准备措施

1.1.硬件准备

仓库内含视频地址, 配套的视频很简单, 不过够用了.仓库地址: 禾木科技/esp32-s3-4inch-001-sdk

  • 使用的硬件是某宝买的一个方案板: [esp32s3蓝牙wifi板4寸RGB屏UI开发LVGL方案电容触摸st7701s驱动]
    在这里插入图片描述

  • 他们提供了驱动库, 有完整配套例程, 和视频讲解,很是方便, 不用关心屏幕的驱动和LVGL库的移植, 所以本文主要讲解使用.

  • 缺点就是把驱动库的.c文件都打包加密了.不能观看学习,也不开源原理图,导致只能自己反推接线.

1.2.环境准备

关于ESP32S3的环境部署, 参考笔记: 【ESP】一小时速通入门笔记

  • 使用ESP32S3作为主控, 关于环境配置可以看我之前的笔记.

1.3.工具准备

官网下载 GUI-Guider-1.9.1-GA
移植参考: GUI-Guider安装使用及项目移植(7.11版本)

  • 仓库配套例程推荐的是 SquareLine_Studio, 它收费, 试用只有1个月, 但因为我的公司电脑, 电脑时间和网络时间不一致, 导致我安装后直接用不了,试了半天没成功, 也没找到破解版, 就放弃了
  • 我搜了一下, 发现NXP有推出一款免费的 GUI-Guider, 注册就可以用.
  • 注意 GUI-Guider 迭代了几个版本, 我看了一下:
    • 1.5.1只支持v7LVGL;
    • 1.6.1支持v7v8LVGL;
    • 1.9.1支持v8v9LVGL;
  • 而且它不同版本直接是不能相互打开工程的!!!示意开始创建工程之前一定要确定使用哪个版本的软件, 避免后面要改, 更改就得重做了.
  • 而且不同版本之间操作方式也有较大的差异性.

2.开始测试

2.1.测试LVGL

例程目录: esp32-s3-4inch-001-sdk\examples\1.lvgl_v8
例程视频: esp32通用RGB驱动板开源lvgl V8.3.11程序下载及86盒方案演示

  • 只需要将仓库的componentsexamples/1.lvgl_v8拷贝到自己的新建文件夹即可,1.lvgl_v8可以修改名字app_main,只要和组件的相对路径不变即可; 类似下图, 我加了.git管理和LVGL工程文件,也放一起.

在这里插入图片描述

  • 然后打开esp-idf终端,
    • cd切换目录,
    • idf.py set-target {esp32s3}切换芯片类型,
    • idf.py build编译文件.
    • idf.py menuconfig修改配置(默认不用,只是我想列从来 ),
    • idf.py {-p COM8} flash下载,
    • idf.py {-p COM8} monitor监听. 最后成功运行, 和视频介绍的一样.
  • 使用vscode打开工程, 为了方便代码之间跳转查看, 添加.vscode/c_cpp_properties.json文件, 在"configurations"内添加"compileCommands": "${workspaceFolder}/build/compile_commands.json".
  • 再编译一下, 没有问题. 速通成功, 下一步.

2.2. 替换GUI-Guider文件

参考笔记: LVGL使用GUI Guider配置STM32界面详细笔记教程

  • 例程自带的是SquareLine Studio的代码文件, 和GUI-Guider不通用, 需要修改.
  • 先打开软件GUI-Guider-1.9.1-GA,创建v8工程:
  • 板子选择默认Simulator,模板选择一个例程SmartBuiding,屏幕选择Custom,尺寸填写480*480;后续会围绕这个模板将LVGL的功能.

在这里插入图片描述

在这里插入图片描述

  • 界面大体如下: 不同版本差别较大, 认准目前讲解的1.9.1版本

在这里插入图片描述

  • 直接编译运行, 初次比较久, 过了一会就能看到弹窗, 可以试一下功能;

在这里插入图片描述

  • 然后开始导出代码,选择放在esp32工程里的新建文件夹.

在这里插入图片描述

然后会导出2个文件夹,customgenerated, 然后将2个文件夹的路径添加到esp32编译文件配置CMakeLists.txt中:

在这里插入图片描述

idf_component_register(    
    SRC_DIRS
    "."
    "gui_app/custom"
    "gui_app/generated"
                    
    INCLUDE_DIRS 
    "."
    "gui_app/custom"
    "gui_app/generated"
    )
  • 然后在main.c文件中直接添加代码即可:
...省略...
#include "gui_app/generated/gui_guider.h"
#include "gui_app/generated/events_init.h"
lv_ui guider_ui;
...省略...

void app_main(void)
{
		...省略...
    lv_port_sem_take();
    setup_ui(&guider_ui);
    events_init(&guider_ui);
    lv_port_sem_give();  
}
  • 然后编译运行(报错), 就能看到板子上呈现一样的界面.唯一问题就是比较卡顿, 可能是因为算力不够,运行太慢.

2.3. 移植GUI-Guider时钟组件dclock

参考笔记: Gui-Guider1.8.1 数字时钟控件找不到定义,无法编译

  • 上面的正常操作最后是会编译报错, 因为使用的这个gui工程使用了私有组件, 需要手动移植;

  • 打开软件所在文件夹, 然后找到以下文件夹: E:\NXP\GUI-Guider-1.9.1-GA\environment\template\project\v8\lvgl\src\extra\widgets

在这里插入图片描述

  • 将文件夹复制到工程里, 记得添加esp32文件编译路径

在这里插入图片描述

idf_component_register(    
    SRC_DIRS
    "."
    "gui_app/custom"
    "gui_app/dclock"
    "gui_app/generated"
                    
    INCLUDE_DIRS 
    "."
    "gui_app/custom"
    "gui_app/dclock"
    "gui_app/generated"
    )
  • 然后找到这个esp32s3组件里的头文件lvgl.h, 添加一行头文件路径.

在这里插入图片描述

/*-----------------
 * EXTRAS
 *----------------*/
#include "src/extra/lv_extra.h"

#include "../../../examples/app_main_s100/main/gui_app/dclock/lv_dclock.h"
/*********************
 *      DEFINES
 *********************/
  • 然后回到lv_dclock.clv_dclock.h,进行如下修改.
// lv_dclock.c:
 
/*********************
 *      INCLUDES
 *********************/
#include "lv_dclock.h"
#if LV_USE_DCLOCK != 0
//注释掉原有的头文件
//#include "../../../core/lv_obj.h"
//#include "../../../misc/lv_assert.h"
//#include "../../../core/lv_group.h"
//#include "../../../draw/lv_draw.h"
//#include "../../../misc/lv_color.h"
//#include "../../../misc/lv_math.h"
//#include "../../../misc/lv_bidi.h"
//#include "../../../misc/lv_txt_ap.h"
//#include "../../../misc/lv_printf.h"
//#include "../../../widgets/lv_label.h"
 
  • 宏定义LV_USE_DCLOCKLV_DCLOCK_TEXT_SELECTION的添加是因为又耦合了另一文件, 避免多余的移植, 就直接将用到的宏定义写在这里.
// lv_dclock.h: 
 
#ifndef LV_DCLOCK_H
#define LV_DCLOCK_H
 
#ifdef __cplusplus
extern "C" {
#endif
 
/*********************
 *      INCLUDES
 *********************/
//#include "../../../lv_conf_internal.h"  //注释掉原有的lv_conf_internal.h文件
 
//观察lv_conf_internal.h文件,选择需要的参数置位1
#define LV_USE_DCLOCK             1   
#define LV_DCLOCK_TEXT_SELECTION  1
 
 
#if LV_USE_DCLOCK != 0
 
#include <stdarg.h>
#include <stdlib.h>
#include "lvgl.h"   //我们自己的lvgl.h头文件,代替其原有调用的一些lvgl有文件
//#include "../../../core/lv_obj.h"
//#include "../../../font/lv_font.h"
//#include "../../../font/lv_symbol_def.h"
//#include "../../../misc/lv_txt.h"
//#include "../../../draw/lv_draw.h"
  • 写在再编译应该就万无一失了, 可以正常运行下载了.

2.4. 测试RS485

官方指南: 通用异步接收器/发送器 (UART)
参考笔记: ESP32 单片机学习笔记 - 01 - gpio&ledc&uart
esp32自带例程地址: \Espressif\frameworks\esp-idf-v5.3.1\examples\peripherals\uart\uart_echo_rs485
参考笔记: 6.RS485模块发布esp32方案86盒温控器加入RS485通信控制
方案板自带例程地址:\esp32-s3-4inch-001-sdk\examples\6.RS485

  • 先用方案板例程跑一下, 正常运行, 使用万用表蜂鸣档和查看手册描述知道, 板载自带的RS485接口的具体IO是IO4/5/6.
  • 然后可以直接使用esp32自带的例程, 使用idf.py menuconfig修改默认配置,引脚和波特率.然后编译下载进去,得到和方案板例程一样的效果.
// 注意: 目标芯片上的某些引脚无法分配用于UART通信.
// 请参阅所选板和目标的文档, 以使用Kconfig配置引脚.
#define ECHO_TEST_TXD           (CONFIG_ECHO_UART_TXD)  // 目前板载的是IO6
#define ECHO_TEST_RXD           (CONFIG_ECHO_UART_RXD)  // 目前板载的是IO4

// RS485半双工模式的 RTS 管理 DE / ~RE
#define ECHO_TEST_RTS           (CONFIG_ECHO_UART_RTS)  // 目前板载的是IO5
  • 就可以把esp32例程里的代码移植到方案板例程上了, 因为方案板库自带的rs485组件功能很少, 不好打包使用, 所以重新实现, 屏幕驱动就用自带的组件 .
  • 注意,将/main/Kconfig.projbuild文件的内容也移植, 它是规定idf.py menuconfig配置的,可以方便修改配置, 具体的语法自行举一反三吧, 我不列举了.

menu "Echo RS485 Example Configuration"

    orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"

    config ECHO_UART_PORT_NUM
        int "UART port number"
        range 0 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
        default 2 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3
        range 0 1
        default 0
        help
            UART communication port number for the example.
            See UART documentation for available port numbers.

    config ECHO_UART_BAUD_RATE
        int "UART communication speed"
        range 1200 115200
        default 9600
        help
            UART communication speed for Modbus example.

    config ECHO_UART_RXD
        int "UART RXD pin number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
        default 22 if IDF_TARGET_ESP32
        default 4 if !IDF_TARGET_ESP32
        help
            GPIO number for UART RX pin. See UART documentation for more information
            about available pin numbers for UART.

    config ECHO_UART_TXD
        int "UART TXD pin number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 23 if IDF_TARGET_ESP32
        default 6 if !IDF_TARGET_ESP32
        help
            GPIO number for UART TX pin. See UART documentation for more information
            about available pin numbers for UART.

    config ECHO_UART_RTS
        int "UART RTS pin number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 18 if IDF_TARGET_ESP32
        default 5 if !IDF_TARGET_ESP32
        help
            GPIO number for UART RTS pin. This pin is connected to
            ~RE/DE pin of RS485 transceiver to switch direction.
            See UART documentation for more information about available pin
            numbers for UART.

endmenu
  • 使用esp32自带的rs485功能, 将DE引脚设置为RTS功能引脚, 就可以在收发时自动切换使能, 免去控制逻辑. 作为主机使用很方便, 因为主机收发固定.从机的话就有点不一样,从机要考虑不发时一直收的情况. 自行判断要不要使用

  • 软件流控: 如果硬件流控处于禁用状态,可使用函数 uart_set_rts()uart_set_dtr() 分别手动设置 RTSDTR 信号电平。

// 设置RS485半双工模式
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));
  • 注意, 我目前遇到了2个未想明白的问题, 在收发函数前后使用打印函数, 老是容易死机重启. 其次就是, 这个 uart_read_bytes() 函数的超时时间, 是判断2次接收数据的时间差, 不能设置大, 不然会导致卡很久,将2次数据认为一次数据, 它居然不是按照 uart_set_rx_timeout() 区分一次数据. 可能还要另一个函数
// 根据超时时间接收数据, 这里自行确保返回的长度是符合缓存大小的
uart_read_bytes(ECHO_UART_PORT, data, BUF_SIZE, pdMS_TO_TICKS(tick)); 

// 设置UART TOUT功能的读取超时
ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT));

3. 编辑界面

推荐中文英文手册网址: https://lvgl.100ask.net/master/intro/introduction.html#requirements

  • 前期铺垫完了, 现在esp32编程和板子驱动, 还有 LVGL 测试确保正常. 开始编写 LVGL 界面工程

3.1. 添加组件

  • 直接点击这个图标就可以打开组件工具箱, 然后拖动组件到画布上就可以添加了, 也支持复制粘贴,

在这里插入图片描述

3.2. 组件属性

  • 组件的属性可以通过右栏设置,
    • 如果组件有不同状态, 就点右边的下拉框选择状态. 比如按钮有按下, 松开等不同状态.
    • 如果组件有不同模式, 就点左边的下拉框选择模式, 比如进度条有底色和进度条颜色, 还有表格, 有表头和表单等组成.
  • 不同模式又可能有多种状态可以供设置. 内置的预设样式也好过没有, 可以参考看看.

在这里插入图片描述

3.3. 布局对齐

  • 有一个容器组件, 可以做嵌套式布局, 形成子级父级关系. 这个软件的坐标系统是 相对父级 来说的, 如果起点都在 左上角, xy始终为正数, 终点在右下角.
  • 软件支持快捷对齐布局操作, 需要注意几点:
    • 上下左右,中心对齐是以 最后一个(第一个?) 选择的组件作为对象, 这个逻辑和画pcb和模型图时用的对齐指令不一样 .
    • 如果发现对齐后没有效果, 是因为两个组件不在同一的父级下, 对齐后坐标系统, 位置不同.
  • 不同组件有显示顺序, 左下角的排序就是顺序,可以直接拖拽排序,排序靠前的会把靠后的遮盖住.

在这里插入图片描述

3.4. 组件事件

  • 模板里有很多可供学习的事件设置, 选择要设置的组件, 然后点击下栏中的 事件添加 即可. 软件提供的是可视化编程. 对于一些组件操作内置了选项, 也支持自定义事件代码.

  • 点击按调用cont_menu容器的isVisible(使能)函数, 达到一个点击图标显示菜单的效果.

在这里插入图片描述

  • 然后在对容器设置,点击后隐藏的效果:

在这里插入图片描述

  • 也可以设置跳转屏幕,和跳转动画等效果.

在这里插入图片描述

  • 也可以选择自定义代码. 更多其他内置事件功能自行尝试, 不赘述了.

在这里插入图片描述

3.5. 组件事件 自定义代码

  • 选择custom code -> Edit code 添加自定义代码.可以选择添加c或python类型. 添加并保存后, 点击右上角的生成代码, 即可自动将代码添加在工程中.在代码编辑中可以看到生成的内容.

在这里插入图片描述

  • 工程代码主要是 Custom 文件夹 和 Generated 文件夹 的内容. 前者是属于可更改的, 后者是不可更改的, 会在每次生成代码时自动刷新.
  • 其中 events_init.c 的内容就是对应 组件事件 中编辑的内容.
  • 需要注意的是, 他们是杂糅堆一起的, 是可以共同调用定义的全局变量, 也需要注意不要重复定义的全局变量!!!

在这里插入图片描述

  • Custom 文件夹的内容不会被自动刷新掉, 一般添加一下打包函数, 可以被 events_init.c 文件调用到.

在这里插入图片描述

3.6. 选择编译

  • 因为LVGL界面编辑是一个工程, 为了能编译运行, 不能添加一些多余的代码. 但最终是要给esp32工程里用的, 必定会用到一些工程里的变量函数, 如果每次导出都刷新掉, 重新更改, 就太不科学了. 所以我想到添加宏定义进行选择编译.

如果你有更好的方法请介绍给我.

#ifdef ESP_PLATFORM // esp32工程编译时自带的宏定义
	// 添加实际所需代码
	esp32_uart_read(rx_buf, 1000); // 假设的伪代码
	lv_spinbox_set_value(guider_ui.Settings_spinbox_2, reg.par.add);  // 控制ui组件
#else // LVGL仿真时执行的测试内容
	// 不耦合的测试内容
	int add = 666; // 测试组件代码正常运行
	lv_spinbox_set_value(guider_ui.Settings_spinbox_2, add);  // 控制ui组件
#endif

4. 总结

  • 剩下的就是枯燥无味, 简单重复的ui的组件修改, 和接口协议匹配的内容.
  • 或许可以尝试丢给ai, 最近挺流行的, 直接去研究怎么让ai帮忙写, 比自己写快多了 .
  • 今天刚通关剑星, 不知道啥时候出现ai觉醒的剧情 .
### RK3588平台NPU调用方法 #### 创建和初始化NPU环境 为了在RK3588平台上成功调用NPU进行神经网络推理或加速,首先需要确保设备已正确配置并加载了相应的驱动程序。Rockchip的官方固件通常已经预装了RKNPU驱动[^3]。 一旦确认硬件准备就绪,可以通过以下方式创建和初始化NPU环境: ```cpp #include "rknn_api.h" // 初始化模型路径和其他参数 const char* model_path = "./model.rknn"; int ret; rknn_context ctx; ret = rknn_init(&ctx, model_path, 0, 0, NULL); if (ret < 0) { printf("Failed to initialize rknn context\n"); } ``` 这段代码展示了如何使用`rknn_api.h`库来初始化一个RKNN上下文对象,这一步骤对于后续的操作至关重要[^2]。 #### 加载和编译模型 接下来,在实际运行之前还需要加载预先训练好的神经网络模型文件(通常是`.rknn`格式)。此过程涉及读取模型二进制数据,并将其传递给RKNN API以便内部处理和优化。 ```cpp // 假设模型已经被转换成 .rknn 文件格式 char *model_data; // 模型的数据指针 size_t model_size; // 模型大小 FILE *fp = fopen(model_path, "rb+"); fseek(fp, 0L, SEEK_END); model_size = ftell(fp); rewind(fp); model_data = (char *)malloc(sizeof(char)*model_size); fread(model_data, sizeof(unsigned char), model_size, fp); fclose(fp); // 将模型数据传入RKNN API ret = rknn_load_rknn(ctx, &model_data, &model_size); free(model_data); if(ret != 0){ printf("Load Model Failed!\n"); } else{ printf("Model Loaded Successfully.\n"); } ``` 这里说明了从磁盘读取模型文件的具体操作流程,并通过API函数将这些信息提交给了底层框架去解析和设置好用于推断所需的资源[^1]。 #### 执行前向传播计算 当一切准备工作完成后就可以开始真正的预测工作——即让NPU执行一次完整的前向传播运算。这个阶段主要是构建输入张量、启动异步任务以及收集输出结果。 ```cpp float input_tensor[INPUT_SIZE]; // 输入特征图数组 float output_tensors[MAX_OUTPUTS][OUTPUT_SIZE]; // 输出特征图数组 struct rknn_input inputs[] = {{input_tensor}}; struct rknn_output outputs[MAX_OUTPUTS]; for(int i=0;i<NUM_ITERATIONS;++i){ memset(inputs, 0 ,sizeof(struct rknn_input)); memcpy(input_tensor, inputData[i], INPUT_SIZE*sizeof(float)); // 启动推理任务 ret = rknn_run(ctx, nullptr); if(ret!=0){ printf("Inference failed at iteration %d", i); break; } // 获取输出结果 for(size_t j=0;j<num_outputs;++j){ struct rknn_output& out = outputs[j]; size_t bufSize = OUTPUT_SIZE * sizeof(float); void* buffer = malloc(bufSize); ret = rknn_get_output(ctx, j, &out.datatype, &buffer, &bufSize, false); if(!ret && buffer){ memcpy(output_tensors[j], buffer, bufSize); free(buffer); } } } printf("All iterations completed successfully."); ``` 上述片段体现了典型的基于RKNN SDK的应用场景:先准备好待测样本作为输入;接着触发内核中的计算逻辑;最后获取到经过变换后的响应值供下一步分析所用[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值