泰山派NAS-LVGL9监控器实现
项目代码GIt地址:https://github.com/ccy-studio/tspi-nas-lvgl
本教程将描述如何使用最新版本LVGL9.1实现在Linux下实现系统监控器项目。
文章教程将围绕一下几个点来逐步讲解。
- 系统环境搭建
- 项目初始配置
- Visual Studio模拟器
- Linux适配FB驱动LVGL画面输出
- Linux适配触摸驱动集成LVGL系统
- Activity组件思想的引入
- GUI框架模拟Android生命周期代码讲解
- GIF动画的使用与控制
- 自定义编译规则
- 启动传参解析方法
- SCP传输文件到开发板
1. 系统环境搭建
LVGL在嵌入式系统中的应用
- LVGL(Light and Versatile Graphics Library)是一个常用于嵌入式系统的GUI框架,主要运行在嵌入式设备中。然而,实际开发中,通常在电脑上进行开发,然后将打包的程序部署到嵌入式设备上运行。这种方式对开发过程并不友好,因为我们无法调试,且运行过程繁琐。
- 开发流程优化
为了优化开发流程,在将程序移植到嵌入式设备之前,我们先在本机模拟器上调试UI。这样做有以下几个优点:
- 即时调试:可以在本机上直接调试UI,快速发现并修复问题。
- 加速开发:减少频繁部署到嵌入式设备的步骤,加快开发速度。
- 简化流程:通过模拟器调试,可以简化开发流程,使开发更加高效和直观。
- 模拟器的使用
在开发过程中,使用本机模拟器进行调试和验证UI设计。调试完成后,再将优化好的程序移植到嵌入式设备中进行实际运行。这一过程中,需要注意以下几点:
- 模拟器配置:确保模拟器的环境和嵌入式设备的实际运行环境尽量一致,以减少移植后的问题。
- 功能测试:在模拟器中尽可能全面地测试所有功能,确保在嵌入式设备上的运行稳定性。
- 性能调优:虽然模拟器能提供良好的调试环境,但需要在实际设备上进行最终的性能优化,以适应嵌入式设备的资源限制。
通过这种方式,可以有效地提高开发效率,减少开发中的不确定因素,确保最终在嵌入式设备上运行的程序稳定可靠。
1.1 代码拉取
-
新建工作目录例如:lvgl_blog_demo
-
拉取LVGL9.1代码
git clone https://github.com/lvgl/lvgl.git -b release/v9.1
-
拉取Visual Studio运行的模拟器源代码
git clone --recurse-submodules https://github.com/lvgl/lv_port_pc_visual_studio.git
-
随便找一个临时文件夹下拉取代码
git clone https://github.com/lvgl/lv_port_linux.git -b release/v9.0
-
将拉取的lv_port_linux目录下的文件:
Makefile、main.c、CMakeLists.txt、.gitignore。复制到lvgl_blog_demo目录下
-
修改Makefile文件内容
注释掉30行的内容:
#CSRCS +=$(LVGL_DIR)/mouse_cursor_icon.c
-
修改CMakeLists.txt文件内容如下
修改:
add_executable(main main.c mouse_cursor_icon.c)
去掉mouse_cursor_icon.c
-
复制lvgl文件夹内lv_conf_template.h文件到根目录(lvgl_blog_demo)下,并且重新命名为lv_conf.h
-
修改lv_conf.h内的文件内容如下:
9.1 第15行打开配置赋值1:
#if 1 /*Set it to "1" to enable content*/
添加屏幕属性自定义参数值:
/*==================== saisaiwa 设置屏幕尺寸 *====================*/ #define DISP_WIDTH 320 #define DISP_HIGHT 240
9.2 修改内存大小为合适大小这里分配的是:#define LV_MEM_SIZE (2048 * 1024U)
9.3 修改刷新时间为10ms: #define LV_DEF_REFR_PERIOD 10 /*[ms]*/
9.4 修改 #define LV_USE_OS LV_OS_PTHREAD
为 LV_OS_PTHREAD
使用Linux的线程函数
9.5 开发过程中需要配合LOG日志打印,设置开启LOG:#define LV_USE_LOG 1
9.6 开启使能FLOAT浮点数支持:#define LV_USE_FLOAT 1
9.7 开启使能GIF:#define LV_USE_GIF 1
;开启GIF的缓存支持:#define LV_GIF_CACHE_DECODE_DATA 1
9.8 如果你想在屏幕显示内存CPU与FPS刷新率请使能:#define LV_USE_SYSMON 1
9.9 开启使能FB子系统 #define LV_USE_LINUX_FBDEV 1
开启双缓存: #define LV_LINUX_FBDEV_BUFFER_COUNT 2 //开启双缓存
9.10 启用对Linux输入子系统(evdev)的支持: #define LV_USE_EVDEV 1
1.2 配置屏幕适配
打开main.c文件,快速定位到 lv_linux_fbdev_create
的实现。
路径为:lvgl\src\drivers\display\fb\lv_linux_fbdev.c
修改此方法,内容如下:
lv_display_t * lv_linux_fbdev_create(int32_t hor_res, int32_t ver_res)
{
static bool inited = false;
if(!inited) {
lv_tick_set_cb(tick_get_cb);
inited = true;
}
lv_linux_fb_t * dsc = lv_malloc_zeroed(sizeof(lv_linux_fb_t));
LV_ASSERT_MALLOC(dsc);
if(dsc == NULL) return NULL;
lv_display_t * disp = lv_display_create(hor_res, ver_res);
if(disp == NULL) {
lv_free(dsc);
return NULL;
}
dsc->fbfd = -1;
lv_display_set_driver_data(disp, dsc);
lv_display_set_flush_cb(disp, flush_cb);
return disp;
}
接下来修改.h头文件的定义:lvgl\src\drivers\display\fb\lv_linux_fbdev.h
将源文件内的:lv_display_t * lv_linux_fbdev_create(void);
更改为:
lv_display_t * lv_linux_fbdev_create(int32_t hor_res, int32_t ver_res);
修改main.c内的方法如下
/*Linux frame buffer device init*/
lv_display_t * disp = lv_linux_fbdev_create(DISP_WIDTH, DISP_HIGHT);
lv_linux_fbdev_set_file(disp, "/dev/fb0");
1.3 配置屏幕触摸
在main.c文件内添加代码
char evdev[41] = "/dev/input/event1";
// 初始化Inpu输入子系统. 这里需要设置
lv_indev_t* cdev = lv_evdev_create(LV_INDEV_TYPE_POINTER, evdev);
lv_evdev_set_swap_axes(cdev, true); // 设置xy交换翻转
lv_evdev_set_calibration(cdev, 0, 0, DISP_WIDTH,
DISP_HIGHT); // 设置触摸最大范围和最小范围
完整的main.c内容如下:
#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
int main(void)
{
lv_init();
/*Linux frame buffer device init*/
lv_display_t * disp = lv_linux_fbdev_create(DISP_WIDTH, DISP_HIGHT);
lv_linux_fbdev_set_file(disp, "/dev/fb0");
char evdev[41] = "/dev/input/event1";
// 初始化Inpu输入子系统. 这里需要设置
lv_indev_t* cdev = lv_evdev_create(LV_INDEV_TYPE_POINTER, evdev);
lv_evdev_set_swap_axes(cdev, true); // 设置xy交换翻转
lv_evdev_set_calibration(cdev, 0, 0, DISP_WIDTH,
DISP_HIGHT); // 设置触摸最大范围和最小范围
/*Create a Demo*/
lv_demo_widgets();
lv_demo_widgets_start_slideshow();
/*Handle LVGL tasks*/
while(1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
1.4 测试Demo编译
默认的main.c内默认运行的是Demo程序
/*Create a Demo*/
lv_demo_widgets();
lv_demo_widgets_start_slideshow();
此默认的程序需要确保lv_conf.h中开启:#define LV_USE_DEMO_WIDGETS 1
所以如果按照上面的步骤配置完成后我们就可以测试编译,然后可以将编译的可执行程序复制到开发版上运行了。 编译的前提需要准备开发环境。
- 将项目文件复制到Ubuntu下,请确保泰山派的SDK你已经配置好了环境解压完成。
- 首先复制文件到Ubutnu系统下。
- 在项目跟目录文件内执行:**
mkdir build
**创建build文件夹 - 设置临时CC编译器环境变量,~/share_file/是我的本机路径。
export CC=~/share_file/tspi_linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc
- 进入build目录下:
cd build
- 执行生成编译文件:
cmake ..
- 生成最终的可执行程序,执行命令:
make -j8
- 成功编译会生成可执行文件
[ 98%] Linking C static library ../lib/liblvgl_examples.a
[ 99%] Built target lvgl_examples
Consolidate compiler generated dependencies of target main
[ 99%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking CXX executable ../bin/main
[100%] Built target main
- 最终生成的可执行文件会放置在项目根目录/bin目录中名为main的文件,此文件可以直接复制到开发板中,在开发板中直接运行./main 即可点亮TFT小屏幕。PS:如果不需要程序自动点击请去掉这行
lv_demo_widgets_start_slideshow();
此编译方式需要配合命令行并且需要每次终端中配置编译器环境变量非常不方便。后面将介绍使用Vscode来快速编译。
1.5 Vscode配置编译
建议在WIN环境下使用远程连接Ubutnu
安装Vscode插件
安装插件完成后配置Ubutnu连接的地址
这里有两个不同的写法配置
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host Ubuntu22.04
HostName 192.168.50.99
User chen
Port 22
PasswordAuthentication yes
ForwardX11 yes
ForwardX11Trusted yes
ForwardAgent yes
PubkeyAuthentication yes
Host Home-Ubuntu22.04
HostName server.natappfree.cc
User chen
Port 43636
第一个HOST是使用ssh的秘钥验证授权免输入密码,但是需要配置到Ubutnu内的.ssh中
第二个是需要输入密码的配置。 具体选择看自身情况,更多细节请自行搜索如何配置远程登录。
接下里需要安装Cmake插件,
按照完成后配置GCC编译工具链,使用快捷键Ctrl+Shift+P 输入cmake 找到并选择下面的选项
添加一项新的配置
{
"name": "GCC 6.3.1 arm-rockchip-tspi",
"compilers": {
"C": "/home/chen/share_file/tspi_linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc",
"CXX": "/home/chen/share_file/tspi_linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++"
},
"isTrusted": true
}
注意路径请改为你自己电脑环境的路径。
上面的操作只是添加环境配置项,接下来我们配置编译器环境应用到项目中。
下图中配置指定我们刚刚新添加的配置,然后等待配置完成即可。
控制台输出这个报错不用理会。
[proc] 命令: aarch64-linux-gnu-gcc -v 失败,出现错误: Error: spawn aarch64-linux-gnu-gcc ENOENT
[proc] 命令: aarch64-linux-gnu-g++ -v 失败,出现错误: Error: spawn aarch64-linux-gnu-g++ ENOENT
点击生成则会自动开始编译并生成可执行程序,当我们每次修改程序后重新打包新程序只需要点击下,一键触发即可快速生成。
1.6 Visual Studio模拟器配置
-
下载并安装: 下载地址
-
启动Visual Studio,选择右边的打开项目或者解决方案
-
选择打开:D:\MyTempProjects\lvgl_blog_demo\lv_port_pc_visual_studio\LVGL.sln
-
打开后选择X64模式然后直接运行下实验效果。
-
修改屏幕尺寸打开文件LvglWindowsSimulator.cpp修改内部的800,400为你自己的TFT屏幕尺寸大小
例如改为350x200lv_display_t* display = lv_windows_create_display( L"LVGL Windows Simulator Display 1", 350, 200, zoom_level, allow_dpi_override, simulator_mode);
2. 开发-Activity组件思想
核心思想采用安卓Activity的模式改造简化版方案组件框架。
设计页面为单独模块,其可拥用生命周期控制和内存管理。
特点:
- UI页面将以组件结构化定义
- 5种页面生命周期回调函数
- 可设置的页面3种压栈模式
- 拥有消息总线模式,可指定方向的事件广播发送
- 独立的EventBus工具
- 每个页面独立的GroupKey
- 动态可设置启动页
2.1 使用说明
核心包文件夹名为: uikit
UI框架实现为ui.c 消息总线为data_bus.c
2.2 构建第一个页面
定义页面ID映射表
示例代码
/**
* @brief 页面ID定义
*/
typedef enum {
PAGE_LUNCH,
PAGE_MONITOR_STYLE1,
} page_id_t;
创建一个页面组件定义一个 page_lunch.c
示例代码
#include "application.h"
static lv_obj_t * container;
static ui_data_t * ui;
LV_IMG_DECLARE(img_start_lunch)
static void timer_cb(lv_timer_t * timer)
{
ui_intent_t intent;
intent.anim = true; //开启页面切换动画
ui_fun_fast_create_intent(ui, PAGE_MONITOR_STYLE1, &intent);
ui_fun_start_activity(&intent);
// 销毁当前页面
ui_fun_finish(ui, false);
}
static void on_page_create(ui_data_t * ui_dat, void * params)
{
ui = ui_dat;
container = lv_image_create(lv_screen_active());
lv_img_set_src(container, &img_start_lunch);
lv_timer_t * timer = lv_timer_create(timer_cb, 3000, NULL);
lv_timer_set_repeat_count(timer, 1);
}
static void on_page_destoy(void * params)
{}
static lv_obj_t * page_get_view()
{
return container;
}
ui_data_t page_lunch = {.id = PAGE_LUNCH,
.launcher = true,
.launcher_mode = SINGLE_TASK,
.fun_get_view = page_get_view,
.fun_on_create = on_page_create,
.fun_on_destoy = on_page_destoy};
其中 ui_data_t
结构体为页面组件实现结构实现体
id 参数需要传入唯一的页面ID作为标识符
launcher 是一个布尔类型传入true可以作为第一个启动页作为根页面
launcher_mode 是设置压栈模式可配置如下
typedef enum {
SINGLE_TOP = 0, // 栈顶复用模式
STANDARD, // 标准模式(最基本的模式,每次启动都会创建一个新的 ACTIVITY)
SINGLE_TASK, // 栈内复用模式(如果栈内复用,那么会 Clear Task 中这个 Activity
// 上面的其他的 Activity)
} launch_mode_t;
因为页面的fun_on_create参数回调是优先级最高回调的,所以需要在此方法内初始化UI组件,最后将根lv_obj节点使用fun_get_view回调的参数返回出去。
页面跳转
ui_intent_t intent;
intent.anim = true; //开启页面切换动画
ui_fun_fast_create_intent(ui, PAGE_MONITOR_STYLE1, &intent);
ui_fun_start_activity(&intent);
// 销毁当前页面
ui_fun_finish(ui, false);
2.3 注册页面组件并启动
我们将多个写好的页面组件 ui_data_t
的实现注册到Activity管理器容器内,由Activity管理器启动我们的UI组件。
示例代码:
#include "application.h"
extern ui_data_t page_lunch;
extern ui_data_t page_monitor;
static ui_data_t* pages[] = {&page_lunch, &page_monitor};
/**
* @brief 初始化页面加载,启动app
*/
void app_lunch() {
ui_init_and_start(pages, sizeof(pages));
}
核心函数为:ui_init_and_start
2.4 main函数中调用
示例:
lv_init();
/*Linux frame buffer device init*/
// 设置屏幕尺寸
lv_display_t* disp = lv_linux_fbdev_create(DISP_WIDTH, DISP_HIGHT);
lv_linux_fbdev_set_file(disp, fb);
// 初始化Inpu输入子系统. 这里需要设置
lv_indev_t* cdev = lv_evdev_create(LV_INDEV_TYPE_POINTER, evdev);
lv_evdev_set_swap_axes(cdev, true); // 设置xy交换翻转
lv_evdev_set_calibration(cdev, 0, 0, DISP_WIDTH,
DISP_HIGHT); // 设置触摸最大范围和最小范围
/*Create a Demo*/
// lv_demo_widgets();
// 启动
app_lunch();
3. GIF的使用
使用GIF动画可直接使用GIF工具,需要在lv_conf.h中开启配置
/*GIF decoder library*/
#define LV_USE_GIF 1 //使能
#if LV_USE_GIF
/*GIF decoder accelerate*/
#define LV_GIF_CACHE_DECODE_DATA 1 //开启缓存
#endif
准备一个GIF动画文件,使用LVGL官方图片转换工具转换得到.c资源文件
在线工具:https://lvgl.io/tools/imageconverter
Color format需要选择CF_RAW或者CF_RAW_ALPHA与透明通道格式。
下载生产的.c文件,复制到项目中。
3.1 使用方法
使宏定义声明引入gif
LV_IMG_DECLARE(gif_radiantboy)
创建GIF组件
// 添加GIF动图
gif_boy_lv = lv_gif_create(container);
lv_gif_set_src(gif_boy_lv, &gif_radiantboy);
lv_obj_set_pos(gif_boy_lv, 112, 40);
暂停GIF播放
lv_gif_pause(gif_boy_lv);
恢复GIF播放
lv_gif_resume(gif_cat_lv);
4. CMAKE添加外部文件夹编译
看下项目结构
- app文件夹为UI组件页面,需要编译。
- assess是原始UI素材和字体,不参与编译
- font是编码后的.c文件,需要编译。
- image是图片解码的c文件,需要编译。
- lv_port_pc_visual_studio是模拟器项目,不需要编译
- lvgl是核心库文件默认编译不用改
- monitor是系统监控器的指标实现,需要编译
- store是存储工具暂时没有用到,也加入编译吧
- uikit是UI框架ActivityManage,需要编译
4.1 修改CMakeLists.txt文件规则
添加头文件扫描
include_directories(${PROJECT_SOURCE_DIR}/app ${PROJECT_SOURCE_DIR}/monitor ${PROJECT_SOURCE_DIR}/uikit ${PROJECT_SOURCE_DIR}/store)
添加c文件扫描
file(GLOB APP ${CMAKE_CURRENT_SOURCE_DIR}/app/*.c)
file(GLOB MONITOR ${CMAKE_CURRENT_SOURCE_DIR}/monitor/*.c)
file(GLOB UIKIT ${CMAKE_CURRENT_SOURCE_DIR}/uikit/*.c)
file(GLOB STORE ${CMAKE_CURRENT_SOURCE_DIR}/store/*.c)
file(GLOB FONT ${CMAKE_CURRENT_SOURCE_DIR}/font/*.c)
file(GLOB IMG ${CMAKE_CURRENT_SOURCE_DIR}/image/*.c)
添加到编译
add_executable(nas_monitor main.c ${STORE} ${UIKIT} ${MONITOR} ${FONT} ${IMG} ${APP})
完整已修改的文件内容
cmake_minimum_required(VERSION 3.10)
project(lvgl)
# set(CMAKE_BUILD_TYPE Release)
set(CMAKE_C_STANDARD 99)#C99 # lvgl officially support C99 and above
set(CMAKE_CXX_STANDARD 17)#C17
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_subdirectory(lvgl)
target_include_directories(lvgl PUBLIC ${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/app ${PROJECT_SOURCE_DIR}/monitor ${PROJECT_SOURCE_DIR}/uikit ${PROJECT_SOURCE_DIR}/store)
file(GLOB APP ${CMAKE_CURRENT_SOURCE_DIR}/app/*.c)
file(GLOB MONITOR ${CMAKE_CURRENT_SOURCE_DIR}/monitor/*.c)
file(GLOB UIKIT ${CMAKE_CURRENT_SOURCE_DIR}/uikit/*.c)
file(GLOB STORE ${CMAKE_CURRENT_SOURCE_DIR}/store/*.c)
file(GLOB FONT ${CMAKE_CURRENT_SOURCE_DIR}/font/*.c)
file(GLOB IMG ${CMAKE_CURRENT_SOURCE_DIR}/image/*.c)
add_executable(nas_monitor main.c ${STORE} ${UIKIT} ${MONITOR} ${FONT} ${IMG} ${APP})
# target_link_libraries(nas_monitor lvgl lvgl::examples lvgl::demos lvgl::thorvg ${SDL2_LIBRARIES} m pthread)
target_link_libraries(nas_monitor lvgl lvgl::thorvg ${SDL2_LIBRARIES} m pthread)
add_custom_target (run COMMAND ${EXECUTABLE_OUTPUT_PATH}/main DEPENDS nas_monitor)
5. 启动传参
我们打包编译好的可执行文件在运行的时候我们希望可以传入一些参数,例如:
./nas_monitor -fb 0 -ev 1 -disk /dev/sda1 -net eth0
格式为 -key value
5.1 参数解析工具
int extractData(int argc, char* argv[], char* buf, const char* key) {
// 遍历命令行参数,查找指定前缀的数据
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], key, strlen(key)) == 0) {
// 找到指定前缀的数据,将数据填充到buf中
if (i + 1 < argc) {
strcpy(buf, argv[i + 1]);
return 0; // 返回0表示找到数据
} else {
return -2; // 参数不足,返回-2
}
}
}
return -1; // 返回-1表示未找到数据
}
5.2 使用方法
我们将解析的数据填充到结构体内:extern monitor_params_t monitor_dat;
typedef struct {
char disk_name[20]; // 挂载硬盘
char net_name[10]; // 网口名
} monitor_params_t;
extern monitor_params_t monitor_dat;
int extractData(int argc, char* argv[], char* buf, const char* key) {
// 遍历命令行参数,查找指定前缀的数据
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], key, strlen(key)) == 0) {
// 找到指定前缀的数据,将数据填充到buf中
if (i + 1 < argc) {
strcpy(buf, argv[i + 1]);
return 0; // 返回0表示找到数据
} else {
return -2; // 参数不足,返回-2
}
}
}
return -1; // 返回-1表示未找到数据
}
int main(int argc, char** argv) {
char flag_fb[2] = "0";
char flag_ev[2] = "1";
extractData(argc, argv, flag_fb, "-fb");
extractData(argc, argv, flag_ev, "-evdev");
extractData(argc, argv, monitor_dat.disk_name, "-disk");
extractData(argc, argv, monitor_dat.net_name, "-net");
if(extractData(argc, argv, NULL, "-h") == -2){
printf("\tNAS MONITOR Helper\n \
INPUT:-fb DESC: Display Output; Example: -fb 0\n \
INPUT:-evdev DESC: Touch the input subsystem used; Example:-evdev 1\n \
INPUT:-disk DESC: The hard drive to be monitored; Example: -disk /dev/sda1\n \
INPUT:-net DESC: The name of the NIC to which you want to monitor the network speed; Example: -net eth0\n");
return 0;
}
char fb[41] = "/dev/fb";
char evdev[41] = "/dev/input/event";
strcat(fb, flag_fb);
strcat(evdev, flag_ev);
lv_init();
/*Linux frame buffer device init*/
// 设置屏幕尺寸
lv_display_t* disp = lv_linux_fbdev_create(DISP_WIDTH, DISP_HIGHT);
lv_linux_fbdev_set_file(disp, fb);
// 初始化Inpu输入子系统. 这里需要设置
lv_indev_t* cdev = lv_evdev_create(LV_INDEV_TYPE_POINTER, evdev);
lv_evdev_set_swap_axes(cdev, true); // 设置xy交换翻转
lv_evdev_set_calibration(cdev, 0, 0, DISP_WIDTH,
DISP_HIGHT); // 设置触摸最大范围和最小范围
/*Create a Demo*/
// lv_demo_widgets();
// 启动
app_lunch();
/*Handle LVGL tasks*/
while (1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
6. SCP传输文件验证
当我们将程序编译好的文件需要复制到开发板中的时候,可以采用SSH的形式复制到开发板内。当然方式很多你也可以根据我的方式来。下面讲解一下如何使用SSH的方式传输文件。
编译好的项目在Vscode中打开终端,输入如下命令
进入编译生成的目录
cd bin
复制编译生成的文件到开发板内使用SCP命令、
scp nas_monitor lckfb@192.168.110.167:~/
复制nas_monitor文件到指定IP地址和用户名下的路径家文件夹内。
评论区