CANopen主站使用指南.md 8.0 KB

CANopen主站使用指南

概述

本指南介绍如何使用 CanOpenMaster 类创建CANopen主站程序,管理多个从站节点,实现NMT控制、SDO读写、PDO通信等功能。

核心组件

1. CanOpenMaster - 主站管理器

主要功能:

  • 管理多个从站节点
  • NMT网络管理(启动/停止/重置节点)
  • SDO服务数据对象通信
  • PDO过程数据对象通信
  • SYNC同步对象
  • 心跳监测
  • 紧急消息处理

2. CanOpenNode - 从站节点信息

记录每个从站节点的状态和统计信息:

  • 节点ID和名称
  • 当前状态(初始化/预操作/运行/停止)
  • 心跳统计
  • SDO通信统计
  • TPDO数据统计
  • 紧急消息统计

快速开始

基本使用流程

using CCDCount.DLL.CanBus;

// 1. 创建主站
using (CanOpenMaster master = new CanOpenMaster(4, 0, 0))
{
    // 2. 注册事件回调
    master.OnTpdoReceived += Master_OnTpdoReceived;
    master.OnHeartbeatReceived += Master_OnHeartbeatReceived;
    master.OnEmergencyReceived += Master_OnEmergencyReceived;
    
    // 3. 初始化主站
    if (!master.Initialize(CanBaudRate.BaudRate_500K))
    {
        Console.WriteLine("初始化失败");
        return;
    }
    
    // 4. 添加从站节点
    master.AddNode(1, "IO_Module_1");
    master.AddNode(2, "Servo_Drive");
    
    // 5. 启动所有节点
    master.StartAllNodes();
    
    // 6. 启用SYNC(可选)
    master.EnableSync(10); // 10ms周期
    
    // 7. 主循环 - 处理数据
    while (running)
    {
        // PDO数据通过事件自动接收
        Thread.Sleep(10);
    }
    
    // 8. 清理资源
    master.DisableSync();
    master.StopAllNodes();
}

详细功能说明

1. 节点管理

添加节点

// 添加单个节点
master.AddNode(1, "IO_Module_1");

// 批量添加节点
byte[] nodeIds = { 1, 2, 3, 4, 5 };
master.AddNodes(nodeIds);

获取节点信息

var node = master.GetNode(1);
if (node != null)
{
    Console.WriteLine($"节点状态: {node.GetStateString()}");
    Console.WriteLine($"是否在线: {node.IsOnline(1000)}");
    Console.WriteLine($"心跳计数: {node.HeartbeatCount}");
}

2. NMT网络管理

控制节点状态

// 启动节点
master.NmtStartNode(1);

// 停止节点
master.NmtStopNode(1);

// 进入预操作状态
master.NmtEnterPreOperational(1);

// 重置节点
master.NmtResetNode(1);

// 重置通信
master.NmtResetCommunication(1);

// 批量控制
master.StartAllNodes();
master.StopAllNodes();

3. SDO通信

读取数据

// 快速读取(不等待响应)
master.SdoReadQuick(1, 0x1000, 0x00);

// 读取并等待响应
byte[] response = master.SdoReadAndWait(1, 0x1000, 0x00, 1000);
if (response != null && response.Length >= 5)
{
    uint value = BitConverter.ToUInt32(response, 4);
    Console.WriteLine($"读取值: 0x{value:X8}");
}

写入数据

// 快速写入(不等待确认)
master.SdoWriteQuick(1, 0x6200, 0x01, 0x00000001);

// 写入并等待确认
bool success = master.SdoWriteAndWait(1, 0x6200, 0x01, 0x00000001, 1000);
if (success)
{
    Console.WriteLine("写入成功");
}

读取设备信息

// 读取设备类型
uint deviceType = master.ReadDeviceType(1);

// 读取厂商ID
uint vendorId = master.ReadVendorId(1);

// 读取产品代码
uint productCode = master.ReadProductCode(1);

// 读取序列号
uint serialNumber = master.ReadSerialNumber(1);

4. PDO通信

SYNC同步

// 发送单次SYNC
master.SendSync();

// 发送带计数器的SYNC
master.SendSync(1);

// 启用周期性SYNC
master.EnableSync(10); // 10ms周期

// 禁用SYNC
master.DisableSync();

配置TPDO

// 配置TPDO传输类型
// transmissionType: 
//   0 = 禁止
//   1 = 同步(周期)
//   254 = 远程请求
//   255 = 事件驱动
master.ConfigureTpdoTransmissionType(1, 1, 0xFF);

