嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > 树莓派之lvgl-实时曲线展示

树莓派之lvgl-实时曲线展示

树莓派之lvgl-实时曲线展示

概述

lvgl是轻量级UI库,采用c语言编程,适用于各种嵌入式平台。今天打算在树莓派上基于lvgl展示实时曲线,用于数据监控和可视化。

结果

使用硬件

  • 树莓派4B:系统:ubunt20.04(无界面)
  • 带HDMI的显示器

编程

主要包含以下内容

  • 移植lvgl
  • 使用chart组件完成数据可视化

移植lvgl

树莓派端基于linux的frame做显示,cmake作为工程的构建工具。

1. 下载源码

lvgl官方提供基于linux的frame的源码。

git clone https://github.com/lvgl/lv_port_linux_frame_buffer

由于lv_driverslvgl很难通过git submodule update --init --recursive命令下载,因此可以直接下载相应的文件:

cd lv_port_linux_frame_buffer
rm -rf lv_drivers
rm -rf lvgl
git clone https://github.com/lvgl/lv_drivers.git
git clone https://github.com/lvgl/lvgl.git

源代码采用makefile作为构建工具,感觉makefile太难写,所以采用cmake作为构建工具。

文件夹结构:

.
├── CMakeLists.txt
├── LICENSE
├── Makefile
├── README.md
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── Makefile
│   ├── cmake_install.cmake
│   ├── little_prj
│   ├── main
│   ├── my_design
│   ├── project
│   └── rpi_lvgl
├── main.c
├── my_design
│   ├── CMakeLists.txt
│   ├── my_UI.c
│   └── my_UI.h
└── rpi_lvgl
    ├── CMakeLists.txt
    ├── lv_conf.h
    ├── lv_drivers
    ├── lv_drv_conf.h
    ├── lvgl
    └── mouse_cursor_icon.c
  • Makefile:官方构建工具(不用)

  • build:编译文件

  • main.c:主函数

  • my_design:实时曲线显示界面代码

  • rpi_lvgl官方代码:包括lv_drivers lvgl

各文件下的CMakeLists.txt需要自己建立和编写。

2. 编译

编写./CMakeLists.txt

#cmake最低使用版本
cmake_minimum_required (VERSION 3.1)
# 编译器
set(CMAKE_C_COMPILER " aarch64-linux-gnu-gcc")
# 编译选项
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_C_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g0 -ggdb ")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

#工程名
project(lvgl_prj)
# 子目录
add_subdirectory(rpi_lvgl)
add_subdirectory(my_design)

# 头文件
include_directories(./rpi_lvgl/)
include_directories(./rpi_lvgl/lvgl/)
include_directories(./rpi_lvgl/lvgl/demos/)
include_directories(./rpi_lvgl/lv_drivers/)
include_directories(./my_design/)
# 添加.c文件
aux_source_directory(./rpi_lvgl DIR_SRC)
aux_source_directory(./ DIR_SRC)
message("DIR_SRC : ${DIR_SRCS}")

# 可执行性文件
add_executable(project ${DIR_SRC})

# 添加thread库
find_package(Threads)

# 设置链接库变量
set(VAR lvgl_lib)
set(VAR ${VAR} lv_drivers_lib my_lvgl_lib)
message("VAR : ${VAR}")
# 链接 lvgl_lib lv_drivers
target_link_libraries(project ${VAR})
# 链接thread
target_link_libraries(project ${CMAKE_THREAD_LIBS_INIT} m)

主要规定编辑器,下层CMakeLists.txt的位置,头文件路径,依赖库。

编写./rpi_lvgl/CMakeLists.txt

# 添加头文件路径
include_directories(.)
include_directories(./lvgl/)
include_directories(./lvgl/demos/)
include_directories(./lv_drivers/)

# 添加子目录
add_subdirectory(lvgl)
add_subdirectory(lv_drivers)

编写./rpi_lvgl/lv_drivers/CMakeLists.txt

file(GLOB_RECURSE SOURCES ./*.c)
# 链接源文件到lv_drivers_lib
add_library(lv_drivers_lib ${SOURCES})

编写./rpi_lvgl/lvgl/CMakeLists.txt


file(GLOB_RECURSE SOURCES src/*.c demos/*.c)
# 链接源文件到lvgl_lib
add_library(lvgl_lib ${SOURCES} )

编译前需要注意输入设备的ivent的编号

// 位置:/lv_port_linux_frame_buffer/rpi_lvgl/lv_drv_conf.h
#  define EVDEV_NAME   "/dev/input/event0"

屏幕尺寸

disp_drv.hor_res    = 640;
disp_drv.ver_res    = 480;

编译工程

cd build
cmake ..
make -j6

运行./project可运行demo


该demo展示的界面为lvgl所有的组件的基础形态。

实现实时曲线

实时曲线展示主要使用chart组件.

数据的更新采用定时器的方式


void creat_char()
{
    
    chart = lv_chart_create(lv_scr_act());
    lv_obj_set_size(chart, 600400);
    lv_obj_align(chart, LV_ALIGN_CENTER, 2020);
    // 折线图 无点
    lv_obj_set_style_size(chart, 0, LV_PART_INDICATOR);
    // 显示数据
    ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_ORANGE), LV_CHART_AXIS_PRIMARY_Y);
    // 点的数量
    lv_chart_set_point_count(chart, 600);
    // 限制坐标轴范围
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -100100);
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 150);
    // 设置坐标
    lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10565true40);
    // 初始值
    lv_chart_set_all_value(chart, ser1, 0);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);

    // 创建一个标签
    lv_obj_t *label;
    label = lv_label_create(chart);
    lv_obj_set_size(label, 10050);
    lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 00);
    lv_label_set_text(label, "Time");

    lv_timer_create(change_data, 5 ,NULL);
    
}

定时器回调函数

void change_data(lv_timer_t * timer)
{
    static float cnt = 0;
    static int num = 0;
    num += timer->period;
    if(num % 1000 == 0 )
    {
        lv_obj_t * label = lv_obj_get_child(chart, 0);
        lv_label_set_text_fmt(label, "time: %d s",  num / 1000);
    }
    cnt += 0.05;
    float a = sin(cnt);
    lv_chart_set_next_value(chart, ser1, (lv_coord_t)(100 * a));  
}