嵌入式Arm Linux实战 (一) - 交叉编译 + LVGL + Modbus通讯

本文介绍一个实战案例 —— 通过Windows交叉编译链,在Linux开发板上实现通过触摸屏和设备进行Modbus通讯

一. 交叉编译

什么是交叉编译

交叉编译是指在一种计算机平台(称为宿主机,如x86 PC)上,编译生成能在另一种不同架构或操作系统平台(称为目标机,如ARM设备)上直接运行的可执行代码的过程。它主要解决目标平台因资源有限、无操作系统或无法安装编译器而无法进行本地编译的问题,在嵌入式系统和移动应用开发中至关重要。

交叉编译工具下载

本文使用的是运行在Windows平台下的嵌入式Linux交叉编译工具,可以在Arm官网找到需要的工具链:
https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
笔者下载的是13.3版本的arm-none-linux-gnueabihf工具包,其中最重要的exe文件在bin目录下,可以加入环境变量。

编译环境搭建

笔者用的是CMake + Ninja作为编译工具链,下载Windows环境下的CMake和Ninja工具链并加入环境变量。
编译时需要的include文件夹,可以从sdk里把开发板系统里的include文件夹拷贝出来,同理lib文件夹也可以拷贝出来,注意lib里的软连接没法直接拷贝,亲测可以用7zip压缩,在windows上以管理员权限解压缩。
把这些开发板的依赖全部放到sysroot文件夹,形成一个依赖路径:

--sysroot
	|--include
	|--lib
	|--usr
		|--include
		|--lib

这里/usr路径某些情况下编译还是会指定,按需把include和lib下的路径拷贝到/usr/下即可(在开发板嵌入式Linux上,/usr/lib是/lib的软连接)

二. Modbus通讯库 —— libmodbus的编译

modbus通讯可以自己写,笔者用了libmodbus这个库,由于开发版sdk不具备这个动态库,所以需要自己编译。

交叉编译 libmodbus 库步骤

针对你的开发环境(从日志可知使用 arm-none-linux-gnueabihf 交叉工具链),以下是交叉编译 libmodbus 的详细步骤:

1. 下载 libmodbus 源码

从官网或 GitHub 获取源码:

wget https://libmodbus.org/releases/libmodbus-3.1.10.tar.gz  # 最新稳定版
tar -zxvf libmodbus-3.1.10.tar.gz
cd libmodbus-3.1.10

2. 安装autoconf

libmodbus的编译依赖autoreconf,笔者用WSL安装并编译,教程如下:

autoreconf 属于 autoconf 工具包,同时生成 configure 脚本还需要 automakelibtool 等配套工具,只需通过 WSL 的包管理器一键安装:

1. 更新包列表(以 Ubuntu/Debian 系 WSL 为例)

sudo apt update && sudo apt upgrade -y

2. 安装 Autotools 全套工具

sudo apt install -y autoconf automake libtool make gcc g++ pkg-config
  • autoconf:提供 autoreconf 命令,用于生成 configure 脚本;
  • automake:配合 autoconf 处理 Makefile.am,生成 Makefile.in
  • libtool:处理动态库/静态库的编译;
  • gcc/g++:基础编译器(后续交叉编译也需要);
  • pkg-config:辅助查找库依赖。

3. 验证安装成功

autoreconf --version

若输出类似 autoreconf (GNU Autoconf) 2.71 的版本信息,说明安装完成。

2. 配置交叉编译参数

创建编译目录并运行 configure 脚本,指定交叉工具链和安装路径:

# 创建输出目录(避免污染源码)
mkdir build && cd build

# 配置交叉编译(关键步骤)
../configure \
  --host=arm-none-linux-gnueabihf \  # 目标架构(与你的工具链匹配)
  --prefix=/path/to/install \        # 安装路径(自定义,如 ./output)
  --enable-static \                  # 生成静态库(可选)
  --enable-shared \                  # 生成动态库
  CC=arm-none-linux-gnueabihf-gcc \  # 交叉编译器
  AR=arm-none-linux-gnueabihf-ar \   # 交叉编译归档工具
  LD=arm-none-linux-gnueabihf-ld     # 交叉编译链接器
  • 若提示 configure: error: cannot guess build type; you must specify one,需补充 --build=x86_64-pc-linux-gnu(根据你的主机架构调整)。

