Lely CANopen 入门:从协议背景到 i.MX8P 交叉编译安装

@[toc]

在这里插入图片描述

先给结论

Lely CANopen 适合做 嵌入式 Linux 侧 CANopen master,尤其适合已经使用 SocketCAN、C++、事件循环或 Yocto SDK 的项目。它不是只能跑在 PC 上的调试工具,而是一套包含 C 核心库、C++ 应用层封装、命令行工具和 DCF/EDS 辅助工具的 CANopen 实现。

如果你的目标是 i.MX8P 这类 ARM64 Linux 板卡,推荐流程是:

  1. 在 x86 Ubuntu 主机安装构建依赖和 i.MX8P Yocto SDK。
  2. 加载 Yocto SDK 环境脚本,得到 aarch64-poky-linux-* 工具链和目标 sysroot。
  3. 从源码构建 Lely CANopen,并通过 --host=aarch64-poky-linux 交叉编译。
  4. make install DESTDIR=... 收集头文件、库、工具和示例配置。
  5. 打包部署到 i.MX8P。
  6. 在 i.MX8P 上启动 vcan0,运行 coctl 验证 CANopen master 启动链路。
1
2
3
4
5
6
7
8
flowchart LR
A[x86 Ubuntu 主机] --> B[加载 Yocto SDK]
B --> C[交叉编译 Lely CANopen]
C --> D[DESTDIR stage 安装]
D --> E[打包部署到 i.MX8P]
E --> F[配置 PATH / LD_LIBRARY_PATH]
F --> G[启动 vcan0]
G --> H[运行 coctl 作为 master]

CANopen 先解决什么问题

CAN 本身只定义了较低层的数据链路能力。真实设备网络还需要回答这些问题:

  • 每个节点怎么编号?
  • 设备启动、停止、复位由谁管理?
  • 参数怎么读写?
  • 周期数据和事件数据怎么发送?
  • 不同厂商设备怎么描述自己的对象、参数和能力?

CANopen 就是在 CAN 之上提供这些规则的一套高层协议。CAN in Automation(CiA)把 CANopen 设备描述为三部分:协议栈、应用软件、对象字典。对象字典连接协议栈和应用层,并保存通信参数、应用参数和诊断数据。[^cia-canopen]

1
2
3
4
flowchart TD
App[应用逻辑\n电机控制 / IO / 传感器 / 执行器] --> OD[对象字典 Object Dictionary\n索引 / 子索引 / 类型 / 访问权限]
OD --> Stack[CANopen 协议栈\nNMT / SDO / PDO / EMCY / Heartbeat]
Stack --> CAN[CAN / SocketCAN / CAN 控制器]

工程上可以把 CANopen 理解成:

层次 作用 常见对象
网络管理 控制节点状态 NMT、Heartbeat
参数访问 读写对象字典 SDO
实时过程数据 低开销传输状态和控制量 PDO
错误上报 设备异常和诊断 EMCY
设备描述 描述对象字典和设备能力 EDS、DCF

CiA 301 是 CANopen classic 的应用层和通信 profile 的核心规范。CiA 还维护大量设备 profile,例如通用 I/O、编码器、驱动器和运动控制等。[^cia-docs]

Lely CANopen 是什么

Lely CANopen 是 Lely core libraries 的一部分。它包含多个 C/C++ 库和工具,其中与 CANopen 最相关的是:

组件 定位
liblely-co CANopen C 核心库,支持 master 和 slave 方向的协议能力
liblely-coapp 面向 C++ 应用开发的高层 CANopen application library
liblely-io2 异步 I/O 抽象,Linux 下可对接 SocketCAN
liblely-ev 事件循环基础设施
coctl 命令行 CANopen control 工具,可用于快速验证 master/slave 流程
DCF/EDS 工具 处理设备描述和配置文件

官方库概览说明,liblely-co 是同时面向 master 和 slave 的 CANopen 实现,并覆盖 CiA 301、302、305、306、309、315 中的大部分功能;liblely-coapp 则提供更高层的 C++ 接口,用于简化 master 应用开发。[^lely-overview]

