Lely CANopen 入门:从协议背景到 i.MX8P 交叉编译安装
@[toc]
先给结论
Lely CANopen 适合做 嵌入式 Linux 侧 CANopen master,尤其适合已经使用 SocketCAN、C++、事件循环或 Yocto SDK 的项目。它不是只能跑在 PC 上的调试工具,而是一套包含 C 核心库、C++ 应用层封装、命令行工具和 DCF/EDS 辅助工具的 CANopen 实现。
如果你的目标是 i.MX8P 这类 ARM64 Linux 板卡,推荐流程是:
- 在 x86 Ubuntu 主机安装构建依赖和 i.MX8P Yocto SDK。
- 加载 Yocto SDK 环境脚本,得到
aarch64-poky-linux-*工具链和目标 sysroot。 - 从源码构建 Lely CANopen,并通过
--host=aarch64-poky-linux交叉编译。 - 用
make install DESTDIR=...收集头文件、库、工具和示例配置。 - 打包部署到 i.MX8P。
- 在 i.MX8P 上启动
vcan0,运行coctl验证 CANopen master 启动链路。
1 | flowchart LR |
CANopen 先解决什么问题
CAN 本身只定义了较低层的数据链路能力。真实设备网络还需要回答这些问题:
- 每个节点怎么编号?
- 设备启动、停止、复位由谁管理?
- 参数怎么读写?
- 周期数据和事件数据怎么发送?
- 不同厂商设备怎么描述自己的对象、参数和能力?
CANopen 就是在 CAN 之上提供这些规则的一套高层协议。CAN in Automation(CiA)把 CANopen 设备描述为三部分:协议栈、应用软件、对象字典。对象字典连接协议栈和应用层,并保存通信参数、应用参数和诊断数据。[^cia-canopen]
1 | flowchart TD |
工程上可以把 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 | flowchart TD |
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 的工程动机可以归纳为:
- 让 CANopen 协议栈可复用:把 CANopen 协议处理从具体设备业务里拆出来。
- 服务 Linux 和嵌入式目标:Linux 是官方推荐和支持较好的平台,SocketCAN 能直接对接内核 CAN 子系统。[^lely-install]
- 降低 C++ master 应用开发复杂度:
liblely-coapp把 CANopen master 常用模式封装到 C++ driver model 和 event loop 中。[^lely-overview] - 保留跨平台裁剪能力: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,先用 vcan0 和 coctl 把 master 启动链路跑通,再切到真实 can0。
本文目标和不做的事情
本文完成:
- 在 x86 Ubuntu 主机上准备 Lely CANopen 源码构建环境。
- 使用 i.MX8P Yocto SDK 交叉编译 Lely CANopen。
- 把构建结果安装到 stage 目录,并打包部署到 i.MX8P。
- 在 i.MX8P 上启动
vcan0。 - 使用
coctl验证 CANopen master 可以启动。 - 编译一个最小 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 | export SDK_ROOT=/opt/fsl-imx-xwayland/6.1-mickledore |
192.0.2.10属于文档示例地址,不是真实板卡地址。发布文章时不要写入内网真实 IP、真实用户名或公司共享目录。
在 x86 Ubuntu 上安装构建依赖
先安装基础依赖:
1 | sudo apt-get update |
这些依赖对应 Lely 官方源码构建要求:Git、GCC/Clang、Make、GNU Autotools、Python DCF 工具相关模块,以及调试 CAN 报文常用的 can-utils。[^lely-install]
确认工具可用:
1 | git --version |
加载 i.MX8P Yocto SDK
加载 SDK 环境:
1 | source ${SDK_ROOT}/environment-setup-armv8a-poky-linux |
确认交叉工具链:
1 | which aarch64-poky-linux-gcc |
如果 which aarch64-poky-linux-g++ 没有输出,优先检查:
1 | ls ${SDK_ROOT}/environment-setup-* |
获取 Lely CANopen 源码
创建工作目录:
1 | mkdir -p ${WORK_ROOT} |
拉取源码:
1 | git clone https://gitlab.com/lely_industries/lely-core.git |
如果生产项目追求可复现构建,建议把 master 换成明确的 tag 或 commit:
1 | git tag --list | tail |
用 Autotools 生成 configure 脚本
Lely 源码构建使用 Autotools。首次从 Git 仓库构建时,需要生成 configure:
1 | cd ${WORK_ROOT}/lely-core |
这一步通常会输出类似信息:
1 | libtoolize: putting auxiliary files in '.' |
这些输出表示 Autotools 正在补齐构建系统需要的辅助脚本、libtool 宏、测试驱动和依赖生成脚本。只要没有 error 结尾,通常可以继续执行 configure。
交叉配置 Lely CANopen
创建独立构建目录:
1 | mkdir -p ${WORK_ROOT}/lely-core/build-imx8p |
配置交叉编译:
1 | ../configure \ |
关键参数说明:
| 参数 | 作用 |
|---|---|
--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 | flowchart TD |
检查交叉构建产物
检查 coctl 架构:
1 | file stage${LELY_PREFIX}/bin/coctl |
期望包含:
1 | ELF 64-bit LSB executable, ARM aarch64 |
检查库和 pkg-config 文件:
1 | find stage${LELY_PREFIX}/lib -maxdepth 1 -type f -o -type l |
检查 DCF/EDS 文件:
1 | find stage${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds" |
如果没有找到 coctl.dcf,先不要修改 coctl 源码,先确认安装输出和示例文件是否完整:
1 | cd ${WORK_ROOT}/lely-core |
打包部署文件
在构建目录执行:
1 | cd ${WORK_ROOT}/lely-core/build-imx8p |
打包后的目录结构类似:
1 | lely-master-imx8p.tar.gz |
准备 CMake 链接验证程序
这一步不是 Lely 本体构建必须项,但对工程集成很有价值。它验证自己的 CMake 项目能否找到交叉编译后的 liblely-coapp,并链接出 ARM64 ELF。
创建目录:
1 | mkdir -p ${WORK_ROOT}/cmake-link-smoke |
创建 main.cpp:
1 |
|
创建 CMakeLists.txt:
1 | cmake_minimum_required(VERSION 3.16) |
创建 toolchain-imx8p.cmake:
1 | set(CMAKE_SYSTEM_NAME Linux) |
设置 pkg-config 只查找目标库:
1 | source ${SDK_ROOT}/environment-setup-armv8a-poky-linux |
确认输出中不应出现主机 x86 路径,例如:
1 | /usr/lib/x86_64-linux-gnu |
如果出现,说明 pkg-config 找到了主机库,需要重新检查 PKG_CONFIG_LIBDIR。
编译链接验证程序:
1 | cmake -S ${WORK_ROOT}/cmake-link-smoke -B ${WORK_ROOT}/build-link-smoke \ |
检查 ELF 架构:
1 | file ${WORK_ROOT}/build-link-smoke/lely_link_smoke.elf |
期望包含:
1 | ELF 64-bit LSB executable, ARM aarch64 |
部署到 i.MX8P
把包和链接验证程序复制到板端:
1 | ssh ${BOARD_LOGIN}@${BOARD_IP} "mkdir -p ${BOARD_SHARE}" |
登录板端:
1 | ssh ${BOARD_LOGIN}@${BOARD_IP} |
在 i.MX8P 上解包:
1 | cd ${BOARD_SHARE} |
配置临时运行环境:
1 | export PATH=${LELY_PREFIX}/bin:${PATH} |
确认工具可执行:
1 | which coctl |
运行链接验证程序:
1 | chmod +x ${BOARD_SHARE}/lely_link_smoke.elf |
期望输出:
1 | Lely CANopen link smoke test for i.MX8P started. |
如果提示缺动态库,先确认:
1 | echo ${LD_LIBRARY_PATH} |
启动 vcan0 做虚拟 CAN 验证
Lely 官方命令行教程也使用 SocketCAN 的虚拟 CAN 接口进行不依赖真实 CAN 硬件的验证。[^lely-cmd]
在 i.MX8P 上执行:
1 | which ip |
期望看到 vcan0 处于 UP 状态。
如果板端已经安装 can-utils,可以开一个终端观察报文:
1 | candump vcan0 |
没有 candump 也可以继续验证 coctl 是否能启动,但会少一个报文观察窗口。
1 | sequenceDiagram |
使用 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 | export PATH=${LELY_PREFIX}/bin:${PATH} |
进入交互界面后执行:
1 | init 0 |
期望看到类似:
1 | running as master |
这个结果说明:
coctl已在 i.MX8P 上运行。- Lely 目标动态库可以被加载。
vcan0SocketCAN 路径可用。- 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 | source ${SDK_ROOT}/environment-setup-armv8a-poky-linux |
仍找不到时:
1 | find ${SDK_ROOT} -name aarch64-poky-linux-g++ 2>/dev/null | head |
pkg-config 找到 x86 主机库
现象是输出里出现:
1 | /usr/lib/x86_64-linux-gnu |
处理:
1 | export LELY_STAGE=${WORK_ROOT}/lely-core/build-imx8p/stage |
板端运行提示缺动态库
先确认临时库路径:
1 | export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH} |
调试阶段建议继续使用临时 LD_LIBRARY_PATH。等部署方式稳定后,再考虑写入系统动态库配置。
ip link add dev vcan0 type vcan 失败
可能原因:
- 内核未启用
CONFIG_CAN_VCAN。 vcan模块未安装。iproute2不完整。- 当前登录用户没有足够权限。
排查:
1 | zcat /proc/config.gz 2>/dev/null | grep CONFIG_CAN_VCAN || true |
找不到 coctl.dcf
先找文件:
1 | find ${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds" |
如果源码和 stage 中都没有 DCF 文件,应优先补齐 DCF/EDS 配置文件,而不是修改 coctl 程序本身。
切到真实 CAN 的入口
虚拟 CAN 验证通过后,真实 CAN 可以从下面的入口开始。假设真实接口为 can0,bitrate 为 500000:
1 | ip link set can0 down |
然后把 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/