3. 编译并安装

# 编译(-j 后面的数字为并行任务数,根据CPU核心数调整)
make -j4

# 安装到指定目录
make install

4. 安装依赖

编译成功后,/path/to/install 目录下会生成:

  • include/:头文件(modbus.h 等)
  • lib/:库文件(libmodbus.so 动态库、libmodbus.a 静态库)

把include文件夹下的头文件拷贝到windows下的/sysroot/include下,库文件拷贝到/sysroot/lib下;
同时,拷贝头文件和库文件各1份到开发板中的/usr/include以及/usr/lib

常见问题解决

  • ** configure: error: C compiler cannot create executables**
    检查交叉工具链是否正确安装,CC 变量是否指向正确的 arm-none-linux-gnueabihf-gcc

  • ** 运行时提示 "error while loading shared libraries: libmodbus.so"**
    确保开发板上已放置 libmodbus.so,并通过 export LD_LIBRARY_PATH=/usr/lib 指定库路径。

  • ** 静态链接**
    若希望静态链接(避免开发板依赖动态库),编译项目时使用 -static -lmodbus 链接选项。

三. 项目实战

做完以上步骤后,下面就可以开始我们的开发,注意我们的开发板已经包含了定制过的lvgl(版本8.4)和其依赖,如果没有,还需要源码编译和安装。本项目用CMake+Ninja作为编译工具链

文件路径

--modbus_master	//项目名称
	|--lvgl8	//开发板配套的lvgl实用函数
		|--lv_port_disp.c/.h
		|--lv_port_indev.c/.h
		|--lv_port_init.c.h
	|--modbus	//调用libmodbus的依赖
		|--modbus-rtu.h
		|--modbus-tcp.h
		|--modbus.h
	|--CMakeLists.txt
	|--armlinux.cmake
	|--main.c
	|--main.h
	|--modbus_comm.c
	|--modbus_comm.h

lvgl8中比较重要的封装是lv_port_init,对lvgl的初始化进行了一次封装,关键代码如下:

/* 0, 90, 180, 270 */
static int g_indev_rotation = 0;
/* 0, 90, 180, 270 */
static int g_disp_rotation = 0;

void lv_port_init(void)
{
    lv_init();

    lv_port_disp_init(0, 0, g_disp_rotation);
    lv_port_indev_init(g_indev_rotation);
}

这里封装了屏幕和触摸板的初始化,仅需要控制g_*参数就可以指定屏幕旋转方向

开发板的lvgl不使用lvgl内置的定时器而是自己封装了一个定时器,从而我们写的lvgl代码不再需要自定义定时器,相关重要配置写在lvgl/lv_conf.h

#define LV_TICK_CUSTOM 1
#define LV_TICK_CUSTOM_INCLUDE "src/misc/lv_systick.h"

下面开始展示我们的代码:
main.h: 公共依赖

#ifndef __MAIN_H__
#define __MAIN_H__

#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <malloc.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include <lvgl/lvgl.h>

#include "lv_port_init.h"
#include "timestamp.h"

#define ALIGN(x, a)     (((x) + (a - 1)) & ~(a - 1))

#endif

modbus_comm.h: 包含modbus功能封装的头文件

#ifndef MODBUS_COMM_H
#define MODBUS_COMM_H

#include "modbus/modbus.h"
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>

// Modbus从站配置(可根据需求修改)
#define MODBUS_SLAVE_IP     "192.168.2.211"
#define MODBUS_SLAVE_PORT   502
#define REGISTER_ADDR       0       // 保持寄存器地址
#define REGISTER_NUM        1       // 读取寄存器数量

// 通讯状态枚举
typedef enum {
    COMM_STATUS_DISCONNECTED = 0,  // 断开
    COMM_STATUS_CONNECTED,         // 连接正常
    COMM_STATUS_ERROR              // 错误
} comm_status_t;