1
2
3
4
5
6
7
flowchart TD
UserApp[你的 C++ master 应用] --> Coapp[liblely-coapp\nC++ CANopen application layer]
Coapp --> Co[liblely-co\nCANopen core]
Coapp --> EV[liblely-ev\nevent loop]
Coapp --> IO[liblely-io2\nasync I/O]
IO --> SocketCAN[Linux SocketCAN\ncan0 / vcan0]
SocketCAN --> CANBus[CAN bus / Virtual CAN]

Lely CANopen 的由来和创建原因

需要先区分两个概念:

  • CANopen 是 CiA 维护的工业通信协议和 profile 体系,不是 Lely 创建的。
  • Lely CANopen 是 Lely Industries 开源的 CANopen 实现,是 Lely core libraries 的一部分。

从公开资料看,Lely core libraries 的目标不是只做一个最小教学栈,而是面向机器人和 IoT 场景提供 C/C++ 库、工具、高性能 I/O、传感器/执行器控制能力,并强调跨平台和较少依赖。Lely CANopen 官网也强调其可配置、API 文档完整,并展示了在实际机器人系统中的使用场景。[^lely-home][^lely-doxygen]

因此,Lely CANopen 的工程动机可以归纳为:

  1. 让 CANopen 协议栈可复用:把 CANopen 协议处理从具体设备业务里拆出来。
  2. 服务 Linux 和嵌入式目标:Linux 是官方推荐和支持较好的平台,SocketCAN 能直接对接内核 CAN 子系统。[^lely-install]
  3. 降低 C++ master 应用开发复杂度liblely-coapp 把 CANopen master 常用模式封装到 C++ driver model 和 event loop 中。[^lely-overview]
  4. 保留跨平台裁剪能力:Python、Cython、C++ 接口、工具、测试等可以按目标裁剪,适合从 PC 验证过渡到目标板部署。[^lely-config]

和其他 CANopen 实现怎么选

下面是工程选型角度的对比。它不是协议一致性认证结论,具体项目仍应结合 license、平台、认证、维护状态和测试覆盖确认。

方案 更适合的场景 优势 注意点
Lely CANopen 嵌入式 Linux master、SocketCAN、C++ 应用、需要命令行验证和源码交叉编译 C core + C++ coapp;Linux/SocketCAN 路径清晰;官方交叉编译文档完整;Apache-2.0 MCU slave 项目可能不如纯 C MCU 栈轻量;需要理解 Autotools、DCF/EDS 和事件循环
CANopenNode MCU、RTOS、standalone 节点、偏 slave 固件 ANSI C;面向微控制器;对象字典编辑器生态较完整 Linux master 应用封装不如 Lely coapp 直接;驱动适配通常由项目承担
python-canopen 测试、自动化、上位机脚本、生产线辅助 Python 接口上手快,适合测试脚本和工具链 项目说明中明确更偏测试和自动化,不应直接当作标准一致的生产 master 栈使用
CanFestival 旧项目维护、已有对象字典和工具链、需要 ANSI-C master/slave 栈 有对象字典编辑工具,PC/RT IPC/MCU 都有历史使用 项目较老;license 和维护状态要逐项审查
商业 CANopen 栈 需要供应商支持、认证材料、长期维护和责任边界 支持、文档、测试和认证通常更完整 成本较高,二次裁剪和问题定位受供应商接口限制

如果你的项目是“i.MX8P Linux 做 master,MCU 或伺服驱动做 slave”,Lely CANopen 的工程路径比较顺:Linux 端用 SocketCAN + liblely-coapp,先用 vcan0coctl 把 master 启动链路跑通,再切到真实 can0

本文目标和不做的事情

