本指南介绍如何使用 CanOpenMaster 类创建CANopen主站程序,管理多个从站节点,实现NMT控制、SDO读写、PDO通信等功能。
主要功能:
记录每个从站节点的状态和统计信息:
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();
}
// 添加单个节点
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}");
}
// 启动节点
master.NmtStartNode(1);
// 停止节点
master.NmtStopNode(1);
// 进入预操作状态
master.NmtEnterPreOperational(1);
// 重置节点
master.NmtResetNode(1);
// 重置通信
master.NmtResetCommunication(1);
// 批量控制
master.StartAllNodes();
master.StopAllNodes();
// 快速读取(不等待响应)
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);
// 发送单次SYNC
master.SendSync();
// 发送带计数器的SYNC
master.SendSync(1);
// 启用周期性SYNC
master.EnableSync(10); // 10ms周期
// 禁用SYNC
master.DisableSync();
// 配置TPDO传输类型
// transmissionType:
// 0 = 禁止
// 1 = 同步(周期)
// 254 = 远程请求
// 255 = 事件驱动
master.ConfigureTpdoTransmissionType(1, 1, 0xFF);
// 触发节点发送TPDO
master.TriggerTpdo(1, 1);
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}");
}
}
心跳通过事件自动接收:
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("节点在线");
}
}
紧急消息通过事件自动接收:
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() 方法,包含:
CanOpenMaster 内部使用独立线程接收CAN帧using 语句确保资源正确释放Dispose() 方法| 方法 | 说明 |
|---|---|
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() |
关闭主站 |
| 属性 | 说明 |
|---|---|
NodeId |
节点ID |
NodeName |
节点名称 |
CurrentState |
当前状态 |
LastHeartbeatTime |
最后心跳时间 |
HeartbeatCount |
心跳计数 |
IsOnline(timeoutMs) |
是否在线 |
GetStateString() |
获取状态字符串 |
var node = master.GetNode(1);
bool online = node.IsOnline(1000); // 1秒内有心跳则认为在线
使用事件异步处理,避免阻塞主线程:
master.OnTpdoReceived += (nodeId, data) =>
{
Task.Run(() => ProcessTpdoData(nodeId, data));
};
PDO映射需要在从站的EDS文件中配置,或通过SDO修改从站的对象字典。
如有问题,请参考: