1. CANopenNode概述与环境搭建

各位同学好,我是老周。做嵌入式这些年,从CAN到CANopen,从自己手撸协议栈到用开源框架,踩过的坑不少。今天咱们开始第一讲——先把CANopenNode这个框架搞清楚,再把环境搭起来。

说实话,CANopen协议在工业现场的地位,就像I²C在板级通信里一样——不是最快的,但绝对是最稳的。我最早接触CANopen是在一个伺服驱动器项目上,当时被对象字典和PDO/SDO的映射关系绕得晕头转向。后来用了CANopenNode,才真正理解了这个协议的精髓。

本章核心目标:

  • 理解CANopen协议的分层结构与核心机制
  • 掌握CANopenNode项目的源码组织方式
  • 在Linux和Windows上搭建可编译的开发环境

1.1 CANopen协议简介——不只是CAN的高层协议

很多人觉得CANopen就是CAN总线加了个应用层,这么说也对,但太粗糙了。CANopen真正厉害的地方,在于它定义了一套标准化的设备描述方法和通信机制。

来看这张协议栈结构图,你就明白了:

CANopen协议栈分层结构 应用层(Application Layer) 用户程序、设备控制逻辑、传感器/执行器接口 对象字典(Object Dictionary) 索引0x1000~0x9FFF:设备类型、通信参数、制造商特定参数 通信层(Communication Layer) PDO(过程数据对象) | SDO(服务数据对象) | NMT(网络管理) CAN数据链路层(Data Link Layer) CAN 2.0B帧格式:11位/29位标识符、数据段0~8字节 物理层(Physical Layer) CANopenNode 主要工作在这两层

从图上可以清楚看到,CANopenNode主要工作在对象字典和通信层。底层CAN驱动它不管,但给你留了接口。

1.2 核心通信机制速览

CANopen定义了四种通信对象,我列个表,你一看就明白:

通信对象 缩写 功能描述 典型用途
过程数据对象 PDO 无确认的广播通信,1个CAN帧传完 实时控制数据(速度、位置、温度)
服务数据对象 SDO 有确认的点对点通信,可传多帧 参数配置、诊断数据读写
网络管理 NMT 节点状态控制(启动、停止、预操作) 设备上电初始化、故障恢复
特殊协议 SYNC/EMCY 同步信号/紧急事件上报 多轴同步、故障报警

我的经验:刚开始学CANopen,别一上来就啃协议文档。先搞懂对象字典和SDO怎么用,这两个是调试的命脉。PDO虽然效率高,但配置复杂,我建议先放一放。

1.3 CANopenNode项目结构——开源框架的骨架

CANopenNode是GitHub上最活跃的CANopen开源实现之一。我选择它,原因有三:

  • 代码干净——C语言编写,模块化设计,移植性好
  • 文档齐全——每个API都有注释,示例工程完整
  • 社区活跃——Issue回复快,PR合并及时

来看看它的目录结构,我挑重点说:

CANopenNode/
├── CANopen.h              # 主头文件,包含所有公共API
├── CANopen.c              # 主实现文件,初始化与主循环
├── CO_driver.h            # 硬件抽象层接口(你需要移植的部分)
├── CO_driver.c            # 硬件抽象层默认实现
├── CO_SDO.h / .c          # SDO协议实现
├── CO_PDO.h / .c          # PDO协议实现
├── CO_NMT.h / .c          # NMT网络管理
├── CO_OD.h / .c           # 对象字典接口
├── CO_Emergency.h / .c    # 紧急事件处理
├── CO_SYNC.h / .c         # 同步对象
├── CO_timer.h / .c        # 软件定时器
├── 301/                   # CiA 301标准实现
├── 302/                   # CiA 302附加功能
├── 305/                   # CiA 305网关功能
├── example/               # 示例工程(Linux SocketCAN、STM32等)
└── stack/                 # 对象字典编辑器生成的代码

你注意看CO_driver.h——这个文件是你移植的关键。我当年在STM32上移植时,就只改了这一个文件里的5个函数:CAN发送、CAN接收、定时器、中断使能、中断屏蔽。

1.4 源码获取——别下错版本

CANopenNode目前有两个主要分支:

分支 特点 适用场景
master(v4.x) 最新架构,支持多实例,代码重构过 新项目首选
v3.x 经典版本,资料多,但不再维护 维护老项目

我个人建议新项目直接用master分支。获取方式很简单:

# 克隆最新代码
git clone https://github.com/CANopenNode/CANopenNode.git

# 进入目录
cd CANopenNode

# 查看分支
git branch -a

# 如果需要稳定版,可以切到v4.0标签
git checkout tags/v4.0

注意:千万不要直接从GitHub页面下载ZIP包!那样会丢失git历史,后续更新很麻烦。我曾经吃过这个亏,后来每次都用git clone。

1.5 编译环境搭建——Linux篇

Linux下搭建最省心,因为CANopenNode原生支持SocketCAN。我以Ubuntu 22.04为例:

1.5.1 安装依赖

sudo apt update
sudo apt install build-essential cmake git
sudo apt install libsocketcan-dev    # SocketCAN开发库
sudo apt install can-utils           # CAN调试工具(cansend、candump等)

1.5.2 编译示例工程

cd CANopenNode
mkdir build && cd build
cmake ..
make -j$(nproc)

编译完成后,在build/example目录下会生成可执行文件。我习惯先跑一下CANopenSocket这个示例,验证环境是否正常。

1.5.3 配置虚拟CAN接口(没有硬件也能调试)

# 加载虚拟CAN驱动
sudo modprobe can
sudo modprobe vcan

# 创建虚拟CAN接口
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

# 验证接口状态
ip link show vcan0

小技巧:把上面几条命令写成一个脚本setup_vcan.sh,每次开机执行一次就行。我电脑上就挂着这个脚本,随时可以调试。

1.6 编译环境搭建——Windows篇

Windows下稍微麻烦点,但也不是不行。我推荐用MSYS2或者WSL。

方案一:MSYS2(轻量级)

# 安装MSYS2后,打开MSYS2 UCRT64终端
pacman -Syu
pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-cmake
pacman -S mingw-w64-ucrt-x86_64-make
pacman -S git

# 然后进入CANopenNode目录编译
mkdir build && cd build
cmake .. -G "MSYS Makefiles"
make

方案二:WSL(功能完整)

如果你用Windows 10/11,直接装WSL2,然后按Linux的步骤来。WSL2支持SocketCAN,还能通过USB/IP转发物理CAN设备。我个人更推荐这个方案。

方案 优点 缺点
MSYS2 启动快,与Windows文件系统无缝集成 不支持SocketCAN,调试需额外工具
WSL2 完整Linux内核,支持SocketCAN 占用内存稍大,跨文件系统性能差

我的建议:如果你只是做应用层开发,MSYS2够用。如果你要调试CAN硬件通信,老老实实用WSL2或者直接上Linux。

1.7 验证环境——跑通第一个示例

环境搭好了,我们来验证一下。以Linux为例:

# 启动虚拟CAN接口
sudo ip link set up vcan0

# 运行CANopenNode示例(从站模式)
cd build/example
./CANopenSocket vcan0 1

# 新开一个终端,用candump监控CAN总线
candump vcan0

# 再开一个终端,发送NMT启动命令
cansend vcan0 000#0101

如果一切正常,你会看到从站节点(节点ID=1)开始发送心跳报文。嗯,到这里,你的CANopenNode环境就搭好了。

常见坑:如果运行示例时提示Cannot open CAN socket,八成是权限问题。用sudo运行,或者把你的用户加入dialout组。

1.8 本章小结

这一章我们干了三件事:

  • 理清了CANopen协议的分层结构和四种通信对象
  • 分析了CANopenNode的源码骨架,知道哪些文件需要关注
  • 在Linux和Windows上搭好了编译环境,并跑通了第一个示例