GuiLite C语言实现版本

简介

本项目是idea4good/GuiLite的C语言实现版本,基于2024-06-20节点的版本(提交ID:e9c4b57)。
本项目仓库:GuiLite_C

需求说明

作为芯片从业人员,国产芯片普遍资源有限(ROM和RAM比较少-都是成本,CPU速度比较高-100MHz),需要在512KB ROM,20KB左右RAM资源上实现手环之类的GUI操作(要有触摸),CPU可以跑96MHz。

第一次搞嵌入式GUI,问了一圈朋友,LVGL直接放弃(太绚丽了,个人觉得也不可能跑得动,而且代码应该也比较复杂,魔改会比较困难),有人建议手撸,那要死人了。最终有朋友推荐了idea4good/GuiLite,看了下介绍,GUI简单直接,所需的ROM和RAM也比较少,效果图里面也有很多所需的场景,持续有更新, Apache-2.0 license,比较符合我的需求。

尝试放到芯片上跑,受限于芯片资源和使用场景(基本没Heap,开发环境基本都是C)。由于要支持C++环境,带进来一堆系统库,作为搞嵌入式裸机环境的程序员,完全无法接受各种调用系统库操作,此外加上C++环境后,code size一下子也膨胀了很多。

关键是调试麻烦!!!调试麻烦!!!调试麻烦!!!看反汇编的时候太痛苦了(因为我是C语言小白)。

没什么说的,一共也就几千行代码,手撸成C语言(没现成的,问下来有人干了这个事情,但是没开源)。

所需资源分析

资源分GUI代码和控件所需的资源以及Framebuffer。Framebuffer是固定的,各个GUI有专门的优化处理。

GUI代码和控件所需资源

对于嵌入式环境而言,code size和ram size至关重要。所以以典型的cm0嵌入式开发环境为例,对code size和ram size进行分析。编译出来的大小见下表。

可以看到不同的例程所需资源差异巨大,这个涉及到GUI用到了哪些控件,字库,图片等。

注意:由于不同lib库对于printf、malloc等接口影响较大,库这些接口都不实现。资源紧张的场景可以按需简易实现。

注意:在porting\cm0\GCC路径下运行porting\cm0\GCC\utils_analysis_elf_size.py脚本,可以打印下面的表格保存在porting\cm0\GCC\output\README.md

appCode(Bytes)RAM(Bytes)
Hello3D193001124
Hello3Ddonut230965824
Hello3Dwave228485240
HelloAnimation1939652680
HelloCircle1886029636
HelloExSimple16332644
HelloFont1646292584
HelloKeypad115961068
HelloLayers14440328
HelloMario387324392
HelloMolecule97641736
HelloNets1692426128
HelloParticle119242304
HelloPendulum31144368
HelloScroll12100762160
HelloSlide39418641512
HelloStar73643848
HelloTimer87820712
HelloTransparent7115963128
HelloWave139247652
HelloWidgets272366696
HelloWindows24617441084

HelloExSimple为例,实现1个button+1个label只需要16332的code size和644的ram size字节,这里面字体占用的code size接近1半。

image-20240627205351143

Framebuffer

其实GuiLite所需的Code Size和Ram Size都比较小,但是其对Framebuffer需要很大。单个页面,不支持动效之类的场景还好,如果需要支持Scroll、Dialog、Slice等功能,就需要很多Framebuffer了。这块优化并不好,如果没有复杂动效的话,不需要提供Framebuffer,所需的资源就上面列表的值。

不然就是要n个Framebuffer了,一个240*320的RGB565的屏幕,需要153,600Bytes=150KBytes,这很可怕了,更别说部分场景还需要乘以n。

代码架构

没什么东西,也就是源代码,例程和编译配置。

  • example:各种GUI例程,基本是照搬GuiLite的来。
  • porting:程序的主入口,根据平台不同,有一些不同实现。
  • src:GuiLite代码实现部分,结构参考GuiLite处理。
  • build.mk和Makefile:Makefile文件。
GuiLite_C
 ├── build.mk
 ├── Makefile
 ├── example
 │   ...
 ├── porting
 │   ...
 └── src
     ...

使用说明

环境搭建

目前暂时只支持Windows编译,最终生成exe,可以直接在PC上跑。

目前需要安装如下环境:

  • GCC环境,笔者用的msys64+mingw,用于编译生成exe,参考这个文章安装即可。Win7下msys64安装mingw工具链 - Milton - 博客园 (cnblogs.com)。

编译说明

本项目都是由makefile组织编译的,编译整个项目只需要执行make all即可,调用make run可以运行。