// Modbus句柄结构体(线程安全)
typedef struct {
    modbus_t *ctx;                 // Modbus上下文
    comm_status_t status;          // 通讯状态
    int reg_value;                 // 寄存器当前值
    pthread_mutex_t lock;          // 互斥锁(保护reg_value)
    pthread_t comm_thread;         // 通讯线程
    bool thread_running;           // 线程运行标志
} modbus_handler_t;

/**
 * @brief 初始化Modbus句柄
 * @param handler Modbus句柄指针
 * @return 0-成功,-1-失败
 */
int modbus_comm_init(modbus_handler_t *handler);

/**
 * @brief 启动Modbus通讯线程(自动连接+读取寄存器)
 * @param handler Modbus句柄指针
 * @return 0-成功,-1-失败
 */
int modbus_comm_start(modbus_handler_t *handler);

/**
 * @brief 停止Modbus通讯线程
 * @param handler Modbus句柄指针
 */
void modbus_comm_stop(modbus_handler_t *handler);

/**
 * @brief 读取当前寄存器值(线程安全)
 * @param handler Modbus句柄指针
 * @param value 输出:寄存器值
 * @return 0-成功,-1-失败
 */
int modbus_comm_read_value(modbus_handler_t *handler, int *value);

/**
 * @brief 写入值到寄存器(线程安全)
 * @param handler Modbus句柄指针
 * @param value 要写入的数值
 * @return 0-成功,-1-失败
 */
int modbus_comm_write_value(modbus_handler_t *handler, int value);

/**
 * @brief 获取当前通讯状态
 * @param handler Modbus句柄指针
 * @return 通讯状态(comm_status_t)
 */
comm_status_t modbus_comm_get_status(modbus_handler_t *handler);

#endif // MODBUS_COMM_H

modbus_comm.c: modbus逻辑实现

#include "modbus_comm.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

// Modbus通讯线程函数(自动重连+读取寄存器)
static void *modbus_comm_thread_func(void *arg) {
    modbus_handler_t *handler = (modbus_handler_t *)arg;
    uint16_t reg_buf[REGISTER_NUM] = {0};

    while (handler->thread_running) {
        // 1. 检查连接状态,断开则尝试重连
        if (handler->ctx == NULL) {
            handler->status = COMM_STATUS_DISCONNECTED;
            // 创建Modbus TCP上下文
            handler->ctx = modbus_new_tcp(MODBUS_SLAVE_IP, MODBUS_SLAVE_PORT);
            if (handler->ctx == NULL) {
                fprintf(stderr, "Modbus: 创建上下文失败: %s\n", modbus_strerror(errno));
                usleep(1000);
                continue;
            }
            // 设置超时(1秒)
            modbus_set_response_timeout(handler->ctx, 1, 0);
            // 尝试连接从站
            if (modbus_connect(handler->ctx) == -1) {
                fprintf(stderr, "Modbus: 连接失败(%s:%d): %s\n", 
                        MODBUS_SLAVE_IP, MODBUS_SLAVE_PORT, modbus_strerror(errno));
                modbus_free(handler->ctx);
                handler->ctx = NULL;
                usleep(1000);
                continue;
            }
            fprintf(stdout, "Modbus: 连接成功(%s:%d)\n", MODBUS_SLAVE_IP, MODBUS_SLAVE_PORT);
            handler->status = COMM_STATUS_CONNECTED;
        }

        // 2. 读取保持寄存器
        pthread_mutex_lock(&handler->lock);
        int ret = modbus_read_registers(handler->ctx, REGISTER_ADDR, REGISTER_NUM, reg_buf);
        // fprintf(stdout, "读取寄存器返回:%d\n", ret);
        if (ret == REGISTER_NUM) {
            // 读取成功,更新寄存器值
            fprintf(stdout, "读取寄存器值:%d\n", reg_buf[0]);
            handler->reg_value = reg_buf[0];
            handler->status = COMM_STATUS_CONNECTED;
        } else {
            // 读取失败,断开连接
            fprintf(stderr, "Modbus: 读取寄存器失败: %s\n", modbus_strerror(errno));
            modbus_close(handler->ctx);
            modbus_free(handler->ctx);
            handler->ctx = NULL;
            handler->status = COMM_STATUS_DISCONNECTED;
        }
        pthread_mutex_unlock(&handler->lock);

        // 3. 根据通讯状态控制LED
        // if (handler->status == COMM_STATUS_CONNECTED) {
        //     led_set_brightness(1);  // 连接正常:LED亮
        // } else {
        //     led_set_brightness(0);    // 连接断开:LED灭
        // }

        // 4. 1秒读取一次
        usleep(1000 * 1000);
    }

    return NULL;
}

