本实现基于创芯科技CAN卡底层库(CanLibraryClass)构建了一个完整的CANopen从站,支持:
CanTest/
├── CanOpenManager.cs # CANopen协议管理器(主站功能)
├── CanOpenSlave.cs # CANopen从站核心类 ⭐
├── ObjectDictionary.cs # 对象字典管理器 ⭐
├── EdsFileGenerator.cs # EDS文件生成器 ⭐
└── Program.cs # 使用示例
using CCDCount.DLL.CanBus;
// 创建节点ID为5的从站
byte nodeId = 5;
CanOpenSlave slave = new CanOpenSlave(
nodeId: 5, // 节点ID (1-127)
deviceType: 4, // USBCAN2
deviceIndex: 0, // 设备索引
canIndex: 0 // CAN通道
);
// 初始化(波特率500kbps)
if (!slave.Initialize(CanBaudRate.BaudRate_500K))
{
Console.WriteLine("初始化失败");
return;
}
// 配置PDO
slave.ConfigureTpdo(1, enabled: true, transmissionType: 0xFF); // TPDO1事件驱动
slave.ConfigureRpdo(1, enabled: true); // RPDO1启用
// 设置心跳时间(1000ms)
slave.SetHeartbeatTime(1000);
// 启动从站
slave.Start();
// 写入对象字典
slave.SetObjectValue(0x6000, 0x01, (byte)0xAA); // 数字输入
slave.SetObjectValue(0x6400, 0x01, (ushort)1234); // 模拟输入
// 读取对象字典
var value = slave.GetObjectValue(0x6200, 0x01); // 读取数字输出
// 发送TPDO1(自动从对象字典打包数据)
slave.SendTpdo(1);
// 生成EDS配置文件(供PLC导入)
string edsPath = "CanOpenSlave_Node5.eds";
slave.GenerateEdsFile(edsPath);
slave.Stop();
slave.Dispose();
参见 Program.cs 中的 RunCanOpenSlave() 方法。
| 索引 | 名称 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 0x1000 | Device Type | UINT32 | RO | 设备类型 |
| 0x1001 | Error Register | UINT8 | RO | 错误寄存器 |
| 0x1008 | Device Name | STRING | RO | 设备名称 |
| 0x1017 | Heartbeat Time | UINT16 | RW | 心跳时间(ms) |
| 0x1018 | Identity | RECORD | RO | 身份对象 |
| 索引范围 | 名称 | 说明 |
|---|---|---|
| 0x1400-0x1403 | RPDO1-4 Communication | RPDO通信参数 |
| 0x1800-0x1803 | TPDO1-4 Communication | TPDO通信参数 |
| 索引范围 | 名称 | 说明 |
|---|---|---|
| 0x1600-0x1603 | RPDO1-4 Mapping | RPDO映射参数 |
| 0x1A00-0x1A03 | TPDO1-4 Mapping | TPDO映射参数 |
| 索引 | 名称 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 0x6000 | Digital Input | UINT8[8] | RO | 数字输入(8位) |
| 0x6200 | Digital Output | UINT8[8] | RW | 数字输出(8位) |
| 0x6400 | Analog Input | UINT16[4] | RO | 模拟输入(4通道) |
| 0x6410 | Analog Output | UINT16[2] | RW | 模拟输出(2通道) |
slave.GenerateEdsFile("MyDevice.eds");
以西门子S7-1200/1500为例:
.eds 文件在PLC中配置PDO映射时,需要指定:
TPDO映射示例(从站到PLC):
TPDO1 映射:
- 0x6000:01 (Digital Input Bit 0) - 8位
- 0x6400:01 (Analog Input Ch1) - 16位
RPDO映射示例(PLC到从站):
RPDO1 映射:
- 0x6200:01 (Digital Output Bit 0) - 8位
- 0x6410:01 (Analog Output Ch1) - 16位
Initializing ──→ PreOperational ──→ Operational
↑ ↑ │
│ │ │
└──── Reset ─────┘ │
↓
Stopped
| 命令码 | 功能 | 说明 |
|---|---|---|
| 0x01 | Start Node | 预操作/停止 → 运行 |
| 0x02 | Stop Node | 运行 → 停止 |
| 0x80 | Enter PreOperational | 任何状态 → 预操作 |
| 0x81 | Reset Node | 任何状态 → 初始化 → 预操作 |
| 0x82 | Reset Communication | 任何状态 → 预操作 |
检查清单:
排查步骤:
ConfigureTpdo(1, true)// 添加自定义对象
slave.SetObjectValue(0x2000, 0x00, (byte)0x12);
// 注意:需要在EDS生成前添加,否则EDS中不会包含
PLC会监测心跳报文,如果超时:
SetHeartbeatTime() 设置值| 功能 | COB-ID计算公式 | 示例(节点5) |
|---|---|---|
| NMT | 0x000 | 0x000 |
| SYNC | 0x080 | 0x080 |
| EMCY | 0x080 + NodeID | 0x085 |
| SDO请求 | 0x600 + NodeID | 0x605 |
| SDO响应 | 0x580 + NodeID | 0x585 |
| TPDO1 | 0x180 + NodeID | 0x185 |
| TPDO2 | 0x280 + NodeID | 0x285 |
| RPDO1 | 0x200 + NodeID | 0x205 |
| RPDO2 | 0x300 + NodeID | 0x305 |
| Heartbeat | 0x700 + NodeID | 0x705 |
| 值 | 含义 |
|---|---|
| 0 | SYNC同步触发(每个SYNC发送) |
| 1-240 | SYNC同步触发(每N个SYNC发送) |
| 252 | RTR远程请求触发 |
| 253 | 事件驱动+定时器 |
| 254 | 事件驱动 |
| 255 | 制造商特定 |
controlcan.dll)本项目仅供学习和测试使用。
如有问题,请查阅: