炸鸡派-echoAI机器人项目解析

环境搭建

电脑win11系统,由于 vmware不支持 win11 的 hyper-v虚拟化监控功能,因此最好还是使用win11 原生的 wsl2来运行 ubuntu。

应用商店下载的 ubuntu会解压成 ext4.vhdx 镜像。然后挂载在 wsl上。

但这里有个坑,我自己情况是隔段时间会发现 ext4.vhdx被自动清理掉了。

因此我是移动了镜像的安装磁盘,来避免 win11 对 c 盘的清理。

移动 wsl 的 ext4.vhdx到指定路径

bash 复制代码
#查看wsl是否有已经安装的子系统
wsl -l -v

适用于 Linux 的 Windows 子系统没有已安装的分发。

则代表目前 wsl 没有安装任何子系统。

bash 复制代码
PS C:\WINDOWS\system32> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-24.04    Stopped         2

则代表 有一个 Ubunt-24.04版本,状态是停用。

我出现这个打印是因为通过应用商店下载的 ubuntu版本,需要先注销,然后卸载。

bash 复制代码
PS C:\WINDOWS\system32> wsl --unregister Ubuntu-24.04

注销了无效的 Ubuntu 24.04 实例,重新安装 Ubuntu 24.04 即可自动生成缺失的 ext4.vhdx 文件,恢复 WSL2 正常使用。

步骤 1:确认 WSL 环境正常(前置检查)

以管理员身份在 PowerShell 执行以下命令,确保 WSL2 内核和组件已启用:

bash 复制代码
# 检查 WSL 版本(确认默认是 WSL2)
wsl --set-default-version 2

# 启用 WSL 和虚拟机平台(若未启用,执行后需重启电脑)
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

应用迁移

方法1:设置-应用中迁移

bash 复制代码
#关闭wsl中运行的所有实例
wsl --shutdown

打开「设置」,依次点击 应用 > 已安装的应用。 找到目标发行版,点击右侧的「...」按钮,选择 移动。

方法 2:使用命令行迁移

关闭实例

bash 复制代码
#关闭wsl中运行的所有实例
wsl --shutdown

执行迁移命令

使用 --move 参数将发行版迁移到新路径:

bash 复制代码
wsl --manage <发行版名称> --move <目标路径>

验证迁移 启动迁移后的发行版,确保其正常运行:

bash 复制代码
wsl -d <发行版名称>

方法 3:导出并导入迁移

导出发行版

将发行版导出为 .tar 文件:

bash 复制代码
wsl --export <发行版名称> <导出文件路径>

注销原发行版

删除原有的发行版:

bash 复制代码
wsl --unregister <发行版名称>

重新导入到新位置

将导出的文件导入到目标路径:

cpp 复制代码
wsl --import <发行版名称> <目标路径> <导出文件路径> --version 2

验证运行状态启动新位置的发行版,确保其正常工作。

软件环境搭建

原项目默认使用 ubuntu 进行 SDK 和相关 APP 的开发。

为了正常能够编译 luckfox 提供的 rv1106 的 SDK, 需要在 ubuntu22.04 上安装(我用的 ubuntu 24.04版本):

bash 复制代码
sudo apt-get install \
    repo \                          # Google多仓库管理工具(同步Android/OpenWRT等多仓库源码)
    git \                           # 分布式版本控制系统(克隆/管理代码仓库)
    ssh \                           # SSH客户端(远程连接服务器、传输文件)
    make \                          # 编译构建工具(解析Makefile自动化编译)
    gcc \                           # GNU C语言编译器(编译C源码)
    gcc-multilib \                  # GCC多架构支持(64位系统编译32位C程序)
    g++-multilib \                  # G++多架构支持(64位系统编译32位C++程序)
    module-assistant \              # 内核模块辅助工具(编译/安装内核驱动)
    expect \                        # 自动化交互脚本工具(免密输入、自动确认)
    g++ \                           # GNU C++语言编译器(编译C++源码)
    gawk \                          # GNU文本处理工具(提取/分析文本内容)
    texinfo \                       # 文档生成工具(从源码注释生成手册)
    libssl-dev \                    # OpenSSL开发库(加密/HTTPS/SSL/TLS开发)
    bison \                         # 语法分析器生成器(编译器前端、内核编译)
    flex \                          # 词法分析器生成器(与bison配合,解析词法规则)
    fakeroot \                      # 模拟root权限工具(制作deb包、文件系统镜像)
    cmake \                         # 跨平台构建工具(自动生成Makefile,适配多系统)
    unzip \                         # ZIP解压工具(解压源码包/资源包)
    gperf \                         # 完美哈希函数生成器(高性能字符串查找)
    autoconf \                      # 自动配置脚本生成器(适配不同系统的编译参数)
    device-tree-compiler \          # 设备树编译器(DTC,嵌入式Linux编译.dts/.dtb)
    libncurses5-dev \               # Ncurses终端图形库(开发终端交互程序)
    pkg-config                      # 库依赖查询工具(自动查找库的头文件/库文件路径)
包名 中文备注 核心用途 适用场景举例
repo Google 多仓库管理工具 批量管理多个 Git 仓库(尤其 Android/OpenWRT 等大型项目的源码同步) 编译 Android 源码、OpenWRT 固件
git 分布式版本控制系统 克隆 / 管理代码仓库、提交 / 回滚代码、分支管理 所有需要版本控制的开发场景
ssh SSH 客户端工具集 通过 SSH 协议远程连接服务器、传输文件(scp/sftp)、免密登录配置 远程开发、代码部署、服务器管理
make 编译构建工具(Makefile 解析器) 解析 Makefile 脚本,自动化编译、链接、安装程序 C/C++ 项目编译、内核 / 驱动编译
gcc GNU C 语言编译器 将 C 语言源码编译为可执行程序 / 目标文件 所有 C 语言项目开发
gcc-multilib GCC 多架构编译支持(32 位 / 64 位) 让 64 位系统编译生成 32 位的 C 程序(兼容老旧系统 / 嵌入式) 嵌入式 32 位程序、跨架构编译
g++ GNU C++ 语言编译器 将 C++ 源码编译为可执行程序 C++ 项目开发、C++ 版开源工具编译
g++-multilib G++ 多架构编译支持(32 位 / 64 位) 64 位系统编译 32 位 C++ 程序 跨架构 C++ 嵌入式开发
module-assistant Linux 内核模块辅助工具 简化内核模块的编译、安装、配置流程 编译 / 安装自定义内核驱动模块
expect 自动化交互脚本工具 自动响应命令行交互(如免密输入密码、自动确认弹窗) 编写自动化部署脚本、批量测试
gawk GNU 文本处理工具(比基础 awk 功能更强) 按规则提取、处理文本内容(日志分析、配置文件解析) 脚本中解析命令输出、文本批量处理
texinfo 文档生成工具(Info 格式) 从源码注释生成手册(Info/PDF/HTML 格式),部分开源软件编译依赖 编译需要生成文档的开源工具
libssl-dev OpenSSL 开发库(含头文件 / 静态库) 开发需要加密 / 解密、HTTPS、SSL/TLS 协议的程序 网络安全工具、HTTPS 客户端开发
bison 语法分析器生成器 将语法规则转换为 C 语言的语法分析器(编译器前端) 编译内核、自定义脚本解释器
flex 词法分析器生成器 将词法规则转换为 C 语言的词法分析器,与 bison 配合使用 编译器 / 解释器开发、内核编译
fakeroot 模拟 root 权限工具 无需真实 root 即可生成带 root 权限的文件(如 deb 包、镜像文件) 打包 deb 软件包、制作文件系统镜像
cmake 跨平台构建工具(替代 Makefile 手动编写) 自动生成适配不同系统的 Makefile / 工程文件,简化跨平台编译 C/C++ 跨平台项目、大型开源库编译
unzip ZIP 压缩包解压工具 解压 .zip 格式文件 解压源码包、资源包
gperf 完美哈希函数生成器 生成高效的哈希函数,用于快速字符串查找(性能优化) 编译器、高性能字符串处理程序
autoconf 自动配置脚本生成器 生成适配不同系统的 configure 脚本,兼容不同环境的编译参数 编译传统开源软件(如 GNU 工具)
device-tree-compiler 设备树编译器(DTC) 编译 / 反编译设备树文件(.dts → .dtb),嵌入式系统必备 嵌入式 Linux 开发、ARM/riscv 内核编译
libncurses5-dev Ncurses 终端图形库(开发版) 开发带终端界面的交互程序(如菜单、窗口、快捷键) 终端工具(如 vim、top)二次开发
pkg-config 库依赖查询工具 自动查找已安装库的头文件 / 库文件路径,简化编译命令 编译时自动链接依赖库(如 libssl)

repo 是 Google 为管理多 Git 仓库(尤其大型项目如 Android/OpenWRT/ 鸿蒙)设计的工具,核心是通过 manifest 清单文件批量管理数十 / 数百个 Git 仓库的同步、分支、版本控制。

repo 仓库批量管理工具

bash 复制代码
# 1. 下载repo脚本并赋予执行权限
sudo curl https://storage.googleapis.com/git-repo-downloads/repo -o /usr/local/bin/repo
sudo chmod a+x /usr/local/bin/repo

# 2. 配置repo的Python环境(Ubuntu 24.04默认用Python3,需指定)
echo 'export REPO_PYTHON=/usr/bin/python3' >> ~/.bashrc
source ~/.bashrc

# 3. 验证安装
repo --version  # 输出repo版本即成功

1. 初始化 repo 仓库(最核心:同步清单 + 初始化目录)

repo init 用于拉取项目的 manifest 清单文件,初始化本地仓库结构:

bash 复制代码
# 示例1:初始化Android 14源码仓库(指定manifest仓库+分支)
mkdir ~/android-14 && cd ~/android-14
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r50

# 示例2:指定自定义manifest文件(如OpenWRT)
mkdir ~/openwrt && cd ~/openwrt
repo init -u https://github.com/openwrt/openwrt.git -b main -m manifest/default.xml

# 参数说明:
# -u:指定manifest清单的Git仓库地址(核心)
# -b:指定分支/标签(如android-14.0.0_r50、main)
# -m:指定manifest文件(默认用default.xml,可选)
# --depth=1:浅克隆(仅拉取最新提交,节省空间,适合仅编译不开发)
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r50 --depth=1

2. 同步所有仓库(拉取源码:初始化后必执行)

repo sync 会根据 manifest 清单,批量克隆 / 更新所有关联的 Git 仓库:

bash 复制代码
# 基础用法:同步所有仓库(首次同步可能需要数小时,视网络而定)
repo sync

# 常用优化参数:
# -j8:开启8线程同步(根据CPU核心数调整,如-j16,加快速度)
# -c:仅同步当前分支(节省空间)
# --no-clone-bundle:禁用bundle压缩包(部分源需关闭)
repo sync -j8 -c --no-clone-bundle

# 同步指定单个仓库(无需同步全部,节省时间)
repo sync platform/build  # 仅同步Android的build仓库

3. 分支管理(统一管理所有仓库的分支)

repo 可批量切换 / 创建 / 删除分支,避免逐个 Git 仓库操作:

bash 复制代码
# 1. 查看当前所有仓库的分支状态
repo branch  # 简写:repo br

# 2. 创建并切换自定义分支(所有仓库同步切换)
repo start my-custom-branch --all  # 为所有仓库创建my-custom-branch分支
repo start my-custom-branch platform/build platform/frameworks/base  # 仅为指定仓库创建分支

# 3. 切换到已有分支(如官方稳定分支)
repo checkout android-14.0.0_r50

# 4. 删除本地分支(批量)
repo abandon my-custom-branch  # 放弃(删除)my-custom-branch分支

4. 提交代码(批量提交到所有修改过的仓库)

bash 复制代码
# 1. 查看所有仓库的修改状态(替代逐个git status)
repo status  # 简写:repo st

# 2. 批量提交修改(进入交互模式,逐个仓库确认提交)
repo commit -m "修复build编译报错:添加libssl依赖"  # 简写:repo ci

# 3. 仅提交指定仓库的修改
repo commit platform/build -m "优化编译脚本"

# 4. 将本地提交推送到远程(批量)
repo upload  # 会列出所有待推送的提交,确认后推送到远程review

5. 版本控制(标签 / 快照)

bash 复制代码
# 1. 查看所有仓库的标签
repo tag

# 2. 同步到指定标签版本(如Android官方发布标签)
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r50
repo sync -j8

# 3. 生成当前所有仓库的版本快照(用于备份)
repo manifest -o my-snapshot.xml  # 导出当前所有仓库的commit ID到xml文件

6. 清理 / 重置(解决同步异常)

bash 复制代码
# 1. 清理未跟踪的文件(批量,替代逐个git clean)
repo clean -d -x  # -d删除空目录,-x删除忽略的文件(如编译产物)

# 2. 重置所有仓库到最新提交(放弃本地修改)
repo forall -c 'git reset --hard HEAD'

# 3. 修复同步中断的问题
repo sync --force-sync  # 强制重新同步

7. 批量执行 Git 命令(forall)

repo forall 是高频用法,可对所有 / 指定仓库批量执行任意 Git 命令:

bash 复制代码
# 1. 对所有仓库执行git log(查看最近提交)
repo forall -c 'git log --oneline -5'

# 2. 对指定仓库执行git pull
repo forall platform/build platform/frameworks/base -c 'git pull'

# 3. 统计所有仓库的修改文件数
repo forall -c 'git diff --name-only | wc -l'

# 参数说明:-c 后接要执行的命令(单引号包裹)

实战场景示例(OpenWRT 编译)

bash 复制代码
# 1. 初始化OpenWRT的repo仓库
mkdir ~/openwrt-repo && cd ~/openwrt-repo
repo init -u https://github.com/openwrt/openwrt.git -b main -m manifest/default.xml

# 2. 同步源码(8线程)
repo sync -j8 -c

# 3. 创建自定义分支
repo start my-openwrt --all

# 4. 批量修改后提交
repo status  # 查看修改
repo commit -m "添加自定义驱动:mt7603e"

# 5. 清理编译产物
repo clean -d -x

Android 源码同步后约 80-100GB,OpenWRT 约 5-10GB,确保磁盘空间充足;

repo 依赖 Python3,Ubuntu 24.04 需指定 REPO_PYTHON=/usr/bin/python3(否则可能报错);

manifest 文件:核心是 xml 格式的清单文件,定义了所有子仓库的地址、分支、路径,可自定义修改适配需求。

module-assistant 内核模块工具

module-assistant 是 Debian/Ubuntu 系统中简化 Linux 内核模块编译、安装、配置的专用工具,核心解决 "手动编译内核模块时依赖复杂、适配内核版本难" 的问题,尤其适合自定义驱动(如网卡、串口、硬件驱动)编译。

前置准备:确认环境(必做)

1. 安装依赖(内核头文件 + 编译基础)

bash 复制代码
# 安装内核头文件(必须匹配当前内核版本)
sudo apt install -y linux-headers-$(uname -r)
# 验证头文件是否安装成功
ls /usr/src/linux-headers-$(uname -r)  # 有输出即成功

2. 查看 module-assistant 帮助(快速了解命令)

bash 复制代码
module-assistant --help  # 查看所有子命令
sudo module-assistant --verbose  # 详细模式(排错用)

核心用法示例(以编译 "自定义网卡驱动" 为例)

假设你有一个 rtl8812au 无线网卡驱动源码(~/rtl8812au-driver),用 module-assistant 编译安装:

场景 1:全自动编译 + 安装内核模块(最简用法)

module-assistant 提供 auto-install 子命令,一键完成 "配置→编译→安装" 全流程:

bash 复制代码
# 1. 进入驱动源码目录
cd ~/rtl8812au-driver

# 2. 一键编译+安装模块(自动适配当前内核)
sudo module-assistant auto-install .
# 说明:
# - `.` 表示当前目录的驱动源码(需包含Makefile)
# - 自动检测内核版本、配置编译参数、生成.ko模块、复制到/lib/modules/目录

场景 2:分步编译(适合调试 / 自定义参数)

若全自动编译失败,可分步执行,便于定位问题:

步骤 1:初始化模块编译环境(prepare)

bash 复制代码
sudo module-assistant prepare .
# 作用:检查内核头文件、配置编译环境、生成Module.symvers(模块依赖符号)

步骤 2:编译模块(build)

bash 复制代码
sudo module-assistant build .
# 作用:执行make编译,生成.ko内核模块(输出在源码目录的src/或当前目录)
# 编译日志会输出到/var/log/module-assistant/,可查看排错
cat /var/log/module-assistant/build.log

步骤 3:安装模块(install)

module-assistant 可将编译好的模块打包为 deb 包,方便在其他同内核版本的 Ubuntu 机器上安装:

bash 复制代码
# 1. 编译并生成deb包
sudo module-assistant build-deb .
# 生成的deb包会在 /usr/src/ 目录下,命名格式:<模块名>-<内核版本>.deb

# 2. 安装生成的deb包
sudo dpkg -i /usr/src/rtl8812au-$(uname -r).deb

# 3. 备份deb包(便于迁移)
cp /usr/src/rtl8812au-$(uname -r).deb ~/backup/

场景 4:清理编译产物(重新编译前)

bash 复制代码
# 清理当前目录的编译文件(.ko、.o、Makefile.dep等)
sudo module-assistant clean .

# 彻底清理所有module-assistant缓存
sudo module-assistant purge .

场景 5:卸载已安装的内核模块

bash 复制代码
# 1. 先卸载正在运行的模块
sudo rmmod rtl8812au  # 替换为你的模块名

# 2. 删除模块文件(或用dpkg卸载deb包)
sudo module-assistant remove .
# 或直接删除:
sudo rm /lib/modules/$(uname -r)/kernel/drivers/net/wireless/rtl8812au.ko
sudo depmod -a  # 更新模块依赖

场景 6:适配不同内核版本编译

若需为其他内核版本编译模块(如安装了 linux-headers-6.5.0-28-generic):

bash 复制代码
# 指定内核版本编译
sudo module-assistant --kernel-version 6.5.0-28-generic build .

# 生成对应内核版本的deb包
sudo module-assistant --kernel-version 6.5.0-28-generic build-deb .

WSL 环境注意事项

WSL2 编译内核模块需满足:

必须使用 WSL2 内核(wsl --version 确认版本≥5.15);

禁用 WSL 的 "快速启动"(否则内核模块加载可能失败);

WSL 中部分内核功能受限(如硬件驱动),适合编译 "虚拟驱动 / 工具类模块",不建议编译物理硬件驱动(优先在物理 Ubuntu 机器上测试);

若提示 no rule to make target 'modules',检查源码目录是否有合法的 Makefile(需包含 obj-m += xxx.o 等内核模块编译规则)

排错常用命令

bash 复制代码
# 查看模块是否加载成功
lsmod | grep rtl8812au  # 替换为你的模块名

# 查看模块加载日志
dmesg | grep rtl8812au

# 重新生成模块依赖
sudo depmod -a