int modbus_comm_init(modbus_handler_t *handler) {
    if (handler == NULL) return -1;

    // 初始化句柄默认值
    handler->ctx = NULL;
    handler->status = COMM_STATUS_DISCONNECTED;
    handler->reg_value = 0;
    handler->thread_running = false;

    // 初始化互斥锁
    if (pthread_mutex_init(&handler->lock, NULL) != 0) {
        fprintf(stderr, "Modbus: 互斥锁初始化失败\n");
        return -1;
    }

    return 0;
}

int modbus_comm_start(modbus_handler_t *handler) {
    if (handler == NULL) return -1;

    handler->thread_running = true;
    // 创建通讯线程
    if (pthread_create(&handler->comm_thread, NULL, modbus_comm_thread_func, handler) != 0) {
        fprintf(stderr, "Modbus: 创建线程失败\n");
        handler->thread_running = false;
        return -1;
    }

    return 0;
}

void modbus_comm_stop(modbus_handler_t *handler) {
    if (handler == NULL) return;

    // 停止线程
    handler->thread_running = false;
    pthread_join(handler->comm_thread, NULL);

    // 释放Modbus资源
    if (handler->ctx != NULL) {
        modbus_close(handler->ctx);
        modbus_free(handler->ctx);
        handler->ctx = NULL;
    }

    // 销毁互斥锁
    pthread_mutex_destroy(&handler->lock);
}

int modbus_comm_read_value(modbus_handler_t *handler, int *value) {
    if (handler == NULL || value == NULL) return -1;

    pthread_mutex_lock(&handler->lock);
    *value = handler->reg_value;
    pthread_mutex_unlock(&handler->lock);

    return 0;
}

int modbus_comm_write_value(modbus_handler_t *handler, int value) {
    if (handler == NULL || handler->ctx == NULL) return -1;

    pthread_mutex_lock(&handler->lock);
    // 写入保持寄存器
    int ret = modbus_write_register(handler->ctx, REGISTER_ADDR, (uint16_t)value);
    if (ret == 1) {
        // 写入成功,更新本地缓存
        handler->reg_value = value;
        pthread_mutex_unlock(&handler->lock);
        return 0;
    } else {
        fprintf(stderr, "Modbus: 写入寄存器失败: %s\n", modbus_strerror(errno));
        // 写入失败,断开连接(触发重连)
        modbus_close(handler->ctx);
        modbus_free(handler->ctx);
        handler->ctx = NULL;
        handler->status = COMM_STATUS_DISCONNECTED;
        pthread_mutex_unlock(&handler->lock);
        return -1;
    }
}

comm_status_t modbus_comm_get_status(modbus_handler_t *handler) {
    if (handler == NULL) return COMM_STATUS_ERROR;
    return handler->status;
}

main.c:主程序实现,包括UI

#include "lvgl8/lv_port_init.h"
#include "modbus_comm.h"
#include "lvgl/lvgl.h"
#include "main.h"

#define FREETYPE_FONT_FILE ("/usr/share/fonts/AlibabaPuHuiTi-2-105-Heavy.otf")

// 全局变量
static modbus_handler_t modbus_handler;
static lv_obj_t *num_label;  // 数字显示框
lv_ft_info_t ft_info;
static int quit = 0;

static void sigterm_handler(int sig) {
    fprintf(stderr, "signal %d\n", sig);
    quit = 1;
}

// -------------------------- UI事件回调 --------------------------
// 加按钮回调
static void btn_inc_cb(lv_event_t *e) {
    if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
        int curr_val = 0;
        if (modbus_comm_read_value(&modbus_handler, &curr_val) == 0) {
            // 写入新值(+1)
            modbus_comm_write_value(&modbus_handler, curr_val + 1);
            // 更新UI显示
            char buf[16] = {0};
            snprintf(buf, sizeof(buf), "%d", curr_val + 1);
            lv_label_set_text(num_label, buf);
        }
    }
}