本文完成:

  1. 在 x86 Ubuntu 主机上准备 Lely CANopen 源码构建环境。
  2. 使用 i.MX8P Yocto SDK 交叉编译 Lely CANopen。
  3. 把构建结果安装到 stage 目录,并打包部署到 i.MX8P。
  4. 在 i.MX8P 上启动 vcan0
  5. 使用 coctl 验证 CANopen master 可以启动。
  6. 编译一个最小 CMake 链接验证程序,确认 liblely-coapp 目标库可被自己的应用链接。

本文不做:

  • 真实 CAN 总线通信。
  • MCU 从机在线验证。
  • CAN 分析仪手工发帧。
  • 伺服驱动 CiA 402 控制流程。
  • 对 CANopen 一致性做认证结论。

环境和变量约定

主机侧:

项目 示例值
主机系统 x86 Ubuntu
目标板 i.MX8P / ARM64 Linux
工具链前缀 aarch64-poky-linux-
Yocto SDK 根目录 /opt/fsl-imx-xwayland/6.1-mickledore
Yocto SDK 环境脚本 ${SDK_ROOT}/environment-setup-armv8a-poky-linux
主机工作目录 ${HOME}/work/lely-imx8p
板端部署目录 /home/imx8p/share/lely-master

统一设置变量:

1
2
3
4
5
6
7
8
export SDK_ROOT=/opt/fsl-imx-xwayland/6.1-mickledore
export WORK_ROOT=${HOME}/work/lely-imx8p

# 文档示例 IP,复制前替换为你的板卡 IP。
export BOARD_IP=192.0.2.10
export BOARD_LOGIN=root
export BOARD_SHARE=/home/imx8p/share
export LELY_PREFIX=${BOARD_SHARE}/lely-master

192.0.2.10 属于文档示例地址,不是真实板卡地址。发布文章时不要写入内网真实 IP、真实用户名或公司共享目录。

在 x86 Ubuntu 上安装构建依赖

先安装基础依赖:

1
2
3
4
5
sudo apt-get update
sudo apt-get install -y \
git build-essential automake autoconf libtool pkg-config \
python3-setuptools python3-wheel python3-empy python3-yaml \
can-utils

这些依赖对应 Lely 官方源码构建要求:Git、GCC/Clang、Make、GNU Autotools、Python DCF 工具相关模块,以及调试 CAN 报文常用的 can-utils。[^lely-install]

确认工具可用:

1
2
3
4
5
6
7
git --version
gcc -v
g++ -v
make -v
autoreconf -V
libtoolize --version
pkg-config --version

加载 i.MX8P Yocto SDK

加载 SDK 环境:

1
source ${SDK_ROOT}/environment-setup-armv8a-poky-linux

确认交叉工具链:

1
2
3
4
5
6
7
which aarch64-poky-linux-gcc
which aarch64-poky-linux-g++
aarch64-poky-linux-gcc --version
aarch64-poky-linux-g++ --version

echo "SDKTARGETSYSROOT=${SDKTARGETSYSROOT}"
test -d "${SDKTARGETSYSROOT}" && echo "sysroot OK"

如果 which aarch64-poky-linux-g++ 没有输出,优先检查:

1
2
ls ${SDK_ROOT}/environment-setup-*
find ${SDK_ROOT} -name aarch64-poky-linux-g++ 2>/dev/null | head

获取 Lely CANopen 源码

创建工作目录:

1
2
mkdir -p ${WORK_ROOT}
cd ${WORK_ROOT}

拉取源码:

1
2
3
git clone https://gitlab.com/lely_industries/lely-core.git
cd lely-core
git checkout master

如果生产项目追求可复现构建,建议把 master 换成明确的 tag 或 commit:

1
2
git tag --list | tail
git checkout <known-good-tag-or-commit>

用 Autotools 生成 configure 脚本

Lely 源码构建使用 Autotools。首次从 Git 仓库构建时,需要生成 configure

1
2
cd ${WORK_ROOT}/lely-core
autoreconf -i

这一步通常会输出类似信息:

1
2
3
4
5
libtoolize: putting auxiliary files in '.'
libtoolize: copying file './ltmain.sh'
configure.ac: installing './compile'
src/can/Makefile.am: installing './depcomp'
parallel-tests: installing './test-driver'

