Lely canopen:用 EDS 和 master.yml 生成 CANopen 主站 DCF
本文以 Lely CANopen 2.4.0、Ubuntu 20.04 和 CANopenNode 从站为例,演示如何在开发机上安装
dcf-tools,编写原生 Lelymaster.yml,并从 EDS 生成可供主站程序使用的master.dcf。
本文要解决的问题
在嵌入式 CANopen 项目中,经常会同时存在两套运行环境:
- 目标板运行交叉编译后的 Lely C/C++ 库;
- 开发机运行 Python 版
dcf-tools,用于检查 EDS/DCF 和生成主站配置。
这两部分相互配合,但不需要安装到同一个系统,也不要求使用同一种架构。目标板可以继续使用 --disable-python --disable-cython 构建 Lely;dcf-tools 只安装在 Ubuntu 开发机上即可。
完成本文后,你将得到:
1 | config/ |
示例环境
本文命令基于以下环境整理。路径、CAN 接口和 Node-ID 可按实际项目替换。
| 项目 | 示例值 |
|---|---|
| 开发机 | Ubuntu 20.04 |
| Lely 源码 | /home/embedsky/share/lely-imx8p/lely-core |
| Lely 版本 | 2.4.0 |
| 目标架构 | AArch64 / i.MX8P |
| CAN 接口 | can1 |
| CAN 位速率 | 1000 kbit/s |
| 从站 | CANopenNode,Node-ID 1 |
| 主站 | Node-ID 127 |
本文使用的示例 EDS 描述了一个 RT-Thread CANopenNode 设备,包含 4 个 RPDO、5 个 TPDO,并声明支持 LSS。后文将使用其中的默认 PDO 配置进行说明。
先理解 dcf-tools 在系统中的位置
Lely 工程中的核心组件可以分为两类:
1 | flowchart LR |
三个命令的职责如下:
| 命令 | 用途 |
|---|---|
dcfchk |
检查 EDS/DCF 的语法和对象字典一致性 |
dcfgen |
根据从站 EDS 和 master.yml 生成主站 DCF 及可选 concise DCF |
dcf2dev |
将 DCF 转换为设备相关源码或数据,具体用途取决于项目集成方式 |
关键点是:dcf-tools 是主机侧生成工具,不参与目标板运行,也不受目标板 ABI 约束。
第一步:固定 Lely 源码版本
不要在生成环境中无条件跟随 master 分支。dcf-tools 的 YAML 字段、模板和生成行为都可能随源码变化,因此应先记录当前 commit。
1 | export WORK_ROOT=/home/embedsky/share/lely-imx8p |
建议满足以下条件后再继续:
- 工作区没有未确认修改;
- 已保存完整 40 字符 commit SHA;
- 目标板 Lely C/C++ 库和开发机
dcf-tools尽量来自同一个 commit; - 自动化构建脚本不再直接执行
git pull。
保存版本信息:
1 | mkdir -p "$TESTER_ROOT/docs/baseline" |
这样做的目的不是增加流程,而是确保未来能够回答两个问题:
- 当前
master.dcf是由哪一版工具生成的? - 升级 Lely 后,生成结果为什么发生变化?
第二步:在开发机安装 dcf-tools
2.1 安装 Python 环境
1 | sudo apt update |
这里不需要安装 AArch64 交叉编译器,也不需要启用 Lely Cython bindings。
2.2 创建项目专用虚拟环境
不要把工具直接安装到系统 Python。项目专用虚拟环境可以避免不同 Lely 版本之间互相污染。
1 | mkdir -p "$TESTER_ROOT" |
确认当前 Python 和 pip 均来自虚拟环境:
1 | command -v python |
输出路径应位于:
1 | /home/embedsky/share/lely-imx8p/canopen-slave-tester/.venv-dcf-tools/bin/ |
2.3 从当前 Lely 源码安装
python/dcf-tools/setup.py 声明的主要依赖是 PyYAML 和 EmPy。对于已经验证过的工程,可以固定版本以提高复现性:
1 | python -m pip install \ |
然后从本地 Lely 源码安装,而不是从 PyPI 获取另一份版本:
1 | python -m pip install --no-deps "$LELY_SRC/python/dcf-tools" |
如果正在调试 dcf-tools 本身,可以改用 editable 模式:
1 | python -m pip uninstall -y dcf-tools |
正式生成环境更适合普通安装。editable 模式会让源码目录中的临时修改立即影响生成结果,不利于复现。
依赖版本应以实际 Lely commit 和项目验证结果为准。升级 Lely 或 Python 后,应重新执行本文后面的生成与语义检查。
2.4 验证安装来源
1 | command -v dcfchk |
检查以下项目:
- 三个命令都来自项目虚拟环境;
pip show的安装位置在项目虚拟环境内;dcf-tools版本与当前源码中的setup.py一致;dcfgen --help至少能看到-d、-r、-S和-v。
保存 Python 依赖基线:
1 | python -m pip freeze > "$TESTER_ROOT/docs/baseline/DCF_TOOL_VERSION.txt" |
第三步:准备 EDS、YAML 和输出目录
推荐目录如下:
1 | mkdir -p "$TESTER_ROOT/config/generated" |
1 | ${TESTER_ROOT}/config/ |
为什么要保留一份 EDS 兼容副本
工程中的原始 EDS 是设备描述和测试证据,不建议为了适配某个工具版本直接修改。更稳妥的做法是:
- 保留
project.eds原文件; - 复制为
generated/project.dcfgen.eds; - 只在兼容副本中修正当前
dcf-tools无法接受的字段; - 对原始 EDS 和兼容副本分别记录 SHA256。
示例工程曾遇到两类兼容问题:
DeviceInfo.NG_Slave不被当前工具识别;DeviceInfo.RevisionNumber与对象0x1018:03不一致。
可以先创建副本:
1 | cd "$TESTER_ROOT/config" |
随后只在 generated/project.dcfgen.eds 中处理兼容项,并保留变更说明。不要把工具兼容性修正伪装成设备固件真实配置的变化。
第四步:编写最小可控的 master.yml
本教程的目标是让测试程序显式控制 NMT、SDO 和 PDO,而不是让主站在启动时自动重配置从站。因此先使用一份“安全、最小、可观察”的配置。
1 | # Native Lely dcfgen 2.4.0 configuration. |
顶层结构必须使用原生 Lely 格式
Lely 2.4.0 原生 dcfgen 使用以下结构:
1 | options: |
除 options、master 和以点开头的模板 section 外,其他顶层 section 都会被当作从站。
不要直接使用某些 ROS 2 或 downstream 项目中的格式:
1 | # 不适用于本文所用的原生 dcfgen 2.4.0 |
原生工具可能把 defaults 和 nodes 误认为两个从站 section。
YAML 中最容易出错的类型
布尔值必须使用真正的 YAML boolean:
1 | boot: false |
不要写成字符串:
1 | boot: "false" |
当前源码会对配置值执行 Python bool() 转换,非空字符串即使内容是 "false",也会被判定为 true。
其他规则:
- 十六进制整数不要加引号;
- PDO 编号使用数字键;
cob_id: auto是特殊字符串;dcf是生成阶段读取的 EDS/DCF 路径;dcf_path是生成物在运行环境中的目录前缀,两者含义不同。
第五步:检查 EDS 并生成 DCF
5.1 从正确目录执行
原生 dcfgen 会直接打开 dcf 字段中的路径。相对路径通常以命令执行时的当前目录为基准,而不是自动以 YAML 文件所在目录为基准。
因此建议始终进入 config/ 后执行:
1 | cd "$TESTER_ROOT/config" |
5.2 清理旧生成物
1 | rm -f generated/master.dcf \ |
不要依赖旧 .bin 是否还在来判断本次是否生成。先清理再生成,结果更容易解释。
5.3 检查从站 EDS
1 | dcfchk -n 1 -p generated/project.dcfgen.eds |
这里的 -n 1 用于按 Node-ID 1 解析 $NODEID 表达式。检查失败时应先处理 EDS 一致性问题,不建议直接使用跳过错误的方式进入正式生成。
5.4 生成主站 DCF
1 | dcfgen \ |
常用参数:
| 参数 | 作用 |
|---|---|
-d generated |
指定输出目录 |
-r |
在主站 DCF 中生成 remote PDO 对象和参数映射 |
-v |
输出生成的 SDO 配置请求,便于检查隐式写操作 |
-S |
lint 失败时继续;正式生成不建议使用 |
5.5 检查主站 DCF
1 | dcfchk -n 127 generated/master.dcf |
生成命令成功只说明文件被写出,不代表主站行为符合测试目标。后面还需要进行语义检查。
第六步:理解生成结果
dcfgen 可能生成以下文件:
| 文件 | 是否必定生成 | 作用 |
|---|---|---|
master.dcf |
是 | Lely 主站运行时设备描述、NMT 和 PDO 配置 |
master.bin |
否 | 主站自身的 concise DCF 数据 |
<slave-name>.bin |
否 | 主站在 boot/configuration 阶段写入对应从站的 concise DCF |
是否生成 .bin 由配置内容决定。不要在脚本中假定它们始终存在。
对于显式测试型主站,第一版配置通常希望:
- 一定生成
master.dcf; - 尽量不生成
mcu_node_1.bin; - 即使存在从站
.bin,主站也不自动 boot/configure 从站。
如果生成了意外的从站 .bin,重点检查这些字段是否与 EDS 默认值不同:
heartbeat_producer;guard_time和life_time_factor;error_behavior;rpdo/tpdo;sdo;- TIME、SYNC 或 EMCY 相关覆盖项。
dcfgen -v 会打印对应的 SDO 写请求,是定位问题的首选手段。
第七步:检查主站是否会自动控制从站
7.1 检查 0x1F80 NMT 启动行为
当前示例配置为:
1 | start: false |
按照 Lely 2.4.0 的生成逻辑,0x1F80 应得到:
1 | 0x0000000D |
这表示主站不会在启动阶段自动进入 operational,也不会自动启动从站。测试程序可以显式发送 NMT 命令,并独立验证状态转换。
0x1F80 的主要生成规则如下:
| 配置 | 对 0x1F80 的影响 |
|---|---|
| 初始值 | bit 0 置 1 |
start_all_nodes: true |
bit 1 置 1 |
start: false |
bit 2 置 1 |
start_nodes: false |
bit 3 置 1 |
reset_all_nodes: true |
bit 4 置 1 |
stop_all_nodes: true |
bit 6 置 1 |
7.2 检查 0x1F81 从站 assignment
从站 section 的以下配置决定主站是否自动管理该节点:
1 | boot: false |
主要位定义:
| 条件 | assignment 位 |
|---|---|
| 节点存在 | bit 0 |
boot: true |
bit 2 |
mandatory: true |
bit 3 |
reset_communication: false |
bit 4 |
software_version != 0 |
bit 5 |
software_file 非空 |
bit 6 |
restore_configuration != 0 |
bit 7 |
retry_factor |
bits 8..15 |
guard_time |
bits 16..31 |
对于协议功能测试,建议先关闭自动 boot 和自动 reset communication。否则主站程序的后台行为可能改变从站状态,导致测试结果难以归因。
第八步:验证示例 EDS 中的默认 PDO
本文示例 EDS 的默认 PDO 可用于验证 -r 生成结果是否合理。
RPDO1
示例 EDS 中 RPDO1 的关键配置为:
1 | COB-ID:$NODEID + 0x200 |
对应的 YAML 覆盖写法如下,但在第一阶段不建议覆盖 EDS 默认配置:
1 | mcu_node_1: |
TPDO1
示例 EDS 中 TPDO1 的关键配置为:
1 | 线上的 CAN-ID:0x181 |
对应 YAML:
1 | mcu_node_1: |
第一阶段更推荐让 dcfgen 直接读取 EDS 默认 PDO,并通过 -r 在主站侧生成 remote PDO。这样可以验证设备原始配置,而不是先由主站改写设备再测试。
PDO 配置注意事项
- 只能引用 EDS 中已经存在的 PDO 编号;
- 映射对象必须存在于对象字典;
- 映射对象应声明支持 PDO mapping;
- 一个经典 CAN PDO 的总映射长度不能超过 64 bit;
inhibit_time单位为 100 μs;event_timer和event_deadline单位为 ms;-r是dcfgen命令行参数,不是 YAML 字段。
第九步:按需启用 heartbeat、SYNC 和 SDO 配置
最小配置通过后,再逐项启用自动化功能。每次只增加一类配置,并比较 verbose SDO 输出和生成物差异。
9.1 Heartbeat
主站监控从站 heartbeat:
1 | master: |
从站生产 heartbeat:
1 | mcu_node_1: |
如果 EDS 中 0x1017 默认值不是 1000,该配置通常会生成一条 SDO 写请求,并可能生成从站 .bin。
不要同时启用有效 heartbeat producer 和 node guarding。当前源码检测到以下组合时会发出警告并清除 guarding:
1 | guard_time != 0 |
9.2 SYNC
主站产生 10 ms 周期的 SYNC:
1 | master: |
sync_period 单位为 μs,对应对象 0x1006。启用前需要确认从站 PDO transmission type 与同步策略匹配。
9.3 显式 SDO 写入
可以在从站 section 中追加配置阶段 SDO:
1 | mcu_node_1: |
字段含义:
| 字段 | 是否必填 | 默认值 |
|---|---|---|
index |
是 | 无 |
sub_index |
否 | 0 |
value |
否 | 0 |
对于自动化测试框架,不建议把测试值固化在 master.yml。更清晰的做法是让测试用例运行时写入、验证并清理对象值,避免生成阶段的隐式配置掩盖协议行为。
master.yml 常用字段速查
options
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dcf_path |
string | "" |
运行时查找 .bin 的目录前缀 |
heartbeat_multiplier |
float | 3.0 |
heartbeat consumer timeout 倍数 |
retry_factor |
int | 3 |
NMT boot 重试因子 |
master
| 字段 | 说明 |
|---|---|
node_id |
主站 Node-ID |
baudrate |
位速率,单位 kbit/s |
vendor_id |
0x1018:01 |
product_code |
0x1018:02 |
revision_number |
0x1018:03 |
serial_number |
0x1018:04 |
sync_period |
0x1006,单位 μs |
sync_window |
0x1007,单位 μs |
sync_overflow |
0x1019 |
time_cob_id |
0x1012 |
emcy_inhibit_time |
0x1015,单位 100 μs |
heartbeat_consumer |
是否生成从站 heartbeat consumer 配置 |
heartbeat_producer |
主站 heartbeat 周期,单位 ms |
error_behavior |
0x1029 |
nmt_inhibit_time |
0x102A,单位 100 μs |
start |
主站是否自行进入 operational |
start_nodes |
是否启动从站 |
start_all_nodes |
是否广播启动所有节点 |
reset_all_nodes |
mandatory 节点异常时是否 reset all |
stop_all_nodes |
mandatory 节点异常时是否 stop all |
boot_time |
mandatory 从站 boot timeout,单位 ms |
从站 section
| 字段 | 说明 |
|---|---|
dcf |
生成阶段读取的 EDS/DCF 文件,必填 |
node_id |
从站 Node-ID |
dcf_path |
运行时 .bin 目录前缀 |
revision_number |
覆盖或校验 0x1018:03 |
serial_number |
覆盖或校验 0x1018:04 |
heartbeat_consumer |
从站是否监控主站 heartbeat |
heartbeat_producer |
从站 heartbeat 周期 |
retry_factor |
boot 重试因子 |
guard_time |
node guarding 周期 |
life_time_factor |
node guarding life time factor |
error_behavior |
从站 0x1029 |
rpdo / tpdo |
覆盖从站 PDO 并生成主站对应映射 |
boot |
是否由主站 boot/configure |
mandatory |
是否为 mandatory 节点 |
reset_communication |
boot 时是否允许 reset communication |
software_file |
firmware 文件 |
software_version |
期望软件版本 |
configuration_file |
concise DCF 文件路径 |
restore_configuration |
恢复配置策略 |
sdo |
配置阶段 SDO 写请求列表 |
当前原生 Lely 2.4.0 不支持某些 downstream 文档中常见的字段,例如:
1 | boot_timeout |
使用字段前应以当前 commit 中 python/dcf-tools/dcfgen/cli.py 为准。
生成后建立可复现基线
生成完成后记录哈希:
1 | cd "$TESTER_ROOT/config/generated" |
建议提交或归档以下内容:
1 | master.yml |
生成物比较不能只看文件是否变化,还应检查语义:
- 主站 Node-ID 是否为 127;
- 位速率是否为 1000 kbit/s;
0x1F80是否为预期值;- 从站 assignment 是否关闭自动 boot;
- remote PDO 是否存在;
dcfgen -v是否出现非预期 SDO 写请求。
常见问题排查
dcfgen: command not found
通常是虚拟环境未激活:
1 | source "$TESTER_ROOT/.venv-dcf-tools/bin/activate" |
调用了错误的系统版本或 PyPI 版本
1 | type -a dcfgen |
应确保命令路径和安装位置均指向项目虚拟环境。
找不到 EDS 文件
通常是从错误目录执行,导致相对路径解析失败:
1 | cd "$TESTER_ROOT/config" |
NG_Slave lint 警告
保留原始 EDS,仅在 project.dcfgen.eds 兼容副本中处理当前工具不识别的字段,并记录变更原因。
RevisionNumber 不一致
使兼容副本中的 DeviceInfo.RevisionNumber 与 0x1018:03 一致。原始 EDS 继续作为设备输入证据保留。
生成了意外的从站 .bin
重新执行:
1 | dcfgen -r -v -d generated master.yml |
根据 verbose 输出定位是 heartbeat、PDO、guarding、error behavior 还是 sdo 产生了写请求。
主站启动后自动改变从站状态
检查:
1 | master: |
同时检查生成后的 0x1F80 和 0x1F81,不要只检查 YAML 文本。
boot: "false" 仍然启用了 boot
原因是 quoted boolean 实际为字符串。改为:
1 | boot: false |
Lely 2.4.0 源码中的三个注意点
以下问题来自当前版本 dcfgen/cli.py 的实现细节。升级 Lely 后应重新核对,不应永久视为所有版本都存在。
1. configuration_file
当前源码读取 configuration_file 时,赋值目标与字段命名的直观含义不完全一致。建议:
- 第一阶段不要主动覆盖该字段;
- 优先使用自动生成的
<dcf_path>/<section-name>.bin; - 如必须自定义路径,应增加生成结果测试。
2. event_deadline
当前源码处理 RPDO event_deadline 时存在变量引用风险。仅配置该字段可能导致异常或错误编码。
在修复和增加针对 0x140x:05 的测试之前,不建议在正式配置中使用它。
3. time_cob_id
当前从站解析路径中出现过 time_cob_id 与 time_cobid 命名不一致。第一阶段可保留 EDS 中 0x1012 的默认值,不通过 YAML 覆盖从站 TIME COB-ID。
升级时可使用以下命令审计字段和模板变化:
1 | grep -nE 'if "[A-Za-z0-9_]+" in cfg' \ |
推荐的日常维护流程
EDS 或 master.yml 发生变化时:
1 | flowchart TD |
Lely commit 发生变化时:
1 | 重建或重装 dcf-tools 虚拟环境 |
完整执行命令汇总
下面给出一次完整生成流程,适合整理为项目脚本:
1 | set -eu |
实际项目中,EDS 兼容处理不应依赖人工编辑,建议固化为可审查的脚本,并对修改前后的 SHA256 和差异内容进行记录。
总结
使用 Lely dcf-tools 时,最重要的不是“生成出一个 master.dcf”,而是保证生成过程可复现、生成行为可解释:
- 固定 Lely commit 和 Python 依赖;
- 把主机侧
dcf-tools与目标板 C/C++ 库分开管理; - 保留原始 EDS,只对兼容副本做工具适配;
- 从最小、禁止自动 boot 的
master.yml开始; - 使用
dcfgen -v检查所有隐式 SDO 写入; - 检查
0x1F80、0x1F81和 remote PDO,而不是只看命令返回值; - 将版本、配置、生成物和 SHA256 一起纳入项目基线。
按这个流程建立基线后,再逐项启用 heartbeat、SYNC、PDO 重映射或 concise DCF,问题会更容易定位,也更适合自动化测试和持续集成。
参考资料
- Lely CANopen 安装文档:
https://opensource.lely.com/canopen/docs/installation/ - Lely dcf-tools 文档:
https://opensource.lely.com/canopen/docs/dcf-tools/ - Lely dcf-tools 源码:
https://gitlab.com/lely_industries/lely-core/-/tree/master/python/dcf-tools - 当前项目重点源码:
python/dcf-tools/setup.pypython/dcf-tools/dcfgen/cli.pypython/dcf-tools/dcfgen/data/master.dcf.em