根据具体需要可以调整一些参数,目前Makefile支持如下参数配置。

  • APP:选择example中的例程,默认选择为Hello3D
  • PORT:选择porting中的环境,也就是当前平台,默认选择为windows,cm0只用于评估code size和ram size需要专门编译。

也就是可以通过如下指令来编译工程:

make all APP=Hello3D

执行make run后,在windows环境就会弹出一个窗口,演示GUI效果了。

image-20240627173328170

改动说明

编译运行

为了方便维护,将idea4good/GuiLite和idea4good/GuiLiteSamples两个合在一起。

这样修改源码和Sample可以同时进行。

windows环境也不再使用Visual Studio编译,直接调用系统API进行窗口绘制。

代码结构

保留原本的代码结构不变,不过将源码和例程合并在一起。

原本只有一个.h文件,变成一个.h和一个.c

GuiLite.h原本是所有代码都放在这里,虽然代码看起来清爽了,但是可维护性和可阅读性比较差。这里只是将所有控件都放进来,应用层只需要引用这个头文件即可。

各个平台实现目前也不做过多考虑了,全部命令行操作。只关心code size和基本使用。目前只实现了windows和cm0的版本。

代码组织方式用Makefile,简单直接。

C++改动说明

还好作者用的C++特性较少,改起来比较轻松,下面对改动进行说明。

所有类用struct来实现,为方便使用,都用typedef声明下。

// C++ class impl
class AAA
{
}



// C struct impl
typedef struct AAA AAA;
struct AAA
{

}

类-成员

成员有结构体成员来实现,有一些类里面static处理,通过外部定义变量来处理,写代码的人自己控制操作空间。

// C++ class impl
class AAA
{
    int m_aaa;
    static int m_bbb;
}



// C struct impl
typedef struct AAA AAA;
struct AAA
{
	int m_aaa;
}

static int AAA_m_bbb;

类-构造函数、方法

public、protect和private就不区分了,软件自己控制操作空间。直接在方法名前面加入class_来区分。第一个传参调整为类对象的指针,名称为self。

对于构造函数(析构也一样,不过本项目没有),定义函数class_init来实现,因为编译器不会帮你调用,所以需要自己手动调用

// C++ class impl
class AAA
{
    AAA(aaa) : m_aaa(aaa) {}
public:
    void func_1(void)
    {
        
    }
    
protect:
    void func_2(void)
    {
        
    }
    
private:
    void func_3(void)
    {
        
    }
    
    int m_aaa;
}



// C struct impl
typedef struct AAA AAA;
struct AAA
{
}
void AAA_func_1(AAA *self)
{

}

void AAA_func_2(AAA *self)
{

}

void AAA_func_3(AAA *self)
{
    
}
void AAA_init(AAA *self, int aaa)
{
	self->m_aaa = aaa;
}

类-同名函数(重载)

函数有多个名称一样的函数,传参不同,或者部分参数有默认值。对于这种情况最全的参数列表为默认名称。最简单为func_simple,有其他参数,使用func_with_xxx

对于构造函数(析构也一样,不过本项目没有),定义函数class_init来实现,因为编译器不会帮你调用,所以需要自己手动调用

// C++ class impl
class AAA
{
    void func_1(int a=10)
    {
        
    }
}



// C struct impl
typedef struct AAA AAA;
struct AAA
{
}
void AAA_func_1(AAA *self, int a)
{

}

void AAA_func_1_simple(AAA *self)
{
	AAA_func_1(self, 10);
}

类-默认传参

最全的参数列表为默认名称。

最简单为func_simple

有其他参数,使用func_with_xxx

类-虚函数

这里最麻烦的就是虚函数了处理了,因为涉及到类继承,函数覆盖等处理。简单的处理就是一个虚函数一个函数指针,但是这样当类里面的虚函数比较多时,所以RAM就很多了。

所以这里用一个麻烦的处理,用函数列表来做,所有集成类的构造函数(也就是class_init)需要重新赋值虚函数表。

为区分,虚函数的函数命令需要在函数前面加入class_vir_。还要声明一个结构体为struct class_vir_api来定义虚函数表,同时类的成员加入const class_vir_api *m_api来存储函数列表指针。

// C++ class impl
class AAA
{
    AAA(aaa) : m_aaa(aaa) {}

    virtual void func_virtual_1(void)
    {
        
    }
    
    int m_aaa;
}



// C virtual api impl
typedef struct AAA_vir_api AAA_vir_api;
struct AAA_vir_api
{
    void (*func_virtual_1)(AAA *self);
}

// C struct impl
typedef struct AAA AAA;
struct AAA
{
    const AAA_vir_api* m_api; // virtual api
}
void AAA_vir_func_virtual_1(AAA *self)
{

}

