GUI 系统开发

更新时间:2025-04-25 10:27:46下载pdf

本文为您介绍如何基于 T5 平台完成 GUI 系统的开发,主要包含系统框架(通讯框架、应用框架)、应用接口的具体说明;同时,还为您提供了系统资源相关参考资料,以及系统调试、编译、示例展示等指导。

系统通讯框架

系统通讯框架 用于实现 CP0 与的 CP1 之间的通讯。

CP0 和 CP1 为 T5 的两个 CPU:

  • CP0:负责物联网平台通信。
  • CP1:负责图形界面。

GUI 系统开发

  • 目前资源文件系统(统一使用 littlefs 格式)位于片外或片内 Flash,统一由 CP0 执行操作。
  • SDK 的版本在 v3.12.7 以下,起始地址:0x600000,长度:0x1cb000(1836 KB = 1880064 Bytes)
  • SDK 的版本在 v3.12.7 及以上,起始地址:0x6cb000,长度:0x100000(1024 KB =1 048576 Bytes)

片内 Flash 文件系统生成执行:

  • SDK 的版本在 v3.12.7 以下:#./mklittlefs -c bk/ -b 4096 -p 256 -s 1880064 bk.bin
  • SDK 的版本在 v3.12.7 及以上:#./mklittlefs -c bk/ -b 4096 -p 256 -s 1048576 bk.bin

系统应用框架

系统应用框架 在系统通讯框架的基础上,进一步展示了框架的各个层级、以及界面应用(GUI App)与 IoT 的产品应用(Product App)之间的联系。

GUI 系统开发

应用接口

本章节介绍以下应用接口:

核间通讯数据结构

单条消息通讯

typedef struct
{
    char *tag;		//对象标识,具有唯一性
    uint32_t obj_type;  	//对象类型(如 Switch/Slider/Button 等)
    uint32_t event;       	//事件(Widget 器件事件/系统事件)
    uint32_t param;     	//事件内容
} LV_DISP_EVENT_S;

CP0(产品应用)

引用头文件

#include "tuya_app_gui_gw_core0.h"
#include "tuya_list.h"
#include "app_ipc_command.h"
#include "tkl_display.h"
#include "smart_frame.h"
#include "tal_sw_timer.h"
#include "uni_log.h"
#include "tdd_lcd.h"
#ifdef TUYA_TFTP_SERVER        //调试阶段用于从局域网通过 TFTP 客户端工具上传资源文件
  #include "tuya_app_gui_tftp.h"
#endif

初始化入口函数

设备在激活状态下执行以下函数之前,务必完成所有 DP 数据的初始化,否则 GUI 侧控件展示可能出现屏幕刷新现象。

STATIC VOID tuya_gui_start(BOOL_T is_mf_test)

入参:

参数 说明
BOOL_T is_mf_test 是否为产测模式

功能:

屏幕及 TP 使用引脚初始化

tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_config.h 中定义相关参数,示例如下:

参数 示例
屏幕名称 TUYA_LCD_IC_NAME
屏幕宽度 TUYA_LCD_WIDTH
屏幕高度 TUYA_LCD_HEIGHT

设置文件系统所在分区(内部或外部 Flash)

OPERATE_RET tuya_app_gui_set_lfs_partiton_type(TUYA_GUI_LFS_PARTITION_TYPE_E partition_type)
参数 说明
partition_type = TUYA_GUI_LFS_SPI_FLASH 当前使用芯片片内 Flash 文件系统,片内文件系统由于空间较小,暂不支持资源从云端动态更新
partition_type = TUYA_GUI_LFS_QSPI_FLASH 当前使用外部 QSPI Flash 文件系统

GUI 系统事件处理回调注册

处理如 GUI 侧的死机、重启、屏幕保护、语言配置及用户自定义的私有事件等请求。

OPERATE_RET tuya_gui_system_event_hdl_register(GUI_SYS_EVENT_CB cb)

需特殊处理的 GUI 控件变化事件处理回调注册

用户需要特殊处理的 GUI 侧控件变换回调。

  • 如产品应用中不需要此功能,可忽略。
  • 组件已经帮忙处理控件对应 DP 的上报逻辑。如果不需要对特定控件变换进行处理,可无需注册。
OPERATE_RET tuya_gui_obj_event_hdl_register(GUI_OBJ_EVENT_CB cb)

用户手动初始化 GUI 控件状态的处理回调注册

