2、CANopen协议基础:CAN总线简介、CANopen协议分层、对象字典(OD)、PDO/SDO、NMT、心跳与节点守护等核心概念
CANopen是一种基于CAN(Controller Area Network)总线的应用层协议,广泛应用于工业自动化、运动控制、I/O从站、传感器和执行器网络。理解CANopen协议基础,是进行CANopenNode工业I/O从站开发的前提。本章将从CAN总线物理特性出发,逐步深入到CANopen的协议分层、对象字典(OD)、PDO/SDO通信机制、NMT网络管理以及心跳与节点守护机制,帮助读者建立完整的协议认知框架。
2.1 CAN总线简介:物理层与数据链路层核心特性
CAN总线由德国BOSCH公司于1986年开发,最初用于汽车电子系统,后经ISO 11898标准化,成为工业控制领域最广泛使用的现场总线之一。CAN总线采用差分信号传输,两条线分别为CAN_H和CAN_L,通过电压差表示显性位(逻辑0)和隐性位(逻辑1)。这种差分结构赋予CAN总线极强的抗电磁干扰能力,适合工业现场恶劣环境。
CAN总线的核心特性包括:多主竞争(任何节点在总线空闲时均可发起发送)、非破坏性仲裁(ID越小优先级越高)、错误检测与自动重发(CRC校验、位填充、应答机制)、短帧结构(数据段最多8字节)。这些特性决定了CANopen协议的设计思路——PDO利用短帧实现高效实时传输,SDO则通过多帧分段实现大数据量配置。
| 特性 | 说明 | 对CANopen的影响 |
|---|---|---|
| 帧类型 | 数据帧、远程帧、错误帧、过载帧 | PDO/SDO/NMT均使用数据帧;远程帧用于节点守护 |
| ID分配 | 11位标准帧 / 29位扩展帧 | CANopen使用11位ID,功能码+节点ID组合 |
| 波特率 | 10kbps ~ 1Mbps | 常用125kbps(设备级)/ 500kbps(运动控制) |
| 最大节点数 | 取决于收发器驱动能力,通常110个 | CANopen标准限制节点ID 1~127 |
| 错误处理 | 错误计数器,节点可进入Bus-off | CANopen需实现错误状态上报(通过心跳/守护) |
2.2 CANopen协议分层:从物理层到应用层的完整架构
CANopen协议遵循OSI参考模型,但仅实现其中部分层次。在CANopenNode的代码结构中,这种分层体现为:CAN驱动层(硬件抽象)、协议核心层(CO_*模块)、应用对象层(用户自定义OD条目)。理解分层有助于定位问题——通信故障排查时,先确认物理层链路,再检查协议层配置。
CANopen协议栈在CAN总线之上定义了四个核心通信对象:PDO(过程数据对象)、SDO(服务数据对象)、NMT(网络管理)、特殊协议(心跳、节点守护、同步SYNC、时间戳TIME)。这些对象通过CAN帧的11位ID进行区分,ID格式为:功能码(4位) + 节点ID(7位)。
功能码范围:
0x000~0x07F:SDO 发送(0x580+NodeID)/ 接收(0x600+NodeID)
0x080~0x0FF:紧急对象 EMCY(0x080+NodeID)
0x100~0x1FF:PDO1 发送(0x180+NodeID)/ 接收(0x200+NodeID)
0x200~0x2FF:PDO2 发送(0x280+NodeID)/ 接收(0x300+NodeID)
0x300~0x3FF:PDO3 发送(0x380+NodeID)/ 接收(0x400+NodeID)
0x400~0x4FF:PDO4 发送(0x480+NodeID)/ 接收(0x500+NodeID)
0x500~0x5FF:SDO 发送(0x580+NodeID)/ 接收(0x600+NodeID)
0x700~0x7FF:NMT 节点控制(0x000)/ 心跳/守护(0x700+NodeID)
以节点ID=3为例:PDO1发送使用0x183,PDO1接收使用0x203,SDO发送使用0x583,SDO接收使用0x603,心跳使用0x703。这种ID分配机制使得CANopen无需MAC地址,直接通过ID识别通信对象和节点身份。
2.3 对象字典(OD):CANopen的核心数据仓库
对象字典是CANopen协议的中枢神经系统,每个节点都维护一张OD表,通过16位索引(Index)和8位子索引(Subindex)唯一标识所有数据对象。OD条目分为三类:通信参数区(0x1000~0x1FFF)、制造商特定区(0x2000~0x5FFF)、标准设备区(0x6000~0x9FFF,如I/O模块的输入输出映射)。
在CANopenNode中,OD通过CO_OD.h和CO_OD.c文件定义,每个条目包含数据类型、访问权限、默认值、存储属性(RAM/EEPROM)等元信息。对于工业I/O从站,典型的OD条目包括:
- 0x1000:设备类型(如I/O模块标识为0x00000191)
- 0x1001:错误寄存器(位映射错误状态)
- 0x1005:SYNC COB ID(同步报文ID配置)
- 0x1016:心跳生产者时间(毫秒)
- 0x1017:心跳消费者时间(多个节点可配置)
- 0x1400~0x15FF:PDO接收参数(映射、类型、禁止时间)
- 0x1600~0x17FF:PDO接收映射(哪些OD条目打包进PDO)
- 0x1800~0x19FF:PDO发送参数
- 0x1A00~0x1BFF:PDO发送映射
- 0x2000~0x5FFF:应用对象(如数字输入状态、输出控制寄存器)
CO_OD.c文件(使用make或od_generator脚本)。手动修改OD数组极易导致索引错位,引发SDO读写异常。建议在开发初期就规划好OD布局,预留扩展空间。
2.4 PDO与SDO:两种截然不同的数据通道
PDO(过程数据对象)用于实时传输周期性或事件驱动的小数据量(1~8字节),采用生产者/消费者模式,无需应答。PDO传输类型包括:同步(SYNC触发)、事件触发(数据变化)、远程帧请求、定时触发。对于I/O从站,数字输入状态通常通过PDO1发送(每10ms或输入变化时),输出控制通过PDO1接收。
SDO(服务数据对象)用于配置参数、读写OD任意条目,采用客户端/服务器模式,每个请求必须有应答。SDO支持快速传输(数据≤4字节)和分段传输(大数据分多帧)。SDO通信开销大、延迟高,不适合实时控制,但可靠性高,适合初始化配置和诊断。
| 对比维度 | PDO | SDO |
|---|---|---|
| 通信模式 | 生产者/消费者(1对多) | 客户端/服务器(1对1) |
| 数据长度 | 最多8字节/帧 | 最多4字节/帧(快速),分段可传任意长度 |
| 应答机制 | 无应答 | 必须有应答(成功/失败) |
| 实时性 | 高(微秒级) | 低(毫秒级) |
| 典型用途 | I/O数据、控制字、状态字 | 参数配置、固件升级、诊断 |
| CANopenNode实现 | CO_PDO.c,支持4个发送+4个接收 |
CO_SDO.c,支持服务器模式 |
// PDO1发送映射(0x1A00):打包8路数字输入
子索引1:0x20000108 // OD 0x2000,子索引1,长度8位
子索引2:0x20000208 // OD 0x2000,子索引2,长度8位
// 实际发送时,将0x2000[1]和0x2000[2]的16位数据打包进1个PDO帧
// PDO1接收映射(0x1600):解包8路数字输出
子索引1:0x20100108 // OD 0x2010,子索引1,长度8位
子索引2:0x20100208 // OD 0x2010,子索引2,长度8位
2.5 NMT网络管理:节点状态机与主站控制
NMT(Network Management)定义了CANopen节点的状态机:初始化(Initialization)→ 预操作(Pre-operational)→ 运行(Operational)→ 停止(Stopped)。节点上电后自动进入初始化,完成OD加载后进入预操作状态。在预操作状态下,节点只能进行SDO通信(允许配置),不能进行PDO通信。主站发送NMT命令后,节点进入运行状态,开始PDO收发。停止状态下,节点仅响应NMT命令和心跳。
NMT命令由主站(节点ID=0)发送,CAN ID固定为0x000,数据段包含命令码和目标节点ID。常用命令:
- 0x01:启动节点(进入运行状态)
- 0x02:停止节点(进入停止状态)
- 0x80:进入预操作状态
- 0x81:复位节点(重新初始化)
- 0x82:复位通信(仅复位通信参数)
在CANopenNode中,NMT状态机由CO_NMT.c管理,应用层通过回调函数(如CO_NMT_reset_callback)响应状态变化。I/O从站开发时,需注意:PDO发送仅在运行状态激活,因此在预操作阶段调试PDO映射时,需先发送NMT启动命令。
2.6 心跳与节点守护:节点状态监控机制
CANopen提供两种节点监控机制:心跳(Heartbeat)和节点守护(Node Guarding)。心跳是主动上报机制,节点周期性地发送0x700+NodeID的报文,数据段为节点状态(0x00=启动中,0x04=停止,0x05=运行,0x7F=预操作)。心跳生产者时间由OD 0x1017配置,消费者时间由OD 0x1016配置(可配置多个消费者)。
节点守护是主站轮询机制,主站周期性地发送远程帧(RTR)到0x700+NodeID,从站收到后应答当前状态。节点守护的轮询时间由主站控制,从站需在OD 0x100C(守护时间)和0x100D(生命周期因子)中配置超时参数。心跳和节点守护不能同时启用,CANopen标准规定:如果配置了心跳生产者时间(0x1017非零),则节点守护自动禁用。
心跳消费者超时 = 生产者心跳时间 × 2 + 100ms(推荐)
例如:生产者心跳时间 = 100ms,则消费者超时 = 300ms
如果消费者在300ms内未收到心跳,判定节点故障
在CANopenNode中,心跳功能由CO_EM.c(错误管理)和CO_NMT.c协同实现。I/O从站开发时,建议将心跳时间设为100~500ms,既保证故障检测及时性,又不增加总线负载。对于安全关键应用,可配置多个心跳消费者(如主PLC和冗余监控器)。
2.7 案例拆解:8路数字输入I/O从站的CANopen配置
假设需要开发一个8路数字输入、8路数字输出的CANopen I/O从站,节点ID=5,波特率=500kbps。以下是完整的配置流程和关键参数:
初始现象与目标:设备上电后,主站无法通过PDO读取输入状态,但SDO通信正常。目标是通过系统化排查,使PDO通信正常运行。
关键参数配置:
// OD关键条目配置
0x1000 = 0x00000191 // 设备类型:I/O模块
0x1017 = 100 // 心跳生产者时间:100ms
0x1400[1] = 0x00000205 // PDO1接收COB ID:0x205(0x200+5)
0x1600[1] = 0x20100108 // PDO1接收映射:OD 0x2010子索引1,8位
0x1600[2] = 0x20100208 // PDO1接收映射:OD 0x2010子索引2,8位
0x1800[1] = 0x00000185 // PDO1发送COB ID:0x185(0x180+5)
0x1A00[1] = 0x20000108 // PDO1发送映射:OD 0x2000子索引1,8位
0x1A00[2] = 0x20000208 // PDO1发送映射:OD 0x2000子索引2,8位
0x2000[1] = 0x00 // 数字输入状态(只读)
0x2010[1] = 0x00 // 数字输出控制(读写)
分析判断过程:SDO通信正常说明物理层和协议栈核心正常。PDO无数据可能原因:①NMT状态未切换至运行;②PDO映射配置错误;③PDO传输类型配置为同步但SYNC未发送;④PDO禁止时间过长。
实际操作步骤:
- 使用总线分析仪发送NMT启动命令:CAN ID=0x000,数据=0x01 0x05(启动节点5)
- 检查节点心跳:应收到0x705,数据=0x05(运行状态)
- 读取OD 0x1800[2](PDO1发送类型),确认是否为255(事件触发)或1(同步)
- 如果为同步模式,主站需周期发送SYNC报文(CAN ID=0x080)
- 检查PDO映射:读取0x1A00[0]确认映射数量为2,读取0x1A00[1]确认映射正确
- 触发输入变化(如短接输入端子),观察PDO报文是否发送
结果对比:修正NMT状态后,PDO1发送报文(0x185)每100ms或输入变化时发出,数据段包含8路输入状态。主站发送PDO1接收报文(0x205)控制8路输出,从站立即响应。
风险与代价:如果PDO映射配置错误(如映射长度超过8位),从站会进入错误状态并停止PDO通信。此时需通过SDO读取0x1001(错误寄存器)和0x1003(预定义错误字段)定位问题。修改PDO映射后,必须复位通信(NMT命令0x82)才能生效。
复盘经验:CANopen调试遵循"从底层到上层"原则:先确认物理层(终端电阻、波特率),再检查数据链路层(CAN帧收发),最后排查应用层(NMT状态、OD配置)。建议在开发阶段使用candump(Linux)或逻辑分析仪记录所有CAN报文,便于回溯问题。
CO_PDO_init重新初始化PDO模块。如果OD映射条目在运行时被修改,必须确保修改期间没有PDO正在传输,否则可能导致数据错位。推荐在预操作状态下修改映射,然后复位通信。