这些输出表示 Autotools 正在补齐构建系统需要的辅助脚本、libtool 宏、测试驱动和依赖生成脚本。只要没有 error 结尾,通常可以继续执行 configure

交叉配置 Lely CANopen

创建独立构建目录:

1
2
mkdir -p ${WORK_ROOT}/lely-core/build-imx8p
cd ${WORK_ROOT}/lely-core/build-imx8p

配置交叉编译:

1
2
3
4
5
../configure \
--host=aarch64-poky-linux \
--prefix=${LELY_PREFIX} \
--disable-python \
--disable-cython

关键参数说明:

参数 作用
--host=aarch64-poky-linux 指定目标平台 triplet,让 Autotools 使用 Yocto SDK 的 aarch64-poky-linux-* 工具链
--prefix=${LELY_PREFIX} 指定目标板最终安装前缀,避免污染 /usr/usr/local
--disable-python 关闭 Python 工具和绑定,减少目标板运行依赖
--disable-cython 关闭 Cython 生成的 Python binding

不要加 --disable-cxx。Lely 官方说明中,关闭 C++ 会同时关闭 C++ CANopen application library,也就是 liblely-coapp。本文后续的 C++ master 链接验证依赖它。[^lely-config]

编译并安装到 stage

执行构建:

1
make -j"$(nproc)"

把产物安装到 stage 目录:

1
make install DESTDIR="${PWD}/stage"

DESTDIR 不会改变程序认为的最终安装前缀。它只是把 ${LELY_PREFIX} 这棵目录临时放到 stage/ 下,便于打包和部署。

1
2
3
4
5
flowchart TD
Prefix[--prefix=/home/imx8p/share/lely-master] --> Runtime[板端运行路径]
Destdir[DESTDIR=build-imx8p/stage] --> Package[主机侧临时打包目录]
Package --> StagePath[stage/home/imx8p/share/lely-master]
StagePath --> Runtime

检查交叉构建产物

检查 coctl 架构:

1
file stage${LELY_PREFIX}/bin/coctl

期望包含:

1
ELF 64-bit LSB executable, ARM aarch64

检查库和 pkg-config 文件:

1
2
find stage${LELY_PREFIX}/lib -maxdepth 1 -type f -o -type l
find stage${LELY_PREFIX}/lib/pkgconfig -type f

检查 DCF/EDS 文件:

