1.1 Echo的main函数

关于这里的/dev和framebuffer,可以参考韦东山的Linux应用内容,主要是读取内容,拿到对应的屏幕像素点所对应的数据位置信息。

#include "lvgl/lvgl.h"                    // 引入LVGL图形库核心头文件
#include "lvgl/demos/lv_demos.h"            // 引入LVGL官方示例(当前未使用)
#include <unistd.h>                         // POSIX系统调用接口:usleep()
#include <pthread.h>                        // 线程库(预留多线程扩展)
#include <time.h>                           // 时间相关函数
#include <stdio.h>                          // 标准输入输出
#include <stdlib.h>                         // 标准库函数

#include "gui_app/common/lv_lib.h"          // 项目自定义LVGL公共库(页面管理器、栈、动画)
#include "gui_app/ui.h"                     // UI应用层接口(ui_init、页面注册、系统参数)

// 【工具函数】获取环境变量,若不存在则返回默认值
// 数据处理:从进程环境变量中按name查找字符串,找不到则返回dflt兜底值
static const char *getenv_default(const char *name, const char *dflt)
{
    return getenv(name) ? : dflt;           // 三元运算符:getenv返回NULL时取dflt
}

#if LV_USE_EVDEV
// 【条件编译 - ARM设备模式】初始化Linux evdev输入设备(触摸屏)
static void lv_linux_indev_init(void)
{
    lv_indev_t * touch;                      // 声明LVGL输入设备句柄
    // 数据处理:创建一个指针类型的输入设备,绑定到/dev/input/event0(触摸屏设备节点)
    // LV_INDEV_TYPE_POINTER 表示这是一个坐标指点类设备(触摸屏/鼠标)
    touch = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event0");
}
#endif

#if LV_USE_LINUX_FBDEV
// 【条件编译 - ARM设备Framebuffer模式】初始化Linux Framebuffer显示设备
static void lv_linux_disp_init(void)
{
    // 数据处理:从环境变量读取framebuffer设备路径,默认为/dev/fb0(第一个帧缓冲设备)
    const char *device = getenv_default("LV_LINUX_FBDEV_DEVICE", "/dev/fb0");
    // 创建LVGL显示设备实例,返回lv_display_t*句柄
    lv_display_t * disp = lv_linux_fbdev_create();
    // 数据处理:将显示设备句柄与具体的framebuffer设备文件绑定
    // 后续LVGL的渲染结果将通过该设备写入显存映射的内存区域
    lv_linux_fbdev_set_file(disp, device);
}
#elif LV_USE_LINUX_DRM
// 【条件编译 - ARM设备DRM模式】初始化Linux DRM(Direct Rendering Manager)显示设备
static void lv_linux_disp_init(void)
{
    // 数据处理:从环境变量读取DRM显卡设备路径,默认为/dev/dri/card0
    const char *device = getenv_default("LV_LINUX_DRM_CARD", "/dev/dri/card0");
    // 创建DRM显示设备实例
    lv_display_t * disp = lv_linux_drm_create();
    // 数据处理:将DRM显示设备与显卡设备文件及plane绑定(-1表示自动选择plane)
    lv_linux_drm_set_file(disp, device, -1);
}
#elif LV_USE_SDL
// 【条件编译 - PC仿真模式】使用SDL2库创建窗口和鼠标输入
static void lv_linux_disp_init(void)
{
    // 数据处理:从环境变量读取SDL窗口宽度,默认320像素(匹配目标屏幕分辨率)
    const int width = atoi(getenv("LV_SDL_VIDEO_WIDTH") ? : "320");
    // 数据处理:从环境变量读取SDL窗口高度,默认240像素
    const int height = atoi(getenv("LV_SDL_VIDEO_HEIGHT") ? : "240");

    // 创建SDL2窗口,尺寸为width×height,作为LVGL的显示输出目标
    // 内部会创建SDL_Renderer用于硬件加速渲染
    lv_sdl_window_create(width, height);
}

// PC仿真模式下使用SDL鼠标模拟触摸/点击输入
static void lv_linux_indev_init(void)
{
    // 数据处理:创建一个基于SDL的鼠标输入设备
    // SDL会将操作系统的鼠标事件(移动、点击)转换为LVGL可识别的输入事件
    lv_sdl_mouse_create();
}

#else
#error Unsupported configuration              // 如果以上三种模式都未启用,编译报错终止
#endif