static const AAA_vir_api AAA_vir_api_table = {
        AAA_vir_func_virtual_1,
};

void AAA_init(AAA *self, int aaa)
{
	self->m_aaa = aaa;
    
    self->m_api = &AAA_vir_api_table; // set virtual api.
}

类-继承

暂时只考虑只继承一个父类,不考虑继承多个父类的处理。

子类需要定义第一个成员为父类base,构造函数需要先调用父类的构造函数,有虚函数重写的,需要重新定义虚函数表,并覆盖。

所有涉及虚函数,使用基类作为函数self传参。虚函数实现的api接口,传参为基类的api,需要转一下BBB *b= (BBB*)self;

// C++ class impl
class AAA
{
    virtual void func_virtual_1(void)
    {
        
    }
}

class BBB : public AAA
{
    virtual void func_virtual_1(void)
    {
        
    }
}


// C virtual api impl
typedef struct AAA_vir_api AAA_vir_api;
struct AAA_vir_api
{
    void (*func_virtual_1)(AAA *self);
}

// C struct impl
typedef struct AAA AAA;
struct AAA
{
    const AAA_vir_api* m_api; // virtual api
}
void AAA_vir_func_virtual_1(AAA *self)
{

}

static const AAA_vir_api AAA_vir_api_table = {
        AAA_vir_func_virtual_1,
};

void AAA_init(AAA *self)
{
    self->m_api = &AAA_vir_api_table; // set virtual api.
}




// C struct impl
typedef struct BBB BBB;
struct BBB
{
    AAA base; // base class
}
void BBB_vir_func_virtual_1(AAA *self)
{
	BBB *bbb = (BBB *)self;
}

static const AAA_vir_api BBB_vir_api_table = {
        BBB_vir_func_virtual_1,
};

void BBB_init(BBB *self)
{
	AAA_init(&self->base); // call base init func
    
    self->base.m_api = &BBB_vir_api_table; // set virtual api.
}

下一步计划

Framebuffer全面移除

对于嵌入式GUI而言,GUI自身所需的Code Size其实完全比不上字体、图片等资源所需的空间(虽然这些可以放在外部存储中)。Ram其实是很关键的,而大头更多是Framebuffer。

目前Surface的设计,多个Surface就需要多个Framebuffer的设计,对于嵌入式而言还是太不友好了。

后续要么只保留一个Framebuffer,要么全部移除。

资源外部加载接口实现

目前想JPG和MP4这些,作者希望大家自己写控件实现,后续考虑提供专门的接口函数,方便MCU使用。当然现在这样也完全没问题,只是跨平台写代码麻烦点。

注释完善

用AI加一些注释,不然对于新人太不友好了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/750551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

英国国王座驾车标的逆向工程

多功能设计和制造解决方案为独特的挑战提供了引人注目的优势。Impossible Creations是一家来自英国的定制扫描、设计和建模公司,专门帮助客户完成无限制得创作任务。在他们最近接到的一个项目中,为了修复象征英国国王座驾的大英帝国吉祥物,Im…

【Docker】docker 替换宿主与容器的映射端口和文件路径

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 docker 替换宿主与容器的映射端口和文件夹 1. 正文 1.1 关闭docker 服务 systemctl stop docker1.2 找到容器的配置文件 cd /var/lib/docker/contain…

Web渗透:文件包含漏洞(part.1)

