# CANopen从站设备使用说明 ## 概述 本项目实现了一个基于Nameless.eds文件的CANopen从站设备,支持以下功能: - ✅ 4个接收PDO (RPDO) - COB-ID: 0x200+nodeId ~ 0x500+nodeId - ✅ 4个发送PDO (TPDO) - COB-ID: 0x180+nodeId ~ 0x480+nodeId - ✅ SDO通信 (读/写对象字典) - ✅ NMT网络管理 (启动/停止/预操作/重置) - ✅ 心跳机制 (可配置) - ✅ SYNC同步支持 ## 硬件要求 - 创芯科技USBCAN2适配器 - controlcan.dll动态库 - CAN总线连接 ## 软件架构 ### 核心类 1. **CanOpenSlaveDevice** - 从站设备主类 - 位置: `CanOpenSlaveDevice.cs` - 功能: 实现完整的CANopen从站协议栈 2. **CanOpenManager** - CAN底层管理器 - 位置: `CanOpenManager.cs` - 功能: 封装CAN帧的发送和接收 3. **CanLibraryClass** - CAN卡驱动接口 - 位置: `CanLibraryClass.cs` - 功能: P/Invoke调用controlcan.dll ### 数据结构 - `NmtState` - NMT状态枚举 - `PdoConfig` - PDO配置 - `PdoMapping` - PDO映射 - `IdentityObject` - 身份对象(1018h) - `TpdoCommParam` - TPDO通信参数 - `RpdoCommParam` - RPDO通信参数 ## 快速开始 ### 1. 基本使用 ```csharp using CCDCount.DLL.CanBus; // 创建从站设备 (节点ID=1) var slave = new CanOpenSlaveDevice(nodeId: 1); // 注册事件 slave.OnRpdoReceived += (nodeId, pdoNum, data) => { Console.WriteLine($"收到RPDO{pdoNum}"); }; slave.OnNmtStateChanged += (oldState, newState) => { Console.WriteLine($"状态改变: {oldState} -> {newState}"); }; // 启动从站 (1Mbps波特率) slave.Start(CanBaudRate.BaudRate_1M); // 配置心跳 (100ms) slave.ConfigureHeartbeat(100); // 发送TPDO slave.SendTpdo(1); // 停止从站 slave.Stop(); slave.Dispose(); ``` ### 2. 运行测试程序 ```bash # 编译项目 msbuild CanOpenSlaveTest.sln # 运行程序 cd bin\Debug CanOpenSlaveTest.exe ``` ## EDS文件配置说明 根据Nameless.eds文件,从站设备配置如下: ### 设备信息 - 厂商: HanLin (Vendor ID: 0x0000000D) - 产品: CCDCountCanOpenSlave (Product Code: 0x00000001) - 版本: 0x00010001 - 支持波特率: 1Mbps ### PDO配置 #### RPDO (接收PDO) | PDO编号 | 索引范围 | COB-ID公式 | 默认传输类型 | |---------|----------|------------|--------------| | RPDO1 | 0x1400/0x1600 | 0x200 + nodeId | 0xFF (事件驱动) | | RPDO2 | 0x1401/0x1601 | 0x300 + nodeId | 0xFF (事件驱动) | | RPDO3 | 0x1402/0x1602 | 0x400 + nodeId | 0xFF (事件驱动) | | RPDO4 | 0x1403/0x1603 | 0x500 + nodeId | 0xFF (事件驱动) | #### TPDO (发送PDO) | PDO编号 | 索引范围 | COB-ID公式 | 默认传输类型 | |---------|----------|------------|--------------| | TPDO1 | 0x1800/0x1A00 | 0x180 + nodeId | 0xFF (事件驱动) | | TPDO2 | 0x1801/0x1A01 | 0x280 + nodeId | 0xFF (事件驱动) | | TPDO3 | 0x1802/0x1A02 | 0x380 + nodeId | 0xFF (事件驱动) | | TPDO4 | 0x1803/0x1A03 | 0x480 + nodeId | 0xFF (事件驱动) | ### 对象字典 #### 必选对象 - **0x1000** - Device type (RO, 4字节) - **0x1001** - Error register (RO, 1字节) - **0x1018** - Identity object (RO, 复合对象) - sub0: Number of entries (3) - sub1: Vendor ID (0x0000000D) - sub2: Product code (0x00000001) - sub3: Revision number (0x00010001) #### 可选对象 - **0x1400-0x1403** - RPDO communication parameters - **0x1600-0x1603** - RPDO mapping parameters - **0x1800-0x1803** - TPDO communication parameters - **0x1A00-0x1A03** - TPDO mapping parameters ## API参考 ### CanOpenSlaveDevice类 #### 构造函数 ```csharp public CanOpenSlaveDevice( byte nodeId = 1, // 节点ID (1-127) UInt32 deviceType = 4, // 设备类型 (4=USBCAN2) UInt32 deviceIndex = 0, // 设备索引 UInt32 canIndex = 0 // CAN通道索引 ) ``` #### 方法 **Start()** - 启动从站 ```csharp public bool Start(CanBaudRate baudRate = CanBaudRate.BaudRate_1M) ``` **Stop()** - 停止从站 ```csharp public void Stop() ``` **SendTpdo()** - 发送TPDO ```csharp public void SendTpdo(byte pdoNumber) // pdoNumber: 1-4 ``` **SendAllTpdos()** - 发送所有启用的TPDO ```csharp public void SendAllTpdos() ``` **ConfigureHeartbeat()** - 配置心跳 ```csharp public void ConfigureHeartbeat(ushort heartbeatTimeMs) // 0表示禁用 ``` #### 事件 **OnRpdoReceived** - RPDO接收事件 ```csharp public event Action OnRpdoReceived // 参数: nodeId, pdoNumber, data ``` **OnNmtStateChanged** - NMT状态改变事件 ```csharp public event Action OnNmtStateChanged // 参数: oldState, newState ``` **OnSdoReadRequest** - SDO读取请求事件 ```csharp public event Action OnSdoReadRequest // 参数: nodeId, index, subIndex ``` **OnSdoWriteRequest** - SDO写入请求事件 ```csharp public event Action OnSdoWriteRequest // 参数: nodeId, index, subIndex, value ``` #### 属性 ```csharp public byte NodeId { get; } // 节点ID public NmtState CurrentState { get; } // 当前NMT状态 public bool IsRunning { get; } // 是否正在运行 ``` ## NMT状态机 ``` Initializing (0x00) ↓ [NMT Reset] PreOperational (0x7F) ←→ Operational (0x05) ↑ ↓ [NMT Stop] └── Stopped (0x04) ``` ### 状态说明 - **Initializing**: 初始化状态,设备上电后进入此状态 - **PreOperational**: 预操作状态,只能进行SDO配置 - **Operational**: 运行状态,可以进行PDO通信 - **Stopped**: 停止状态,暂停所有通信 ## 常见问题 ### Q1: 如何修改节点ID? A: 在构造函数中指定: ```csharp var slave = new CanOpenSlaveDevice(nodeId: 5); // 节点ID=5 ``` ### Q2: 如何更改波特率? A: 在Start()方法中指定: ```csharp slave.Start(CanBaudRate.BaudRate_500K); // 500kbps ``` ### Q3: 如何处理接收到的RPDO数据? A: 注册OnRpdoReceived事件: ```csharp slave.OnRpdoReceived += (nodeId, pdoNum, data) => { // 处理data数组 int value = data[0] | (data[1] << 8); }; ``` ### Q4: 如何自定义TPDO数据? A: 需要扩展CanOpenSlaveDevice类,添加自定义数据填充逻辑。 ### Q5: 心跳时间如何设置? A: 使用ConfigureHeartbeat()方法: ```csharp slave.ConfigureHeartbeat(100); // 100ms心跳 slave.ConfigureHeartbeat(0); // 禁用心跳 ``` ## 调试技巧 ### 1. 启用日志输出 程序会自动输出关键事件到控制台: - CAN帧收发 - NMT状态变化 - SDO读写 - PDO数据传输 ### 2. 使用CAN分析仪 推荐使用以下工具监控CAN总线: - CANalyzer - PCAN-View - USB-CAN Tool ### 3. 检查COB-ID 确保主站和从站的COB-ID配置一致: - RPDO: 0x200+nodeId ~ 0x500+nodeId - TPDO: 0x180+nodeId ~ 0x480+nodeId - SDO: 0x600+nodeId (请求), 0x580+nodeId (响应) - NMT: 0x000 - Heartbeat: 0x700+nodeId ## 扩展开发 ### 添加自定义对象字典条目 在`InitializeObjectDictionary()`方法中添加: ```csharp // 例如: 添加0x2000自定义对象 m_objectDictionary[0x2000] = new MyCustomObject(); ``` ### 实现PDO映射 目前PDO映射功能为框架级别,需要根据实际应用完善: ```csharp // 在ProcessRpdo()中解析映射表 // 在SendTpdo()中根据映射表构建数据 ``` ### 添加紧急消息(EMCY)支持 可以扩展EMCY处理: ```csharp public void SendEmergency(ushort errorCode, byte errorRegister) { byte[] emcyData = new byte[8]; emcyData[0] = (byte)(errorCode & 0xFF); emcyData[1] = (byte)((errorCode >> 8) & 0xFF); emcyData[2] = errorRegister; uint emcyCobId = 0x080 + m_nodeId; m_canManager.SendCanFrame(emcyCobId, emcyData); } ``` ## 技术支持 如有问题,请参考: - CiA DS301标准文档 - Nameless.eds文件详细说明 - 创芯科技CAN卡开发手册 ## 版本历史 - v1.0 (2026-05-09) - 初始版本 - 基于Nameless.eds实现 - 支持基本CANopen从站功能