1. CANopen协议概述:从协议栈到实战落地
各位同学,咱们今天正式开讲CANopen。说实话,我第一次接触CANopen是在一个工业伺服驱动的项目里。当时甲方要求必须支持CANopen从站,我翻着那本几百页的CiA 301标准,心里直犯嘀咕——这玩意儿也太复杂了吧?但真正用起来才发现,它的设计思路其实很清晰。
这一章,我会把CANopen的核心骨架给你拆开来讲。包括协议栈架构、对象字典、PDO和SDO怎么用、NMT状态机怎么跑。你不需要一次性全记住,但得有个整体印象。后面咱们在STM32上移植CANopenNode的时候,你会反复回来翻这一章的内容。
1.1 CANopen协议栈架构
CANopen的协议栈,说白了就是分层的。它不像TCP/IP那么复杂,但每一层都有明确的职责。
| 层级 | 名称 | 主要职责 |
|---|---|---|
| 应用层 | CiA 301 / 设备行规 | 定义对象字典、通信对象、设备行为 |
| 数据链路层 | CAN 2.0B | 帧封装、仲裁、错误检测 |
| 物理层 | ISO 11898-2 | 差分信号、总线电平、终端电阻 |
我个人习惯把CANopen协议栈想象成一个「三层小楼」:
一楼是物理层——就是那两根双绞线,CAN_H和CAN_L,加上120Ω终端电阻。这层咱们在STM32上基本不用操心,硬件电路画对就行。
二楼是数据链路层——CAN控制器(比如STM32的bxCAN或FDCAN)负责收发报文、处理仲裁和错误。这层咱们得配好波特率、滤波器。
三楼是应用层——这才是CANopen真正干活的地方。对象字典、PDO、SDO、NMT全在这一层。
关键认知:CANopen不是重新发明了一套物理层,它就是在标准CAN 2.0B上面跑的一套应用层协议。你只要会CAN收发,就能跑CANopen。
1.2 对象字典(OD)——CANopen的灵魂
对象字典是什么?我打个比方:它就像设备的「户口本」。每个设备能做什么、有什么参数、当前状态如何,全记在这个本子里。
对象字典是一个线性索引表,每个条目由16位索引(Index)和8位子索引(SubIndex)唯一标识。比如:
0x1000—— 设备类型0x1017—— 心跳生产者时间0x6000+NodeID—— 数字量输入(具体看行规)
每个对象条目包含以下属性:
| 属性 | 说明 | 举例 |
|---|---|---|
| Index | 16位索引地址 | 0x1017 |
| SubIndex | 8位子索引 | 0x00 |
| Name | 对象名称 | 心跳生产者时间 |
| Type | 数据类型 | UNSIGNED16 |
| Access | 访问权限 | rw / ro / wo |
| PDO Mapping | 是否可映射到PDO | Yes / No |
| Default Value | 默认值 | 0x0000 |
实战经验:我在做第一个CANopen从站时,对象字典配置错了索引范围,结果主站怎么都读不到设备类型。后来查了半天,发现是0x1000的访问权限写成了wo(只写)。记住:通信对象区(0x1000-0x1FFF)的配置一定要仔细核对CiA 301标准。
1.3 PDO与SDO——两种通信方式
CANopen里通信就两种路子:PDO(过程数据对象)和SDO(服务数据对象)。它们各有各的脾气。
PDO:快,但不管丢不丢
PDO是生产者-消费者模式。一个设备发,其他设备收,不需要应答。所以它快——一个PDO报文最多传8字节数据,适合传实时性要求高的数据,比如编码器位置、电机转速。
PDO分两种:
- TPDO(发送PDO):从站主动往外发数据
- RPDO(接收PDO):从站接收主站或其他设备的数据
触发方式有几种:
- 事件触发:数据变化了就发(比如位置变了1mm)
- 定时触发:每隔N毫秒发一次
- 远程帧请求:主站发远程帧来要数据
- SYNC同步触发:收到SYNC报文后统一发
注意:PDO没有应答机制。你发了,对方收没收到,你不管。所以对可靠性要求高的参数(比如配置参数),别用PDO,用SDO。
SDO:慢,但可靠
SDO是客户端-服务器模式。主站(客户端)发起请求,从站(服务器)回复应答。每个SDO事务需要2~4个CAN帧才能完成,所以慢,但保证数据到达。
SDO专门用来读写对象字典。比如:
- 主站想修改从站的
0x1017(心跳时间),就发SDO写请求 - 主站想读取从站的设备名称,就发SDO读请求
SDO的CAN ID有固定规则:
- 主站→从站(请求):
0x600 + NodeID - 从站→主站(应答):
0x580 + NodeID
举个例子,节点ID=5的从站:
- 主站发请求:CAN ID = 0x605
- 从站回复:CAN ID = 0x585
| 特性 | PDO | SDO |
|---|---|---|
| 通信模式 | 生产者-消费者 | 客户端-服务器 |
| 应答机制 | 无 | 有(确认/错误响应) |
| 数据长度 | 最多8字节 | 最多4字节(分段可更多) |
| 传输速度 | 快 | 慢 |
| 典型用途 | 实时过程数据 | 配置参数、诊断 |
1.4 NMT状态机——设备的生命周期
每个CANopen从站都有一个状态机,叫NMT(网络管理)状态机。它定义了设备从上电到正常工作的完整流程。
状态机有这几个状态:
- 初始化(Initialisation):上电后自动进入,硬件自检、加载默认参数
- 预操作(Pre-Operational):SDO可用,PDO不可用。可以配置参数
- 操作(Operational):SDO和PDO都可用。正常工作模式
- 停止(Stopped):SDO和PDO都不可用。只有NMT命令能唤醒
状态切换由主站发NMT命令控制。NMT命令的CAN ID固定为0x000,数据字节包含命令码和目标节点ID。
常用的NMT命令:
| 命令码 | 功能 | 目标状态 |
|---|---|---|
| 0x01 | 启动远程节点 | Operational |
| 0x02 | 停止远程节点 | Stopped |
| 0x80 | 进入预操作 | Pre-Operational |
| 0x81 | 复位节点 | Initialisation |
| 0x82 | 复位通信 | Initialisation |
避坑指南:我曾经在一个项目里,从站上电后直接进了Operational状态,没经过Pre-Operational。结果主站还没来得及配置参数,从站就开始发PDO了,数据全是默认值,导致执行器乱动。后来我强制在初始化后进入Pre-Operational,等主站配置完再发NMT启动命令。记住:上电后一定要先进Pre-Operational,别急着跑。
1.5 知识结构总览
下面这张图把CANopen的核心关系画出来了。你盯着看两分钟,后面学起来会顺很多。
这张图里,你重点看三个东西:
- 对象字典是核心——NMT、PDO、SDO都在操作它
- NMT状态机控制设备生命周期——上电→预操作→操作,别跳步
- PDO和SDO分工明确——实时数据走PDO,配置数据走SDO
我的建议:刚开始学CANopen,别急着看代码。先把对象字典的结构搞清楚,把NMT状态机的转换条件背下来。这两样搞明白了,后面移植CANopenNode就是体力活。