1
find stage${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"

如果没有找到 coctl.dcf,先不要修改 coctl 源码,先确认安装输出和示例文件是否完整:

1
2
cd ${WORK_ROOT}/lely-core
find . -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"

打包部署文件

在构建目录执行:

1
2
3
4
5
cd ${WORK_ROOT}/lely-core/build-imx8p
export LELY_PREFIX_REL=${LELY_PREFIX#/}

tar -C stage -czf ${WORK_ROOT}/lely-master-imx8p.tar.gz ${LELY_PREFIX_REL}
ls -lh ${WORK_ROOT}/lely-master-imx8p.tar.gz

打包后的目录结构类似:

1
2
3
4
5
6
7
8
9
lely-master-imx8p.tar.gz
└── home/
└── imx8p/
└── share/
└── lely-master/
├── bin/
├── include/
├── lib/
└── share/ 或 etc/

准备 CMake 链接验证程序

这一步不是 Lely 本体构建必须项,但对工程集成很有价值。它验证自己的 CMake 项目能否找到交叉编译后的 liblely-coapp,并链接出 ARM64 ELF。

创建目录:

1
2
mkdir -p ${WORK_ROOT}/cmake-link-smoke
cd ${WORK_ROOT}/cmake-link-smoke

创建 main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

#include <lely/coapp/master.hpp>
#include <lely/io2/linux/can.hpp>
#include <lely/io2/sys/io.hpp>

int main()
{
std::cout << "Lely CANopen link smoke test for i.MX8P started." << std::endl;
std::cout << "This binary only validates cross include/link/runtime loading." << std::endl;
return 0;
}

创建 CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cmake_minimum_required(VERSION 3.16)
project(lely_link_smoke C CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PkgConfig REQUIRED)
pkg_check_modules(LELY_COAPP REQUIRED IMPORTED_TARGET liblely-coapp)

add_executable(lely_link_smoke
main.cpp
)

target_link_libraries(lely_link_smoke
PRIVATE
PkgConfig::LELY_COAPP
pthread
rt
)

target_compile_definitions(lely_link_smoke
PRIVATE
COMPILE_FOR_IMX8P
)

target_link_options(lely_link_smoke
PRIVATE
-Wl,-Map=lely_link_smoke.map
)

创建 toolchain-imx8p.cmake

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(TOOLCHAIN_PREFIX aarch64-poky-linux-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(CMAKE_STRIP ${TOOLCHAIN_PREFIX}strip)

if(DEFINED ENV{SDKTARGETSYSROOT})
set(CMAKE_SYSROOT $ENV{SDKTARGETSYSROOT})
else()
set(CMAKE_SYSROOT /opt/fsl-imx-xwayland/6.1-mickledore/sysroots/armv8a-poky-linux)
endif()

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(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(CPP_FLAGS "${CPP_FLAGS} -DSPDLOG_COMPILED_LIB -Warray-bounds -Wall -Wextra -g -O0")
set(CPP_FLAGS "${CPP_FLAGS} -Wno-write-strings -Wno-unused-variable -Wno-unused-parameter -Wno-unused-function")
set(CPP_FLAGS "${CPP_FLAGS} -Wno-unused-but-set-variable -Wno-implicit-fallthrough")
set(CPP_FLAGS "${CPP_FLAGS} -Wno-missing-field-initializers")
set(CPP_FLAGS "${CPP_FLAGS} -Wno-format -DIGNORE_SOME_WARNINGS")

add_definitions(-DCOMPILE_FOR_IMX8P)

set(CMAKE_C_FLAGS "${CPP_FLAGS}")
set(CMAKE_CXX_FLAGS "${CPP_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt")

set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")

设置 pkg-config 只查找目标库:

1
2
3
4
5
6
7
8
source ${SDK_ROOT}/environment-setup-armv8a-poky-linux

export LELY_STAGE=${WORK_ROOT}/lely-core/build-imx8p/stage
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}${LELY_PREFIX}/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig

pkg-config --cflags liblely-coapp
pkg-config --libs liblely-coapp

确认输出中不应出现主机 x86 路径,例如:

1
/usr/lib/x86_64-linux-gnu

如果出现,说明 pkg-config 找到了主机库,需要重新检查 PKG_CONFIG_LIBDIR

编译链接验证程序:

1
2
3
4
5
cmake -S ${WORK_ROOT}/cmake-link-smoke -B ${WORK_ROOT}/build-link-smoke \
-DCMAKE_TOOLCHAIN_FILE=${WORK_ROOT}/cmake-link-smoke/toolchain-imx8p.cmake \
-DCMAKE_BUILD_TYPE=Debug

cmake --build ${WORK_ROOT}/build-link-smoke -j"$(nproc)"

检查 ELF 架构:

1
file ${WORK_ROOT}/build-link-smoke/lely_link_smoke.elf

期望包含:

1
ELF 64-bit LSB executable, ARM aarch64

部署到 i.MX8P

把包和链接验证程序复制到板端:

1
2
3
ssh ${BOARD_LOGIN}@${BOARD_IP} "mkdir -p ${BOARD_SHARE}"
scp ${WORK_ROOT}/lely-master-imx8p.tar.gz ${BOARD_LOGIN}@${BOARD_IP}:${BOARD_SHARE}/
scp ${WORK_ROOT}/build-link-smoke/lely_link_smoke.elf ${BOARD_LOGIN}@${BOARD_IP}:${BOARD_SHARE}/

登录板端:

1
ssh ${BOARD_LOGIN}@${BOARD_IP}

在 i.MX8P 上解包:

1
2
cd ${BOARD_SHARE}
tar -C / -xzf ${BOARD_SHARE}/lely-master-imx8p.tar.gz

配置临时运行环境:

1
2
export PATH=${LELY_PREFIX}/bin:${PATH}
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}

确认工具可执行:

1
2
3
which coctl
coctl --help || true
file ${LELY_PREFIX}/bin/coctl || true

运行链接验证程序:

1
2
chmod +x ${BOARD_SHARE}/lely_link_smoke.elf
${BOARD_SHARE}/lely_link_smoke.elf

期望输出:

1
2
Lely CANopen link smoke test for i.MX8P started.
This binary only validates cross include/link/runtime loading.

如果提示缺动态库,先确认:

1
2
3
echo ${LD_LIBRARY_PATH}
ls ${LELY_PREFIX}/lib
ldd ${BOARD_SHARE}/lely_link_smoke.elf || true

启动 vcan0 做虚拟 CAN 验证

Lely 官方命令行教程也使用 SocketCAN 的虚拟 CAN 接口进行不依赖真实 CAN 硬件的验证。[^lely-cmd]

在 i.MX8P 上执行:

1
2
3
4
5
6
7
which ip
modprobe vcan || true

ip link show vcan0 >/dev/null 2>&1 && ip link delete vcan0 || true
ip link add dev vcan0 type vcan
ip link set up vcan0
ip -details link show vcan0

期望看到 vcan0 处于 UP 状态。

如果板端已经安装 can-utils,可以开一个终端观察报文:

1
candump vcan0

没有 candump 也可以继续验证 coctl 是否能启动,但会少一个报文观察窗口。

1
2
3
4
5
6
7
8
9
sequenceDiagram
participant Shell1 as 终端 1: coctl
participant Kernel as Linux SocketCAN vcan0
participant Shell2 as 终端 2: candump
Shell2->>Kernel: candump vcan0
Shell1->>Kernel: coctl vcan0 coctl.dcf
Shell1->>Kernel: init 0
Kernel-->>Shell2: 显示虚拟 CAN 报文
Shell1-->>Shell1: running as master

使用 coctl 启动 CANopen master

先查找 DCF 文件:

1
find ${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"

假设找到:

1
/home/imx8p/share/lely-master/etc/coctl.dcf

启动 coctl

1
2
3
4
export PATH=${LELY_PREFIX}/bin:${PATH}
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}

coctl vcan0 ${LELY_PREFIX}/etc/coctl.dcf

进入交互界面后执行:

1
init 0

期望看到类似:

1
running as master

这个结果说明:

  1. coctl 已在 i.MX8P 上运行。
  2. Lely 目标动态库可以被加载。
  3. vcan0 SocketCAN 路径可用。
  4. CANopen master 角色可以启动。

它仍不代表真实 CAN 设备通信已经通过。真实设备验证还需要接入 can0、设置 bitrate、连接从站,并按从站 EDS/DCF 配置 PDO、SDO 和 NMT 流程。

验收标准

序号 验收项 命令 通过标准
1 SDK 环境可用 which aarch64-poky-linux-g++ 能找到交叉编译器
2 sysroot 正确 echo ${SDKTARGETSYSROOT} 指向 ARM64 目标 sysroot
3 Lely 可交叉配置 ../configure --host=aarch64-poky-linux ... 无配置错误
4 Lely 可交叉构建 make -j$(nproc) 无编译错误
5 stage 安装完整 make install DESTDIR=... 生成 stage${LELY_PREFIX}
6 coctl 架构正确 file stage${LELY_PREFIX}/bin/coctl ARM aarch64 ELF
7 CMake 链接验证可构建 cmake --build ... 生成 lely_link_smoke.elf
8 链接验证程序架构正确 file lely_link_smoke.elf ARM aarch64 ELF
9 板端可加载 Lely 库 ${BOARD_SHARE}/lely_link_smoke.elf 正常输出,不缺库
10 vcan0 可启动 ip -details link show vcan0 状态为 UP
11 coctl 可启动 master coctl vcan0 ... + init 0 出现 running as master

常见问题定位

找不到 aarch64-poky-linux-g++

通常是 SDK 环境脚本没有加载,或者 SDK 安装路径不同。

1
2
source ${SDK_ROOT}/environment-setup-armv8a-poky-linux
which aarch64-poky-linux-g++

仍找不到时:

1
find ${SDK_ROOT} -name aarch64-poky-linux-g++ 2>/dev/null | head

pkg-config 找到 x86 主机库

现象是输出里出现:

1
/usr/lib/x86_64-linux-gnu

处理:

1
2
3
4
5
6
export LELY_STAGE=${WORK_ROOT}/lely-core/build-imx8p/stage
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}${LELY_PREFIX}/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig

pkg-config --cflags liblely-coapp
pkg-config --libs liblely-coapp

板端运行提示缺动态库

先确认临时库路径:

1
2
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}
ldd ${BOARD_SHARE}/lely_link_smoke.elf || true

