4622|3

224

帖子

0

TA的资源

一粒金砂(高级)

楼主
 

【正点原子i.MX93开发板】4-LVGL的GUI制作(显示更新系统信息) [复制链接]

本帖最后由 qiao--- 于 2024-6-30 02:14 编辑

前言:在我的第二期测评我们成功的移植了LVGL框架,本期测评就可以利用这个框架开发出一些好看的UI。本人的审美能力欠缺,UI制作的可能并不是很好看(这是UI工程师干的活),帖友们不要介意,重要的是开发过程。本期测评将分为以下三个部分的:

  1. 用GUI_Guider开发一个简易界面并移植运行
  2. 写一个C语言程序读出系统信息(ip地址,cpu占用率,cpu温度等)
  3. 将以上两者综合移植

1.用GUI_Guider开发一个简易界面并移植运行

经常看我的测评的贴友应该知道,GUI_Guider是我的老朋友了,我经常用这个软件开发我的lvgl应用。里面自带模拟器开发起来方便,就是里面的控件有点少,不过也够用了。有对GUI_Guider不熟悉的贴友可以看我之前的文章:

【ACM32G103RCT6】8-基于RTC和LVGL的手机时间界面 https://bbs.eeworld.com.cn/thread-1270419-1-1.html

【Sipeed 博流BL808全能板】5-手表界面开发 https://bbs.eeworld.com.cn/thread-1273077-1-1.html

这两篇文章讲了GUI_Guider工程的创建和移植,但是不是Linux下的移植,本期 测评将移植于Linux系统。

在GUI_Guider创建一个工程后简单添加一些控件,并运行模拟器,我的效果如下图所示:

 

成功运行,接下来就是把我们这个工程移植到我们之前的lvgl框架中。移植步骤还是移植下面两个文件夹,由于我的custom文件夹下没有内容,我就不移植了,我就只移植generated这一个文件夹。

 

将这个文件夹复制到我们第二期测评拉下来代码的lvgl目录中,目录结构如下所示:

 

此时这个项目还不知道我们移植这个文件的.c和.h文件的位置,因此还要修改Makefile文件

修改lvgl目录下的lvgl.mk文件,添加以下内容:

 

include $(LVGL_DIR)/$(LVGL_DIR_NAME)/generated/generated.mk

在generated目录下的generated.mk文件添加以下内容

 

PRJ_DIR ?= $(LVGL_DIR)/$(LVGL_DIR_NAME)

修改完成后我们还需要修改我们的main.c中的代码,调用我们UI代码。代码主要是添加一个头文件,然后调用系统生成的UI创建代码。

 

 

然后进行编译make -j4  ,就可以编译成功。我们运行这个程序,效果如下所示:

 

但是这个UI里面的数据不是动态显示的,我们还要添加一些代码。

 

2.写一个C语言程序读出系统信息(ip地址,cpu占用率,cpu温度等)

由于上面的代码不能动态显示,我们还需要写一个demo,读出系统的信息,最后将他移植到我们项目中。Linux 的一些系统信息都是通过文件的形式来提供接口的,我们只需要通过文件IO来访问这些接口就行了。我的示例代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <time.h>
#define MAX_BUFFER_SIZE 256
#define MAX_IP_LENGTH 16 // 定义IPv4地址的最大长度为15个字符,加上字符串终止符'\0'

// 实现获取本地IP地址的函数
int get_local_ip(char *ip_buffer) {
    int fd; // 套接字描述符
    struct ifreq ifr; // 网络接口请求结构体

    // 创建一个套接字
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) {
        perror("socket"); // 输出错误信息并返回
        return -1;
    }

    // 指定要查询的网络接口(这里使用eth0,可根据实际情况修改)
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ - 1); // 拷贝接口名到请求结构体

    // 获取接口的IP地址
    if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
        perror("ioctl"); // 输出错误信息并返回
        close(fd); // 关闭套接字
        return -1;
    }

    // 将获取到的IP地址转换成字符串形式并存储在ip_buffer中
    inet_ntop(AF_INET, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, ip_buffer, INET_ADDRSTRLEN);

    close(fd); // 关闭套接字

    return 0; // 返回成功
}

// 获取 CPU 温度
float get_cpu_temperature() {
    FILE *fp;
    float temp = 0.0;

    fp = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
    if (fp == NULL) {
        perror("Error opening temperature file");
        return -1.0;
    }

    fscanf(fp, "%f", &temp);
    fclose(fp);

    return temp / 1000.0; // 转换为摄氏度
}