此注册函数提供给用户在上电时初始化控件状态,一般在设备没有被激活、无法通过组件的 DP 自身去初始化控件的情况下使用。

OPERATE_RET tuya_gui_dp2obj_pre_init_hdl_register(GUI_DP2OBJ_PRE_INIT_EVENT_CB cb)

GUI 控件至涂鸦 DP 转换回调注册

用户需要根据实际应用,将 GUI 侧控件事件数据 LV_DISP_EVENT_S 转换为涂鸦的 DP 数据 DP_REPT_CB_DATA

OPERATE_RET tuya_gui_obj2dp_convert_hdl_register(GUI_OBJ2DP_CONVERT_CB cb)

核心函数初始化

VOID tuya_gui_init(BOOL_T is_mf_test, TKL_DISP_INFO_S *info, CHAR_T *weather_code)
  • 如用户对天气预报数据的内容有特殊要求,可自定义入参中的 weather_code,否则为系统默认的天气数据。更多信息,请参考 MCU 标准协议接入 > 天气服务

  • 如需要输出天气数据温度、最高温度、最低温度、湿度、天气概况数字编码,可将入参 weather_code 设置为如下 JSON 数组字符串:

    ["w.temp","w.thigh","w.tlow","w.humidity","w.conditionNum","w.date.1"]
    
    • 这里使用天气概况数字编码,主要是为了适用针对不同国家的多语言配置。
    • w.date.1 表示需要预报一天的数据,支持 1-7 天的预报。必须包括该参数,否则后续天气数据解析会有问题。联网成功后,每半小时更新一次。

TFTP 服务初始化

应用框架支持开启宏定义 TUYA_TFTP_SERVER 来支持本地资源(图片、语言配置及字库文件)通过 TFTP 的方式上传至设备端。如产品量产固件,建议关闭该功能。

tuya_app_gui_tftp_server_init()

CP1(GUI 应用)

本章节介绍以下 GUI 应用及 GUI 相关接口:

绘图方式

目前支持以下绘图方式:

NXP GUI-Guider-1.7.2 (LVGL v8.3.10)(工具辅助)

  1. 将工具生成的代码目录custom&generated导入到 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\ 下。

  2. 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\generated\gui_duider.h 中增加以下行:

     #include "tuya_app_gui.h"
    
  3. 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_config.h 中打开宏定义 "NXP_GUI_GUIDER",同时关闭宏定义 “EEZ_STUDIO”

EEZ Studio (0.13.1) (LVGL v8.3/LVGL v8.3 with EEZ Flow)(工具辅助)

  1. 将工具生成的代码目录src导入到 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\下。

  2. 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\src\ui\ui.h中增加以下行:

    #include "tuya_app_gui.h"
    
  3. 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_config.h 中打开宏定义 "EEZ_STUDIO",同时关闭宏定义 “NXP_GUI_GUIDER”

直接手动绘制

  1. 将手动编写的绘图 C 代码导入到 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\ 下。

  2. 项目名称\apps\T5s_gui_demo_quickstart\src\gui\tuya_app_gui_config.h 中,同时关闭宏定义 “NXP_GUI_GUIDER""EEZ_STUDIO"

  3. 项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_main.c 中,将以下红色框中的函数名修改为您自己的绘图入口函数名(如不需要支持背景图启动和图片预加载,可直接设置为 NULL):

    GUI 系统开发

    GUI 系统开发

    GUI 系统开发

注意事项

  • 对象标识:由于 GUI 对象状态和业务逻辑相关联,当 GUI 对象发生改变、或业务逻辑端 DP 状态改变需要同步到 GUI 对象时,都会触发事件回调。因此,对象需要有一个唯一在业务与 GUI 之间作为信息交互的身份识别标识( 了解该对象标识,请参考 核间通讯数据结构 )。

    用户可以在添加事件回调函数前,通过 lv_obj_set_tag 函数注册控件的标识(此标识必须在整个项目中具有唯一性),如下红色框标记:

    GUI 系统开发

  • 事件标志:另外通过接口 aic_lvgl_msg_event_change 给 CP0 发送控件变化事件时,数据 lvglMsg_t 中的 event 成员是否带标志 LLV_EVENT_BY_DIRECT_REPORT 有如下含义:

    • LLV_EVENT_BY_DIRECT_REPORT:表示控件对应的 DP 数据直接上报云端,不需要 CP0 处理任何业务逻辑。
    • 不带 LLV_EVENT_BY_DIRECT_REPORT:表示控件对应的 DP 数据先由 CP0 业务处理后,由业务逻辑自己上报。
  • 页面切换:如果创建一个屏幕对象,同时给该屏幕对象通过 lv_obj_set_tag 设置过 Tag,切换页面时:

    • 仅通过 lv_obj_clean() 清除当前页面子对象,一定要手动执行 lvMsgEventDel 来清除当前页面的 Tag。
    • 若通过 lv_obj_del() 清除当前页面子对象及页面父对象时,无需执行 lvMsgEventDel 来清除当前页面的 Tag。