调试阶段建议继续使用临时 LD_LIBRARY_PATH。等部署方式稳定后,再考虑写入系统动态库配置。

可能原因:

  1. 内核未启用 CONFIG_CAN_VCAN
  2. vcan 模块未安装。
  3. iproute2 不完整。
  4. 当前登录用户没有足够权限。

排查:

1
2
3
4
zcat /proc/config.gz 2>/dev/null | grep CONFIG_CAN_VCAN || true
lsmod | grep vcan || true
modprobe vcan || true
which ip

找不到 coctl.dcf

先找文件:

1
2
find ${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"
find ${WORK_ROOT}/lely-core -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"

如果源码和 stage 中都没有 DCF 文件,应优先补齐 DCF/EDS 配置文件,而不是修改 coctl 程序本身。

切到真实 CAN 的入口

虚拟 CAN 验证通过后,真实 CAN 可以从下面的入口开始。假设真实接口为 can0,bitrate 为 500000

1
2
3
4
5
ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up
ip -details link show can0
candump can0

然后把 coctl 的接口从 vcan0 换成 can0

1
coctl can0 ${LELY_PREFIX}/etc/coctl.dcf

真实 CAN 阶段还需要确认:

  • i.MX8P 的 CAN 控制器和 pinmux 是否正确。
  • CAN 收发器供电和 standby 引脚是否正确。
  • 总线两端是否有 120 Ω 终端电阻。
  • 所有节点 bitrate 是否一致。
  • 从站 node-id、EDS/DCF、PDO mapping 和 heartbeat 配置是否一致。

参考资料

[^cia-canopen]: CAN in Automation, “CANopen,” https://www.can-cia.org/can-knowledge/canopen
[^cia-docs]: CAN in Automation, “Technical documents,” https://www.can-cia.org/cia-groups/technical-documents
[^lely-home]: Lely CANopen, “Lely CANopen,” https://opensource.lely.com/canopen/
[^lely-install]: Lely CANopen, “Installation,” https://opensource.lely.com/canopen/docs/installation/
[^lely-overview]: Lely CANopen, “Library overview,” https://opensource.lely.com/canopen/docs/overview/
[^lely-cross]: Lely CANopen, “Cross-compilation,” https://opensource.lely.com/canopen/docs/cross-compilation/
[^lely-config]: Lely CANopen, “Build configuration,” https://opensource.lely.com/canopen/docs/configuration/
[^lely-cmd]: Lely CANopen, “Command-line tutorial,” https://opensource.lely.com/canopen/docs/cmd-tutorial/
[^lely-license]: Lely CANopen, “License,” https://opensource.lely.com/canopen/docs/license/
[^lely-doxygen]: Lely core libraries Doxygen, https://lely_industries.gitlab.io/lely-core/doxygen/
[^canopennode]: CANopenNode documentation, https://canopennode.github.io/CANopenNode/
[^python-canopen]: python-canopen project description, https://pypi.org/project/canopen/
[^canfestival]: CanFestival project page, https://sourceforge.net/projects/canfestival/