// 减按钮回调
static void btn_dec_cb(lv_event_t *e) {
    if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
        int curr_val = 0;
        if (modbus_comm_read_value(&modbus_handler, &curr_val) == 0) {
            // 写入新值(-1)
            modbus_comm_write_value(&modbus_handler, curr_val - 1);
            // 更新UI显示
            char buf[16] = {0};
            snprintf(buf, sizeof(buf), "%d", curr_val - 1);
            lv_label_set_text(num_label, buf);
        }
    }
}

// LVGL定时器:定时更新数字显示(500ms)
static void ui_update_timer_cb(lv_timer_t *timer) {
    (void)timer;
    int curr_val = 0;
    if (modbus_comm_read_value(&modbus_handler, &curr_val) == 0) {
        char buf[16] = {0};
        snprintf(buf, sizeof(buf), "%d", curr_val);
        lv_label_set_text(num_label, buf);
    }
}

// -------------------------- 创建UI界面 --------------------------
static void create_ui(void) {
    // 1. 主容器(全屏)
    lv_obj_t *main_cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(main_cont, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));
    lv_obj_center(main_cont);
    lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(main_cont, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
    lv_obj_set_style_pad_all(main_cont, 20, 0);

    // 2. 标题
    lv_obj_t *title = lv_label_create(main_cont);
    lv_label_set_text(title, "Modbus TCP 控制器");
    lv_obj_set_style_text_font(title, ft_info.font, 0);
    lv_obj_set_style_pad_bottom(title, 30, 0);

    // 3. 数字显示框
    num_label = lv_label_create(main_cont);
    lv_label_set_text(num_label, "0");
    lv_obj_set_style_text_font(num_label, &lv_font_montserrat_32, 0);
    lv_obj_set_style_pad_bottom(num_label, 30, 0);

    // 4. 按钮容器
    lv_obj_t *btn_cont = lv_obj_create(main_cont);
    lv_obj_set_size(btn_cont, lv_disp_get_hor_res(NULL) - 100, 80);
    lv_obj_set_flex_flow(btn_cont, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(btn_cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);

    // 5. 减按钮
    lv_obj_t *btn_dec = lv_btn_create(btn_cont);
    lv_obj_set_size(btn_dec, 100, 60);
    lv_obj_add_event_cb(btn_dec, btn_dec_cb, LV_EVENT_ALL, NULL);
    lv_obj_t *dec_label = lv_label_create(btn_dec);
    lv_label_set_text(dec_label, "-");
    lv_obj_set_style_text_font(dec_label, &lv_font_montserrat_32, 0);
    lv_obj_center(dec_label);

    // 6. 加按钮
    lv_obj_t *btn_inc = lv_btn_create(btn_cont);
    lv_obj_set_size(btn_inc, 100, 60);
    lv_obj_add_event_cb(btn_inc, btn_inc_cb, LV_EVENT_ALL, NULL);
    lv_obj_t *inc_label = lv_label_create(btn_inc);
    lv_label_set_text(inc_label, "+");
    lv_obj_set_style_text_font(inc_label, &lv_font_montserrat_32, 0);
    lv_obj_center(inc_label);

    // 7. 创建定时更新定时器(500ms)
    lv_timer_create(ui_update_timer_cb, 500, NULL);
}

// -------------------------- 主函数 -------------------------- 
int main(int argc, char *argv[]) {
    signal(SIGINT, sigterm_handler);

    // 一切LVGL应用的开始,必须加上这个初始化
    lv_port_init();

    /*****************************用户程序开始*************************************/
    ft_info.name = FREETYPE_FONT_FILE; // 使用绝对路径指定字体文件
    ft_info.weight = 50;               // 字体大小
    ft_info.style = FT_FONT_STYLE_NORMAL;
    ft_info.mem = NULL;

    system("echo none > /sys/class/leds/work/trigger");

    // 初始化字体
    if (!lv_ft_font_init(&ft_info)) {
        printf("create failed.");
    }

    // 2. 初始化Modbus通讯
    if (modbus_comm_init(&modbus_handler) != 0) {
        fprintf(stderr, "Modbus初始化失败\n");
        return -1;
    }

    // 3. 启动Modbus通讯线程
    if (modbus_comm_start(&modbus_handler) != 0) {
        fprintf(stderr, "Modbus线程启动失败\n");
        return -1;
    }

    // 4. 创建UI界面
    create_ui();

    // 5. 主循环(阻塞)
    while (1) {
        lv_task_handler();
        usleep(1000);
    }

    // 6. 清理资源(实际不会执行到)
    modbus_comm_stop(&modbus_handler);
    return 0;
}

工具链文件armlinux.cmake

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_SYSTEM_VERSION 1)