// 获取 CPU 占用率
float get_cpu_usage() {
    FILE *fp;
    char buffer[MAX_BUFFER_SIZE];
    long double a[4], b[4];
    float usage;

    fp = fopen("/proc/stat", "r");
    if (fp == NULL) {
        perror("Error opening CPU stat file");
        return -1.0;
    }

    fgets(buffer, sizeof(buffer), fp);
    sscanf(buffer, "cpu %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2], &a[3]);
    fclose(fp);

    sleep(1); // 等待一段时间再次获取数据
    fp = fopen("/proc/stat", "r");
    if (fp == NULL) {
        perror("Error opening CPU stat file");
        return -1.0;
    }

    fgets(buffer, sizeof(buffer), fp);
    sscanf(buffer, "cpu %Lf %Lf %Lf %Lf", &b[0], &b[1], &b[2], &b[3]);
    fclose(fp);

    usage = ((b[0] + b[1] + b[2]) - (a[0] + a[1] + a[2])) / ((b[0] + b[1] + b[2] + b[3]) - (a[0] + a[1] + a[2] + a[3])) * 100.0;

    return usage;
}

int main() {
    float cpu_temp, cpu_usage;

    cpu_temp = get_cpu_temperature();

    time_t rawtime;
    struct tm *timeinfo;
    char timeBuffer[80];

    char ip_address[MAX_IP_LENGTH]; // 用于存储IP地址的字符数组

    time(&rawtime);                 // 获取当前时间
    timeinfo = localtime(&rawtime); // 将时间转换为本地时间结构体
    strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", timeinfo); // 格式化时间为字符串
    printf("当前时间是: %s\n", timeBuffer);

    // 调用获取本地IP地址的函数
    if (get_local_ip(ip_address) == 0) {
        printf("Local IP address: %s\n", ip_address); // 打印获取到的IP地址
    } else {
        printf("Failed to get local IP address.\n"); // 获取失败时的错误提示
    }
    
    if (cpu_temp != -1.0) {
        printf("CPU Temperature: %.2f°C\n", cpu_temp);
    } else {
        printf("Failed to get CPU Temperature.\n");
    }

    cpu_usage = get_cpu_usage();
    if (cpu_usage != -1.0) {
        printf("CPU Usage: %.2f%%\n", cpu_usage);
    } else {
        printf("Failed to get CPU Usage.\n");
    }

    return 0;
}

在系统上编译并运行就可以成功的打印出系统信息,运行结果如下图所示:

 

 

3.将以上两步综合移植

现在我们就需要将上面两步综合让我们的UI能够动态显示。

我的想法是创建一个子线程来更新UI标签,这样就可以动态显示了。我的线程执行代码如下所示:

// 线程函数,用于更新标签文本
void *update_label_thread(void *arg) {
    float cpu_temp, cpu_usage;
    char cpu_temp_s[10],cpu_usage_s[10];
    char ip_address[MAX_IP_LENGTH]; // 用于存储IP地址的字符数组
    char timeBuffer[80];
    while (1) {
        time_t rawtime;
        struct tm *timeinfo;
        time(&rawtime);
        timeinfo = localtime(&rawtime);
        strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d\r\n%H:%M:%S", timeinfo);

        cpu_temp = get_cpu_temperature();
        if (cpu_temp != -1.0) {
            printf("CPU Temperature: %.2f°C\n", cpu_temp);
        } else {
            printf("Failed to get CPU Temperature.\n");
        }

        // 调用获取本地IP地址的函数
        if (get_local_ip(ip_address) == 0) {
            printf("Local IP address: %s\n", ip_address); // 打印获取到的IP地址
        } else {
            printf("Failed to get local IP address.\n"); // 获取失败时的错误提示
        }

        cpu_usage = get_cpu_usage();
        if (cpu_usage != -1.0) {
            printf("CPU Usage: %.2f%%\n", cpu_usage);
        } else {
            printf("Failed to get CPU Usage.\n");
        }

        // 更新 lvgl 标签的文本
        lv_label_set_text(guider_ui.screen_timeInfo, timeBuffer);
        lv_label_set_text(guider_ui.screen_ipInfo, ip_address);
        sprintf(cpu_usage_s, "%.2f%%", cpu_usage);
        lv_label_set_text(guider_ui.screen_CPULoad, cpu_usage_s);
        sprintf(cpu_temp_s, "%.2f *C", cpu_temp);
        lv_label_set_text(guider_ui.screen_CPUTemp, cpu_temp_s);

        // 等待一段时间,这里简单地使用 usleep 模拟
        usleep(10000); // 等待 10毫秒
    }
    return NULL;
}

 

然后再在main函数里创建线程调用上面那个线程执行函数。

 

最后进行编译就OK了。动态显示效果如下的视频所示:

VID_20240630_015922

我在这里解压了一个程序,可以看到CPU占用率立即就上去了,而且时间也一直再变,可见我们的程序没有出错。

我改变一下我的ip地址试试。

VID_20240630_020300

IP地址也变了,移植成功!

 

总结:通过本期测评我们成功用LVGL框架开发出了一个UI,并且能够显示系统信息到UI上,后面我们将移植MQTT程序将这些数据上传到云平台上(这个板子没有传感器,只能采取读系统信息这种方式)。

最新回复

GUI_Guider免费还是收费的呀?   详情 回复 发表于 2024-6-30 11:45
点赞 关注

回复
举报

6587

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

用LVGL框架开发出了一个UI,并且能够显示系统信息到UI上,看来是比较成功的,加油

 
 

回复

6773

帖子

2

TA的资源

版主

板凳
 

GUI_Guider免费还是收费的呀?

点评

完全免费的  详情 回复 发表于 2024-7-1 14:48
 
 
 

回复

224

帖子

0

TA的资源

一粒金砂(高级)

4
 
wangerxian 发表于 2024-6-30 11:45 GUI_Guider免费还是收费的呀?

完全免费的

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表