"文件包含漏洞"(File Inclusion Vulnerability)是一种常见的Web应用程序漏洞,攻击者可以通过这个漏洞在目标系统上包含或执行任意文件。主要有两种类型的文件包含漏洞: 本地文件包含(Local File Inclusion, …

[MySQL]购物管理系统—简略版

本文内容需以MySQL支持 特别感谢baidu comate AI提供的少量虚拟数据 0.建库(建立数据库——utf8字符集,utf8_general_ci排序规则) 1.此项目ER图如下 2.DDLDML(共九表,27数据) SET FOREIGN_KEY_CHECKS 0;DROP TABLE IF EXISTS goods; CREATE TABLE g…

计算机的错误计算(十四)

摘要 解读 GPU和CPU计算上的精度差异:GPU 的 3个输出的相对误差分别高达 62.5%、50%、62.5%。 例1. 计算下列两个矩阵的乘积: 显然,其结果为第一列: 这个结果是准确的。 例2. 已知上面 3 个矩阵是由下面代码产生或输出&…

HTML【重点标签】

一、列表标签 1.无序列表 父级别: 无序列表的标题 ----表示无序列表的整体,用于包裹li标签 子级别: 无序列表一行的内容 ----表示无序列表的每一项,用于包含一行的内容 语义:构建没有顺序的列表 特点:列…

C# YoloV8 模型效果验证工具(OnnxRuntime+ByteTrack推理)

C# YoloV8 模型效果验证工具(OnnxRuntimeByteTrack推理) 目录 效果 项目 代码 下载 效果 模型效果验证工具 项目 代码 using ByteTrack; using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using Sys…

ACC:Automatic ECN Tuning for High-Speed Datacenter Networks 相关知识点介绍(一)

目录 ACC(Adaptive Congestion Control) 总结 结合 ACC 和 ECN ECN ECN(Explicit Congestion Notification) 静态 ECN 动态 ECN 对比 总结 FCT——flow completion time 具体解释 小鼠流和大象流 小鼠流(…

2024百度之星第二场-小度的01串

补题链接: 码蹄集 一道经典线段树板子题。 区间修改01置换,区间查询子串权值。 唯一区别,权值要求的是相邻字符都不同所需修改的最小字符个数。 我们在线段树节点上分别维护当前连续区间: 奇数位是0的个数(j0&…

ROS1通信机制——以topic为例

ROS1 的通信机制 ROS1是一个分布式框架,为用户提供多节点(进程)之间的通信服务。 ROS1通信时有一个中心节点(ROS Master),进行信息匹配等工作。 ROS1 的话题通信机制 通信链接:XML/RPC 信息传…

YOLOV8图像分割预测后输出mask图

训练一个yolov8后,用官方的预测脚本一般是: results model.predict(img_path, saveTrue, save_diroutput_folder) 运行此代码会直接在run里面生成一个文件夹,保存预测图像。如果要获取分割后的mask点,或mask的轮廓点&#xff0…

WIFI各版本的带宽

带宽的定义: 带宽在网络领域通常指信道带宽,即信号在频谱中占用的频宽,单位是MHz(兆赫)。在无线通信中,带宽越宽,能够传输的数据量越大,因此信道带宽直接影响着数据传输速率。WiFi标…

SKYDROID-C12—— 让美景近在眼前

C12是一款小型高清双光吊舱,使用新一代影像芯片,搭配高清无畸变摄像头,有效像素达到500万,拥有强悍的2K视频录制和拍照能力,支持数字变倍,随时随地捕捉清晰的图像,让远处美景近在眼前。

Clickhouse 的性能优化实践总结

文章目录 前言性能优化的原则数据结构优化内存优化磁盘优化网络优化CPU优化查询优化数据迁移优化 前言 ClickHouse是一个性能很强的OLAP数据库,性能强是建立在专业运维之上的,需要专业运维人员依据不同的业务需求对ClickHouse进行有针对性的优化。同一批…

【Android11】开机启动日志捕捉服务

一、前言 制作这个功能的原因是客户想要自动的记录日志中的报错和警告到设备的内存卡里面。虽然开发者模式中有一个“bug report” 会在/data/user_de/0/com.android.shell/files/bugreports/目录下生成一个zip包记录了日志。但是客户觉得这个日志很难获取到他们需要的信息&am…

Transformer教程之神经网络和深度学习基础

在当今的人工智能领域,Transformer已经成为了一个热门的词汇。它不仅在自然语言处理(NLP)领域取得了巨大的成功,还在计算机视觉等其他领域展现出了强大的潜力。然而,要真正理解Transformer,我们首先需要扎实…

希喂生骨肉冻干值得入手吗?拯救瘦弱、增强抵抗力最强主食测评!

希喂生骨肉冻干值得入手吗?很多小姐妹觉着自家猫咪太瘦了、体质不咋好,换季还敏感、掉毛、不吃东西,听说生骨肉冻干好吸收、营养好,可以改善体质、拯救瘦弱、增强抵抗力,为了图省事,开始盲入生骨肉冻干&…

Linux—进程与计划管理

目录 一、程序 二、进程 1、什么是进程 2、进程的特点 3、进程、线程、携程 3.1、进程 3.2、线程 3.3、携程 三、查看进程信息 1、ps -aux 2、ps -elf 3、top ​3.2、输出内容详解 3.2.1、输出第一部分解释 3.2.2、输出第二部分解释 4、pgrep 5、pstree 四、进…

The ‘textprediction‘ attribute will be removed in the future

页面标签不展示,明明是复制的,反复检查,眼睛都看瞎了,也没找到,最后还是看后台报错,The textprediction attribute will be removed in the future说什么要被废弃,但是好好的标签怎么会无缘无辜…

C语言 | Leetcode C语言题解之第191题位1的个数

题目: 题解: int hammingWeight(uint32_t n) {int ret 0;while (n) {n & n - 1;ret;}return ret; }