Lely CANopen 入门:从协议背景到 i.MX8P 交叉编译安装
文章目录
- [Lely CANopen 入门:从协议背景到 i.MX8P 交叉编译安装](#Lely CANopen 入门:从协议背景到 i.MX8P 交叉编译安装)
-
- 先给结论
- [CANopen 先解决什么问题](#CANopen 先解决什么问题)
- [Lely CANopen 是什么](#Lely CANopen 是什么)
- [Lely CANopen 的由来和创建原因](#Lely CANopen 的由来和创建原因)
- [和其他 CANopen 实现怎么选](#和其他 CANopen 实现怎么选)
- 本文目标和不做的事情
- 环境和变量约定
- [在 x86 Ubuntu 上安装构建依赖](#在 x86 Ubuntu 上安装构建依赖)
- [加载 i.MX8P Yocto SDK](#加载 i.MX8P Yocto SDK)
- [获取 Lely CANopen 源码](#获取 Lely CANopen 源码)
- [用 Autotools 生成 configure 脚本](#用 Autotools 生成 configure 脚本)
- [交叉配置 Lely CANopen](#交叉配置 Lely CANopen)
- [编译并安装到 stage](#编译并安装到 stage)
- 检查交叉构建产物
- 打包部署文件
- [准备 CMake 链接验证程序](#准备 CMake 链接验证程序)
- [部署到 i.MX8P](#部署到 i.MX8P)
- [启动 vcan0 做虚拟 CAN 验证](#启动 vcan0 做虚拟 CAN 验证)
- [使用 coctl 启动 CANopen master](#使用 coctl 启动 CANopen master)
- 验收标准
- 常见问题定位
-
- [找不到 `aarch64-poky-linux-g++`](#找不到
aarch64-poky-linux-g++) - [`pkg-config` 找到 x86 主机库](#
pkg-config找到 x86 主机库) - 板端运行提示缺动态库
- [`ip link add dev vcan0 type vcan` 失败](#
ip link add dev vcan0 type vcan失败) - [找不到 `coctl.dcf`](#找不到
coctl.dcf)
- [找不到 `aarch64-poky-linux-g++`](#找不到
- [切到真实 CAN 的入口](#切到真实 CAN 的入口)
- 参考资料

先给结论
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 启动链路。
#mermaid-svg-T0s14HgnIIv1ISd0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-T0s14HgnIIv1ISd0 .error-icon{fill:#552222;}#mermaid-svg-T0s14HgnIIv1ISd0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-T0s14HgnIIv1ISd0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-T0s14HgnIIv1ISd0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-T0s14HgnIIv1ISd0 .marker.cross{stroke:#333333;}#mermaid-svg-T0s14HgnIIv1ISd0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-T0s14HgnIIv1ISd0 p{margin:0;}#mermaid-svg-T0s14HgnIIv1ISd0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster-label text{fill:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster-label span{color:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster-label span p{background-color:transparent;}#mermaid-svg-T0s14HgnIIv1ISd0 .label text,#mermaid-svg-T0s14HgnIIv1ISd0 span{fill:#333;color:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 .node rect,#mermaid-svg-T0s14HgnIIv1ISd0 .node circle,#mermaid-svg-T0s14HgnIIv1ISd0 .node ellipse,#mermaid-svg-T0s14HgnIIv1ISd0 .node polygon,#mermaid-svg-T0s14HgnIIv1ISd0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-T0s14HgnIIv1ISd0 .rough-node .label text,#mermaid-svg-T0s14HgnIIv1ISd0 .node .label text,#mermaid-svg-T0s14HgnIIv1ISd0 .image-shape .label,#mermaid-svg-T0s14HgnIIv1ISd0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-T0s14HgnIIv1ISd0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-T0s14HgnIIv1ISd0 .rough-node .label,#mermaid-svg-T0s14HgnIIv1ISd0 .node .label,#mermaid-svg-T0s14HgnIIv1ISd0 .image-shape .label,#mermaid-svg-T0s14HgnIIv1ISd0 .icon-shape .label{text-align:center;}#mermaid-svg-T0s14HgnIIv1ISd0 .node.clickable{cursor:pointer;}#mermaid-svg-T0s14HgnIIv1ISd0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-T0s14HgnIIv1ISd0 .arrowheadPath{fill:#333333;}#mermaid-svg-T0s14HgnIIv1ISd0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-T0s14HgnIIv1ISd0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-T0s14HgnIIv1ISd0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-T0s14HgnIIv1ISd0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-T0s14HgnIIv1ISd0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-T0s14HgnIIv1ISd0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster text{fill:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 .cluster span{color:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-T0s14HgnIIv1ISd0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-T0s14HgnIIv1ISd0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-T0s14HgnIIv1ISd0 .icon-shape,#mermaid-svg-T0s14HgnIIv1ISd0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-T0s14HgnIIv1ISd0 .icon-shape p,#mermaid-svg-T0s14HgnIIv1ISd0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-T0s14HgnIIv1ISd0 .icon-shape .label rect,#mermaid-svg-T0s14HgnIIv1ISd0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-T0s14HgnIIv1ISd0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-T0s14HgnIIv1ISd0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-T0s14HgnIIv1ISd0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} x86 Ubuntu 主机
加载 Yocto SDK
交叉编译 Lely CANopen
DESTDIR stage 安装
打包部署到 i.MX8P
配置 PATH / LD_LIBRARY_PATH
启动 vcan0
运行 coctl 作为 master
CANopen 先解决什么问题
CAN 本身只定义了较低层的数据链路能力。真实设备网络还需要回答这些问题:
- 每个节点怎么编号?
- 设备启动、停止、复位由谁管理?
- 参数怎么读写?
- 周期数据和事件数据怎么发送?
- 不同厂商设备怎么描述自己的对象、参数和能力?
CANopen 就是在 CAN 之上提供这些规则的一套高层协议。CAN in Automation(CiA)把 CANopen 设备描述为三部分:协议栈、应用软件、对象字典。对象字典连接协议栈和应用层,并保存通信参数、应用参数和诊断数据。[1](#1)
#mermaid-svg-KVQbtN6S8xS6m248{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KVQbtN6S8xS6m248 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KVQbtN6S8xS6m248 .error-icon{fill:#552222;}#mermaid-svg-KVQbtN6S8xS6m248 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KVQbtN6S8xS6m248 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KVQbtN6S8xS6m248 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KVQbtN6S8xS6m248 .marker.cross{stroke:#333333;}#mermaid-svg-KVQbtN6S8xS6m248 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KVQbtN6S8xS6m248 p{margin:0;}#mermaid-svg-KVQbtN6S8xS6m248 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KVQbtN6S8xS6m248 .cluster-label text{fill:#333;}#mermaid-svg-KVQbtN6S8xS6m248 .cluster-label span{color:#333;}#mermaid-svg-KVQbtN6S8xS6m248 .cluster-label span p{background-color:transparent;}#mermaid-svg-KVQbtN6S8xS6m248 .label text,#mermaid-svg-KVQbtN6S8xS6m248 span{fill:#333;color:#333;}#mermaid-svg-KVQbtN6S8xS6m248 .node rect,#mermaid-svg-KVQbtN6S8xS6m248 .node circle,#mermaid-svg-KVQbtN6S8xS6m248 .node ellipse,#mermaid-svg-KVQbtN6S8xS6m248 .node polygon,#mermaid-svg-KVQbtN6S8xS6m248 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KVQbtN6S8xS6m248 .rough-node .label text,#mermaid-svg-KVQbtN6S8xS6m248 .node .label text,#mermaid-svg-KVQbtN6S8xS6m248 .image-shape .label,#mermaid-svg-KVQbtN6S8xS6m248 .icon-shape .label{text-anchor:middle;}#mermaid-svg-KVQbtN6S8xS6m248 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KVQbtN6S8xS6m248 .rough-node .label,#mermaid-svg-KVQbtN6S8xS6m248 .node .label,#mermaid-svg-KVQbtN6S8xS6m248 .image-shape .label,#mermaid-svg-KVQbtN6S8xS6m248 .icon-shape .label{text-align:center;}#mermaid-svg-KVQbtN6S8xS6m248 .node.clickable{cursor:pointer;}#mermaid-svg-KVQbtN6S8xS6m248 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KVQbtN6S8xS6m248 .arrowheadPath{fill:#333333;}#mermaid-svg-KVQbtN6S8xS6m248 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KVQbtN6S8xS6m248 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KVQbtN6S8xS6m248 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KVQbtN6S8xS6m248 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KVQbtN6S8xS6m248 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KVQbtN6S8xS6m248 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KVQbtN6S8xS6m248 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KVQbtN6S8xS6m248 .cluster text{fill:#333;}#mermaid-svg-KVQbtN6S8xS6m248 .cluster span{color:#333;}#mermaid-svg-KVQbtN6S8xS6m248 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KVQbtN6S8xS6m248 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KVQbtN6S8xS6m248 rect.text{fill:none;stroke-width:0;}#mermaid-svg-KVQbtN6S8xS6m248 .icon-shape,#mermaid-svg-KVQbtN6S8xS6m248 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KVQbtN6S8xS6m248 .icon-shape p,#mermaid-svg-KVQbtN6S8xS6m248 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KVQbtN6S8xS6m248 .icon-shape .label rect,#mermaid-svg-KVQbtN6S8xS6m248 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KVQbtN6S8xS6m248 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KVQbtN6S8xS6m248 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KVQbtN6S8xS6m248 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用逻辑
电机控制 / IO / 传感器 / 执行器
对象字典 Object Dictionary
索引 / 子索引 / 类型 / 访问权限
CANopen 协议栈
NMT / SDO / PDO / EMCY / Heartbeat
CAN / SocketCAN / CAN 控制器
工程上可以把 CANopen 理解成:
| 层次 | 作用 | 常见对象 |
|---|---|---|
| 网络管理 | 控制节点状态 | NMT、Heartbeat |
| 参数访问 | 读写对象字典 | SDO |
| 实时过程数据 | 低开销传输状态和控制量 | PDO |
| 错误上报 | 设备异常和诊断 | EMCY |
| 设备描述 | 描述对象字典和设备能力 | EDS、DCF |
CiA 301 是 CANopen classic 的应用层和通信 profile 的核心规范。CiA 还维护大量设备 profile,例如通用 I/O、编码器、驱动器和运动控制等。[2](#2)
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 应用开发。[3](#3)
#mermaid-svg-shtvl7C2LoXC668d{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-shtvl7C2LoXC668d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-shtvl7C2LoXC668d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-shtvl7C2LoXC668d .error-icon{fill:#552222;}#mermaid-svg-shtvl7C2LoXC668d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-shtvl7C2LoXC668d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-shtvl7C2LoXC668d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-shtvl7C2LoXC668d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-shtvl7C2LoXC668d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-shtvl7C2LoXC668d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-shtvl7C2LoXC668d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-shtvl7C2LoXC668d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-shtvl7C2LoXC668d .marker.cross{stroke:#333333;}#mermaid-svg-shtvl7C2LoXC668d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-shtvl7C2LoXC668d p{margin:0;}#mermaid-svg-shtvl7C2LoXC668d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-shtvl7C2LoXC668d .cluster-label text{fill:#333;}#mermaid-svg-shtvl7C2LoXC668d .cluster-label span{color:#333;}#mermaid-svg-shtvl7C2LoXC668d .cluster-label span p{background-color:transparent;}#mermaid-svg-shtvl7C2LoXC668d .label text,#mermaid-svg-shtvl7C2LoXC668d span{fill:#333;color:#333;}#mermaid-svg-shtvl7C2LoXC668d .node rect,#mermaid-svg-shtvl7C2LoXC668d .node circle,#mermaid-svg-shtvl7C2LoXC668d .node ellipse,#mermaid-svg-shtvl7C2LoXC668d .node polygon,#mermaid-svg-shtvl7C2LoXC668d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-shtvl7C2LoXC668d .rough-node .label text,#mermaid-svg-shtvl7C2LoXC668d .node .label text,#mermaid-svg-shtvl7C2LoXC668d .image-shape .label,#mermaid-svg-shtvl7C2LoXC668d .icon-shape .label{text-anchor:middle;}#mermaid-svg-shtvl7C2LoXC668d .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-shtvl7C2LoXC668d .rough-node .label,#mermaid-svg-shtvl7C2LoXC668d .node .label,#mermaid-svg-shtvl7C2LoXC668d .image-shape .label,#mermaid-svg-shtvl7C2LoXC668d .icon-shape .label{text-align:center;}#mermaid-svg-shtvl7C2LoXC668d .node.clickable{cursor:pointer;}#mermaid-svg-shtvl7C2LoXC668d .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-shtvl7C2LoXC668d .arrowheadPath{fill:#333333;}#mermaid-svg-shtvl7C2LoXC668d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-shtvl7C2LoXC668d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-shtvl7C2LoXC668d .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-shtvl7C2LoXC668d .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-shtvl7C2LoXC668d .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-shtvl7C2LoXC668d .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-shtvl7C2LoXC668d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-shtvl7C2LoXC668d .cluster text{fill:#333;}#mermaid-svg-shtvl7C2LoXC668d .cluster span{color:#333;}#mermaid-svg-shtvl7C2LoXC668d div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-shtvl7C2LoXC668d .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-shtvl7C2LoXC668d rect.text{fill:none;stroke-width:0;}#mermaid-svg-shtvl7C2LoXC668d .icon-shape,#mermaid-svg-shtvl7C2LoXC668d .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-shtvl7C2LoXC668d .icon-shape p,#mermaid-svg-shtvl7C2LoXC668d .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-shtvl7C2LoXC668d .icon-shape .label rect,#mermaid-svg-shtvl7C2LoXC668d .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-shtvl7C2LoXC668d .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-shtvl7C2LoXC668d .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-shtvl7C2LoXC668d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 你的 C++ master 应用
liblely-coapp
C++ CANopen application layer
liblely-co
CANopen core
liblely-ev
event loop
liblely-io2
async I/O
Linux SocketCAN
can0 / vcan0
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 文档完整,并展示了在实际机器人系统中的使用场景。[4](#4)[5](#5)
因此,Lely CANopen 的工程动机可以归纳为:
- 让 CANopen 协议栈可复用:把 CANopen 协议处理从具体设备业务里拆出来。
- 服务 Linux 和嵌入式目标 :Linux 是官方推荐和支持较好的平台,SocketCAN 能直接对接内核 CAN 子系统。[6](#6)
- 降低 C++ master 应用开发复杂度 :
liblely-coapp把 CANopen master 常用模式封装到 C++ driver model 和 event loop 中。[3](#3) - 保留跨平台裁剪能力 :Python、Cython、C++ 接口、工具、测试等可以按目标裁剪,适合从 PC 验证过渡到目标板部署。[7](#7)
和其他 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 |
统一设置变量:
bash
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 上安装构建依赖
先安装基础依赖:
bash
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。[6](#6)
确认工具可用:
bash
git --version
gcc -v
g++ -v
make -v
autoreconf -V
libtoolize --version
pkg-config --version
加载 i.MX8P Yocto SDK
加载 SDK 环境:
bash
source ${SDK_ROOT}/environment-setup-armv8a-poky-linux
确认交叉工具链:
bash
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++ 没有输出,优先检查:
bash
ls ${SDK_ROOT}/environment-setup-*
find ${SDK_ROOT} -name aarch64-poky-linux-g++ 2>/dev/null | head
获取 Lely CANopen 源码
创建工作目录:
bash
mkdir -p ${WORK_ROOT}
cd ${WORK_ROOT}
拉取源码:
bash
git clone https://gitlab.com/lely_industries/lely-core.git
cd lely-core
git checkout master
如果生产项目追求可复现构建,建议把 master 换成明确的 tag 或 commit:
bash
git tag --list | tail
git checkout <known-good-tag-or-commit>
用 Autotools 生成 configure 脚本
Lely 源码构建使用 Autotools。首次从 Git 仓库构建时,需要生成 configure:
bash
cd ${WORK_ROOT}/lely-core
autoreconf -i
这一步通常会输出类似信息:
text
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
创建独立构建目录:
bash
mkdir -p ${WORK_ROOT}/lely-core/build-imx8p
cd ${WORK_ROOT}/lely-core/build-imx8p
配置交叉编译:
bash
../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 链接验证依赖它。[7](#7)
编译并安装到 stage
执行构建:
bash
make -j"$(nproc)"
把产物安装到 stage 目录:
bash
make install DESTDIR="${PWD}/stage"
DESTDIR 不会改变程序认为的最终安装前缀。它只是把 ${LELY_PREFIX} 这棵目录临时放到 stage/ 下,便于打包和部署。
#mermaid-svg-DB0zG3UrVTIQP7hb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DB0zG3UrVTIQP7hb .error-icon{fill:#552222;}#mermaid-svg-DB0zG3UrVTIQP7hb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DB0zG3UrVTIQP7hb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DB0zG3UrVTIQP7hb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DB0zG3UrVTIQP7hb .marker.cross{stroke:#333333;}#mermaid-svg-DB0zG3UrVTIQP7hb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DB0zG3UrVTIQP7hb p{margin:0;}#mermaid-svg-DB0zG3UrVTIQP7hb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster-label text{fill:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster-label span{color:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster-label span p{background-color:transparent;}#mermaid-svg-DB0zG3UrVTIQP7hb .label text,#mermaid-svg-DB0zG3UrVTIQP7hb span{fill:#333;color:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb .node rect,#mermaid-svg-DB0zG3UrVTIQP7hb .node circle,#mermaid-svg-DB0zG3UrVTIQP7hb .node ellipse,#mermaid-svg-DB0zG3UrVTIQP7hb .node polygon,#mermaid-svg-DB0zG3UrVTIQP7hb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DB0zG3UrVTIQP7hb .rough-node .label text,#mermaid-svg-DB0zG3UrVTIQP7hb .node .label text,#mermaid-svg-DB0zG3UrVTIQP7hb .image-shape .label,#mermaid-svg-DB0zG3UrVTIQP7hb .icon-shape .label{text-anchor:middle;}#mermaid-svg-DB0zG3UrVTIQP7hb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DB0zG3UrVTIQP7hb .rough-node .label,#mermaid-svg-DB0zG3UrVTIQP7hb .node .label,#mermaid-svg-DB0zG3UrVTIQP7hb .image-shape .label,#mermaid-svg-DB0zG3UrVTIQP7hb .icon-shape .label{text-align:center;}#mermaid-svg-DB0zG3UrVTIQP7hb .node.clickable{cursor:pointer;}#mermaid-svg-DB0zG3UrVTIQP7hb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DB0zG3UrVTIQP7hb .arrowheadPath{fill:#333333;}#mermaid-svg-DB0zG3UrVTIQP7hb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DB0zG3UrVTIQP7hb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DB0zG3UrVTIQP7hb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DB0zG3UrVTIQP7hb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DB0zG3UrVTIQP7hb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DB0zG3UrVTIQP7hb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster text{fill:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb .cluster span{color:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DB0zG3UrVTIQP7hb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DB0zG3UrVTIQP7hb rect.text{fill:none;stroke-width:0;}#mermaid-svg-DB0zG3UrVTIQP7hb .icon-shape,#mermaid-svg-DB0zG3UrVTIQP7hb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DB0zG3UrVTIQP7hb .icon-shape p,#mermaid-svg-DB0zG3UrVTIQP7hb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DB0zG3UrVTIQP7hb .icon-shape .label rect,#mermaid-svg-DB0zG3UrVTIQP7hb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DB0zG3UrVTIQP7hb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DB0zG3UrVTIQP7hb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DB0zG3UrVTIQP7hb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} --prefix=/home/imx8p/share/lely-master
板端运行路径
DESTDIR=build-imx8p/stage
主机侧临时打包目录
stage/home/imx8p/share/lely-master
检查交叉构建产物
检查 coctl 架构:
bash
file stage${LELY_PREFIX}/bin/coctl
期望包含:
text
ELF 64-bit LSB executable, ARM aarch64
检查库和 pkg-config 文件:
bash
find stage${LELY_PREFIX}/lib -maxdepth 1 -type f -o -type l
find stage${LELY_PREFIX}/lib/pkgconfig -type f
检查 DCF/EDS 文件:
bash
find stage${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"
如果没有找到 coctl.dcf,先不要修改 coctl 源码,先确认安装输出和示例文件是否完整:
bash
cd ${WORK_ROOT}/lely-core
find . -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"
打包部署文件
在构建目录执行:
bash
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
打包后的目录结构类似:
text
lely-master-imx8p.tar.gz
└── home/
└── imx8p/
└── share/
└── lely-master/
├── bin/
├── include/
├── lib/
└── share/ 或 etc/
准备 CMake 链接验证程序
这一步不是 Lely 本体构建必须项,但对工程集成很有价值。它验证自己的 CMake 项目能否找到交叉编译后的 liblely-coapp,并链接出 ARM64 ELF。
创建目录:
bash
mkdir -p ${WORK_ROOT}/cmake-link-smoke
cd ${WORK_ROOT}/cmake-link-smoke
创建 main.cpp:
cpp
#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:
cmake
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:
cmake
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 只查找目标库:
bash
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 路径,例如:
text
/usr/lib/x86_64-linux-gnu
如果出现,说明 pkg-config 找到了主机库,需要重新检查 PKG_CONFIG_LIBDIR。
编译链接验证程序:
bash
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 架构:
bash
file ${WORK_ROOT}/build-link-smoke/lely_link_smoke.elf
期望包含:
text
ELF 64-bit LSB executable, ARM aarch64
部署到 i.MX8P
把包和链接验证程序复制到板端:
bash
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}/
登录板端:
bash
ssh ${BOARD_LOGIN}@${BOARD_IP}
在 i.MX8P 上解包:
bash
cd ${BOARD_SHARE}
tar -C / -xzf ${BOARD_SHARE}/lely-master-imx8p.tar.gz
配置临时运行环境:
bash
export PATH=${LELY_PREFIX}/bin:${PATH}
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}
确认工具可执行:
bash
which coctl
coctl --help || true
file ${LELY_PREFIX}/bin/coctl || true
运行链接验证程序:
bash
chmod +x ${BOARD_SHARE}/lely_link_smoke.elf
${BOARD_SHARE}/lely_link_smoke.elf
期望输出:
text
Lely CANopen link smoke test for i.MX8P started.
This binary only validates cross include/link/runtime loading.
如果提示缺动态库,先确认:
bash
echo ${LD_LIBRARY_PATH}
ls ${LELY_PREFIX}/lib
ldd ${BOARD_SHARE}/lely_link_smoke.elf || true
启动 vcan0 做虚拟 CAN 验证
Lely 官方命令行教程也使用 SocketCAN 的虚拟 CAN 接口进行不依赖真实 CAN 硬件的验证。[8](#8)
在 i.MX8P 上执行:
bash
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,可以开一个终端观察报文:
bash
candump vcan0
没有 candump 也可以继续验证 coctl 是否能启动,但会少一个报文观察窗口。
终端 2: candump Linux SocketCAN vcan0 终端 1: coctl 终端 2: candump Linux SocketCAN vcan0 终端 1: coctl #mermaid-svg-gHgCBA4byKELeCAt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gHgCBA4byKELeCAt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gHgCBA4byKELeCAt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gHgCBA4byKELeCAt .error-icon{fill:#552222;}#mermaid-svg-gHgCBA4byKELeCAt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gHgCBA4byKELeCAt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gHgCBA4byKELeCAt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gHgCBA4byKELeCAt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gHgCBA4byKELeCAt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gHgCBA4byKELeCAt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gHgCBA4byKELeCAt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gHgCBA4byKELeCAt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gHgCBA4byKELeCAt .marker.cross{stroke:#333333;}#mermaid-svg-gHgCBA4byKELeCAt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gHgCBA4byKELeCAt p{margin:0;}#mermaid-svg-gHgCBA4byKELeCAt .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gHgCBA4byKELeCAt text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gHgCBA4byKELeCAt .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gHgCBA4byKELeCAt .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gHgCBA4byKELeCAt .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gHgCBA4byKELeCAt .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gHgCBA4byKELeCAt #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gHgCBA4byKELeCAt .sequenceNumber{fill:white;}#mermaid-svg-gHgCBA4byKELeCAt #sequencenumber{fill:#333;}#mermaid-svg-gHgCBA4byKELeCAt #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gHgCBA4byKELeCAt .messageText{fill:#333;stroke:none;}#mermaid-svg-gHgCBA4byKELeCAt .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gHgCBA4byKELeCAt .labelText,#mermaid-svg-gHgCBA4byKELeCAt .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gHgCBA4byKELeCAt .loopText,#mermaid-svg-gHgCBA4byKELeCAt .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gHgCBA4byKELeCAt .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gHgCBA4byKELeCAt .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gHgCBA4byKELeCAt .noteText,#mermaid-svg-gHgCBA4byKELeCAt .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gHgCBA4byKELeCAt .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gHgCBA4byKELeCAt .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gHgCBA4byKELeCAt .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gHgCBA4byKELeCAt .actorPopupMenu{position:absolute;}#mermaid-svg-gHgCBA4byKELeCAt .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gHgCBA4byKELeCAt .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gHgCBA4byKELeCAt .actor-man circle,#mermaid-svg-gHgCBA4byKELeCAt line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gHgCBA4byKELeCAt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} candump vcan0 coctl vcan0 coctl.dcf init 0 显示虚拟 CAN 报文 running as master
使用 coctl 启动 CANopen master
先查找 DCF 文件:
bash
find ${LELY_PREFIX} -name coctl.dcf -o -name "*.dcf" -o -name "*.eds"
假设找到:
text
/home/imx8p/share/lely-master/etc/coctl.dcf
启动 coctl:
bash
export PATH=${LELY_PREFIX}/bin:${PATH}
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}
coctl vcan0 ${LELY_PREFIX}/etc/coctl.dcf
进入交互界面后执行:
text
init 0
期望看到类似:
text
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 安装路径不同。
bash
source ${SDK_ROOT}/environment-setup-armv8a-poky-linux
which aarch64-poky-linux-g++
仍找不到时:
bash
find ${SDK_ROOT} -name aarch64-poky-linux-g++ 2>/dev/null | head
pkg-config 找到 x86 主机库
现象是输出里出现:
text
/usr/lib/x86_64-linux-gnu
处理:
bash
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
板端运行提示缺动态库
先确认临时库路径:
bash
export LD_LIBRARY_PATH=${LELY_PREFIX}/lib:${LD_LIBRARY_PATH}
ldd ${BOARD_SHARE}/lely_link_smoke.elf || true
调试阶段建议继续使用临时 LD_LIBRARY_PATH。等部署方式稳定后,再考虑写入系统动态库配置。
ip link add dev vcan0 type vcan 失败
可能原因:
- 内核未启用
CONFIG_CAN_VCAN。 vcan模块未安装。iproute2不完整。- 当前登录用户没有足够权限。
排查:
bash
zcat /proc/config.gz 2>/dev/null | grep CONFIG_CAN_VCAN || true
lsmod | grep vcan || true
modprobe vcan || true
which ip
找不到 coctl.dcf
先找文件:
bash
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:
bash
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:
bash
coctl can0 ${LELY_PREFIX}/etc/coctl.dcf
真实 CAN 阶段还需要确认:
- i.MX8P 的 CAN 控制器和 pinmux 是否正确。
- CAN 收发器供电和 standby 引脚是否正确。
- 总线两端是否有 120 Ω 终端电阻。
- 所有节点 bitrate 是否一致。
- 从站 node-id、EDS/DCF、PDO mapping 和 heartbeat 配置是否一致。
参考资料
-
CAN in Automation, "CANopen," https://www.can-cia.org/can-knowledge/canopen ↩︎
-
CAN in Automation, "Technical documents," https://www.can-cia.org/cia-groups/technical-documents ↩︎
-
Lely CANopen, "Library overview," https://opensource.lely.com/canopen/docs/overview/ ↩︎ ↩︎
-
Lely CANopen, "Lely CANopen," https://opensource.lely.com/canopen/ ↩︎
-
Lely core libraries Doxygen, https://lely_industries.gitlab.io/lely-core/doxygen/ ↩︎
-
Lely CANopen, "Installation," https://opensource.lely.com/canopen/docs/installation/ ↩︎ ↩︎
-
Lely CANopen, "Build configuration," https://opensource.lely.com/canopen/docs/configuration/ ↩︎ ↩︎
-
Lely CANopen, "Command-line tutorial," https://opensource.lely.com/canopen/docs/cmd-tutorial/ ↩︎