GUI 图片预解码

图片放在资源文件系统,程序启动时建议做预解码,以提高系统运行期间的图片加载速度(但可能会占用大量内存)。参考 \apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_basic_demo\lv_example_switch_1.c 中的 void tuya_app_gui_img_pre_decode(void) 实现。

  • 使用jpg_img_loadpng_img_load做预解码时,必须使用tuya_app_gui_get_picture_full_path获取文件的全路径。

    切换页面时,必须调用 jpg_img_unload/png_img_unload 释放对应的图片数据内存,否则会有内存泄漏。

  • 使用img_file_load_by_id做预解码时,用户只需要指定图片 ID。

    切换页面时,切记一定调用 img_file_unload 释放对应的图片数据内存,否则会有内存泄漏。

语言文字使用

在可以支持多语言的应用中,建议一定按照下图所示步骤使用:

GUI 系统开发

  1. 首先,通过接口函数 const void *tuya_app_gui_display_text_font_get(char *node_name) 输入入参文本标签,得到指定的字库信息(语言 > data.file 中提及)。

    如获取不到字库信息,请不要强通过 lv_obj_set_style_text_font 函数设置字体,否则会因字库内容异常而导致系统崩溃。

  2. 然后,通过接口函数 const void *tuya_app_gui_display_text_get(char *node_name) 输入入参文本标签,得到指定的文本信息(语言 > data.file 中提及)。

    如获取不到文本信息,请不要强制通过 lv_label_set_text 函数设置文本,否则会因文本异常而导致系统崩溃。

  3. 切换语言时,系统会清除之前的旧语言环境、使用新语言环境,所以在响应语言切换的控件回调处理函数中,必须要更新页面中所有使用到字库的文字控件。

    如果只更新部分文字控件,会导致 LVGL 在下一个屏幕刷新周期来到时,因指向旧语言环境的文字控件找不到旧语言环境而导致系统异常。

GUI 相关接口使用

  • CP0 侧 DP 数据变化通知注册回调

    捕获 CP0 侧 DP 发生变化时处理回调函数。用户回调处理函数要区分是普通 DP 数据(DP_CNTL_S *)或是 Raw 类型数据(TY_RAW_DP_REPT_S *),然后将不同的数据信息转变成对应的控件信息。

    void tuya_app_gui_dp_update_notify_callback_register (gui_dp_update_cb_t cb)
    
  • CP1 请求获取指定 DP 的相关信息接口(仅在设备激活状态有效):

    通过指定的 DPID 获取相关的信息(DP_CNTL_S *)。Raw 类型数据信息暂不支持获取,需要数据变化时用户自己缓存。

    void *tuya_app_gui_get_dp_cntl(unsigned char dpid)
    
  • CP1 请求获取当前 Wi-Fi 连接状态信息接口

    查询 Wi-Fi 是否连接上云端。仅当连接上时,输出的 RSSI 及 SSID 为有效。

    bool tuya_app_gui_is_wifi_connected(signed char *out_rssi, char *ssid_buff, int ssid_buff_size)
    
  • CP1 请求获取当前设备激活状态信息接口

    bool tuya_app_gui_is_dev_activeted(void)
    
  • CP1 请求解绑当前设备接口

    void tuya_app_gui_request_dev_unbind(void)
    
  • CP1 请求获取当前时间信息接口

    bool tuya_app_gui_request_local_time(char *time_buff, int time_buff_size)
    
  • CP1 请求获取天气预报数据接口

    查询当前天气信息(天气预报数据格式为:LKTLV)。更多天气服务相关,请参考 MCU 标准协议接入 > 天气服务

    bool tuya_app_gui_request_local_weather(char **local_weather, uint32_t *weather_len)
    

    local_weather为输出的天气信息字符串,使用完后需要用户手动释放其指向的内存。

  • CP1 请求资源更新接口

    查询云端是否有新的资源可供更新。

    void tuya_app_gui_query_resource_update(void)
    
  • 配置读写相关接口(由于以下接口写操作均采用了异步操作,所以不支持执行写操作后立即回读):

    • KV 操作相关(配置保存在片内 KV 区域):
      • 读接口:value 所指向的内存空间需要使用者自己释放。

        bool tuya_app_gui_kv_common_read(char *key, unsigned char **value, uint32_t *p_len)
        
      • 写接口:

        bool tuya_app_gui_kv_common_write(char *key, unsigned char *value, uint32_t len)
        
    • KV 文件操作相关(配置保存在 Flash 的文件系统区域):
      • 读接口:value 所指向的内存空间需要使用者自己释放。

        bool tuya_app_gui_fs_kv_common_read(char *key, unsigned char **value, uint32_t *p_len);
        
      • 写接口:

        bool tuya_app_gui_fs_kv_common_write(char *key, unsigned char *value, uint32_t len)
        