set(CMAKE_C_COMPILER_WORKS TRUE CACHE BOOL "" FORCE)
set(CMAKE_CXX_COMPILER_WORKS TRUE CACHE BOOL "" FORCE)

set(CMAKE_CXX_COMPILER "path/to/arm-none-linux-gnueabihf-g++.exe")
set(CMAKE_C_COMPILER "path/to/arm-none-linux-gnueabihf-gcc.exe")
set(CMAKE_LINKER "path/to/arm-none-linux-gnueabihf-ld.exe")
set(CMAKE_AR "path/to/arm-none-linux-gnueabihf-ar.exe")
set(CMAKE_NM "path/to/arm-none-linux-gnueabihf-nm.exe")

set(CMAKE_SYSROOT "path/to/sysroot")
set(CMAKE_C_FLAGS "--sysroot=${CMAKE_SYSROOT} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SYSROOT}/lib/libc_nonshared.a --sysroot=${CMAKE_SYSROOT} -L ${CMAKE_SYSROOT}/usr/lib=${CMAKE_SYSROOT}/lib -L ${CMAKE_SYSROOT}/lib -lc -lm -lpthread -static-libgcc" CACHE STRING "" FORCE)

set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS "")
set(CMAKE_LINK_DEF_FILE_FLAG "")
set(CMAKE_EXECUTABLE_SUFFIX "")

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

CMakeLists.txt:

cmake_minimum_required(VERSION 3.8)

project(Modbus_Master C)  # 明确指定仅编译C语言,避免默认C++带来的额外依赖

include_directories(
        ${CMAKE_SYSROOT}/include/
        ${CMAKE_SYSROOT}/include/lvgl
        ${CMAKE_SYSROOT}/include/lvgl/lv_drivers
        ${PROJECT_SOURCE_DIR}
        ${PROJECT_SOURCE_DIR}/lvgl8
        ${PROJECT_SOURCE_DIR}/common
        ${PROJECT_SOURCE_DIR}/modbus
        ${PROJECT_SOURCE_DIR}/sys
)

add_definitions(-DUSE_RK3506=1)
add_definitions(-DUSE_DRM=1 -DUSE_EVDEV=1)

aux_source_directory(${PROJECT_SOURCE_DIR}/lvgl8 SRCS)
aux_source_directory(${PROJECT_SOURCE_DIR}/sys SRCS)
aux_source_directory(${PROJECT_SOURCE_DIR}/common SRCS)
aux_source_directory(${PROJECT_SOURCE_DIR}/modbus SRCS)
aux_source_directory(. SRCS)

add_executable(${PROJECT_NAME} ${SRCS})

target_link_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SYSROOT}/lib ${CMAKE_SYSROOT}/usr/lib=${CMAKE_SYSROOT}/lib)
target_link_libraries(${PROJECT_NAME}
        ${CMAKE_SYSROOT}/lib/libc_nonshared.a
        lvgl lv_drivers freetype drm evdev modbus
)

编译步骤:

mkdir build
cd build
cmake .. -G"Ninja" --toolchain ..\armlinux.cmake
ninja

编译后生成可执行文件modbus_master,拷贝到开发板上便可运行,可以对modbus保持寄存器地址0进行数据读取和写入

posted @ 2025-12-26 16:09  Asp1rant  阅读(2)  评论(0)    收藏  举报