// 触发节点发送TPDO
master.TriggerTpdo(1, 1);

接收TPDO数据

TPDO数据通过事件自动接收:

private static void Master_OnTpdoReceived(byte nodeId, byte[] data)
{
    string dataStr = BitConverter.ToString(data).Replace("-", " ");
    Console.WriteLine($"节点{nodeId} TPDO数据: [{dataStr}]");
    
    // 解析数据
    if (data.Length >= 2)
    {
        ushort value = BitConverter.ToUInt16(data, 0);
        Console.WriteLine($"解析值: {value}");
    }
}

5. 心跳监测

心跳通过事件自动接收:

private static void Master_OnHeartbeatReceived(byte nodeId, byte status)
{
    string statusStr = GetNodeStatusString(status);
    Console.WriteLine($"节点{nodeId}: {statusStr}");
    
    // 检查节点是否在线
    var node = master.GetNode(nodeId);
    if (node != null && node.IsOnline(1000))
    {
        Console.WriteLine("节点在线");
    }
}

6. 紧急消息处理

紧急消息通过事件自动接收:

private static void Master_OnEmergencyReceived(byte nodeId, ushort errorCode)
{
    Console.WriteLine($"节点{nodeId} 紧急错误: 0x{errorCode:X4}");
    
    // 根据错误代码采取相应措施
    switch (errorCode)
    {
        case 0x0000:
            Console.WriteLine("错误恢复");
            break;
        case 0x1000:
            Console.WriteLine("通用错误");
            break;
        case 0x2000:
            Console.WriteLine("电流错误");
            break;
        // ... 更多错误代码
    }
}

完整示例

参考 Program.cs 中的 RunCanOpenMaster() 方法,包含:

  • 主站初始化和配置
  • 节点管理和扫描
  • NMT控制
  • SDO读写
  • PDO数据处理
  • SYNC同步
  • 状态监控

注意事项

1. 线程安全

  • CanOpenMaster 内部使用独立线程接收CAN帧
  • 事件回调在接收线程中执行,注意线程安全
  • 如需更新UI,请使用Invoke/BeginInvoke

2. 超时处理

  • SDO读写都有超时机制(默认1000ms)
  • 可以通过参数调整超时时间
  • 超时后返回null或false

3. 总线负载

  • 避免频繁发送SDO请求
  • PDO适合高速数据传输
  • SYNC周期不宜过小(建议>=1ms)
  • 多个节点间添加适当延时

4. 资源释放

  • 使用 using 语句确保资源正确释放
  • 或手动调用 Dispose() 方法
  • 关闭前应先停止SYNC和节点

5. 错误处理

  • 检查所有方法的返回值
  • 捕获可能的异常
  • 记录日志便于调试

API参考

CanOpenMaster 主要方法

方法 说明
Initialize(baudRate) 初始化主站
AddNode(nodeId, nodeName) 添加从站节点
RemoveNode(nodeId) 移除从站节点
GetNode(nodeId) 获取节点对象
NmtStartNode(nodeId) 启动节点
NmtStopNode(nodeId) 停止节点
SdoReadAndWait(nodeId, index, subIndex, timeout) SDO读取
SdoWriteAndWait(nodeId, index, subIndex, data, timeout) SDO写入
SendSync(counter) 发送SYNC
EnableSync(intervalMs) 启用周期性SYNC
DisableSync() 禁用SYNC
Close() 关闭主站

CanOpenNode 主要属性

属性 说明
NodeId 节点ID
NodeName 节点名称
CurrentState 当前状态
LastHeartbeatTime 最后心跳时间
HeartbeatCount 心跳计数
IsOnline(timeoutMs) 是否在线
GetStateString() 获取状态字符串

常见问题

Q1: 如何判断节点是否在线?

var node = master.GetNode(1);
bool online = node.IsOnline(1000); // 1秒内有心跳则认为在线

Q2: 如何处理大量PDO数据?

使用事件异步处理,避免阻塞主线程:

master.OnTpdoReceived += (nodeId, data) =>
{
    Task.Run(() => ProcessTpdoData(nodeId, data));
};

Q3: SDO读写失败怎么办?

  • 检查节点是否处于预操作或运行状态
  • 检查索引和子索引是否正确
  • 增加超时时间
  • 查看日志输出

Q4: 如何配置PDO映射?

PDO映射需要在从站的EDS文件中配置,或通过SDO修改从站的对象字典。

技术支持

如有问题,请参考:

  • CANopen协议规范 CiA DS 301
  • 创芯科技CAN卡开发文档
  • 项目中的其他示例代码