用户自定义的私有事件

如果已有的事件类型无法满足用户在 CP0 与 CP1 之间的交互需求,用户可以使用自定义的私有事件 LLV_EVENT_USER_PRIVATE

  1. 在 CP1 侧定义一个处理函数,其中 datalvglMsg_t 类型的指针。

    void tuya_app_gui_user_private_event_process(void *data)
    
  2. 定义一个私有的数据结构,交互时将指针指向 lvglMsg_t 中的 param 参数。

系统资源(littlefs 格式)

开发板首次使用

首次使用开发板时,板载的外挂 Flash 挂载失败会导致屏幕无法点亮,需要在日志命令行手动执行以下操作后重启:

  1. 首先,执行 xqspi fce 擦除外部 Flash。
  2. 然后,执行 lfs mkfs 格式化外部 Flash。

文件系统生成

屏幕点亮后,由于 Flash 中文件系统为空,所以屏幕没有相关图片及文字展示,需要按照以下方法生成文件系统:
项目名称\tuyaos_iot_t5_gui_demo_product_classT5_gui_demo_quickstart\sr\gui\project_resource_sample\littlefs 下有一个系统资源例程,其中,bk 文件夹是要打包的文件系统内容(注意:所有资源文件名长度(包含扩展名)不能超过 32 个字符),将要打包的内容按照文件夹的目录树结构方式存放到里面,通过工具生成文件系统镜像 bk.bin

  • 片内 Flash 文件系统生成

    ./mklittlefs -c bk/ -b 4096 -p 256 -s 1880064 bk.bin
    

    执行完成后,按照下图所示的指导,将 bk.bin 导入工具,并按照 系统通讯框架 中描述的值设置好 起始地址。(“文件长度” 即文件的大小。文件越大,地址的长度会越长;文件长度会自动生成,无需额外操作。)

    GUI 系统开发

  • 片外 Flash 文件系统生成(请根据实际使用的 Flash 规则参数确定参数 -b -p -s ):

    ./mklittlefs -c bk/ -b xxx -p xxx -s xxx bk.bin
    
    • 执行完成后将 bk.bin 烧录至 QSPI Flash。
  • 工具参数定义

    参数 说明
    -b 块大小,默认 4096
    -p 页面大小,默认 256
    -c 输入的素材文件夹
    -s 输出镜像大小,不能小于实际素材大小(如果使用片外 Flash,就是 Flash 大小的字节数目)
    -bk.bin 输出的镜像文件名称

目录结构树

GUI 系统开发

语言

多语言配置会消耗较大内存资源。如果产品应用只有一种语言,建议不要使用以下配置,而使用静态字库方式。

  • data_file:该文件夹中保存 GUI 中所使用的的语言文本定义,请严格按照 JSON 例程中的格式来组织。

    • 中文语言文本定义:

      GUI 系统开发

    • 英文语言文本定义:

      GUI 系统开发

    • 各种语言文件夹定义见下表。(如有其它语言需求,请联系涂鸦的开发者。)

      语言 文件夹
      中文 “ch”
      英文 “en”
      韩文 “kr”
      日文 “jp”
      法文 “fr”
      德文 “de”
      俄文 “ru”
      印度文 “in”
  • font:该文件夹保存字体库(目前仅支持 TTF 字体库),请使用授权的正版字体库,否则系统字体解析会出现异常。使用时,该字体库的名称(除后缀 .ttf 外)一定要保持与上述语言文件夹中的字库名一致。

  • 使用:例如,lv_label_set_text(language_label, tuya_app_gui_display_text_get("language_test")) 中,表示对象 language_label 使用文本标签 "language_test" 所对应的文字信息。

    • 如当前语言设置为中文,该 language_label 则会显示 共以 (展示文字,无实际意义)。
    • 如当前语言设置为英文,则会显示 ENGLISH