// ==================== 程序入口函数 ====================
int main(void)
{
    // ===== 第1步:LVGL核心库初始化 =====
    // 数据处理:
    //   1. 初始化LVGL内部全局状态机(内存管理器、对象链表、定时器列表等)
    //   2. 分配LV_MEM_SIZE(1MB)大小的内存池给LVGL使用
    //   3. 设置默认显示刷新周期LV_DEF_REFR_PERIOD=20ms
    //   4. 此时还没有任何显示设备和输入设备,只是一个空壳框架
    lv_init();

    /*Linux display device init*/
    // ===== 第2步:显示设备初始化 =====
    // 根据dev_conf.h中的宏定义选择不同的显示驱动:
    //   - PC仿真模式(LV_USE_SDL):创建SDL窗口,像素数据通过SDL_Renderer输出到桌面窗口
    //   - ARM设备FB模式(LV_USE_LINUX_FBDEV):打开/dev/fb0,通过mmap映射显存直接写屏
    //   - ARM设备DRM模式(LV_USE_LINUX_DRM):通过DRM KMS接口控制GPU输出到屏幕
    // 数据处理:创建并配置显示设备后,LVGL获得了一个可以输出像素的目标表面
    lv_linux_disp_init();

    // ===== 第3步:输入设备初始化 =====
    // 根据平台选择不同的输入驱动:
    //   - PC仿真模式:SDL鼠标,捕获操作系统鼠标事件
    //   - ARM设备模式:evdev触摸屏,读取/dev/input/event0的原始触摸数据
    // 数据处理:注册输入回调后,LVGL能够在每次lv_timer_handler()中轮询获取用户输入
    lv_linux_indev_init();

    /*Create a Demo*/
    // lv_demo_widgets();                    // 注释掉的LVGL官方控件演示
    // lv_demo_widgets_start_slideshow();     // 注释掉的控件幻灯片演示

    // ===== 第4步:应用层UI初始化 =====
    // 数据处理(详见ui.c中的ui_init函数):
    //   1. 加载/创建系统参数配置文件(时间、亮度、音量、WiFi、位置等)
    //   2. 初始化GPIO硬件引脚(LED蓝灯、两个电机控制引脚)
    //   3. 设置LVGL主题样式(蓝色主色调+红色辅助色)
    //   4. 初始化页面管理器(分配页面栈内存,容量5层)
    //   5. 注册11个应用页面到页面管理器(HomePage、SettingPage、WeatherPage等)
    //   6. 打开HomePage主页(调用其init函数创建所有UI控件)
    //   7. 创建1秒周期的定时器(_maintimer_cb),用于LED闪烁和参数保存
    ui_init();

    /*Handle LVGL tasks*/
    // ===== 第5步:主事件循环(程序的核心执行引擎)=====
    while(1) {
        // ★★★ lv_timer_handler() - LVGL核心调度函数 ★★★
        //
        // 【执行频率】:约1000次/秒(每次循环1ms延时)
        // 【内部数据处理流程】:
        //
        // ┌──────────────────────────────────────────────────────┐
        // │ ① 系统时间更新                                        │
        // │   调用lv_tick_inc(1)递增LVGL内部时钟计数器            │
        // │   该计数器是所有LVGL定时器的基准时间源                │
        // ├──────────────────────────────────────────────────────┤
        // │ ② 定时器回调扫描与执行                                │
        // │   遍历LVGL内部定时器链表,检查每个定时器是否到期       │
        // │   到期的定时器将被触发执行其回调函数:                 │
        // │   - _maintimer_cb (每1000ms):LED闪烁 + 每5分钟存参数│
        // │   - ui_home_timer_cb (每5000ms):更新时间标签文字      │
        // │   - 动画定时器 (每~16ms):更新正在播放的动画属性值     │
        // ├──────────────────────────────────────────────────────┤
        // │ ③ 输入设备数据采集                                    │
        // │   调用已注册输入设备的read_cb函数:                    │
        // │   - SDL模式:从SDL事件队列取鼠标状态                  │
        // │   - EVDEV模式:从/dev/input/event0读取触摸坐标        │
        // │   原始输入数据被转换为LVGL内部的indev_data结构         │
        // ├──────────────────────────────────────────────────────┤
        // │ ④ 输入事件分发与处理                                  │
        // │   将原始输入数据转换为高级事件:                       │
        // │   - LV_EVENT_PRESSED(按下)                          │
        // │   - LV_EVENT_PRESSING(持续按压/拖拽)                │
        // │   - LV_EVENT_RELEASED(释放)                         │
        // │   - LV_EVENT_CLICKED(单击=按下+释放)                │
        // │   - LV_EVENT_GESTURE(滑动手势:左/右/上/下)        │
        // │   匹配到事件目标对象后,调用其注册的事件回调函数       │
        // │   例如:按钮点击→ui_event_AppsBtn→lv_lib_pm_OpenPage  │
        // ├──────────────────────────────────────────────────────┤
        // │ ⑤ 对象脏区标记与重绘计算                              │
        // │   检查哪些对象的属性在上一周期发生了变化               │
        // │   (位置、大小、颜色、透明度、文字内容等)             │
        // │   将这些对象标记为"脏",计算需要重绘的最小矩形区域     │
        // ├──────────────────────────────────────────────────────┤
        // │ ⑥ 渲染输出                                            │
        // │   对脏区域进行软件光栅化渲染:                         │
        // │   - 绘制背景色/背景图                                 │
        // │   - 绘制边框                                           │
        // │   - 绘制阴影                                           │
        // │   - 绘制文字(使用FreeType/TinyTTF字体引擎)          │
        // │   - 绘制图片(PNG/JPG/BMP解码后绘制)                 │
        // │   - 处理混合模式(Alpha合成)                         │
        // │   最终像素数据写入显示设备的flush buffer               │
        // ├──────────────────────────────────────────────────────┤
        // │ ⑦ 显示刷新(Flush)                                   │
        // │   调用显示设备的flush_cb函数:                        │
        // │   - SDL模式:调用SDL_RenderPresent呈现到窗口          │
        // │   - FB模式:mmap内存已写入,LCD控制器自动读取刷新      │
        // │   - DRM模式:通过ioctl提交framebuffer到显示队列        │
        // └──────────────────────────────────────────────────────┘
        lv_timer_handler();

        // 数据处理:让出CPU 1ms(1000微秒)
        // 作用:
        //   1. 防止while(1)死循环占用100% CPU导致系统卡顿
        //   2. 控制lv_timer_handler的调用频率约为1000Hz
        //   3. 给其他线程/进程(如网络请求线程)让出CPU时间片
        // 注意:实际运行频率可能低于1000Hz,因为lv_timer_handler本身也有执行耗时
        usleep(1000);                            // 1000微秒 = 1毫秒
    }

    return 0;                                   // 理论上不可达(无限循环)
}

posted @ 2026-09-03 16:12  JaredWang  阅读(6)  评论(0)    收藏  举报