# 强制加载模块
sudo modprobe rtl8812au
流程阶段 不使用 module-assistant(手动编译) 使用 module-assistant(自动化)
1. 环境准备 1. 手动安装内核头文件:sudo apt install linux-headers-$(uname -r)2. 手动确认头文件路径:ls /usr/src/...3. 手动配置 KERNELDIR 环境变量(若 Makefile 未适配)4. 手动检查依赖(如 gcc/make 1. 仅需安装内核头文件(同左)2. 执行 module-assistant prepare . 一键完成:- 自动检测内核版本 + 头文件- 自动配置编译环境- 自动生成 Module.symvers(模块符号依赖)
2. 编译模块 1. 手动执行 make(需进入源码目录)2. 编译失败需手动排查:- 内核版本不匹配→手动改 Makefile- 符号缺失→手动拷贝 Module.symvers3. 无统一日志,需看终端输出排错 1. 执行 module-assistant build . 一键编译2. 自动适配内核版本,补全依赖符号3. 编译日志自动保存到 /var/log/module-assistant/,排错更高效
3. 安装模块 1. 手动执行 sudo make install2. 手动复制 .ko 文件到内核模块目录:sudo cp xxx.ko /lib/modules/$(uname -r)/kernel/drivers/3. 手动更新依赖:sudo depmod -a4. 手动加载模块:sudo modprobe xxx 1. 执行 module-assistant install . 一键完成:- 自动拷贝 .ko 到正确目录- 自动执行 depmod -a- 可选自动加载模块
4. 生成 deb 包(分发) 1. 手动编写 debian/ 打包配置(需懂 deb 打包规范)2. 手动执行 dpkg-buildpackage -b3. 手动处理打包依赖(如 fakeroot 1. 执行 module-assistant build-deb . 一键生成 deb 包2. 自动生成符合规范的打包配置3. 包文件自动输出到 /usr/src/,无需手动配置
5. 清理产物 1. 手动执行 make clean2. 手动删除残留的 .ko/.o/ 临时文件3. 手动删除已安装的模块文件 1. 执行 module-assistant clean . 一键清理编译产物2. 执行 module-assistant purge . 彻底清理缓存 + 已安装模块
6. 多内核版本适配 1. 手动修改 KERNELDIR 指向其他内核头文件目录2. 手动编译多次,每次改参数3. 手动命名不同版本的 deb 包 1. 仅需加 --kernel-version 参数:module-assistant --kernel-version 6.5.0-28 build .2. 自动生成对应内核版本的模块 / 包
场景 推荐方式 原因
新手编译内核模块 优先用 module-assistant 降低门槛,避免手动步骤出错
快速验证驱动是否能编译 / 运行 module-assistant 一键操作,节省时间
需为多内核版本编译模块 module-assistant 参数化适配,无需重复配置
需分发模块(给其他机器使用) module-assistant 一键生成 deb 包,分发成本低
深度定制编译参数(如调试开关) 手动编译 module-assistant 封装过深,自定义参数需改脚本,不如手动改 Makefile 灵活
非 Debian/Ubuntu 系统(如 CentOS) 手动编译 module-assistant 仅适配 Debian 系,其他系统无此工具

expect 自动化交互脚本

核心场景:免密执行需要交互的命令(如 ssh 登录、sudo 输入密码、ftp 操作)

示例脚本(免密 ssh 登录)

bash 复制代码
# 保存为 ssh_auto.exp,执行:expect ssh_auto.exp
#!/usr/bin/expect
set timeout 10  # 超时时间
set host "192.168.1.100"
set user "root"
set pass "your_password"

spawn ssh $user@$host  # 启动ssh交互
expect {
    "yes/no" { send "yes\r"; exp_continue }  # 首次登录确认指纹
    "password:" { send "$pass\r" }          # 输入密码
}
interact  # 进入交互模式(也可替换为 send "command\r" 执行命令后退出)

gawk 文本提取工具

核心场景:按列提取、文本过滤、数据统计(比 sed 更擅长结构化文本处理)

示例 1:提取日志中特定列

bash 复制代码
# 提取nginx日志的访问IP(第1列)和请求路径(第7列),过滤404请求
awk '$9 == 404 {print $1, $7}' /var/log/nginx/access.log

示例 2:按分隔符处理 CSV 文件

bash 复制代码
# 以逗号分隔,提取第2列(姓名),并筛选年龄(第3列)>30的行
awk -F ',' '$3 > 30 {print $2}' user.csv

示例 3:统计词频

bash 复制代码
# 统计文本中每个单词出现次数(忽略大小写)
cat text.txt | tr '[:upper:]' '[:lower:]' | awk '{for(i=1;i<=NF;i++) count[$i]++} END {for(w in count) print w, count[w]}'

texinfo 文档生成工具

核心场景:从源码注释生成手册(GNU 项目标配,如 GCC、bash 的手册)

bash 复制代码
# 1. 编写texinfo源文件(如 mytool.texi),包含文档结构、注释、示例
# 2. 生成纯文本手册
makeinfo mytool.texi -o mytool.txt
# 3. 生成HTML手册
makeinfo --html mytool.texi -o doc/html
# 4. 生成PDF手册(需texlive)
texi2pdf mytool.texi -o mytool.pdf

fakeroot 模拟 root 权限制作镜像/包

核心场景:无需真实 root,制作 deb 包、文件系统镜像(避免权限污染)

示例 1:制作简单 deb 包

bash 复制代码
# 1. 创建包结构
mkdir -p mypkg/DEBIAN mypkg/usr/bin
echo -e '#!/bin/bash\necho "hello fakeroot"' > mypkg/usr/bin/hello
chmod +x mypkg/usr/bin/hello
# 2. 编写控制文件(DEBIAN/control)
cat > mypkg/DEBIAN/control << EOF
Package: hello
Version: 1.0
Architecture: amd64
Maintainer: yourname <your@email.com>
Description: A test package
EOF
# 3. 用fakeroot打包(无需root)
fakeroot dpkg-deb --build mypkg

示例 2:制作根文件系统镜像

bash 复制代码
# 模拟root权限打包目录为ext4镜像
fakeroot mkfs.ext4 -d rootfs/ rootfs.img  # rootfs/为自定义根目录

5. autoconf(跨系统编译配置)

核心场景:为源码生成适配不同系统的configure脚本

示例流程(C 项目):

bash 复制代码
# 1. 编写configure.ac(核心配置文件)
cat > configure.ac << EOF
AC_INIT([myapp], [1.0], [bug@example.com])  # 项目信息
AC_PREREQ([2.69])                          # autoconf版本
AC_CONFIG_SRCDIR([src/main.c])             # 验证源码存在
AC_CONFIG_HEADERS([config.h])              # 生成配置头文件

# 检查编译器、库
AC_PROG_CC                                 # 检查C编译器
AC_CHECK_LIB([m], [sqrt])                  # 检查数学库(-lm)

AC_CONFIG_FILES([Makefile])                # 生成Makefile
AC_OUTPUT                                 # 输出脚本
EOF

# 2. 编写Makefile.am(automake配置,需配合automake)
cat > Makefile.am << EOF
bin_PROGRAMS = myapp
myapp_SOURCES = src/main.c
myapp_LDADD = -lm  # 链接数学库
EOF

# 3. 生成configure脚本
autoreconf -ivf  # -i:安装缺失文件,-v:详细输出,-f:强制更新

# 4. 配置编译(适配当前系统)
./configure --prefix=/usr/local  # 指定安装路径

# 5. 编译安装
make && make install

6. libncurses5-dev(终端图形库开发)

核心场景:开发带界面的终端程序(如 htop、vim 的终端界面)

示例(简单 C 程序:终端窗口输出):

bash 复制代码
// 保存为 ncurses_demo.c,编译:gcc ncurses_demo.c -lncurses
#include <ncurses.h>

int main() {
    initscr();          // 初始化ncurses
    cbreak();           // 禁用行缓冲(立即响应按键)
    noecho();           // 不回显输入字符
    keypad(stdscr, TRUE); // 启用功能键(如方向键)

    printw("Hello Ncurses! Press q to quit.\n");  // 输出文本
    refresh();          // 刷新屏幕(ncurses需手动刷新)

    // 等待按键,q退出
    while (getch() != 'q');

    endwin();           // 关闭ncurses,恢复终端
    return 0;
}

编译运行:

bash 复制代码
sudo apt install libncurses5-dev  # 安装开发库
gcc ncurses_demo.c -lncurses -o ncurses_demo
./ncurses_demo

7. pkg-config(库依赖查询)

核心场景:自动获取库的编译 / 链接参数(替代手动写 - I/-L/-l)

示例 1:查询库的编译参数

bash 复制代码
# 查询ncurses库的编译(--cflags)和链接(--libs)参数
pkg-config --cflags --libs ncurses
# 输出示例:-I/usr/include -lncurses

示例 2:编译时自动引用参数

bash 复制代码
# 替代手动写 -I/-L/-l,直接用pkg-config获取参数
gcc myprog.c -o myprog $(pkg-config --cflags --libs gtk+-3.0)  # GTK3示例
gcc myprog.c -o myprog $(pkg-config --cflags --libs libxml-2.0) # libxml2示例

示例 3:检查库是否安装

bash 复制代码
pkg-config --exists libcurl  # 检查libcurl是否安装
echo $?  # 0=存在,1=不存在

工具联动示例(实战场景)

bash 复制代码
# 场景:自动化编译源码包,免密输入sudo密码,用pkg-config确认依赖
expect -c '
    spawn ./configure && make && sudo make install
    expect "password:" { send "your_sudo_pass\r" }
    interact
'

# 场景:用gawk分析编译日志,提取错误信息
gawk '/error:/ {print "Line " NR ": " $0}' make.log

# 场景:用fakeroot打包编译好的程序为deb包,autoconf适配系统
autoreconf -ivf && ./configure && make
fakeroot dpkg-deb --build mydeb-package/

RK驱动助手

为搭载瑞芯微(Rockchip)芯片的设备提供 USB 通信驱动支持,是设备与 PC 正常连接、刷机及系统开发的基础工具。
瑞芯微驱动助手

安装了瑞芯微驱动助手,PC才能通过USB识别到瑞芯微的芯片。

下拉完整项目(SDK和软件Demo)

拉取含子仓库的项目

bash 复制代码
# 1. 克隆 Echo-Mate 远程仓库到本地当前目录
# 执行后会生成名为 Echo-Mate 的文件夹,包含项目主代码(但子模块目录为空)
git clone https://github.com/No-Chicken/Echo-Mate.git

# 2. 进入克隆后的项目根目录(切换工作目录)
# 后续操作子模块必须在项目根目录执行,否则会找不到子模块配置
cd Echo-Mate

# 3. 初始化并递归更新项目中所有子模块
# --init:初始化未被初始化的子模块(创建子模块目录、加载.gitmodules配置中的子模块地址)
# --recursive:递归处理嵌套子模块(如果子模块本身还依赖其他子模块,也会一并拉取)
# 执行后:子模块目录会被填充为对应版本的代码,保证项目依赖完整
git submodule update --init --recursive

由于 Echo-Mate是分了主仓库和子仓库的,

git clone 仅拉取主仓库代码。

如果不进入项目根目录执行 git submodule update会发现子仓库仅创建了目录但内容为空。

--init:初始化未被初始化的子模块(创建子模块目录、加载.gitmodules配置中的子模块地址)。

--recursive:如果 Echo-Mate 项目的子模块中还嵌套了其他子模块(比如子模块 A 依赖子模块 B),不加 --recursive 会导致嵌套子模块代码缺失,可能引发编译 / 运行报错。

如果要递归更新所有子模块,请执行:

bash 复制代码
# 更新所有嵌套子模块至其远程仓库最新版,合并到本地(保留修改)
# --remote:拉取子模块远程最新提交 |
# --merge:合并更新(非强制重置) |
# --recursive:递归处理嵌套子模块
git submodule update --remote --merge --recursive

|-------------------------------|----------------|--------------------|
| git submodule update --init | 仅拉取主仓库记录的子模块版本 | 首次拉取项目、恢复子模块历史版本 |
| 本命令(--remote --merge) | 拉取子模块远程最新版并合并 | 主动同步子模块到最新版、保留本地修改 |

由于子仓库有大文件LFS,需要执行:

bash 复制代码
# 拉取当前主仓库中Git LFS追踪的大文件(如模型、二进制包、数据集等)
git lfs pull

# 递归遍历所有嵌套子模块,在每个子模块目录执行LFS大文件拉取(补齐子模块中LFS管理的实际文件)
# --recursive:递归处理嵌套子模块 | 'git lfs pull':子模块内执行LFS大文件拉取
git submodule foreach --recursive 'git lfs pull'

git submodule update(含 --remote/--merge/--recursive 等参数)的核心作用是拉取子模块的 Git 版本控制元数据和普通文件,但无法处理 Git LFS(大文件存储)管理的文件,

Git LFS 会将大文件(如视频、编译包、数据集)从普通 Git 仓库中分离存储,仓库里仅保留「指针文件」(几 KB 的文本引用)。submodule update 只能拉取这些指针文件,无法自动拉取背后的实际大文件,导致本地仅存空指针、实际文件缺失。

git lfs的用法

Git LFS 是 Git 官方扩展,用于解决「大文件(如模型、二进制包、数据集、视频)在 Git 仓库中版本控制效率低、仓库体积膨胀」的问题 ------ 它不会把大文件直接存入 Git 仓库,而是在仓库中保留「指针文件」(几 KB),实际大文件存储在 LFS 服务器(GitHub/GitLab 均内置支持),拉取 / 推送时自动同步实际文件。

1. 安装 git lfs

bash 复制代码
# 1.2 Linux(Debian/Ubuntu)
sudo apt-get install git-lfs

# 1.3 验证安装(输出 git-lfs/版本号 即成功)
git lfs version

2. 在项目中初始化 Git LFS

bash 复制代码
# 进入你的 Git 项目根目录
cd your-project-dir

# 初始化 LFS(全局/本地二选一,推荐本地仅当前项目生效)
# 本地初始化(仅当前项目)
git lfs install --local
# 全局初始化(所有 Git 项目生效)
# git lfs install --global

# 备注:初始化后,Git 会自动配置钩子,确保提交/推送时识别 LFS 追踪文件

3. 配置 LFS 追踪规则(核心:指定哪些文件走 LFS)

bash 复制代码
# 示例1:追踪单个类型(如所有 .bin 二进制文件)
git lfs track "*.bin"

# 示例2:追踪多个类型(模型、数据集、压缩包)
git lfs track "*.pth"  # PyTorch 模型
git lfs track "*.h5"   # 深度学习模型
git lfs track "*.zip"  # 压缩包
git lfs track "*.iso"  # 镜像文件
git lfs track "*.mp4"  # 视频文件

# 示例3:追踪指定目录下所有文件(如 data/ 下的所有大文件)
git lfs track "data/**/*"

# 备注1:执行 track 后,项目根目录会生成 .gitattributes 文件(必提交到仓库!),该文件记录 LFS 追踪规则,确保团队成员同步规则
# 备注2:查看已配置的追踪规则
git lfs track

4. 提交并推送 LFS 文件(含常规 Git 流程)

注意将 .gitattributes文件添加进仓库。

提交的时候会自动将 lfs追踪的文件传输至 lfs服务器而非 git 仓库服务器。

bash 复制代码
# 1. 先添加 .gitattributes(关键!否则团队拉取时无法识别 LFS 规则)
git add .gitattributes

# 2. 添加需要版本控制的大文件(LFS 会自动识别追踪的文件)
git add your-large-file.bin data/dataset.zip

# 3. 提交(LFS 会自动替换大文件为指针文件,提交的是指针而非实际文件)
git commit -m "feat: 接入 LFS,添加模型文件和数据集"

# 4. 推送到远程仓库(LFS 会自动将实际大文件推送到 LFS 服务器)
git push origin main  # main 替换为你的分支名

# 备注:若推送失败,可强制触发 LFS 推送:git lfs push --all origin main

5. 拉取含 LFS 文件的仓库(含子模块场景)

bash 复制代码
# 场景1:首次克隆含 LFS 的仓库(自动拉取 LFS 文件)
git clone https://github.com/your/your-repo.git
# 若克隆后 LFS 文件未自动拉取,手动触发:
git lfs pull

# 场景2:「子模块场景」------子模块中含 LFS 文件
# 先更新子模块代码(仅拉取指针文件)
git submodule update --remote --merge --recursive
# 递归拉取所有子模块的 LFS 实际文件(核心!否则子模块仅存指针)
git submodule foreach --recursive 'git lfs pull'

镜像烧录

Echo 开发板预留 SPI NAND FLASH 和 SD 卡两种存储介质,推荐优先使用 SD 卡,便捷高效。驱动与环境配置完成、设备就绪后,即可开展后续操作。

1. NAND镜像烧录

建议不使用拓展坞,直接USB连电脑,有群友反馈拓展坞烧录时会出问题

打开瑞芯微 SocToolKit 并选择 RV1106,按住Boot 键后连接 USB 至电脑,松开 Boot 键即可进入 Maskrom 模式。
会提示是否需要重载镜像的 env.img 镜像文件,
重载了全部勾选然后下载重启就行
nand-build-镜像

文件名 类型 / 用途分类 具体作用
.env.txt 配置文件 烧录 / 升级脚本执行前读取
sd_update.txt 升级配置文件 定义通过 SD 卡升级系统的流程(镜像路径、校验规则等)
tftp_update.txt 升级配置文件 定义通过 TFTP 网络升级系统的流程(镜像路径、网络参数等)
uboot.img 引导程序镜像 系统上电后第一个运行的程序,负责初始化硬件、加载内核并启动系统
boot.img 内核 + 启动镜像 包含 Linux 内核、设备树、初始内存文件系统,是系统启动的核心组件
env.img 引导环境变量镜像 存储 U-Boot 的启动配置(如启动延迟、默认启动项),便于备份 / 恢复
rootfs.img 根文件系统镜像 包含系统所有基础文件(命令、配置、应用),是操作系统运行的 "环境载体"
oem.img 厂商定制分区镜像 存储硬件厂商的定制化数据(如设备标识、预装工具)
userdata.img 用户数据分区镜像 存储用户应用数据、配置文件(系统运行中产生的个性化数据)
idblock.img 标识 / 分区表镜像 存储设备的唯一标识或分区表信息,用于硬件 / 系统的身份识别
download.bin 升级工具 / 镜像文件 通常是升级过程中用到的辅助程序(如刷机工具、临时引导程序)
update.img 完整系统镜像 包含所有分区的整合镜像,可直接用于系统全量刷写 / 升级

其实可以这么分类

一、直接用于烧录 / 升级的 "脚本类文件"(配置型)

文件名 角色 / 用途
sd_update.txt SD 卡升级的核心配置脚本:1. 定义烧录的镜像路径(比如rootfs.img在哪);2. 指定烧录的目标分区(比如写入 SD 卡的第 2 分区);3. 配置校验规则(比如镜像 MD5 校验);4. 被底层可执行脚本(如sd_upgrade.sh)读取,指导烧录动作
tftp_update.txt TFTP 网络升级的配置脚本:1. 定义网络参数(比如 TFTP 服务器 IP、镜像文件名);2. 指定烧录顺序(先烧uboot.img,再烧rootfs.img);3. 配置升级失败后的回滚规则;4. 是网络烧录流程的 "操作指南"。
.env.txt 辅助配置文件:给上面两个升级脚本提供参数(比如镜像版本、设备型号),不直接执行,但脚本会读取它的内容。

二、配合脚本完成烧录 / 升级的 "镜像文件"

文件名 烧录 / 升级中的作用
update.img 全量系统镜像:脚本可直接读取这个整合镜像,一键烧录所有分区(uboot/boot/rootfs 等),是最常用的烧录文件。
uboot.img/boot.img/rootfs.img 单分区镜像:脚本可按配置,单独烧录某一个分区(比如只升级内核boot.img,不碰用户数据userdata.img)。
download.bin 升级辅助程序:部分场景下,脚本会先烧录这个文件到硬件,作为 "临时引导程序",再由它完成后续镜像的烧录。

这类.txt配置脚本,会被硬件厂商提供的可执行脚本 / 工具(比如 Linux 下的.sh脚本、Windows 下的.exe烧录工具)读取;

比如执行./sd_upgrade.sh时,脚本内部会解析 sd_update.txt 里的配置,自动完成镜像烧录。

2. NAND Flash擦除

若SD卡作为启动介质时,必须让NAND Flash清空!避免启动优先级冲突,否则识别到 NAND Flash中存在系统,可能优先从 NAND Flash启动而非从 SD卡启动。

若 NAND Flash 中保留userdata.img/oem.img等分区,SD 卡启动的系统可能会误挂载 NAND 中的分区

3. SD卡镜像烧录

SD卡做启动介质时,需要保证NAND Flash是清空没固件的,因为默认首用NAND的。

准备一张SD卡, SD卡不要太大, 16G以内就行, 不然有可能不行, 再准备一个读卡器, 使用SD Card Formatter等工具进行格式化清空

核心原因是嵌入式硬件 / 引导程序(U-Boot)对 SD 卡的兼容性限制,嵌入式设备的 U-Boot 通常是厂商定制的老旧版本(为了稳定性不会频繁更新),这类 U-Boot:

只支持 MBR 分区表(主引导记录),而 MBR 的最大支持容量是 2TB,但实际对 SD 卡的支持上限会被厂商固件限制(比如只认≤16G);

不支持 GPT 分区表(GUID 分区表)------ 大容量 SD 卡(尤其是 64G/128G)出厂默认是 GPT 格式,U-Boot 无法识别,直接判定 "无有效启动介质"。
MBR 分区表通过主引导记录(位于磁盘首个扇区,含分区表和引导程序)管理分区,

而 GPT 分区表则基于全局唯一标识符(GUID),用分区表头和分区项数组来记录分区信息,且自带备份分区表提升容错性。

嵌入式系统的启动分区(存放uboot.img/boot.img)几乎都要求格式化为 FAT32,因为:

U-Boot 对 FAT32 的支持最成熟,而对 NTFS/exFAT 的支持极差(需要额外编译驱动,厂商一般不会加);

FAT32 的单个分区最大支持 32G,但很多嵌入式工具(比如 SD Card Formatter 的默认配置)对 16G 以上的卡格式化时,可能会出现分区大小异常(比如超出 U-Boot 识别范围);
格式化清空,我这里用的30GB的SD卡

下载安装RK瑞芯微烧录工具准备进行烧录,使用管理员身份启动,否则无法检测到SD卡

首先使用读卡器,把SD卡插到电脑上, 没有出现就重新插一下, 再将打包好的镜像烧录到SD卡. 不要选update.img这个文件, 创建时会报错是因为这个问题。

然后插卡到板子上,上电即可用。

SD 卡启动工具的制作原理

通过工具将嵌入式系统的各功能镜像文件,按硬件启动要求的分区顺序、地址规则写入 SD 卡,让 SD 卡模拟成硬件的 "启动存储介质",使硬件上电后能从 SD 卡加载系统。

瑞芯微 SD 卡启动工具把芯片启动规则和配置逻辑内置在工具里,同时.img 镜像自身包含启动参数,所以用户只需选镜像,工具自动按规则完成 SD 卡启动介质制作,无需手动选配置文件。

开发板使用操作

1. 开发板连接与登录

首先需要在网上下载MobaXterm终端工具, 进行开发板的调试.

1.1 串口登录

USB转串口连接至开发板的RX TX后,进入MobaXterm,创建一个session选择serial,选择好波特率115200进入即可.

(usb转串口模块的3.3v和5v其实是给外接设备供电的,模块自身有usb接口供电)

根据usb转串口芯片的不同,驱动也不一样,常见的是ch341和cp210x。

bash 复制代码
登录账号: root
登录密码: root

主要是串口设置,
和设置串口通信引擎使用putty

高级串口设置中,"Serial engine" 选择了 "PUTTY",表示本次串口连接会借用 Putty 的串口通信引擎来实现。

Putty是一款免费开源的终端仿真工具(常见于 Windows 系统),支持串口、SSH、Telnet 等多种通信方式,可用于连接远程设备(如服务器、路由器)或本地串口设备(如单片机),实现命令行交互、数据传输等功能。

流控是串口通信中防止数据丢失的机制:接收方处理不过来时,发信号让发送方暂停,就绪后再恢复发送。

1.2 usb虚拟网口SSH登录

不连接WIFI的情况,可以通过 USB 模拟网口使用静态 IP 进行登录.

本质是把 USB 物理连接,伪装成 "有线网络",再通过静态 IP 打通主机与设备的通信链路,最后用 SSH 协议完成登录。
给开发板插电发现就能识别到 NDIS了
配置 ip地址和子网掩码,
其实是将 PC的虚拟网卡和开发板的虚拟网卡配置在同一网段

开发板的 usb0 是「服务端虚拟网卡」,PC 端的是「客户端虚拟网卡」,两者通过 USB 数据线构成一条「虚拟以太网链路」。

开发板内置的 g_ether 驱动(虚拟网卡核心)会通过 USB 总线,向 PC 发送「RNDIS/CDC-ECM 协议指令」,相当于告诉 PC:"我是一个虚拟网卡设备,你可以通过我上网 / 通信"。

Windows:识别到 "RNDIS 兼容设备",自动安装驱动后,在「网络适配器」中创建一个名为「USB 以太网」「Remote NDIS Compatible Device」的虚拟网卡;

Linux/macOS:系统原生支持 RNDIS/CDC-ECM 协议,无需手动装驱动,自动创建虚拟网卡(Linux 通常命名为 enxXXXX,macOS 命名为「USB 以太网」)。


将开发板配置成 USB RNDIS(虚拟网卡)设备,

其实是通过内核配置的。

瑞芯微官方在制作内核时,将 g_ether(USB 虚拟网卡)驱动以 y 选项编译进了内核镜像 ,因此:

无需 .ko 文件,也无需 /lib/modules 目录;

内核启动后,驱动会自动运行,只要 USB 口切换为 Device 模式,就会创建 usb0 网卡。
usb虚拟网卡

Linux内核驱动有两种编译/运行方式,内置驱动或者模块驱动。

驱动形态 编译选项 存在形式 加载方式 依赖条件
内置驱动(Built-in) y 直接嵌入内核镜像(zImage/Image 内核启动时自动加载,无需手动操作 无(驱动随内核一起运行)
模块驱动(Module) m 独立的 .ko 文件 insmod/modprobe 手动加载 依赖 /lib/modules 目录

下面提一下模块驱动,

开发板 Linux 内核需开启 USB RNDIS 虚拟网卡功能,通过 menuconfig 配置:

bash 复制代码
make menuconfig

启用以下选项(编译为模块或内置):

bash 复制代码
Device Drivers → USB support → USB Gadget Support → 
  [*] USB Gadget Drivers (CONFIG_USB_GADGET)
  [*] RNDIS Ethernet Gadget (CONFIG_USB_GADGET_RNDIS)
  [*] USB Gadget Functions (CONFIG_USB_FUNCTIONFS)
  [*] Ethernet Gadget (CONFIG_USB_ETH)

编写 Shell 脚本(usb_rndis_init.sh),在开发板启动时自动配置虚拟网卡 IP,

bash 复制代码
#!/bin/sh
# 加载RNDIS模块(若编译为模块)
insmod /lib/modules/$(uname -r)/kernel/drivers/usb/gadget/function/usb_f_rndis.ko
insmod /lib/modules/$(uname -r)/kernel/drivers/usb/gadget/legacy/g_ether.ko

# 配置虚拟网卡usb0的IP(静态地址,与PC端同网段)
ifconfig usb0 down
ifconfig usb0 192.168.7.2 netmask 255.255.255.0 up

# 启用IP转发(可选,用于开发板访问外网)
echo 1 > /proc/sys/net/ipv4/ip_forward

usb_f_rndis.ko 和 g_ether.ko 都不需要自己写。

维度 g_ether.ko(Ethernet Gadget) usb_f_rndis.ko(RNDIS Function)
内核框架定位 属于 Legacy(传统)USB Gadget 驱动(一体式驱动),直接实现完整的 USB 以太网设备功能,无需依赖其他组件。 属于 FunctionFS 框架下的独立功能驱动 (模块化驱动),仅实现 RNDIS 协议功能,需配合 USB Gadget 核心、复合设备(如 libcomposite)才能工作。
协议支持 同时支持 CDC-ECM (通用以太网控制模型,Linux/macOS 原生兼容)和 RNDIS(微软定制协议,Windows 兼容),加载后可自动适配或手动指定协议。 仅专注支持 RNDIS 协议(无 CDC-ECM 支持),是专门为 Windows 兼容性优化的 RNDIS 功能模块。
使用方式 极简:直接 insmod g_ether.ko 即可创建虚拟网卡(如 usb0),无需额外配置;可通过模块参数指定 IP、MAC(如 insmod g_ether.ko dev_addr=xx:xx:xx:xx:xx:xx)。 需配合复合设备配置:加载后需通过 configfs 或代码配置 USB Gadget 复合设备,将 RNDIS 功能挂载到复合设备上才能生效;步骤稍多,但灵活性更高。
兼容性 对老内核(Linux 2.6/3.x)、老开发板更友好,适配性广;Windows 下需手动装 RNDIS 驱动,Linux/macOS 自动识别 CDC-ECM。 对新内核(Linux 4.x+)更适配,是内核推荐的 RNDIS 实现方式;Windows 下驱动识别更稳定(专门的 RNDIS 功能)。
功能灵活性 功能固定,仅支持基础以太网转发,难以扩展(如叠加其他 USB 功能:串口、存储)。 可与其他 USB Function(如 usb_f_serial.ko 串口、usb_f_mass_storage.ko 存储)组合成复合 USB 设备(比如一个 USB 口同时出虚拟网卡 + 串口 + U 盘)。
内核配置项 CONFIG_USB_ETH(Ethernet Gadget) CONFIG_USB_GADGET_RNDIS(RNDIS Ethernet Gadget)
源码路径 drivers/usb/gadget/legacy/g_ether.c drivers/usb/gadget/function/usb_f_rndis.c

这两个驱动都是 Linux 内核原生自带的标准组件,芯片厂商提供的 BSP(板级支持包)中也会针对自家硬件(如 RK、IMX、STM32MP1 等)预编译好这两个驱动,你只需通过内核配置开启、加载即可,仅极特殊定制场景需少量修改(无需从零开发)。

bash 复制代码
# 1. 进入内核源码目录,开启RNDIS驱动配置
make menuconfig
# 启用:Device Drivers → USB support → USB Gadget Support → RNDIS Ethernet Gadget
# 2. 编译内核模块(生成usb_f_rndis.ko)
make modules
# 3. 拷贝到开发板/lib/modules/目录下
cp drivers/usb/gadget/function/usb_f_rndis.ko /root/
# 4. 加载驱动
insmod /root/usb_f_rndis.ko

usb_f_rndis.ko 和 g_ether.ko 依赖完全独立,属于不同 USB Gadget 框架,加载一个就行。新内核建议使用usb_f_rndis.ko。

usb_f_rndis.ko 需依赖 libcomposite.ko(复合设备核心),但绝对不加载 g_ether.ko:

bash 复制代码
# 仅加载RNDIS相关模块(无g_ether)
insmod /lib/modules/$(uname -r)/kernel/drivers/usb/gadget/libcomposite.ko
insmod /lib/modules/$(uname -r)/kernel/drivers/usb/gadget/function/usb_f_rndis.ko

# 后续通过configfs配置复合设备(如前文示例)
mkdir -p /sys/kernel/config/usb_gadget/g1
# ... 省略设备信息配置 ...
ln -s functions/rndis.usb0 configs/c.1/
echo $(ls /sys/class/udc) > UDC

# 配置IP
ifconfig usb0 192.168.7.2 netmask 255.255.255.0 up

配置 USB 虚拟网卡(本质是 RNDIS/CDC-ECM 协议) 的核心是硬件支持 USB Device 模式 + 软件 / 内核支持 USB Gadget 框架 + 网络协议栈

硬件层面需求

条件 说明
USB 控制器类型 芯片必须有支持 USB Device/Peripheral(从设备)模式 的 USB 控制器(如 USB OTG 控制器);仅支持 USB Host(主设备)模式的控制器(比如仅能插 U 盘 / 鼠标的 USB 口)无法实现虚拟网卡。
硬件接线 / 引脚 开发板需引出 USB Device 模式的引脚(如 USB OTG 的 ID 脚、VBUS 检测脚);部分开发板需通过硬件跳线 / 电阻配置 USB 口为 Device 模式(比如 RK3399 的 OTG 口默认是 Host,需改电阻)。
芯片架构兼容性 主流嵌入式架构(ARM、RISC-V、MIPS)均支持,无架构限制(比如 STM32MP1、IMX6、全志 H6、树莓派 CM4 都满足);即使是单片机(如 STM32F4/F7/H7),只要有 USB OTG 控制器 + 足够 RAM(≥64KB)也能实现(基于 RT-Thread/FreeRTOS)。

软件 / 系统层面(核心依赖)

条件 说明
内核 / 系统支持 USB Gadget 框架 Linux 系统:必须开启内核的CONFIG_USB_GADGET(USB Gadget 核心框架);非 Linux 系统(RT-Thread/FreeRTOS):需有对应的 USB Device 栈(如 RT-Thread 的usb_device组件)。
网络协议栈 系统需内置 TCP/IP 协议栈(实现虚拟网卡的网络转发、IP 寻址);Linux 默认自带,RT-Thread 需开启lwip组件,FreeRTOS 需移植 LwIP。
驱动协议支持 内核需开启 RNDIS(CONFIG_USB_GADGET_RNDIS)或 CDC-ECM(CONFIG_USB_ETH_CM)协议驱动(二者都是 USB 虚拟网卡的标准协议,RNDIS 更兼容 Windows,CDC-ECM 更兼容 Linux/macOS)。

2. WIFI连接

开启wifi

bash 复制代码
ifconfig wlan0 up

进入wpa conf,

bash 复制代码
vi /etc/wpa_supplicant.conf

,配置wifi名和密码。(可以网上搜一下参数配置)

bash 复制代码
# 核心全局配置
ctrl_interface=/var/run/wpa_supplicant  # wpa_cli工具与wpa_supplicant进程的通信路径
ap_scan=1                               # AP扫描模式:1=主动扫描(默认),0=仅被动监听
update_config=1                         # 允许wpa_cli修改配置并自动保存到文件

# WiFi网络配置块(可配置多个,按优先级连接)
network={
        ssid="xxx-5G"                # WiFi名称(SSID)
        psk="12345"                  # WiFi密码(WPA-PSK/WPA2-PSK预共享密钥)
        key_mgmt=WPA-PSK                # 加密认证方式(兼容WPA/WPA2-PSK)
        scan_ssid=1                     # 1=扫描隐藏SSID的WiFi,0=仅扫描可见SSID
}

再就是由于现在大部分路由器都有 5G和 2.4G的配置,但某些嵌入式板卡是不支持 5G的,因此需要确认一下。

bash 复制代码
[root@root root]# iw list | grep "5.0"
[root@root root]# iw list | grep "2.4"
                        * 2442 MHz [7] (20.0 dBm)
                        * 2447 MHz [8] (20.0 dBm)

# iw list用于查看 wlan0 网卡支持的频段(2.4G/5G)、信道、加密方式(WPA-PSK 等)、带宽等

关于mobaxterm输入进去的字体可能乱码

bash 复制代码
echo $LANG

#无输出则默认utf-8

创建一个 socket文件存储路径

bash 复制代码
# 核心作用:递归创建wpa_supplicant通信socket文件的存放目录(不存在则建,已存在不报错)
mkdir 
-p          # 递归创建(自动补全父目录,目录已存在时不报错)
/var/run/wpa_supplicant  # wpa_supplicant与wpa_cli通信的socket文件存储路径
复制代码
# 核心原因:socket文件是WiFi控制工具与认证进程的本地通信桥梁
创建socket文件(Unix域套接字)是为实现「wpa_supplicant(WiFi认证后台进程)」和「wpa_cli(WiFi控制工具)」的本地进程通信(IPC):
1. 进程分离:wpa_supplicant后台处理WiFi认证,wpa_cli是前端操作入口,二者需socket文件传递指令/状态;
2. 轻量高效:嵌入式场景下,Unix域套接字(socket文件)无需网络协议栈,比网络socket更适配开发板资源;
3. 功能交互:用户通过wpa_cli发的连接/配置指令、wpa_supplicant返回的认证结果,均通过该socket文件传输(对应配置中ctrl_interface=/var/run/wpa_supplicant,即socket文件存放路径)。

然后使用

bash 复制代码
# 核心作用:后台启动WiFi认证进程,绑定wlan0网卡并加载指定配置文件连接WiFi
wpa_supplicant 
-B          # 后台运行进程(不占用终端,嵌入式场景必备)
-c /etc/wpa_supplicant.conf  # 指定WiFi配置文件(含SSID/密码/加密方式)
-i wlan0    # 指定要绑定的无线网卡接口(开发板默认无线网卡名)
bash 复制代码
1. 命令中`wpa_supplicant` ≠ socket文件存储路径:
   - `wpa_supplicant`是**WiFi认证进程的可执行程序名**(嵌入式系统默认自带的二进制程序);
   - socket文件存储路径是配置中`ctrl_interface=/var/run/wpa_supplicant`(目录名),二者完全不同。

2. 更名规则:
   - socket存储目录(/var/run/wpa_supplicant):可更名(需同步修改配置文件中`ctrl_interface`路径,否则进程与wpa_cli无法通信);
   - 程序名`wpa_supplicant`:不建议更名(嵌入式系统默认路径/命名,更名后需全路径调用,易导致脚本/工具兼容问题)。

连接 wifi,然后需要等待一会,如果是串口调试,会输出以下内容(SSH调试不会自动出现,需要使用 dmesg命令查看更多 log):

串口 → 系统级控制台终端,直接输出内核/全局日志;

SSH → 用户级伪终端,仅显示当前会话内命令输出,不自动转发系统日志。

bash 复制代码
[root@root ]# wpa_supplicant -B -c /etc/wpa_supplicant.conf -i wlan0

Successfully initialized wpa_supplicant
rfkill: Cannot open RFKILL control device
[  670.124975] RTL8723BS: rtw_set_802_11_connect(wlan0)  fw_state = 0x00000008
[  678.988193] RTL8723BS: rtw_set_802_11_connect(wlan0)  fw_state = 0x00000008
[  688.127631] RTL8723BS: rtw_set_802_11_connect(wlan0)  fw_state = 0x00000008
[  697.804890] RTL8723BS: rtw_set_802_11_connect(wlan0)  fw_state = 0x00000008
[  698.446240] RTL8723BS: start auth
[  698.466241] RTL8723BS: auth success, start assoc
[  698.521065] RTL8723BS: rtw_cfg80211_indicate_connect(wlan0) BSS not found !!
[  698.521119] RTL8723BS: assoc success
[  698.598174] RTL8723BS: send eapol packet
[  698.643221] RTL8723BS: send eapol packet
[  698.644951] RTL8723BS: set pairwise key camid:4, addr:9e:a4:d3:f5:da:8d, kid:0, type:AES
[  698.647953] RTL8723BS: set group key camid:5, addr:9e:a4:d3:f5:da:8d, kid:1, type:AES

初始化 wpa_supplicant 应用成功

rfkill 是嵌入式 Linux 管理无线硬件开关的工具,该报错表示系统缺少 /dev/rfkill 设备文件,或 WiFi 硬件被物理 / 软件禁用,导致 wpa_supplicant 无法检测 WiFi 硬件状态(不影响 WiFi 实际连接,仅为状态检测告警)。

上面的wpa_supplicant服务启动后,建议等待一会,再配置IP

bash 复制代码
# 核心作用:为wlan0无线网卡发起DHCP请求,自动获取动态IP/子网掩码/网关/DNS
udhcpc 
-i wlan0    # 指定需获取IP的网卡接口(开发板无线网卡通常为wlan0)

从路由器获取 ip 地址成功

udhcpc 是 BusyBox 内置的嵌入式 Linux 轻量 DHCP 客户端,专为资源受限的开发板设计,核心是向路由器 DHCP 服务器发起请求,自动为网卡获取动态 IP、子网掩码、网关、DNS 等参数,无需手动配置静态 IP。

特性:仅几十 KB(较桌面 dhclient 体积缩 90%+),仅保留 DHCP 核心功能,开发板默认自带;

工作流程(udhcpc -i wlan0):

① 向 wlan0 网段广播 DHCP 发现报文找服务器;

② 路由器回复含网络参数的提供报文;

③ 发送请求报文确认使用该 IP;

④ 路由器回复确认报文,udhcpc 自动配置 wlan0 参数。

工具 适用场景 体积 功能
udhcpc 嵌入式 Linux 极小 仅核心 DHCP
dhclient 桌面 Linux(Ubuntu/CentOS) 较大 全功能 DHCP
dhcpclient Windows - 系统内置

然后你就可以ping一下baidu等网站测下网络了

如果想要切换WiFi,需要重启 wpa_supplicant 服务,需要运行

bash 复制代码
# 核心作用:强制终止系统中所有wpa_supplicant(WiFi认证)进程
killall 
-9          # 发送强制终止信号(SIGKILL),进程无法拒绝/忽略
wpa_supplicant  # 需终止的进程名(WiFi连接认证核心进程)

3. 时区设置

打开文件

bash 复制代码
vi /etc/profile

全局环境变量配置文件(系统级初始化脚本),所有用户登录系统时会自动执行该文件,是配置全局系统环境的核心文件。

配置全局环境变量:如设置 PATH(系统命令查找路径)、LANG/LC_ALL(字符编码,解决中文乱码);

设置登录默认行为:如自定义命令别名、登录后自动执行的基础指令;

永久生效系统配置:嵌入式中常用来添加「永久生效的编码配置」(如 export LC_ALL=en_US.UTF-8)、自定义工具路径等;

加载扩展脚本:自动执行 /etc/profile.d 目录下的脚本(若存在)。

添加内容

bash 复制代码
export TZ=CST-8  
# 导出时区环境变量,Time Zone
# CST:实际指向 "中国标准时间(China Standard Time)"
# -8:表示该时区比世界协调时间(UTC)快 8 小时(东八区)

4. 屏幕测试

调节背光

bash 复制代码
echo 49 > /sys/class/backlight/backlight/brightness
#这是 Linux sysfs 文件系统下的虚拟属性文件(内核硬件抽象接口)
#	暴露硬件属性(如亮度值),简化硬件控制

显示面板(LCD/HDMI):字符设备(/dev/fb0)
触摸屏:字符设备(/dev/input/eventX)
背光控制:sysfs 虚拟文件(非字符 / 块设备)

  1. 可通过 sysfs 暴露的硬件

特征:有明确属性 / 状态(数值、开关),无复杂流式交互;

嵌入式常见:背光、GPIO、CPU 温度 / 频率、网卡状态、LED 灯、电池电量。

  1. 不可 / 极少通过 sysfs 暴露的硬件

特征:需流式 IO 或复杂功能交互;

嵌入式常见:串口、磁盘、摄像头、触摸屏、4G/5G 模组、加密芯片。

背光驱动的写法示例

从驱动角度可参考如下写法注册背光设备,并通过sysfs暴露属性

backnight 专用子系统硬编码写法
cpp 复制代码
#include <linux/module.h>
#include <linux/backlight.h>
#include <linux/gpio.h>

// 私有数据:背光亮度值 + 背光控制GPIO(嵌入式常用GPIO控背光)
static int bl_brightness = 50; // 默认亮度
static int bl_gpio = 10;       // 背光控制GPIO号(需适配硬件)

// 1. 亮度获取函数(sysfs读brightness时调用)
static int bl_get_brightness(struct backlight_device *bl_dev)
{
    return bl_brightness;
}

// 2. 亮度设置函数(sysfs写brightness时调用)
static int bl_set_brightness(struct backlight_device *bl_dev)
{
    bl_brightness = bl_dev->props.brightness;
    // 硬件层:通过GPIO/PWM设置背光(嵌入式核心逻辑,示例用GPIO模拟)
    gpio_set_value(bl_gpio, bl_brightness > 0 ? 1 : 0); // 非0亮,0灭
    return 0;
}

// 3. 背光操作集(关联读写函数)
static const struct backlight_ops bl_ops = {
    .get_brightness = bl_get_brightness,
    .update_status  = bl_set_brightness, // 写亮度时触发
};

// 4. 驱动入口:注册背光设备(自动创建sysfs属性)
static int __init bl_driver_init(void)
{
    struct backlight_device *bl_dev;
    struct backlight_properties props;

    // 初始化GPIO(硬件适配)
    gpio_request(bl_gpio, "backlight_gpio");
    gpio_direction_output(bl_gpio, 1);

    // 背光属性初始化(亮度范围0~255)
    memset(&props, 0, sizeof(props));
    props.max_brightness = 255;
    props.brightness = bl_brightness;

    // 注册背光设备:内核自动创建/sys/class/backlight/backlight/brightness
    bl_dev = backlight_device_register("backlight", NULL, NULL, &bl_ops, &props);
    if (IS_ERR(bl_dev)) return PTR_ERR(bl_dev);

    return 0;
}

// 5. 驱动出口:注销设备
static void __exit bl_driver_exit(void)
{
    backlight_device_unregister(backlight_get_device_by_name("backlight"));
    gpio_free(bl_gpio);
}

module_init(bl_driver_init);
module_exit(bl_driver_exit);
MODULE_LICENSE("GPL");

这个背光驱动示例既不是手动注册的字符设备(无 cdev 相关代码),也不是平台设备(无 platform_driver 相关逻辑),而是基于 Linux 内核 backlight 专用子系统 实现的 ------ 该子系统底层已封装字符设备逻辑,无需开发者手动调用 cdev_init/cdev_add等接口。

平台设备自动适配写法
cpp 复制代码
#include <linux/module.h>
#include <linux/backlight.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>

// 平台设备私有数据
struct bl_plat_data {
    int bl_gpio;
    int bl_brightness;
};

static int bl_get_brightness(struct backlight_device *bl_dev)
{
    struct bl_plat_data *pdata = bl_get_data(bl_dev);
    return pdata->bl_brightness;
}

static int bl_set_brightness(struct backlight_device *bl_dev)
{
    struct bl_plat_data *pdata = bl_get_data(bl_dev);
    pdata->bl_brightness = bl_dev->props.brightness;
    gpio_set_value(pdata->bl_gpio, pdata->bl_brightness > 0 ? 1 : 0);
    return 0;
}

static const struct backlight_ops bl_ops = {
    .get_brightness = bl_get_brightness,
    .update_status  = bl_set_brightness,
};

// 平台设备probe函数(匹配设备树/板级文件时执行)
static int bl_plat_probe(struct platform_device *pdev)
{
    struct bl_plat_data *pdata;
    struct backlight_device *bl_dev;
    struct backlight_properties props;

    // 从设备树/板级资源获取GPIO(替代硬编码)
    pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    pdata->bl_gpio = of_get_named_gpio(pdev->dev.of_node, "bl-gpio", 0);
    pdata->bl_brightness = 50;

    gpio_request(pdata->bl_gpio, "backlight_gpio");
    gpio_direction_output(pdata->bl_gpio, 1);

    memset(&props, 0, sizeof(props));
    props.max_brightness = 255;
    props.brightness = pdata->bl_brightness;

    bl_dev = backlight_device_register("backlight", &pdev->dev, pdata, &bl_ops, &props);
    platform_set_drvdata(pdev, bl_dev);
    return 0;
}

// 平台设备匹配表(适配设备树)
static const struct of_device_id bl_of_match[] = {
    { .compatible = "myboard,backlight" }, // 匹配设备树compatible属性
    { },
};

static struct platform_driver bl_plat_driver = {
    .probe = bl_plat_probe,
    .driver = {
        .name = "backlight-plat",
        .of_match_table = bl_of_match,
    },
};

module_platform_driver(bl_plat_driver);
MODULE_LICENSE("GPL");

驱动基于 platform设备模型实现硬件资源解耦,基于 backlight子系统简化字符设备封装;

核心数据结构(驱动基础)

结构体 作用
bl_plat_data 私有数据载体:存储背光控制 GPIO 号、当前亮度值,关联硬件资源与业务状态
backlight_ops 背光操作集:绑定亮度读 / 写函数,定义 backlight 子系统核心操作接口
platform_driver 平台驱动结构体:关联 probe 函数、设备树匹配规则,适配多板卡硬件

核心功能函数(亮度控制逻辑)

bl_get_brightness:

触发时机:用户读取 sysfs 的brightness文件时;

核心逻辑:从私有数据中读取当前亮度值并返回。

bl_set_brightness:

触发时机:用户写入 sysfs 的brightness文件时;

核心逻辑:更新私有数据的亮度值 → 通过 GPIO 控制背光(非 0 亮 / 0 灭)。

平台设备核心流程(probe 函数,驱动入口)

内存分配:devm_kzalloc动态分配私有数据内存(内核自动管理释放,避免泄漏);

资源获取:of_get_named_gpio从设备树读取 bl-gpio属性,获取背光 GPIO(替代硬编码);

GPIO 初始化:申请 GPIO 资源 → 设置为输出模式 → 默认拉高(背光开启);

背光属性配置:设置亮度范围(0~255)、默认亮度(50);

设备注册:backlight_device_register注册背光设备 → 内核自动创建 sysfs 的brightness文件;

设备绑定:platform_set_drvdata关联匹配到的平台设备与注册的背光设备,方便后续操作。

platform_device(pdev):代表硬件本身(如开发板上的背光 GPIO 资源);

backlight_device(bl_dev):代表内核抽象的背光设备(关联 sysfs、亮度逻辑);

两者是分离的,需手动绑定才能在后续操作中 "通过硬件找到驱动对象"。

比如注销驱动的时候,需要通过pdev找到注册的抽象设备 bl_dev,如果手动绑定是找不到的。

测试花屏和清屏

cpp 复制代码
cat /dev/urandom > /dev/fb0
cat /dev/zero > /dev/fb0
命令 作用
cat /dev/urandom > /dev/fb0 读取随机数设备的字节流,写入帧缓冲(/dev/fb0),屏幕显示随机噪点 / 花屏(测试显示面板可点亮)
cat /dev/zero > /dev/fb0 读取全 0 字节设备的数据流,写入帧缓冲,屏幕变为全黑(清空帧缓冲 / 测试黑屏功能)

5. 调节音量

板子上的 Mic是麦克风,Speaker是扬声器。

bash 复制代码
# 设置扬声器音量(0~30)
amixer -c 0 cset name='DAC LINEOUT Volume' 18

#amixer是 Linux 系统中ALSA 音频子系统的命令行混音器工具
#-c 0:指定操作第 0 号音频设备(声卡);
#DAC LINEOUT Volume:音频 DAC 线路输出音量调节项(嵌入式音频核心调节点);
#18:目标音量值(0 静音,30 最大音量)。
bash 复制代码
#查看当前音量值
amixer -c 0 cget name='DAC LINEOUT Volume'
bash 复制代码
# 查看声卡驱动加载情况
aplay -l  # 输出需显示"card 0: xxx"(0号声卡正常识别)
# 查看音频设备节点
ls /dev/snd/  # 需有pcmC0D0p(播放设备)、controlC0(控制设备)

0 号声卡(rv-acodec)已正常识别(驱动加载无问题)
音频设备节点加载正常

文件 作用
controlC0 0 号声卡的控制节点(amixer 操作的核心)
pcmC0D0c 麦克风输入节点(Capture)
pcmC0D0p 扬声器输出节点(Playback)→ 核心播放节点
by-path 设备路径软链接,无实际使用价值

不同嵌入式声卡的音频控制项名称不统一,精简声卡的播放默认开启,而且某些板卡音频输出的默认通道并非扬声器,可自行检查。

bash 复制代码
# 直接往播放节点pcmC0D0p写测试音(适配RV1106的48kHz采样率)
aplay -D hw:0,0 -f S16_LE -r 48000 -c 2 /dev/urandom

#-D hw:0,0	指定 0 号声卡的 0 号播放设备(精准指向硬件)
#-r 48000	采样率 48kHz(适配 RV1106 的 rv-acodec)
#/dev/urandom	输入随机字节流(生成白噪音,无需音频文件)

aplay是 Linux ALSA 音频子系统的命令行播放工具,核心作用是直接向声卡硬件输出音频数据(支持原始音频流、WAV 等格式),是嵌入式音频调试的核心工具。

有图形界面的音频播放器,不会直接使用aplay命令,但底层大概率依赖aplay所属的 ALSA 音频子系统。

这时候设备连线正常的话会发出白噪声。

bash 复制代码
aplay /tmp/test.wav          # 播放WAV音频文件(有文件时更直观)
aplay ... -d 10              # 播放10秒后自动停止(避免持续发声)
aplay -l                     # 查看声卡列表(确认hw:0,0的来源)
bash 复制代码
# 列出0号声卡的所有音频控制项(核心命令)
amixer -c 0 controls
bash 复制代码
dd if=/dev/urandom of=/dev/dsp bs=8k count=1
#dd按指定大小数量复制数据

6. 文件传输

在MobaXterm使用SSH,连接上后,文件拖入即可.

scp(Secure Copy)是 基于 SSH 协议的安全文件传输工具,依赖 SSH 协议,Mobaxterm 作为 SSH 客户端,内置了 scp 工具,连接后自动复用 SSH 通道,无需额外配置。

bash 复制代码
# 传输文件
scp ./send_file.txt root@172.32.0.93:/root
# 传输文件夹
scp -r ./send_files root@172.32.0.93:/root

Samba(桑巴) 是基于SMB/CIFS 协议的文件共享服务(图形化操作),核心作用是让 Linux / 嵌入式设备(如你的 RV1106)的文件夹,变成 Windows 电脑能直接访问的 "网络共享文件夹"------ 无需记命令,像操作本地文件夹一样拖放文件。

ADB(Android Debug Bridge)------ 安卓 / 嵌入式调试桥是谷歌为安卓设备设计的调试工具,很多嵌入式 Linux 设备(如 RV1106、RK 系列板卡)也兼容 ADB,核心作用是通过 USB / 网络连接,在电脑和嵌入式设备之间执行命令、传文件、调试程序(比scp功能更全,不止传文件)。

具体工具可自行了解。

7. 自启动设置

linux启动流程:

内核启动 → 执行 init进程 → 读取 →

按运行级别(runlevel) 执行对应 rc*.d目录的软链接 →

软链接调用 /etc/init.d/的脚本。

自启动时机 对应目录 / 文件 适用场景(你的 RV1106)
系统启动最早期(内核后) /etc/rcS.d/(软链接指向 init.d 脚本) 初始化硬件、挂载分区
系统服务就绪后(推荐) /etc/rc3.d/(软链接指向 init.d 脚本) 启动音频测试、业务程序(等声卡 / 网络)
用户登录后(仅登录生效) 全局:/etc/profile、/etc/profile.d/单个用户:/root/.bash_profile 登录后自动执行命令(如调音量)
系统关机 / 重启前 /etc/rc0.d/(关机)、/etc/rc6.d/(重启) 清理日志、停止程序
现代 systemd 系统(少数 RV1106) /etc/systemd/system/(放.service 文件) 替代 init.d,需用systemctl enable启用

/etc/init.d 目录

命名格式 含义
Sxxxxxx S=Start(启动),xx= 执行优先级(00~99,数字越小越先执行),xxxx= 脚本功能
rcS/rcK 启动 / 关机总控脚本:rcS负责按顺序执行所有Sxx启动脚本,rcK负责关机时执行停止脚本

极致精简版嵌入式 Linux的特性 ------ 系统直接省略了rc*.d软链接层,去掉了rcS.d/rc3.d等所有rc*.d目录;

直接通过/etc/init.d/rcS脚本,按数字顺序遍历执行 /etc/init.d 下所有 Sxx 开头的脚本;

自启动的知识点

.service 是 systemd 初始化系统 的自启动单元文件(后缀为.service),是目前主流 Linux(如 Ubuntu 16.04+、CentOS 7+、Debian 9+)替代传统 init.d脚本(SysVinit)的新方式,功能更强、管理更灵活。

维度 .service 文件 你板卡的 Sxx 脚本(SysVinit)
依赖系统 必须有 systemd 进程(`ps -ef grep systemd` 能查到) 你的 RV1106 是 SysVinit(只有init进程),无systemd
存放目录 /etc/systemd/system/(自定义)、/usr/lib/systemd/system/(系统) /etc/init.d/
管理命令 systemctl enable xxx.service(启用)、systemctl start xxx.service(启动) 直接执行脚本 / 重启自动执行
适用场景 主流桌面 Linux、中高端嵌入式(如 RK3588) 极致精简嵌入式(RV1106、RV1126)

.desktop 是 Linux 图形桌面环境(如 GNOME、KDE、LXDE、XFCE)的 "用户登录后自启动" 配置文件,仅针对 "登录桌面后自动启动程序",和系统级自启动无关

维度 .desktop 文件 你板卡的 Sxx 脚本(SysVinit)
依赖环境 必须有图形桌面(如 LXDE、XFCE) 你的 RV1106 是无桌面的纯命令行系统
存放目录 /etc/xdg/autostart/(全局)、~/.config/autostart/(用户级) /etc/init.d/
作用时机 用户登录桌面后(比如打开桌面就启动播放器) 系统启动阶段(无用户登录也执行)
适用场景 树莓派装了 Raspbian 桌面、Ubuntu 桌面 无桌面的嵌入式精简系统

运行桌面机器人

0. 前提条件

想要Desk_Bot所有的功能齐全,需要保证:

正常登录开发板。

WIFI连接网络 (非必须,跟自动更新时间等有关系)。

AI chat Server端有运行。

1. 虚拟机仿真运行

前面环境搭建的时候我们只配置了最基本的软件包,其他适配项目代码的并没有安装。

下拉仓库并更新

bash 复制代码
git clone https://github.com/No-Chicken/Echo-Mate.git
cd Echo-Mate
git submodule update --init --recursive

安装Demo所需要的库

安装 SDL2 (LVGL仿真需要)

bash 复制代码
sudo apt-get install libsdl2-dev

SDL2(Simple DirectMedia Layer 2)是一款跨平台的轻量级多媒体开发库(开源、免费),核心作用是屏蔽不同操作系统(Windows/Linux/macOS)的硬件差异,为开发者提供统一的接口来实现「图形渲染、窗口创建、输入交互、音频播放」等功能。

对 LVGL 仿真来说,SDL2 的定位是:LVGL 在 PC 端的 "硬件模拟器" ------ 因为 LVGL 本身是为嵌入式设备(如你的 RV1106)设计的 GUI 库,依赖嵌入式的 LCD 屏、触摸屏驱动,而 PC 没有这些硬件,SDL2 会模拟出这些硬件功能,让你在 PC 上调试 LVGL 界面,不用每次都烧录到开发板。

安装 jsoncpp和 json-c

bash 复制代码
sudo apt-get install libjsoncpp-dev
sudo apt install libjson-c-dev

安装 opus

bash 复制代码
sudo apt install libopus-dev

Opus 是开源免费的高性能音频编码格式,核心特点是低延迟(≤20ms)+ 高压缩比,能同时适配语音(如 RV1106 的语音对讲)和音乐场景,比 MP3/AAC 更适合嵌入式实时音频传输(省带宽、延迟低),广泛用于 VoIP、蓝牙音频、直播等场景。

安装 portaudio(依赖ALSA)

bash 复制代码
sudo apt-get install libasound-dev
sudo apt-get -y install libportaudio2

PortAudio 是跨平台轻量级音频 I/O 库,在 Linux(如你的 RV1106)下依赖 ALSA 作为底层驱动接口调用声卡,屏蔽不同系统 / 声卡的硬件差异,提供统一 API 实现音频采集(麦克风)和播放(扬声器),无需直接编写复杂的 ALSA 底层代码。

安装 websocketpp(依赖boost)

bash 复制代码
sudo apt-get install libboost-dev
git clone https://github.com/zaphoyd/websocketpp.git
cd websocketpp #进入目录
cmake CMakeLists.txt #执行cmake
sudo make
sudo make install

websocketpp 是开源纯 C++ 头文件库,实现 WebSocket 协议解析,依赖 Boost 库(核心用其异步 IO、线程、容器等组件),为 RV1106 等嵌入式设备提供轻量的 WebSocket 通信能力。

安装 curl

curl 是 Linux 下跨协议的网络数据传输工具分两层核心形态:

命令行工具:直接在终端执行curl 网址,快速测试 HTTP/HTTPS/FTP 等网络请求(如访问接口、下载文件);

开发库(libcurl)可集成到 C/C++ 代码中,让程序实现网络数据交互(如调用 HTTP 接口、传输文件),是嵌入式 / 桌面程序实现网络通信的常用库。

跨平台:一套接口适配 Linux(RV1106)、Windows 等系统。

bash 复制代码
sudo apt update #更新包源,看是否有可升级的版本
sudo apt install libcurl4-openssl-dev

安装 BLAS 开发库(Ubuntu/Debian 系统,推荐装优化版)

第三方依赖 snowboy(语音唤醒库)需要 BLAS 做数值计算

bash 复制代码
# 安装OpenBLAS(BLAS的优化版,性能更好,兼容原生BLAS)
sudo apt install libopenblas-dev

# 若OpenBLAS安装失败,可装原生BLAS开发库(备选)
# sudo apt install libblas-dev

进入Demo, 进行编译

注意更改 DeskBot_demo/conf/dev_conf中, 将 LV_USE_SIMULATOR置1.

bash 复制代码
cd ./Demo
cd ./DeskBot_demo
mkdir ./build
cd ./build
cmake ..
make

然后就可以在ubuntu上运行了, 需要进入bin文件夹执行, 有相对路径的依赖

bash 复制代码
cd ../bin
./main

2. 开发板上执行桌面机器人程序

编译 Git 中的 DeskBot 工程:不想交叉编译可直接使用已编译的 bin 文件夹,打包传至开发板;自行编译需修改 conf/dev_conf,将 LV_USE_SIMULATOR 置 0(电脑仿真用 SDL,置 0 后使用 fbdev 设备)。

修改 system_para.conf,填入高德 / 阿里云百炼 API key;需修改红色框内设置(不符将无法连接服务器),其中 V2 版本端口为 8000、protocol 版本为 2;server_url 填写可 ping 通的服务器地址(可通过 ifconfig 查看)。

如何获取高德 API KEY和阿里云 API KEY可自行搜索。

由于开发板系统是buildroot定制的极简系统,不具备包管理工具。因此在开发板上配置编译环境和运行环境极不方便,常见做法是在 PC上使用交叉编译工具,静态编译,不依赖动态库。

动态库支持运行时通过 dlopen()/dlsym() 函数动态加载 / 卸载,可根据设备环境、配置需求选择性加载库(如:RV1106 设备无音频硬件时,不加载 ALSA 库);

而且 buildroot极简系统使用 uclib库且不支持 dpkg等包文件管理系统,想动态编译的话只有依赖全部打包到一个文件夹中然后 Copy到系统里。

这里使用 动态编译,打包集成依赖环境到开发板运行的做法

对比项 静态编译 动态编译
生成库文件 静态库(.a) 动态库(.so,含主文件 + 软链接,如 libxxx.so.1.0)
编译核心选项 禁用共享库(--disable-shared/STATIC=1) 启用共享库(--enable-shared/SHARED=1)+ -fPIC(位置无关代码,必需)
项目链接方式 打包 .a 到可执行文件(单一文件,无外部依赖) 链接 .so(可执行文件体积小,运行时依赖 .so 文件)
CMake / 配置关键 指定 .a 文件路径链接 指定 .so 文件路径链接,无需打包库代码

位置无关代码指的是基于相对寻址和寄存器基地址,使得代码不依赖于特定内存地址。

SDK 目录分析

最外层 SDK 根目录(./
目录/文件 核心作用
README.md 全局 SDK 概述文档,包含整体介绍、使用前提、快速入门指引等
assets/ 存放辅助截图资源(如烧录步骤、工具配置截图),用于配合文档说明,帮助理解操作流程
核心目录:rv1106-sdk/
文件 核心作用
README.md / README_CN.md rv1106-sdk 中英文核心文档,包含 SDK 功能、编译流程、硬件适配、注意事项等关键信息
UPDATE_LOG.md / UPDATE_LOG_CN.md SDK 中英文更新日志,记录版本迭代、Bug 修复、功能新增、兼容性变更等内容
build.sh(软链接到 ./project/build.sh SDK 一键构建脚本入口,用于初始化编译环境、批量编译整个 SDK 生成固件 / 可执行程序
子目录 核心作用
external/ 第三方依赖固件存放地,放了 rtlbt,
media/ 音视频 / AI / 图像处理核心模块(rv1106 核心价值所在)
project/ 项目构建与应用层开发目录,负责 SDK 整体构建配置和上层应用开发
sysdrv/ 底层系统驱动与源码目录,负责底层内核、驱动的编译与配置
tools/ 跨平台开发工具集,存放 Linux/Windows 平台的开发、烧录、升级工具
media子目录
media子目录 核心作用
alsa-lib/ 音频驱动库(ALSA 架构),存放不同架构预编译包,支持音频采集、播放、混音等基础音频功能
avs/ 音频增强模块,提供音频降噪、增益优化等能力,包含头文件、预编译库和算法查找表
cfg/ 媒体模块全局编译配置目录,cfg.mk 定义媒体模块的编译规则、路径映射、依赖项等
common_algorithm/ 通用音视频算法模块,存放音频检测、视频移动检测、格式转换等通用处理算法
isp/ 图像信号处理(ISP)模块,存放 rv1106/rv1126/rk3588 平台的预编译库,负责相机图像的自动对焦、曝光、白平衡、降噪等图像质量优化
iva/ 智能视觉应用(IVA)模块,支持人脸检测、目标跟踪、行为分析等 AI 视觉功能,适配 rv1106 NPU 硬件加速
ive/ 智能视觉增强(IVE)模块,配合 NPU 实现图像预处理硬件加速(如裁剪、增强),提升 AI 推理效率
libdrm/ DRM 显示驱动库,存放预编译包,支持嵌入式设备对接屏幕、显示器,实现图像 / 视频显示输出
libv4l/ V4L2 视频采集驱动库,支持摄像头等视频设备的采集控制,负责将硬件采集的视频数据转换为标准格式
luckfox/ Luckfox 开发板配套功能模块,包含硬件加密、示例代码等,适配 Luckfox 定制化硬件
mpp/ 瑞芯微媒体处理平台(MPP)核心模块,存放预编译库,负责视频 / 图像的硬件加速编解码(H.264/H.265 等格式)
rga/ 瑞芯微图像加速(RGA)模块,存放预编译库,负责图像旋转、缩放、裁剪、格式转换等硬件加速操作,降低 CPU 占用
rockit/ 媒体渲染与拼接模块,支持多视频流拼接、图形层叠加、高清视频渲染输出
samples/ 音视频 / AI 示例代码目录,包含可直接运行的音频、视频、AI 推理 demo,是快速上手 SDK 核心功能的最佳参考
security/ 安全加密模块,存放硬件加密库,支持数据加密 / 解密、安全校验、固件防篡改等功能
sysutils/ 媒体模块辅助系统工具,提供加密、外设管理等辅助功能,保障媒体模块正常运行
project子目录 / 文件 核心作用
app/ 应用层开发核心目录,存放 rv1106 上层业务应用
app/component/ 应用开发组件库,存放通用业务组件(如网络、数据库),减少重复开发
app/ipcweb/ IPC 设备 Web 服务应用,支持通过网页对 IPC 设备进行配置、管理和视频预览
app/rkipc/ IPC 设备核心应用,负责视频采集、传输、AI 分析、云对接等 IPC 核心业务
app/wifi_app/ WiFi / 蓝牙应用,支持 rv1106 无线通信配置、连接管理等功能
build.sh SDK 核心构建脚本,负责解析配置、批量编译驱动、内核、应用,生成最终可烧录固件
cfg/ 项目硬件配置目录,BoardConfig_IPC 是 IPC 设备的专属硬件配置文件,定义外设参数、引脚映射等
scripts/ 构建辅助脚本目录,存放固件打包、烧录、升级、补丁生成等辅助脚本,支撑 SDK 构建流程
sysdrv子目录
sysdrv子目录 核心作用
cfg/ 系统驱动编译配置目录,定义驱动、内核的编译规则、依赖包、架构参数等
drv_ko/ 内核驱动模块目录,存放 WiFi、MPP、电机等硬件的驱动 ko 文件和编译脚本,支持驱动的单独加载 / 卸载
source/ 底层核心源码目录,存放可修改的源码,支持用户定制化底层系统
source/kernel/ Linux 内核源码(适配 rv1106),用户可修改内核配置、添加驱动、优化性能
source/uboot/ U-Boot 引导程序源码(适配 rv1106),负责硬件初始化、内核引导、固件烧录等底层启动流程
tools/ 底层系统辅助工具,存放开发板配置、PC 端调试工具,支撑内核 / UBoot 的编译与调试
tools 子目录
tools子目录 核心作用
linux/ Linux 平台工具集,用于 Linux 环境下的 SDK 开发与设备调试
linux/toolchain/ 交叉编译工具链核心目录,存放 arm-rockchip830-linux-uclibcgnueabihf 工具链,是 rv1106 程序交叉编译的必备依赖
linux/Linux_Pack_Firmware/ Linux 平台固件打包工具,将编译产物打包为可烧录的镜像文件
linux/Linux_Upgrade_Tool/ Linux 平台设备升级工具,用于给 rv1106 开发板在线 / 离线升级固件
linux/SocToolKit/ 瑞芯微 Soc 调试工具集,用于硬件配置、固件分析、故障排查
windows/ Windows 平台工具集,用于 Windows 环境下的固件烧录和开发(适合新手快速上手)
windows/FactoryTool/ Windows 平台工厂烧录工具,支持批量给 rv1106 开发板烧录固件
windows/DriverAssitant/ Windows 平台 USB 驱动助手,安装后实现 PC 与 rv1106 开发板的 USB 通信

查看系统信息

bash 复制代码
# 方法1:通用系统信息文件(多数Linux都支持,信息最完整)
cat /etc/os-release

# 方法2:精简版系统标识(嵌入式开发板常用)
cat /etc/issue

# 方法3:适配老款/定制化嵌入式系统
cat /etc/version

找交叉编译工具

Echo-Mate项目提供了 rv1106-sdk,该 sdk中提供了交叉编译工具链

因此通常不需要特地去下载交叉编译工具链,

RV1106 SDK 中已经内置了 arm-rockchip830-linux-uclibcgnueabihf 工具链,这个工具链是瑞芯微针对 RV1106 芯片(Cortex-A7 内核)和配套 Buildroot 2023.02.6 系统定制优化的:

精准匹配开发板的 uClibc 库(Linaro 工具链默认更偏向 glibc,和你的 Buildroot 系统兼容性不如专属工具链);

内置了 RV1106 硬件专属的指令集支持(如 neon-vfpv4),比通用 Linaro 工具链更适配硬件;

uClibc(现在多是 uClibc-ng):极致精简,剔除了很多非必要的扩展和调试功能,体积只有 glibc 的 1/10 到 1/5,甚至更小。只满足核心C标准,如C99,不支持很多glibc的高级扩展。

工具链类型 适配场景 是否用到(你的场景)
Linaro 7.5 通用工具链 多数 32 位 ARM 开发板(树莓派、通用 Cortex-A 开发板)、glibc 系统 否(仅作为备选)
RV1106 SDK 专属工具链 RV1106 芯片、Buildroot 2023.02.6、uClibc 系统 是(核心工具链)

如没有,则需要尝试使用以下备用方案,

下载交叉编译工具,

bash 复制代码
# 下载工具链包(7.5版本稳定,适配多数Buildroot)
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

# 若wget下载慢,可手动打开链接在浏览器下载,再传到PC的~/project目录

解压到 /opt/ 目录,并配置永久生效的环境变量

bash 复制代码
# 解压到/opt(通用工具链目录,需sudo权限)
sudo tar -xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/

# 临时配置环境变量(当前终端生效)
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH

# 永久配置(重启终端仍生效,推荐)
echo 'export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
部分 全称 通俗解释(适配你的 Buildroot 开发板)
gnu GNU 表示工具链遵循 GNU 标准,使用 GNU 生态的 C 库(glibc/uClibc/musl)------ 你的 Buildroot 2023.02.6 完全兼容 GNU 标准
eabi Embedded ABI 嵌入式应用二进制接口,是 ARM 嵌入式系统的专属二进制规范(区别于 PC 的 ABI),所有 ARM 开发板都遵循这个规范
hf Hard Float(硬浮点) 表示工具链支持硬件浮点运算,对应的是 "sf(软浮点)"------ 靠软件模拟浮点运算,性能差

验证工具链是否安装成功,

bash 复制代码
# 检查工具链是否能识别
arm-linux-gnueabihf-gcc -v

CMakeList解析

首先,从开头可以看到,当ARM交叉编译开关开启的时候,会从 toolchain.cmake这个文件夹去读取工具链,
记得刚拿到源码,把ARM交叉编译开关改成ON

这里,CMAKE_SYSROOT是在 toolchain.cmake文件里设置的,CMakeList中并没有显式定义。
CMAKE_TOOLCHAIN_FILE 不是普通变量,它是 CMake 官方定义的内置特殊变量------CMake 在执行脚本时,会在 project() 命令执行之前,主动检查这个变量,如果有值则会自动读取并执行值路径文件的内容

在根目录 CMakeLists.txt 中,有一段 "引入子模块" 的代码

bash 复制代码
# 引入子模块(LVGL核心、GUI应用、工具类、公共模块)
add_subdirectory(lvgl)      # 关联 lvgl 目录下的 CMakeLists.txt
add_subdirectory(gui_app)   # 【核心关联】关联 gui_app 目录下的 CMakeLists.txt
add_subdirectory(utils)     # 关联 utils 目录下的 CMakeLists.txt
add_subdirectory(common)    # 关联 common 目录下的 CMakeLists.txt

当根目录 CMake 执行到 add_subdirectory(gui_app) 时,会自动进入 gui_app 目录,查找并执行子模块目录下的 CMakeLists.txt;

子目录 CMakeLists.txt 中定义的所有目标

(如 add_library(gui_app)、add_executable(gui_app_demo))、变量、库链接配置等,都会被注册到全局 CMake 环境中,根目录可以直接引用;

可选第二个参数

(如 add_subdirectory(gui_app {CMAKE_BINARY_DIR}/gui_app_build)),用于指定子目录的编译产物输出目录,不指定则默认在 {CMAKE_BINARY_DIR}/gui_app 下。

交叉编译工具链中的sysroot

首先,明确一点,

Echo-Mate项目提供了 rv1106-sdk,该 sdk中提供了交叉编译工具链,且工具链中有可用的 sysroot。

这个 sysroot 是专为交叉编译打造的「精简编译依赖包」,不是完整的开发板运行时根文件系统,因此只保留了交叉编译必需的组件,剔除了所有无关目录。

同时,为了保持系统环境的纯洁,隔离软件所需的sysroot与pc的根文件目录,通常将交叉编译后的库文件放置在 sysroot目录下。

当前的 sysroot不具备编译、运行整个项目所需的全部库文件,因此部分依赖需要我们手动安装并整理到 sysroot目录。满足项目 cmakelist以及由 cmakelist产生的 makefile所需的依赖。

为了讲解知识点,下面给出 sysroot的制作流程。

Sysroot制作(可选)

在 PC 上创建 sysroot 空目录

bash 复制代码
# 在 PC 上创建存放 sysroot 的目录(路径自定义,建议放在 SDK 目录下)
mkdir -p ~/project/echo-machine/Echo-Mate/SDK/rv1106-sdk/sysroot_rv1106
cd ~/project/echo-machine/Echo-Mate/SDK/rv1106-sdk/sysroot_rv1106

从开发板拷贝核心根文件系统(制作 sysroot)

Buildroot 系统的核心依赖在 /lib、/usr/include、/usr/lib 等目录,只需拷贝这些静态目录(跳过 /dev、/proc 等动态目录),执行以下 scp 命令(替换为你的开发板 IP):

bash 复制代码
# 拷贝核心库目录(必须)
scp -r root@192.168.7.2:/lib ./
scp -r root@192.168.7.2:/usr/lib ./usr/
scp -r root@192.168.7.2:/usr/include ./usr/ #极简版系统可能找不到头文件夹库/usr/include

# 拷贝必要的系统配置/头文件(可选,增强兼容性)
scp -r root@192.168.7.2:/usr/share ./usr/
scp -r root@192.168.7.2:/etc ./

纠正拷贝过来的文件夹的软链接的绝对目录为相对目录

bash 复制代码
#!/bin/bash

# 核心功能:将绝对路径软链接转为相对路径,解决跨环境路径依赖问题

# 修正 ./lib 目录下的软链接(绝对路径→相对路径,层级:../)
if [ -d "lib" ]; then
  find lib -type l | while read link; do  # 遍历lib下所有软链接
    target=$(readlink "$link")            # 读取软链接指向的原目标路径
    if [[ "$target" == /* ]]; then        # 判断目标是否为绝对路径(以/开头)
      rm "$link"                          # 删除原绝对路径软链接
      ln -s "../$target" "$link"          # 重建为相对路径软链接(对应lib/目录层级)
    fi
  done
fi

# 修正 ./usr/lib 目录下的软链接(绝对路径→相对路径,层级:../../)
if [ -d "usr/lib" ]; then
  find usr/lib -type l | while read link; do  # 遍历usr/lib下所有软链接
    target=$(readlink "$link")                # 读取软链接指向的原目标路径
    if [[ "$target" == /* ]]; then            # 判断目标是否为绝对路径(以/开头)
      rm "$link"                              # 删除原绝对路径软链接
      ln -s "../../$target" "$link"           # 重建为相对路径软链接(对应usr/lib/目录层级)
    fi
  done
fi

这个时候会发现 scp: /usr/include: No such file or directory,找不到 /usr/include文件夹,

Buildroot 编译开发板镜像时,默认会开启 BR2_STRIP_all 等精简选项,删除 /usr/include(头文件)、静态库等 "编译相关文件",只保留运行程序必需的动态库 / 可执行文件,所以开发板上没有 /usr/include 是正常的,无需纠结。

编译所需的头文件库,可以在交叉编译工具链中找到,

bash 复制代码
# 进入工具链根目录
cd ~/project/echo-machine/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf

# 全局查找 include 目录
find . -name "include" -type d
bash 复制代码
teng@TengSea:~/project/echo-machine/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf$ find . -name "include" -type d
./arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/include
./arm-rockchip830-linux-uclibcgnueabihf/debug-root/usr/include
./arm-rockchip830-linux-uclibcgnueabihf/include
./lib/gcc/arm-rockchip830-linux-uclibcgnueabihf/8.3.0/install-tools/include
./lib/gcc/arm-rockchip830-linux-uclibcgnueabihf/8.3.0/plugin/include
./lib/gcc/arm-rockchip830-linux-uclibcgnueabihf/8.3.0/include
./share/licenses/gcc/include
./share/licenses/gdb/include
./share/licenses/binutils/include

其中 ./arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/include 是核心系统级头文件目录(包含 stdio.h、stdlib.h 等编译必需的基础头文件),

./arm-rockchip830-linux-uclibcgnueabihf/include 是工具链补充头文件,这两个是我们需要的核心路径。

!!!!注意,这个时候我们发现问题了!!!!

交叉编译需要包含 uclib或者 glibc 的头文件库,也就是 /usr/include目录,

但是开发板的系统,精简掉了这个目录,我们找项目源码,找到的又是厂商提供的交叉编译工具链的 sysroot的下的目录,

直接用 工具链的 sysroot目录不就好了

是的!

由于系统本身是精简的镜像,想通过 Lubancat那样直接复制板卡烧录的系统中的主要目录,来作为 sysroot是行不通的。

要么直接用交叉编译工具链提供的精简 sysroot,可以满足编译需求。(不一定满足运行需求,运行需求由于芯片或板卡的sdk提供方已经提供了对应的镜像,通常是由镜像满足的)

要么自己 编译Buildroot/SDK 源码(如你的 RV1106 有 Buildroot 源码)生成完整的 sysroot(包含头文件、库)。

完整系统镜像没有开启 BR2_STRIP_all 等精简选项,保留了 /usr/include(头文件)、静态库(.a)、动态库(.so),同时目录结构完整,既有运行时文件,也有编译时文件,满足交叉编译的所有需求。

所以,我们这里直接用交叉编译工具链提供的 sysroot就好。

*检查 toolchain.cmake

DeskBot_Demo目录下有 toolchain.cmake文件

DeskBot_Demo目录下有 toolchain.cmake文件,

该文件的路径在 cmakelist中通过 set 指令与CMAKE_TOOLCHAIN_FILE 变量绑定。

CMAKE_TOOLCHAIN_FILE 不是普通变量,它是 CMake 官方定义的内置特殊变量------CMake 在执行脚本时,会在 project() 命令执行之前,主动检查这个变量,如果有值则会自动读取并执行值路径文件的内容。

toolchain.cmake 是一个 CMake 工具链文件 ,用于指定目标平台的编译器路径,设置目标平台的系统根目录(Sysroot),定义目标平台的架构(如 ARMv7)、CPU 特性(如浮点支持)、以及其他编译选项(如优化级别、是否生成调试信息等)。

bash 复制代码
#文件路径:DeskBotDemo目录下项目根目录的 CMakeList对应的 toolchain.cmake

# 指定目标系统和处理器架构
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# 设置 SDK 路径
set(SDK_PATH "/home/teng/project/Echo-Mate/SDK/rv1106-sdk")

# 指定交叉编译器路径
set(CMAKE_C_COMPILER ${SDK_PATH}/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${SDK_PATH}/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++)

# 指定 sysroot 路径(如果可用)
set(CMAKE_SYSROOT ${SDK_PATH}/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot)
# 下面注释掉的是原内容,SDK/.../sysdrv/source/buildroot
#set(CMAKE_SYSROOT ${SDK_PATH}/sysdrv/source/buildroot/buildroot-2023.02.6/output/host/arm-buildroot-linux-uclibcgnueabihf/sysroot)

# 设置查找路径,优先在 sysroot 中查找库和头文件
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})

# 配置查找模式
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)  # 不在 sysroot 中查找可执行程序
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)  # 仅在 sysroot 中查找库文件
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)  # 仅在 sysroot 中查找头文件

可以发现我们注释掉了 CMAKE_SYSROOT 的路径,将其从指向 SDK/sysdrv/source下的 buildroot 改成了将其指向 SDK/tools/ 目录下,交叉编译工具链带有的 sysroot。

原因是我们在 SDK中并没有找到 buildroot编译后的根目录, 这个目录相比交叉编译工具链中的 sysroot 更完整。但需要手动配置并编译buildroot源码。

我们查找SDK中的 buildroot相关文件时只发现以下文件,

文件/目录路径 核心作用
./tools/board/mirror_select/buildroot_mirror_select.sh Buildroot 镜像源选择脚本,用于切换 Buildroot 编译所需的源码下载源(比如从国外源切换到国内镜像源),解决源码下载慢、下载失败的问题,仅在编译 Buildroot 时使用。
./tools/board/buildroot/ Buildroot 核心资源存放目录,仅包含源码包,无已编译产物。
./tools/board/buildroot/buildroot-2023.02.6.tar.gz Buildroot 源码压缩包(版本 2023.02.6),Buildroot 是一款轻量级嵌入式根文件系统构建工具,可通过配置生成自定义的嵌入式 Linux 根文件系统(包含 libc、基础工具、自定义库等)。

而原路径

bash 复制代码
set(CMAKE_SYSROOT \
    ${SDK_PATH}/sysdrv/source/    \
    buildroot/buildroot-2023.02.6/output/host/  \
    arm-buildroot-linux-uclibcgnueabihf/sysroot)

这是 Buildroot 构建过程中生成的 sysroot 目录。output/host 是 Buildroot 构建输出目录,arm-buildroot-linux-uclibcgnueabihf 是目标架构和工具链的标识,sysroot 是目标系统的根目录,包含了目标平台的头文件、库文件和其他资源。

对比维度 交叉工具链自带的 sysroot Buildroot 编译生成的 sysroot
核心本质 工具链配套的轻量基础根文件系统,仅满足编译依赖 自定义构建的完整嵌入式根文件系统,与目标板运行环境一致
完整性 极简,仅包含核心 C/C++ 标准库、编译器依赖(如 libc.solibstdc++.so),无额外工具 / 库 完整,可包含你需要的所有依赖库(如 sqlite3、zlib)、工具(如 ls、cp)、自定义组件,按需增减
定制化能力 无,不可修改(预编译打包在工具链中),无法添加 / 裁剪内容 极强,通过 make menuconfig 配置,可裁剪冗余组件、添加自定义库 / 工具、优化固件体积
与目标板的一致性 较低,仅保证编译兼容性,工具链 sysroot 中的库可能与目标板最终运行的根文件系统不一致 极高,保证编译 + 运行一致性,Buildroot 生成的 sysroot(output/target/)就是目标板最终的根文件系统,避免 "编译通过、运行报错"(库缺失 / 版本不匹配)
使用成本 极低,无需额外操作,直接可用,无学习成本 较高,需编译 Buildroot(耗时久、配置复杂),有一定学习成本(熟悉 Buildroot 配置规则)
适用场景 快速开发、基础编译、依赖 SDK 预编译库的场景 量产部署、高度自定义、需要统一编译 / 运行环境的场景

当前项目依赖的核心库(如 zlib、sqlite3、rkmpi 等)已在 rv1106-sdk 的 media/、project/ 目录下提供预编译包,工具链自带的 sysroot 已能满足基础编译依赖(C/C++ 标准库),额外库可通过 CMakeLists.txt 手动链接 rv1106-sdk 中的预编译库,完全无需 Buildroot 支持。

关于工具链中的 sysroot的完善,和后续整个项目的依赖的打包运行,放到后续章节。

Sysroot的完善

CURL库

在build目录中执行 cmake ..会报错找不到 CURL库,
错误发生在 gui_app/CMakeList

注意,工具链是arm-rockchip830-linux-uclibcgnueabihf(uclibc 版),而系统安装的libcurl4-openssl-dev:armhf是 glibc 版 ARM 库,所以不能直接通过 apt软件管理工具从软件源下载安装 CURL。

bash 复制代码
# 更新软件源
sudo apt update
# 安装ARM架构(armhf)的CURL开发包(适配RV1106等ARM开发板,gclib库)
sudo apt install libcurl4-openssl-dev:armhf
# 安装 x86架构的CURL开发包
sudo apt install libcurl4-openssl-dev

系统安装的 ARM 版 CURL 是 glibc 架构,项目的 uclibc 工具链不兼容,且 SDK sysroot 目录中也没有适配该 uclibc 工具链的 CURL 库。

检查 SDK sysroot 中是否已有 CURL

bash 复制代码
# 检查CURL头文件
ls /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/include/curl/curl.h

# 检查CURL库文件
ls /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/lib/libcurl.so

如果无输出,说明需要手动编译适配 uclibc 的 CURL 到 sysroot。

由于现在我们的目的,是把项目缺失,且SDK的编译器的 sysroot中没有的库,安装到 sysroot,便于后面我们直接整个项目打包到开发板运行。

所以先创建一个文件夹,用来管理第三方源码,然后交叉编译了安装到 sysroot。

bash 复制代码
cd ~/project/Echo-Mate
mkdir third_lib && cd third_lib
bash 复制代码
# 1. 下载CURL源码(选稳定版)
wget https://curl.se/download/curl-8.5.0.tar.gz
tar -zxvf curl-8.5.0.tar.gz
编译环境配置
bash 复制代码
# 1. 先进入curl源码目录(确保路径正确)
cd ~/project/Echo-Mate/third_lib/curl-8.5.0

# 2. 配置环境变量(重新定义,避免之前的变量失效)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export SYSROOT=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot

# 3. 设置编译器参数(核心:通过CFLAGS/LDFLAGS传递sysroot)
export CFLAGS="--sysroot=${SYSROOT} -march=armv7-a -mtune=cortex-a7 -mfpu=vfpv3 -mfloat-abi=hard"
export LDFLAGS="--sysroot=${SYSROOT}"

# 4. 修正后的configure命令(移除--sysroot,保留其他有效参数)
./configure \
  --host=arm-rockchip830-linux-uclibcgnueabihf \
  --prefix=${SYSROOT}/usr \
  --enable-shared \
  --enable-static \
  --without-ssl \
  --without-tls-srp \
  --disable-ldap \
  --disable-ldaps \
  --disable-docs \
  --disable-tests

./configure 是一个可执行脚本,通常位于源代码目录中。

用于检查系统环境、设置编译选项、生成 Makefile。

在 .configure脚本的执行过程中,会自动读取 CFLAGS 和 LDFLAGS,

CFLAGS 会被传递给编译器(如 gcc),用于编译源代码。

LDFLAGS 会被传递给链接器(如 ld),用于链接生成的可执行文件或库。

安装
bash 复制代码
# 清空旧的编译产物(避免残留)
make clean

# 多线程编译
make -j4

# 安装到sysroot(sudo确保权限)
sudo make install
验证

检查文件是否存在,

bash 复制代码
# 1. 检查CURL头文件(必须存在,否则CMake找不到头文件)
ls ${SYSROOT}/usr/include/curl/curl.h

# 2. 检查CURL动态库(.so,运行时依赖)
ls ${SYSROOT}/usr/lib/libcurl.so

# 3. 检查CURL静态库(.a,编译时链接)
ls ${SYSROOT}/usr/lib/libcurl.a

# 4. 检查库的版本符号链接(可选,确认库版本)
ls ${SYSROOT}/usr/lib/libcurl.so*

检查库架构,

bash 复制代码
# 用系统file命令(识别架构)
file ${SYSROOT}/usr/lib/libcurl.so

# 如果是软链接file命令会找到实际文件,实际文件则会显示文件信息,包括架构
file ${SYSROOT}/usr/lib/libcurl.so.4.8.0

确认是ARM架构

ALSA库

再回到 DeskBot_demo 执行 cmake ..,提示找不到音频库。
common子模块的 CMakeList报错

bash 复制代码
# 进入第三方库目录(和CURL同目录,方便管理)
cd ~/project/Echo-Mate/third_lib

# 下载alsa-lib 1.2.10(稳定版,适配嵌入式)
wget https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.10.tar.bz2
tar -jxvf alsa-lib-1.2.10.tar.bz2
cd alsa-lib-1.2.10

注意:

alsa-lib 不支持同时编译静态库和动态库(提示do not try to compile static and shared libraries together),这是 alsa-lib 的编译限制。

bash 复制代码
# 1. 配置环境变量(和编译CURL一致,无需修改)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export SYSROOT=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot

# 2. 设置编译器参数(传递sysroot和ARM架构选项)
export CFLAGS="--sysroot=${SYSROOT} -march=armv7-a -mtune=cortex-a7 -mfpu=vfpv3 -mfloat-abi=hard"
export LDFLAGS="--sysroot=${SYSROOT}"

# 修正后的configure命令(只启用动态库,禁用静态库)
./configure \
  --host=arm-rockchip830-linux-uclibcgnueabihf \
  --prefix=${SYSROOT}/usr \
  --enable-shared=yes \
  --enable-static=no \
  --disable-python \
  --disable-aload \
  --disable-hwdep \
  --disable-topology

# 4. 编译并安装到sysroot
make clean
make -j4
sudo make install
bash 复制代码
# 多线程编译(根据CPU核心数调整,如-j4)
make -j4

# 安装到sysroot(sudo获取写入权限)
sudo make install
bash 复制代码
# 检查ALSA头文件(核心)
ls ${SYSROOT}/usr/include/alsa/asoundlib.h

# 检查ALSA动态库(核心)
ls ${SYSROOT}/usr/lib/libasound.so

# 检查库架构(必须是ARM)
file ${SYSROOT}/usr/lib/libasound.so

安装成功

WEBSOCKETPP头文件

AIChat_demo/Client/CMakeLists.txt找不到 WEBSOCKETPP

下载 WEBSOCKETPP 源码(纯头文件,无需编译)

bash 复制代码
# 进入第三方库目录(和CURL/ALSA同目录,方便管理)
cd ~/project/Echo-Mate/third_lib

# 下载WEBSOCKETPP稳定版(0.8.2是长期支持版)
wget https://github.com/zaphoyd/websocketpp/archive/refs/tags/0.8.2.tar.gz -O websocketpp-0.8.2.tar.gz
tar -zxvf websocketpp-0.8.2.tar.gz

# 简化目录名(方便后续引用)
mv websocketpp-0.8.2 websocketpp

# 复制WEBSOCKETPP核心头文件到sysroot的/usr/include(关键)
sudo cp -r websocketpp/websocketpp ${SYSROOT}/usr/include/
bash 复制代码
# 检查sysroot中是否存在WEBSOCKETPP核心头文件
ls ${SYSROOT}/usr/include/websocketpp/client.hpp

复制到sysroot的 usr/include目录下

把 WEBSOCKETPP 头文件复制到 sysroot,但 CMake 仍报错 ------ 核心原因是:

find_package(WEBSOCKETPP) 默认走「Config 模式」

(找WEBSOCKETPPConfig.cmake),

而 WEBSOCKETPP 没有这个配置文件;

即使头文件在 sysroot,CMake 的 Config 模式也识别不了(和编译器找头文件是两回事)。

编译器(g++):会从sysroot/usr/include找头文件 → 你复制的头文件能满足编译器需求;

CMake:执行find_package(WEBSOCKETPP)时,优先找WEBSOCKETPPConfig.cmake(Config 模式)→ 找不到就报错,根本不会去查头文件路径;

给 CMake 提供 FindWEBSOCKETPP.cmake(Module 模式),

告诉 CMake,WEBSOCKETPP 是纯头文件库,头文件在 sysroot 里。

bash 复制代码
# 在任意目录创建(比如third_lib),后续cmake指定路径即可
cat > ~/project/Echo-Mate/third_lib/FindWEBSOCKETPP.cmake << EOF
# 极简版FindWEBSOCKETPP.cmake - 仅满足CMake找包逻辑
find_path(WEBSOCKETPP_INCLUDE_DIR NAMES websocketpp/client.hpp PATHS ${SYSROOT}/usr/include NO_DEFAULT_PATH)
set(WEBSOCKETPP_FOUND TRUE)  # 告诉CMake找到包了
set(WEBSOCKETPP_INCLUDE_DIRS ${WEBSOCKETPP_INCLUDE_DIR})  # 匹配原CMakeLists的变量
set(WEBSOCKETPP_LIBRARIES "")  # 纯头文件库,无链接库
EOF

<< EOF 表示从当前行开始,直到遇到下一个 EOF,所有内容都将被传递给 cat。

<< 是 Here Document 的开始标记。 EOF 是一个标记,表示 Here Document 的结束。

bash 复制代码
# 进入AIChat_demo的build目录
cd ~/project/Echo-Mate/Demo/AIChat_demo/Client/build
rm -rf *  # 清空旧缓存

# 回到DeskBot_demo执行cmake(核心:添加CMAKE_MODULE_PATH指向Find文件目录)
cmake \
  -DTARGET_ARM=ON \
  -DCMAKE_TOOLCHAIN_FILE=../../../../DeskBot_demo/toolchain.cmake \
  -DCMAKE_MODULE_PATH=~/project/Echo-Mate/third_lib \  # 指向FindWEBSOCKETPP.cmake所在目录
  -DSYSROOT=${SYSROOT} \  # 传递sysroot路径给Find文件
  ..

CMAKE_MODULE_PATH 是一个 CMake 变量,用于指定 CMake 模块文件的搜索路径。当你运行 cmake 命令时,可以通过 -DCMAKE_MODULE_PATH=<path> 参数将自定义路径添加到 CMAKE_MODULE_PATH 中。

根目录的 CMakeList会先找 CMAKE_MODULE_PATH 的 路径,然后找 .cmake文件。

snowboy库

AIChat_demo/Client/third_party/snoboy/CMakeList.txt 找不到 OpenBLAS线性代数库

bash 复制代码
# 1. 下载OpenBLAS稳定版(0.3.26,适配32位ARM)
cd ~/project/Echo-Mate/third_lib
wget https://github.com/OpenMathLib/OpenBLAS/archive/refs/tags/v0.3.26.tar.gz -O OpenBLAS-0.3.26.tar.gz
tar -zxvf OpenBLAS-0.3.26.tar.gz
cd OpenBLAS-0.3.26

# 2. 定义环境变量(复用之前的交叉编译环境)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld
export SYSROOT="/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot"

# 3. 交叉编译OpenBLAS(适配RV1106的ARMv7-A架构)
# TARGET=ARMV7:指定ARMv7架构(Cortex-A7);NO_LAPACK=1:禁用LAPACK(嵌入式无需);PREFIX:安装到sysroot
make \
  TARGET=ARMV7 \
  NO_LAPACK=1 \
  NO_AFFINITY=1 \  # 嵌入式无CPU亲和性,禁用
  USE_THREAD=0 \   # 禁用多线程,减少资源占用
  CC=${CC} \
  CXX=${CXX} \
  AR=${AR} \
  LD=${LD} \
  HOSTCC=gcc \     # 主机编译器(编译辅助工具)
  -j4

# 4. 安装到sysroot
sudo make PREFIX=${SYSROOT}/usr install

这个 wget GITHUB下载慢,直接复制网址手动下。

同时源码包有一点bug,比如 rm写成了 ru,

bash 复制代码
# 进入OpenBLAS源码根目录
cd ~/project/Echo-Mate/third_lib/OpenBLAS-0.3.26

# 针对正确路径替换"ru"为"rm"(修复拼写错误)
sudo sed -i 's/ru /rm /g' ./Makefile.tail
sudo sed -i 's/ru /rm /g' Makefile
sudo sed -i 's/ru /rm /g' exports/Makefile

# 验证替换结果(可选,确认错误命令已修复)
grep -n "rm " ./Makefile.tail

重新配置编译参数(禁用动态库,只编译静态库),

嵌入式场景下静态库更稳定,且适配 uclibc,

bash 复制代码
# 清空旧的编译产物
make clean

# 重新定义环境变量(确保生效)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld
export SYSROOT="/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot"

# 编译静态库(禁用动态库,适配uclibc)
make \
  TARGET=ARMV7 \
  NO_LAPACK=1 \
  NO_AFFINITY=1 \
  USE_THREAD=0 \
  USE_OPENMP=0 \
  BUILD_SHARED=0 \  # 关键:禁用动态库,只编译静态库
  BUILD_STATIC=1 \   # 强制编译静态库
  CC=${CC} \
  CXX=${CXX} \
  AR=${AR} \
  LD=${LD} \
  HOSTCC=gcc \
  -j4
修复 -march架构参数丢失

执行 OpenBLAS的 make的时候出现了,
native架构未识别,
-march参数丢失

交叉编译器是为 ARM 设计的,-march=native是让编译器自动识别主机 CPU 架构(比如 x86_64),ARM 编译器不认识这个参数;

make clean时 OpenBLAS 会先执行getarch脚本检测架构,默认调用交叉编译器,导致触发这个错误。

架构检测失败会影响后续编译,必须修复这个错误。

bash 复制代码
# 重新定义环境变量(确保生效)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld
export SYSROOT="/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot"

# 编译静态库(新增ARCH=armv7,彻底解决-march=native问题)
make \
  ARCH=armv7 \          # 强制指定ARMv7架构,跳过自动检测
  TARGET=ARMV7 \        # 适配RV1106的Cortex-A7
  NO_LAPACK=1 \
  NO_AFFINITY=1 \
  USE_THREAD=0 \
  USE_OPENMP=0 \
  BUILD_SHARED=0 \      # 禁用动态库,适配uclibc
  BUILD_STATIC=1 \      # 强制编译静态库
  CC=${CC} \
  CXX=${CXX} \
  AR=${AR} \
  LD=${LD} \
  HOSTCC=gcc \          # 主机编译器,用于编译辅助工具(避免交叉编译器冲突)
  -j4

提示归档器 AR的参数不对

OpenBLAS 的 Makefile 中给 AR 工具(归档器)传递了冲突的参数------RV1106 的 uclibc 版arm-rockchip830-linux-uclibcgnueabihf-ar对参数格式更严格,不支持 OpenBLAS 默认的组合参数(比如rcs),需要拆分参数并修复 Makefile 中的 AR 命令写法。

修复 OpenBLAS Makefile 中的 AR 命令参数
bash 复制代码
# 进入OpenBLAS源码根目录
cd ~/project/Echo-Mate/third_lib/OpenBLAS-0.3.26

# 1. 找到所有调用AR的地方,将组合参数"rcs"拆分为"-rcs"(加短横线,适配uclibc)
sudo sed -i 's/$(AR) rcs/$(AR) -rcs/g' $(find . -type f -name "Makefile*" -o -name "*.mk")

# 2. 额外修复:确保AR命令参数格式统一(避免其他参数冲突)
sudo sed -i 's/ar rcs/ar -rcs/g' $(find . -type f -name "Makefile*" -o -name "*.mk")

重新执行 clean(指定 ARCH=armv7)

bash 复制代码
make ARCH=armv7 clean

仍然提示 -march = native不能被识别

解决 -march = native不被识别

先临时取消交叉编译器的环境变量,让getarch用主机的 gcc 执行 clean(避开 - march=native),再重新导出交叉编译器编译。

bash 复制代码
# 进入OpenBLAS源码根目录
cd ~/project/Echo-Mate/third_lib/OpenBLAS-0.3.26

# 临时取消交叉编译器的CC/CXX/AR/LD(让getarch用主机gcc)
unset CC CXX AR LD

# 现在执行make clean(用主机gcc,不会触发-march=native错误)
make ARCH=armv7 clean

提示

配置脚本把 ARM 架构特有的浮点编译选项 (-mfpu=vfpv3、-mfloat-abi=hard)传给了主机的 x86 编译器(cc/gcc),但 x86 编译器完全不认识这些 ARM 专属选项,导致配置阶段(生成 config.h)失败。

当你指定ARCH=armv7/TARGET=ARMV7时,getarch脚本会自动生成适配 ARMv7 架构的编译参数,预编译配置文件 Makefile.prebuild是 OpenBLAS 的 "编译前检测脚本",会调用getarch的结果,把这些 ARM 参数传给编译器做 "兼容性检测"。

但由于执行 make clean是在 X86/64主机上执行的,导致不能识别。

重新导出交叉编译器,修复 AR 参数并编译

bash 复制代码
# 重新指定交叉编译器(仅用于编译目标代码,不影响配置阶段)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld
export SYSROOT="/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot"

# 修复AR命令参数(rcs→-rcs,适配uclibc)
sudo sed -i 's/$(AR) rcs/$(AR) -rcs/g' $(find . -type f -name "Makefile*" -o -name "*.mk")
sudo sed -i 's/ar rcs/ar -rcs/g' $(find . -type f -name "Makefile*" -o -name "*.mk")

# 编译静态库(交叉编译器会自动处理ARM浮点选项,无需手动指定)
make \
  ARCH=armv7 \          # 强制ARMv7架构
  TARGET=ARMV7 \        # 适配RV1106的Cortex-A7
  NO_LAPACK=1 \
  NO_AFFINITY=1 \
  USE_THREAD=0 \
  USE_OPENMP=0 \
  BUILD_SHARED=0 \      # 禁用动态库,适配uclibc
  BUILD_STATIC=1 \      # 强制静态库
  CC=${CC} \
  CXX=${CXX} \
  AR=${AR} \
  LD=${LD} \
  HOSTCC=gcc \          # 辅助工具用主机gcc(无浮点参数,可正常执行)
  -j4

报错,

静态编译+不装libfortan

OpenBLAS 编译时默认尝试链接 Fortran 库(libgfortran),但你的交叉编译环境缺少 ARM 版的 libgfortran,链接器误找了主机 x86 版的 libgfortran.so(格式不兼容);

同时即使指定了BUILD_SHARED=0,OpenBLAS 仍残留 "生成共享库(shared)" 的目标,触发了不必要的链接步骤。

不建议装 !因为 snowboy 只用到 OpenBLAS 的 C 语言接口(矩阵运算的核心功能),完全不需要 Fortran 支持;而装 ARM 版 libgfortran 的步骤非常繁琐 (uclibc 版的交叉工具链几乎没有预编译的 gfortran 包,需要手动编译),纯粹是 "做无用功"。

libgfortran 是一个为 Fortran 编程语言提供运行时支持的库,主要用于实现 Fortran 语言的运行时功能,例如数组操作、I/O 处理、内存管理等,确保 Fortran 程序能够正确运行。

OpenBLAS 默认启用 Fortran,会触发 libgfortran 链接,需要彻底关闭:

bash 复制代码
# 1. 编辑Makefile.system,添加禁用Fortran的宏
sudo sed -i '$a NO_FORTRAN=1' Makefile.system

# 2. 编辑主Makefile,强制只编译静态库,跳过shared目标
sudo sed -i 's/^all: .*/all: static/g' Makefile  # 把默认目标改为只编译static
sudo sed -i '/^shared:/d' Makefile              # 删除shared目标规则
bash 复制代码
make \
  ARCH=armv7 \
  TARGET=ARMV7 \
  NO_LAPACK=1 \
  NO_AFFINITY=1 \
  USE_THREAD=0 \
  USE_OPENMP=0 \
  BUILD_SHARED=0 \
  BUILD_STATIC=1 \
  CC=${CC} \
  CXX=${CXX} \
  AR=${AR} \
  LD=${LD} \
  HOSTCC=gcc \
  -j4

编译成功

安装
bash 复制代码
# 安装库到sysroot(参数和编译时完全一致)
sudo make \
  ARCH=armv7 \
  TARGET=ARMV7 \
  NO_LAPACK=1 \
  NO_AFFINITY=1 \
  USE_THREAD=0 \
  USE_OPENMP=0 \
  BUILD_SHARED=0 \
  BUILD_STATIC=1 \
  NO_FORTRAN=1 \
  PREFIX=${SYSROOT}/usr \
  install
验证
bash 复制代码
# 1. 检查库文件是否存在
ls ${SYSROOT}/usr/lib/libopenblas*.a

# 2. 检查库架构(必须输出"ARM"相关内容)
file ${SYSROOT}/usr/lib/libopenblas_armv7-r0.3.26.a
libdrm库

yolo的CMakeList找不到 drm库

libdrm 是用于与 Linux 内核的 DRM(Direct Rendering Manager)子系统交互的库,主要用于管理 GPU 硬件资源,如缓冲区分配、硬件加速等;(显示输出)

通过 libdrm,绕过传统窗口控制系统,直接与显示控制器交互。

SDL(Simple DirectMedia Layer) 是一个跨平台的应用程序开发库,用于创建多媒体应用程序,如游戏和图形界面,它封装了底层的图形、音频和输入设备接口,提供简单的 API,方便开发者进行多媒体开发。(lvgl用,图形渲染)

检查库到底有没有

确认交叉编译 sysroot 中是否有 ARM 版 libdrm,

首先检查你的 RV1106 工具链 sysroot 里是否预装了 libdrm(大部分 rockchip 工具链会自带):

bash 复制代码
# 查找libdrm的头文件
find ${SYSROOT}/usr/include -name "drm*.h"

# 查找libdrm的库文件
find ${SYSROOT}/usr/lib -name "libdrm*.so" -o -name "libdrm*.a"

libdrm在 sdk的 sysroot找得到 头文件,找不到库文件。

可以手动从 要运行的 arm系统中直接拷贝过去

或者,源码自己编译
源码有 meson.build,支持 meson构建,ninja编译

下载

注意,新 meson 和旧版本的 libdrm会存在兼容性问题。注意下载的 libdrm版本。

bash 复制代码
# 1. 下载libdrm源码(推荐2.4版本,兼容性好)
git clone https://gitlab.freedesktop.org/mesa/drm.git -b libdrm-2.4.120
cd drm

# 2. 创建编译目录
mkdir build && cd build
编译安装
bash 复制代码
# 安装meson和ninja(Ubuntu/Debian)
sudo apt update && sudo apt install meson ninja-build -y

创建 Meson 交叉编译配置文件(适配 RV1106)

在 libdrm/build目录下创建cross_arm_rv1106.txt,指定 ARM 交叉编译参数:

bash 复制代码
# rv1106_meson_cross.txt
[binaries]
c = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc'
cpp = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++'
ar = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar'
ld = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld'
strip = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-strip'

[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7-a'  # RV1106是ARMv7架构
endian = 'little'

[properties]
# 指定sysroot路径(关键:让编译器使用目标平台的头文件和库)
sys_root = '/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot'
# 禁用不需要的驱动(减少编译体积)
c_args = ['-DNO_INTEL=1', '-DNO_RADEON=1', '-DNO_AMDGPU=1', '-DNO_NOUVEAU=1']
cpp_args = ['-DNO_INTEL=1', '-DNO_RADEON=1', '-DNO_AMDGPU=1', '-DNO_NOUVEAU=1']

回到 libdrm 源码目录,执行以下命令完成编译和安装(库文件会自动安装到 sysroot 中):

bash 复制代码
# 进入libdrm源码目录
cd /home/teng/project/Echo-Mate/third_lib/drm

# 删除之前无效的build目录(可选)
rm -rf build && mkdir build && cd build

# 执行Meson配置(指定交叉编译文件和安装路径)
meson setup \
  --cross-file=../rv1106_meson_cross.txt \
  --prefix=/usr \  # 安装到sysroot的/usr目录(对应sysroot/usr)
  --libdir=lib \   # 库文件安装到lib目录
  --buildtype=release \
  ..

项目构建成功

bash 复制代码
# 编译(-j4根据CPU核心数调整)
# 正确写法:DESTDIR=路径 放在 ninja install 前面(sudo后需用env保留环境变量)
sudo env DESTDIR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot ninja install

#env 是一个用于设置环境变量的命令,它会启动一个子进程,并在该子进程中设置指定的环境变量。
#这些变量仅在子进程中有效,不会影响当前的 shell 环境。

Q&A

sudo 的变量隔离机制

用户层:你的当前 shell 中,

${SYSROOT}能正确解析为目标路径(比如/home/teng/.../sysroot);

sudo 层:sudo 为了安全,默认会重置所有非基础环境变量(只保留PATH等极少数变量) ,即使你在 sudo 后写SYSROOT=xxx,这个赋值也只是 "临时拼在命令行",并没有真正导入到 sudo 启动的ninja进程的环境中;

bash 复制代码
# 对比:普通用户下echo $SYSROOT有值,sudo下echo $SYSROOT为空
echo "普通用户:$SYSROOT"
sudo echo "sudo环境:$SYSROOT"
bash 复制代码
sudo env xxx=aaa ./nnn

env 是一个用于设置环境变量的命令,它会启动一个子进程,并在该子进程中设置指定的环境变量。

#这些变量仅在子进程中有效,不会影响当前的 shell 环境。

确认库

执行以下命令,确认 ARM 版本的 libdrm 库文件已出现在 sysroot 中:

bash 复制代码
find ${SYSROOT} -name "libdrm*.so" -o -name "libdrm*.a"
# 正常输出示例:
# /home/teng/.../sysroot/usr/lib/libdrm.so
# /home/teng/.../sysroot/usr/lib/libdrm.a
# /home/teng/.../sysroot/usr/lib/libdrm.so.2
# /home/teng/.../sysroot/usr/lib/libdrm.so.2.4.0

编译 DeskBot_Demo

json.h

编译器提示找不到 json-c/json.h头文件

bash 复制代码
# 检查头文件
find ${SYSROOT} -name "json.h" | grep "json-c"
# 检查库文件
find ${SYSROOT} -name "libjson-c*.so" -o -name "libjson-c*.a"

没有的话,老方法,直接拷贝或者编译

bash 复制代码
# 1. 下载json-c源码(推荐0.16版本,兼容性好)
git clone https://github.com/json-c/json-c.git -b json-c-0.16
cd json-c && mkdir build && cd build

克隆慢可以直接官网下载了复制,

bash 复制代码
cmake \
  -DCMAKE_TOOLCHAIN_FILE=/home/teng/project/Echo-Mate/Demo/DeskBot_demo/toolchain.cmake \
  -DCMAKE_INSTALL_PREFIX=/usr \
  -DCMAKE_BUILD_TYPE=release \
  -DBUILD_TESTING=OFF \
  ..
bash 复制代码
make -j4  # 根据你的CPU核心数调整,比如-j8
bash 复制代码
sudo env DESTDIR=${SYSROOT} make install
bash 复制代码
# 检查头文件(必须有输出)
find ${SYSROOT} -name "json.h" | grep "json-c"
# 检查库文件(必须有输出)
find ${SYSROOT} -name "libjson-c*.so" -o -name "libjson-c*.a"

portaudio.h

snowboy的demo2.c提示找不到 portaudio.h

bash 复制代码
# 进入third_lib目录(统一存放第三方库源码)
cd /home/teng/project/Echo-Mate/third_lib

# 下载PortAudio源码(v19.7.0是稳定版,适配嵌入式平台)
wget https://github.com/PortAudio/portaudio/archive/refs/tags/v19.7.0.tar.gz -O portaudio-19.7.0.tar.gz

# 解压源码
tar -zxvf portaudio-19.7.0.tar.gz
cd portaudio-19.7.0
bash 复制代码
# 重新导出工具链和sysroot参数(确保环境变量生效)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld
export CFLAGS="--sysroot=${SYSROOT} -DPA_USE_JACK=0"  # 强制屏蔽Jack宏
export CXXFLAGS="--sysroot=${SYSROOT} -DPA_USE_JACK=0"
export LDFLAGS="--sysroot=${SYSROOT}"
bash 复制代码
# 执行configure(新增--without-jack,强化禁用Jack)
./configure \
  --host=arm-rockchip830-linux-uclibcgnueabihf \
  --prefix=/usr \
  --disable-shared \
  --enable-static \
  --disable-oss \
  --disable-pulse \
  --enable-alsa \
  --without-jack \          # 显式不使用Jack(关键新增)
  --disable-jack \          # 保留原禁用参数,双重保险
  --disable-dsound \
  --disable-wmme \
  --disable-asihpi
bash 复制代码
make -j4
bash 复制代码
# 安装到sysroot
sudo env DESTDIR=${SYSROOT} make install

# 验证安装结果
find ${SYSROOT} -name "portaudio.h"  # 应有输出
find ${SYSROOT} -name "libportaudio.a"  # 应有输出

jack/types.h

编译 portaudio的时候找不到 jack/types.h文件

Jack(JACK Audio Connection Kit)是为专业音频工作站设计的(比如 Linux 桌面端的音频制作、多设备音频路由),核心特点是低延迟的音频流调度,而你的 RV1106 是嵌入式设备,做的是 AI 聊天 / 语音交互 demo,核心依赖的是ALSA(嵌入式 Linux 标准音频驱动),而非 Jack。

PortAudio 只是一个音频抽象层,禁用 Jack 后,它会只编译你需要的 ALSA 后端 ------ 这正是 RV1106 需要的音频驱动适配层,你的 demo 需要的 "录音 / 播放语音" 核心功能,完全由 ALSA 提供,和 Jack 无关。

opis.h

AIChat_demo/Client/Audio的 AudioProcess.h找不到 opus/opus.h

bash 复制代码
# 进入third_lib目录(统一存放第三方库)
cd /home/teng/project/Echo-Mate/third_lib

# 下载Opus 1.4源码(稳定版,无冗余依赖)
wget https://downloads.xiph.org/releases/opus/opus-1.4.tar.gz

# 解压源码
tar -zxvf opus-1.4.tar.gz
cd opus-1.4
bash 复制代码
# 1. 导出交叉编译工具链(复用之前的RV1106工具链)
export CC=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
export CXX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
export AR=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ar
export LD=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-ld

# 2. 传递sysroot给编译器(关键)
export CFLAGS="--sysroot=${SYSROOT}"
export CXXFLAGS="--sysroot=${SYSROOT}"
export LDFLAGS="--sysroot=${SYSROOT}"

# 3. 执行configure(适配嵌入式,编译静态库)
./configure \
  --host=arm-rockchip830-linux-uclibcgnueabihf \  # 目标平台
  --prefix=/usr \                                  # 安装到sysroot/usr
  --disable-shared \                               # 编译静态库(嵌入式更稳定)
  --enable-static                                  # 启用静态库
bash 复制代码
make -j4  # 根据CPU核心数调整,如-j8
bash 复制代码
# 用env传递DESTDIR,避免sudo清空环境变量
sudo env DESTDIR=${SYSROOT} make install
bash 复制代码
# 检查头文件(应有输出)
find ${SYSROOT} -name "opus.h" | grep "opus"
# 检查库文件(应有输出)
find ${SYSROOT} -name "libopus.a" -o -name "libopus.so"

boost/version.hpp

boost 库 提供如智能指针(shared_ptr、unique_ptr早期实现)、多线程(boost::thread)、正则表达式(boost::regex)、文件系统操作(boost::filesystem)等,在C++11/14/17标准采纳前就已广泛使用。

bash 复制代码
# 方案1:使用Boost官方备份链接(推荐,稳定)
wget https://sourceforge.net/projects/boost/files/boost/1.83.0/boost_1_83_0.tar.gz/download -O boost_1_83_0.tar.gz

Boost 用b2替代传统 make,需先生成该工具:

bash 复制代码
# 生成b2工具(无报错即成功,仅需几十秒)
./bootstrap.sh --prefix=/usr  # --prefix指定安装到sysroot/usr

创建user-config.jam文件,指定交叉编译工具链(Boost 的 b2 通过该文件识别交叉编译环境),写入以下配置,

bash 复制代码
cat > user-config.jam << EOF
# 正确格式:using gcc : 版本别名(数字/字母混合) : 编译器路径 ;
# 把第二个字段改为armv7(非纯数字,避免被解析为版本号)
using gcc : armv7 : 
    /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc ;

# C++编译器配置(必需,Boost是C++库)
using gcc : armv7 : 
    /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++ ;
EOF

避免权限问题,改下 sysroot权限和所属,

bash 复制代码
# 仅修改sysroot/usr目录的权限(关键:不要改整个sysroot)
sudo chmod -R 775 ${SYSROOT}/usr
# 同时修改目录所属用户(避免后续权限冲突)
sudo chown -R teng:teng ${SYSROOT}/usr

Boost 的 b2 构建系统 并不会自动使用 CC 或 CXX 环境变量 ------ 它依赖于 toolset=gcc 时默认调用主机上的 g++,除非你显式告诉它使用哪个编译器。

所以请格外注意环境变量。

它需要通过 user-config.jam 文件显式配置交叉编译工具链。

在 user-config.jam文件中硬编码路径避免无法解析。

bash 复制代码
cd ~/project/Echo-Mate/third_lib/boost_1_83_0

touch user-config.jam

填入以下内容,,

Boost.Build 期望工具集名称是预定义的(如 gcc、clang),而不是自定义名称。

bash 复制代码
cat > user-config.jam << EOF
using gcc : : 
    /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++
    :
    <compileflags>--sysroot=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot
    <compileflags>-D_GNU_SOURCE
    <compileflags>-U__STRICT_ANSI__
    <compileflags>-fPIC
    <compileflags>-I/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/include/c++/13
    <compileflags>-I/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/include/c++/13/arm-rockchip830-linux-uclibcgnueabihf
    <linkflags>--sysroot=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot
    <linkflags>-lpthread
    ;
EOF
bash 复制代码
./b2 \
  toolset=arm-rockchip \  # ✅ 正确:使用定义的工具集名称(不是 gcc-arm-rockchip)
  target-os=linux \
  architecture=arm \
  address-model=32 \
  --prefix=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr \
  --with-system --with-thread \
  link=static \
  variant=release \
  threading=multi \
  install
bash 复制代码
# 检查头文件(应有输出)
find ${SYSROOT} -name "version.hpp" | grep "boost"
# 检查库文件(应有输出)
find ${SYSROOT} -name "libboost_system.a" -o -name "libboost_thread.a"

安装成功
文件也找得到

jsoncpp库

bash 复制代码
# 搜索sysroot中是否有jsoncpp相关库
find /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot -name "libjsoncpp*"
bash 复制代码
# 下载 jsoncpp 源码(如果尚未下载)
git clone https://github.com/open-source-parsers/jsoncpp.git
cd jsoncpp

# 创建构建目录
mkdir build
cd build

# 使用 ARM 交叉编译工具链配置 CMake
cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/share/cmake/toolchain.cmake \
  -DCMAKE_INSTALL_PREFIX=/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr \
  -DBUILD_SHARED_LIBS=OFF  # 静态链接

# 编译
make -j$(nproc)

# 安装
make install
bash 复制代码
cd ~/project/Echo-Mate/third_lib/jsoncpp
cat > arm-toolchain.cmake << EOF
# ARM cross-compilation toolchain for JsonCpp
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# Set the compiler paths
set(CMAKE_C_COMPILER /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc)
set(CMAKE_CXX_COMPILER /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-g++)

# Set sysroot
set(CMAKE_SYSROOT /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot)

# Set search paths
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
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)

# Set target architecture flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon")
EOF

编译和安装,

bash 复制代码
make -j$(nproc)
make install

验证安装结果,

bash 复制代码
# 检查静态库是否存在
ls ${SYSROOT}/usr/lib/libjsoncpp.a

# 检查头文件是否存在
ls ${SYSROOT}/usr/include/json/json.h

# 验证库的架构(关键,确保是ARM版)
${SYSROOT}/../bin/arm-rockchip830-linux-uclibcgnueabihf-readelf -h 
bash 复制代码
# 搜索交叉工具链中的readelf工具
find /home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/ -name "arm-rockchip830-linux-uclibcgnueabihf-readelf"

#用找到的readelf工具验证库的架构
/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-readelf -h 
/home/teng/project/Echo-Mate/SDK/rv1106-sdk/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/arm-rockchip830-linux-uclibcgnueabihf/sysroot/usr/lib/libjsoncpp.a | grep "Machine"

编译成功

bash 复制代码
teng@TengSea:~/project/Echo-Mate/Demo/DeskBot_demo/build$ make
[  0%] Built target imageutils
[ 41%] Built target lvgl
[ 47%] Built target lvgl_thorvg
[ 74%] Built target lvgl_examples
[ 89%] Built target lvgl_demos
[ 95%] Built target gui_app
[ 95%] Built target utils
[ 96%] Built target common
[ 96%] Built target snowboy
[ 97%] Built target snowboy-detect-c-wrapper
[ 99%] Built target AIChatCore
[ 99%] Built target AIchat-c-interface
[ 99%] Built target fileutils
[ 99%] Built target imagedrawing
[100%] Built target yoloCameraCore
[100%] Linking CXX executable /home/teng/project/Echo-Mate/Demo/DeskBot_demo/bin/main
Copying system_para.conf to bin directory
Copying gaode_adcode.json to bin directory
Copying cacert.pem to bin directory
Copying AI Chat necessary resource files to executable output directory
Copying yolov5 model files to executable output directory
Copying yolov5.rknn to executable output directory
Creating lib directory for shared libraries
Copying librga.so to executable output directory
Copying librknnmrt.so to executable output directory
[100%] Built target main
[100%] Building CXX object AIChat_build/CMakeFiles/AIChatClient.dir/main.cc.o
[100%] Linking CXX executable AIChatClient
[100%] Built target AIChatClient
[100%] Building CXX object AICamera_build/CMakeFiles/rknn_yolov5_demo.dir/main.cc.o
[100%] Linking CXX executable rknn_yolov5_demo
[100%] Built target rknn_yolov5_demo

main、AiChatClient、rknn_yolov5_demo都是可执行文件。

关于静态库动态库地址无关等参数

因为在 子模块 gui_app的 CMakeList中,

bash 复制代码
file(GLOB_RECURSE GUI_APP_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/*.c"  # 查找当前目录及子目录中的所有 .c 文件,放到变量GUI_APP_SOURCES
)

# 定义 gui_app 模块为一个静态库
add_library(gui_app STATIC ${GUI_APP_SOURCES})

# 设置 gui_app 的头文件路径
target_include_directories(gui_app PUBLIC
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/common
    ${CMAKE_CURRENT_SOURCE_DIR}/common/stack
    ${CMAKE_CURRENT_SOURCE_DIR}/common/page_manager
)

# 查找包
find_package(json-c REQUIRED)
find_package(CURL REQUIRED)
# 将库链接到 gui_app
target_link_libraries(gui_app PRIVATE json-c)
target_include_directories(gui_app PRIVATE ${CURL_INCLUDE_DIRS})
target_link_libraries(gui_app PRIVATE CURL::libcurl)

add_library():CMake 中用于创建库目标的命令,这里用于构建 gui_app 模块库;

static 指定了该模块库是一个静态库,(生成的文件名为 libgui_app.a),

静态库会被完整打包到最终的 main 可执行文件中,运行时无需依赖外部 .so 动态库,适合无包管理的嵌入式 uClibc 环境。

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

由于我们对模块采取静态编译方式,对库采取动态编译方式。

因此对 json-c之类的库,开启了 share共享库,并在编译器添加了 **地址无关代码(fpic)**选项。

而对 子模块使用的是 STATIC标记为静态库


这里解释一下,静态编译的子模块和动态编译的子模块依赖,并不矛盾。

静态库(gui_app.a) 的本质:是编译后的目标文件(.o)的归档包,仅存放自身业务代码,不进行任何链接操作,也不会打包依赖库(无论是静态还是动态)的代码。

动态库(libjson-c-internal.so 的本质:是独立的可执行代码库,仅提供符号表(函数 / 接口声明)和运行时代码,编译 / 链接时不将自身代码打包进其他库或可执行文件。

依赖关系的本质:gui_app 静态库依赖 json-c 动态库,只是「记录了需要调用 json-c 的接口」,并未包含任何 json-c 的实现代码。

编译阶段(生成 gui_app.a 静态库)------ 正常完成,无报错,链接阶段(用 gui_app.a 生成可执行文件)------ 必须显式链接 json-c 动态库,否则报错

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

json-c 是第三方开源库(用于 JSON 数据解析 / 构建),不属于 uClibc 核心库,工具链 sysroot 仅包含「核心系统依赖」,不包含这类第三方库;

CMake 的 find_package() 命令默认会在 CMAKE_FIND_ROOT_PATH(即你的工具链 sysroot)下查找库的配置文件,而你的 sysroot 中缺少 json-c 的 *.cmake 配置文件、头文件(json-c/json.h)和静态库(libjson-c.a),因此报错;

buildroot 无 apt-get/opkg 等包管理工具,无法直接安装 json-c,

只能通过「静态集成源码」或「交叉编译安装到 sysroot」两种方式解决。

其实两种方法异曲同工,

手动集成源码\],就是在子模块的 CMakeList中添加 json-c库的源码路径和编译过程,在编译静态子模块库的时候顺手把 json-c也静态编译了,然后引用。 \[交叉编译安装到 sysroot\] 其实就是先把 json-c编译了安装到 sysroot的 usr目录下,后面编译静态子模块库的时候就可以 `find_package()` 直接找到。 图方便,我们直接修改子模块的 CMakeList来手动集成 json-c源码,并对子模块使用静态编译,对子模块依赖的库使用动态编译,整体打包。 相同子模块的不同库,到最后一个库统一给出 CMakeList.txt文件。 ### 根目录的CMakeList 先从上到下,给出根目录的 CMakeList, ```bash cmake_minimum_required(VERSION 3.10) # 架构选择开关:ON=ARM交叉编译 OFF=x86原生编译 option(TARGET_ARM "Build for ARM architecture" ON) # ARM编译时自动加载默认工具链文件(未指定时) if(TARGET_ARM) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/toolchain.cmake" CACHE FILEPATH "Default toolchain file for ARM builds") message(STATUS "No toolchain file specified; using default: ${CMAKE_TOOLCHAIN_FILE}") endif() message(STATUS "Building for ARM using toolchain file: ${CMAKE_TOOLCHAIN_FILE}") # ARM交叉编译时设置pkg-config路径 set(ENV{PKG_CONFIG_PATH} "${CMAKE_SYSROOT}/usr/lib/pkgconfig") else() message(STATUS "Building for x86 native architecture.") endif() # 项目基础配置 project(DeskBot) set(CMAKE_C_STANDARD 99) # LVGL要求C99+ set(CMAKE_CXX_STANDARD 17) # C++17标准 set(CMAKE_CXX_STANDARD_REQUIRED ON) set(OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 可执行文件/资源输出目录 # 引入子模块(LVGL核心、GUI应用、工具类、公共模块) add_subdirectory(lvgl) add_subdirectory(gui_app) add_subdirectory(utils) add_subdirectory(common) # LVGL头文件路径配置 target_include_directories(lvgl PUBLIC ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/conf) # 主程序构建:指定源文件+生成可执行文件 set(MAIN_SOURCES main.c) add_executable(main ${MAIN_SOURCES}) # 链接自定义模块依赖 target_link_libraries(main gui_app utils common) # 引入DRM库+SDL2/SDL2_image依赖(LVGL显示相关) include(${CMAKE_CURRENT_LIST_DIR}/lvgl/tests/FindLibDRM.cmake) include_directories(${Libdrm_INCLUDE_DIRS}) find_package(SDL2) find_package(SDL2_image) include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS}) # 链接系统/第三方库(LVGL、数学库、线程库等) target_link_libraries(main lvgl lvgl::examples lvgl::demos lvgl::thorvg ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} ${Libdrm_LIBRARIES} m pthread) add_custom_target (run COMMAND ${OUTPUT_PATH}/main DEPENDS main) # 快捷运行目标 # 复制配置文件到输出目录(系统参数、高德编码、证书) add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/utils/system_para.conf ${OUTPUT_PATH}/system_para.conf COMMENT "Copy system_para.conf") add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/utils/gaode_adcode.json ${OUTPUT_PATH}/gaode_adcode.json COMMENT "Copy gaode_adcode.json") add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/utils/cacert.pem ${OUTPUT_PATH}/cacert.pem COMMENT "Copy cacert.pem") # 集成AIChat_demo:引入源码+链接库 set(AI_CHAT_CLIENT_PATH "${PROJECT_SOURCE_DIR}/../AIChat_demo/Client" CACHE PATH "Path to AIChat_demo Client") add_subdirectory(${AI_CHAT_CLIENT_PATH} ${CMAKE_BINARY_DIR}/AIChat_build) target_link_libraries(main AIchat-c-interface AIChatCore) # 复制AIChat资源文件(唤醒词模型、音频文件等) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_PATH}/third_party/snowboy/resources/models ${OUTPUT_PATH}/third_party/audio COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AI_CHAT_CLIENT_PATH}/third_party/snowboy/resources/common.res ${OUTPUT_PATH}/third_party/snowboy/resources/common.res COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AI_CHAT_CLIENT_PATH}/third_party/snowboy/resources/models/echo.pmdl ${OUTPUT_PATH}/third_party/snowboy/resources/models/echo.pmdl COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AI_CHAT_CLIENT_PATH}/third_party/audio/waked.pcm ${OUTPUT_PATH}/third_party/audio/waked.pcm COMMENT "Copy AIChat resource files" ) # ARM架构专属:集成yolov5_demo(AI摄像头) if(TARGET_ARM) # 引入yolov5源码+链接核心库 set(AI_CAMERA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../yolov5_demo" CACHE PATH "Path to yolov5_demo") add_subdirectory(${AI_CAMERA_PATH}/cpp ${CMAKE_BINARY_DIR}/AICamera_build) target_link_libraries(main yoloCameraCore) # 复制yolov5模型文件(图片、标签、rknn模型) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_PATH}/model COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AI_CAMERA_PATH}/model/bus.jpg ${OUTPUT_PATH}/model/bus.jpg COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AI_CAMERA_PATH}/model/coco_80_labels_list.txt ${OUTPUT_PATH}/model/coco_80_labels_list.txt COMMENT "Copy yolov5 base model files" ) # 批量复制所有.rknn模型文件 file(GLOB RKNN_FILES "${AI_CAMERA_PATH}/model/*.rknn") foreach(RKNN_FILE ${RKNN_FILES}) get_filename_component(FILE_NAME ${RKNN_FILE} NAME) add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${RKNN_FILE} ${OUTPUT_PATH}/model/${FILE_NAME} COMMENT "Copy ${FILE_NAME}") endforeach() # 复制ARM共享库(rga、rknnmrt)+创建lib目录 set(SHARED_LIBS "${AI_CAMERA_PATH}/cpp/3rdparty/librga/Linux/armhf_uclibc/librga.so" "${AI_CAMERA_PATH}/cpp/3rdparty/rknpu2/Linux/armhf-uclibc/librknnmrt.so") add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_PATH}/lib COMMENT "Create lib dir for shared libs") foreach(LIB_FILE ${SHARED_LIBS}) get_filename_component(FILE_NAME ${LIB_FILE} NAME) add_custom_command(TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${LIB_FILE} ${OUTPUT_PATH}/lib/${FILE_NAME} COMMENT "Copy ${FILE_NAME}") endforeach() # 设置运行时rpath:让可执行文件自动加载./lib下的共享库 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,\$ORIGIN/lib") endif() # 指定main可执行文件的输出目录(覆盖默认编译目录) set_target_properties(main PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_PATH}") # 自定义clean-all目标:清空CMake编译目录下所有文件 if(NOT TARGET clean-all) add_custom_target(clean-all COMMAND find "${CMAKE_BINARY_DIR}" -mindepth 1 -maxdepth 1 -exec rm -rf {} + COMMENT "Clean all generated files") endif() ``` ![](https://i-blog.csdnimg.cn/direct/52f35c1a0b7a476d90e3a227f6f8fed1.png) 将 AI_CHAT_CLIENT作为子项目集成,并将源码复制到了 DeskBot_demo的 build目录中 ## cmake和 autoconf区别 | 对比维度 | autoconf | CMake(CMakeLists.txt) | |-----------|----------------------------------------------------|------------------------------------------------------------------------| | 核心定位/所属生态 | 属于 GNU Autotools 工具链,传统类 Unix 构建工具 | 独立跨平台构建配置工具,现代项目主流选择 | | 跨平台能力 | 弱,仅支持类 Unix 系统(Linux、macOS 等) | 强,原生支持 Linux、macOS、Windows 等多平台 | | 构建产物生成方式 | 直接生成可用于编译的 Makefile | 先生成中间构建文件(类 Unix 下为 Makefile,Windows 下为 Visual Studio 工程文件等),再衍生最终构建产物 | | 配置文件/语法难度 | 基于 m4 宏语言,配置文件(configure.ac/Makefile.am)晦涩,编写维护成本高 | 专属简洁语法,配置文件(CMakeLists.txt)逻辑清晰,接近高级语言,易编写易维护 | | 适用场景 | 传统 GNU 开源项目、老旧类 Unix 平台项目 | 现代大型项目、跨平台项目(如 Qt、OpenCV、FFmpeg 等) | | 系统依赖/安装要求 | 类 Unix 系统通常自带,无需额外手动安装 | 需提前手动安装对应平台版本,无系统默认自带 |

相关推荐
唐装鼠18 小时前
linux vscode解压版 AI账号无法登陆问题(浏览器无法打开vscode)
linux·运维·vscode
一个平凡而乐于分享的小比特18 小时前
Linux最小系统详解
linux·最小系统
Levin__NLP_CV_AIGC18 小时前
Ubuntu部署Dufs
linux·运维·服务器·ubuntu·ssh
wulalalalalalalal19 小时前
Linux 内网服务器通过代理访问外网
linux·运维·服务器
C_心欲无痕19 小时前
ts - 模板字面量类型与 `keyof` 的魔法组合:`keyof T & `on${string}`使用
linux·运维·开发语言·前端·ubuntu·typescript
fy zs19 小时前
网络编程套接字
linux·服务器·网络·c++
望眼欲穿的程序猿19 小时前
基于Linux&MacOS 开发Ai8051U
linux·运维·macos
Bigbig.19 小时前
驱动工程师面试题 - 操作系统1
linux·开发语言·面试·硬件架构
oMcLin19 小时前
如何在 CentOS Stream 9 上配置并优化 PostgreSQL 15,支持高并发的数据插入与快速查询?
linux·postgresql·centos