图片

picture:该文件夹保存 GUI 所使用到的图片资源,支持 JPG、PNG 及 GIF 等格式。为保证 GUI 在使用过程中显示图片的流畅性,建议做图片的预解码处理(注意:预解码的图片可能会导致占用大量内存)。了解更多图片预解码相关,请参考 图片预解码

  • 图片的命名方式:每张图片按照唯一数字 ID 的方式命名(从 1.jpg1.png1.gif 开始,ID 不能重名,依次往后增加),用户程序在需要使用的地方调用对应的图片名称。
  • 图片像素要求
    系统内部解码图像时按照 4 字节搬运。如使用 JPG 图片,需要注意:
    • 16 位色深时,图片的像素宽必须是偶数(如 138x42),不要使用像素为奇数的图片(如 135x41),否则用户可通过相关图片编辑工具将奇数的像素转换为偶数像素。
    • 24 位色深时,图片的像素宽必须是 4 的倍数(如 192x192)。

音乐

music:音乐文件的命名方式。每首音乐按照唯一数字 ID 的方式命名(从 1.mp3开始,ID 不能重名,依次往后增加),用户程序在需要使用的地方调用对应的名称。

该行暂不支持删除。

资源版本信息

version.inf:如 0.1

使用例程

关于资源的具体使用示例教程,请参考 Demo 文件中的 lv_example_switch_1.c C 文件。

使用 TFTP 下载资源至设备(调试阶段)

  1. 前往 Tftpd64 下载客户端。

  2. 开启设备端 TFTF Server 服务:
    项目名称\apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_config.h中打开宏定义"TUYA_TFTP_SERVER"

    量产阶段,固件务必关闭此宏定义,避免出现安全问题。

  3. 从设备日志端获取 TFTF Server IP 及端口信息,如下:

    GUI 系统开发

  4. 在 Windows 端打开 TFTP 客户端工具,按照下图所示进行配置:

    GUI 系统开发

    配置完成后:

    • 单击 Put 即可上传。
    • 单击 Get 即可下载并读取设备文件。
  5. 目前仅识别如下几种 GUI 相关的文件格式:

    文件 格式说明
    语言配置文件 .json
    字体库文件 .ttf
    音频文件(暂不支持) .mp3。音频文件名相关格式,请参考 音乐
    图片文件 .jpg/.png/.gif。图片文件名相关格式,请参考 图片
    资源信息文件 version.inf。固定名称,不可改变
  6. 上传语言配置文件时(语言前缀相关,请参考 语言 中的详细描述),命名规则必须为 语言_文件名.json,例如:

    • 上传一个中文配置文件 xxx.json 时,更改名称为 ch_xxx.json
    • 上传一个英文配置文件 yyy.json 时,更改为 en_yyy.json
    • 上传一个韩文配置文件 jjj.json 时,更改为 kr_jjj.json

系统调试

  • 日志口:UART1,波特率为 460800。
  • 固件烧录口:UART2,波特率为 2000000。

系统编译

带 GUI 例程的工程名为 tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart,编译代码如下所示:

./build_app.sh apps/tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart/ tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart 1.0.0

屏幕切换

开发包已支持两种屏幕大小的可选宏定义配置:

  • 3.5 寸屏幕厂商型号:T35P128CQ
  • 5 寸屏幕厂商型号:T50P181CQ

用户可根据已有开发板,在路径 \apps\tuyaos_iot_t5_gui_demo_product_class-T5_gui_demo_quickstart\src\gui\tuya_app_gui_config.h)下进行配置,如下图所示:

GUI 系统开发

示例屏幕展示

GUI 系统开发

其他配置注意

开发框架

文件 tuyaos_iot_t5_gui_demo_product_class\apps\T5_gui_demo_quickstart\default_gpio_config.json 与当前系统使用的 GPIO 中断配置有关。
例如,当 TP 触控屏的中断脚 tp_intr 配置为 TUYA_GPIO_NUM_55 时,对应文件中的配置应同步如下图所示:

GUI 系统开发