向羽 孟 2448685770 添加项目文件。 před 1 měsícem
..
Dll 2448685770 添加项目文件。 před 1 měsícem
Properties 2448685770 添加项目文件。 před 1 měsícem
App.config 2448685770 添加项目文件。 před 1 měsícem
CanLibraryClass.cs 2448685770 添加项目文件。 před 1 měsícem
CanOpenManager.cs 2448685770 添加项目文件。 před 1 měsícem
CanOpenSlaveDevice.cs 2448685770 添加项目文件。 před 1 měsícem
CanOpenSlaveTest.csproj 2448685770 添加项目文件。 před 1 měsícem
CanOpenSlaveTest.sln 2448685770 添加项目文件。 před 1 měsícem
FIX_CHINESE_ENCODING.md 2448685770 添加项目文件。 před 1 měsícem
FIX_DESIGNER_ERROR.md 2448685770 添加项目文件。 před 1 měsícem
FIX_TYPE_CONVERSION.md 2448685770 添加项目文件。 před 1 měsícem
IMPLEMENTATION_SUMMARY.md 2448685770 添加项目文件。 před 1 měsícem
PROJECT_OVERVIEW.md 2448685770 添加项目文件。 před 1 měsícem
Program.cs 2448685770 添加项目文件。 před 1 měsícem
QUICKSTART.md 2448685770 添加项目文件。 před 1 měsícem
README.md 2448685770 添加项目文件。 před 1 měsícem
SlaveTestForm.cs 2448685770 添加项目文件。 před 1 měsícem
SlaveTestForm.resx 2448685770 添加项目文件。 před 1 měsícem

README.md

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. 基本使用

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. 运行测试程序

# 编译项目
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类

构造函数

public CanOpenSlaveDevice(
    byte nodeId = 1,           // 节点ID (1-127)
    UInt32 deviceType = 4,     // 设备类型 (4=USBCAN2)
    UInt32 deviceIndex = 0,    // 设备索引
    UInt32 canIndex = 0        // CAN通道索引
)

方法

Start() - 启动从站

public bool Start(CanBaudRate baudRate = CanBaudRate.BaudRate_1M)

Stop() - 停止从站

public void Stop()

SendTpdo() - 发送TPDO

public void SendTpdo(byte pdoNumber)  // pdoNumber: 1-4

SendAllTpdos() - 发送所有启用的TPDO

public void SendAllTpdos()

ConfigureHeartbeat() - 配置心跳

public void ConfigureHeartbeat(ushort heartbeatTimeMs)  // 0表示禁用

事件

OnRpdoReceived - RPDO接收事件

public event Action<byte, byte, byte[]> OnRpdoReceived
// 参数: nodeId, pdoNumber, data

OnNmtStateChanged - NMT状态改变事件

public event Action<NmtState, NmtState> OnNmtStateChanged
// 参数: oldState, newState

OnSdoReadRequest - SDO读取请求事件

public event Action<byte, ushort, byte> OnSdoReadRequest
// 参数: nodeId, index, subIndex

OnSdoWriteRequest - SDO写入请求事件

public event Action<byte, ushort, byte, uint> OnSdoWriteRequest
// 参数: nodeId, index, subIndex, value

属性

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: 在构造函数中指定:

var slave = new CanOpenSlaveDevice(nodeId: 5);  // 节点ID=5

Q2: 如何更改波特率?

A: 在Start()方法中指定:

slave.Start(CanBaudRate.BaudRate_500K);  // 500kbps

Q3: 如何处理接收到的RPDO数据?

A: 注册OnRpdoReceived事件:

slave.OnRpdoReceived += (nodeId, pdoNum, data) => {
    // 处理data数组
    int value = data[0] | (data[1] << 8);
};

Q4: 如何自定义TPDO数据?

A: 需要扩展CanOpenSlaveDevice类,添加自定义数据填充逻辑。

Q5: 心跳时间如何设置?

A: 使用ConfigureHeartbeat()方法:

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()方法中添加:

// 例如: 添加0x2000自定义对象
m_objectDictionary[0x2000] = new MyCustomObject();

实现PDO映射

目前PDO映射功能为框架级别,需要根据实际应用完善:

// 在ProcessRpdo()中解析映射表
// 在SendTpdo()中根据映射表构建数据

添加紧急消息(EMCY)支持

可以扩展EMCY处理:

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从站功能