树莓派之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_drivers
和lvgl
很难通过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, 600, 400);
lv_obj_align(chart, LV_ALIGN_CENTER, 20, 20);
// 折线图 无点
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, -100, 100);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 1, 50);
// 设置坐标
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 40);
// 初始值
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, 100, 50);
lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 0, 0);
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));
}