canopen协议详解【C#二次开发】

1先看单帧数据:

单位是bit

【SOF】占1bit(起始位),1bit长度的逻辑0,表示帧开始

【identifier】占11bit(帧id),帧id就是从站地址【其中4bit功能码,7bit从站地址】

【RTR】占1bit(远程帧)0为数据帧,1为远程帧【用于让从站发一个数据帧回应】

【IDE】占1bit(帧id长度选择)0标准帧【11bit】,1扩展帧【29bit】

【R0】占1bit()保留【隐性1】

【DLC】占4bit(有效字节数0~8)表示数据包内有效字节数,

【0-8字节数据】固定占64bit(数据包64bit)就算不用,也得发64个0

表示【数据包】,固定64bit长度(且数据包内有效字节由DLC值决定)

DLC的值:0~8:决定64bit数据包内多少字节是有效的。byte0》byte1》2》3......byte7

数据区域【0-8字节数据】:实际就是数据包,固定64bit长度。【就算不用,也得发64个0】
比如:  DLC=0  数据包= 00 00 00 00 00 00 00 00

【EOF】占7bit(帧结束)连续7bit长度逻辑1,表示帧结束。

标准帧的bit数是固定的 1+11+3+4+64+16+3+7=109bit 【9174帧/秒】

【1Mbps】1000 000 ÷ 109  ≈  9174.311帧/秒

在二次开发中,只用到3个变量:

帧id:一般用标准帧 【11bit】
DLC:数据包内有效字节
数据包:【固定64bit,低字节先发 D0。。D7】
//===============================
编程一般用148字节格式存取【数据帧】对象
1表是DLC(byte)
4表示帧id(11或29bit)
8表示数据包(固定64bit)

台达can伺服从站【0x01~0x7F】1~127,所以使用标准帧id,11bit【其中4bit是功能码,7bit是从站地址】
台达A2当modbus站号为0xFF时,从站具有自动回复功能。

单个bit的逻辑定义

隐性【逻辑1】HL都为+2.5V

 

显性【逻辑0H3.5V,L1.5V

 

伺服CAN电压和数据帧_canopen读电压_cfqq1989的博客-CSDN博客

CAN用差分信号。
HL线对于0v而言,电压有3态【H大于L】【H小于L】【H等于L】
没数据的时候:逻辑true 【H等于L】电压相等,都是2.5v
有数据的时候:逻辑false【H大于L】HL电压差大于1.5v
逻辑分析仪下,HL线,电平始终是反的,【跟跷跷板一样】H线高电压,那L线就是低电压。
帧id=全0时,权限最高。
帧id有11bit(标准帧), 29bit(扩展帧)
CAN伺服用的都是标准帧【11bit】

如果4芯差分
TX+,TX-,RX+,RX-
把TX-和RX-短接
TX+和RX+就是差分电压,对应A,B信号
差分信号AB电压始终对着干,A是白天,那B就是夜里

11bit(帧id)定义:有时需要拆成 4+7(4bit为功能码,7bit为从站地址)

伺服使用标准帧【帧id总长11bit】

 NMT,SYNC和TIME 帧id是固定的。

NMT:总线命令 00h (帧id=0x00)

SYNC:同步命令 80h (帧id=0x80)

EMCY:从站故障帧 80h+从站地址

TIME:时间命令 100h(帧id=0x100)

PDO1:批量读写寄存器 发200h+180h+(属于指针命令,批量写寄存器)

PDO2:批量读写寄存器 发300h+280h+

PDO3:批量读写寄存器 发400h+380h+

PDO4:批量读写寄存器 发500h+480h+

SDO:读写单个寄存器设置 发600h+580h+(读写单个寄存器)

从站心跳帧【从站状态】700h+从站id  【0x1017心跳ms】


SDO属于读写单个寄存器
PDO批量读写离散寄存器
PDO属于指针,指向寄存器(批量读写寄存器)因为数据包是64bit,所以单帧可以同时写好几个寄存器值
通讯最小单位是一帧,如果64bit只读写一个寄存器,就显得很浪费,所以PDO能最大利用64bit

心跳帧:【从站伺服发出】 700h+从站id地址

0x1016    【主站】喂狗从站心跳帧监视,时间≥1017h 心跳生产时间×2
0x1017    【从站】从站生产心跳时间_ms【循环生产心跳】

关闭从站心跳:

Set_Send(0x08, 0x600 + Address, 0x00000000010172b);

603  //从站id = 3
08   //DLC = 8
2b 17 10 00  00 00 00 00

远程帧:
主站发【Set_Send(0x08, 0x700 + Address, 0x0000000000000000);】
从站回帧【0x01, 0x700 + Address, 0x000000000000007F);】 ' 7F表示待机模式'

寄存器【0x1017心跳ms】

先看从站回帧 700h+从站地址 , 8字节数据包(BYTE8)

【DLC】【帧id】【64bit数据包】

01,701,0000000 0000007F(表示:帧id=0x701,从站1状态7F:待机状态)

01,701,0000000 00000005(表示:从站1状态05:工作状态)

01,77F,0000000 0000007F(表示:从站127状态7F:待机状态)

01,77F,0000000 00000005(表示:从站127状态05:工作状态)

01】【0700+从站id】【00000000 000000AA


【回帧状态码】//回帧由从站发出,报告从站当前状态
//===========================
DLC=0x01  //有效字节1个【64bit数据包内只使用1个字节】
帧id =【700h+从站id】
Byte8: 8字节数据包。
//===========================
byte0:定义如下(从站状态值bit6~bit0)
0x00 正在初始化//(伺服还在上电中...)
0x01 未连接
0x02 连接
0x03 预备
0x04 停止状态(只受NMT控制)
0x05 工作状态(PDO有效)可批量读写寄存器(运动控制)【电机运行中】
0x7F 待机状态(PDO失效)只能用SDO配置寄存器【电机配置参数中】
注:由0x1017寄存器控制回帧周期ms,默认2000ms
//=======================================
RTR: 1远程帧,0数据帧
远程帧:呼叫远方从站【你还活着吗?】从站会应答一个【700h+帧id】的数据帧
远程帧:【00】【700h+帧id】【0000000000000000】
回帧:  【01】【700h+帧id】【00000000000000AA】
//======================================
从站心跳:  0x1017 _ms //从站心跳间隔,
监视主机:  0x1016 _ms //监视主机心跳,第1帧开始激活。【大于1.8倍生产者时间】

NMT:总线命令 00h(帧id=0x00)

NMT总线命令 00h+B0+B1

【02】【0000】【000000000000BBAA


00h                  B0                            B1
总线NMT:             命令字:AA                    从站地址:BB
帧id=0x00            0x01【工作】从站(回05h)       01~0x7F从站地址(1~127)
                     0x02【停止】从站(回04h)       00广播(所有从站)
                     0x80【预操作】(回7fh)//SDO专用
                     0x81从站复位(回7fh)
                     0x82通讯复位(回7fh)
//=======================
NMT是总线命令:(可以把从站踢下去)
DLC长度固定2字节         (DLC=0x02)
11bit的值,固定为  0x00 (帧id=0x00)
//=======================
Byte0:功能码          (64bit数据包)
Byte1:从站地址
 
就是说11bit的值,固定为0x00,并且只有主站才使用这个数据帧。(帧id=0x00)
全0的帧id是权限最高的。
DLC=0x02:8字节数据包内,只使用2字节
 
byte0:01h 从站启动(回帧05h运行模式)PDO有效
       02h 从站停止(回帧04h停止模式)只有NMT有效
       80h 从站预运行(回帧7Fh预运行模式)PDO失效【伺服配置参数时,用这个模式】
       81h 从站重启(一般从站伺服报警后才用,或重启才能生效的参数)(回帧7Fh预运行模式)
       82h 从站通讯重连(回帧 7Fh预运行模式)
byte1:00h 所有从站
      01~7f 从站地址(1~127)
//==============================================
plc开机发送: 02-0000-0000000000000081 
// DLC=0x02,帧id=0x00,Byte8(从站重启命令81,所有从站00)
//====节点保护======【节点/寿命】============================
主站定时发送一个站号的心跳包: 02-077F-0000000000000005;//从站127,从站工作状态05
节点保护:上位机查询从站。
寿命保护:从站监听主站帧或其他从站帧。
0x100C保护时间     主站每隔100C时间询问从站【700h+从站id】,从站必须应答RTR=1
0x100D寿命因子


————————————————
版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808

SYNC:同步帧 发80h (帧id=0x80)

【00】【0080】【0000000000000000】


0x1005    关80h,开40000080h【次高位】//从站做SYNC的生产者,【注:全网只能有一个sync生产者】
0x1006    同步周期_us
0x1007    忽略n个同步帧后,开始同步【同步齿轮】
0x100C    节点守护时间u16
0x100D    寿命因子u8【参考1.8倍】
00-0080-0000000000000000;
//DLC有效字节0,帧id=0x0080,Byte8数据包=全0【64个0】;
上位机周期性发送SYNC (单位:微秒 us) 周期>2ms
//=======================
DLC=0x00
帧id=0x80:帧id值固定为0x80,8字节数据包值无效(全0)
Byte8=全0;
//=======================
pdo设置好所有从站后,0x80触发从站PDO参数生效(或动作)【1400-02=0~240时】
0x6040寄存器值需要上升沿触发时:【先赋值低电平,SYNC(1),再高电平,SYNC(1)。】

同步对象 COB-ID 的次高位决定是否激活同步发生器。【200h+从站id】

TIME:时间帧 发100h

【06】【0100】【0000FFEEDDCCBBAA


06-0100-0000XXxx XXxxXXxx

//=======================
DLC=0x06; // 长度使用6字节
帧id=0x100:帧id值固定为0x100;
Byte8=0x 0000112233445566; // 6字节有效【 8字节数据值(64bit时间值)】
//=======================
广播时间(跟PLC时间64bit一样,由时间设备广播) 1970-01-01 + u64值
CAN大部分驱动器不支持,由主站或时间设备按周期循环广播。
//================================== C#
                DateTime dateTime = DateTime.Now;
                Int64 now = dateTime.ToBinary();
                TIME(now);// DLC=6字节

EMCY:从站故障帧 回80h+从站地址,B0,B1,B2,B3,B4,B5,B6,B7

【08】【0080+从站id】【CCCC CCCC CCBB AAAA

【08】【0082】【0000000004118120】//雷赛,从站2故障,错误被动模式

 【08】【00FF】【0000000011807305】//台达,从站127故障 【11】【80】【7305】

0x603F=0x7305;//应急故障码 【LSB是低字节】

0x1001=0x80; // 错误寄存器

=0x11; // 台达伺服LED故障码:AL.011        位置檢出器異常


0x1014最高位【1关】【0开】//EMCY功能开关
由从站设备发出:
从站故障后,会上传 80h+从站地址的帧
//==================
DLC=08;
帧id= 0x80+从站号;
Byte8= //  0x0000000004118120
//===================
B1 B0      应急错误代码= 0x8120      //寄存器 0x603F
B2         故障码=0x11              //寄存器 0x1001
B4 B3      厂家故障码=0x04          //寄存器 【禾川是0x213F】

最近错误记录 0x1003数组
0x1001故障码:
bit0 一般性错误
bit1    电流//
bit2    电压
bit3    温度
bit4    通讯//
bit5    协议402
bit6    保留
bit7    厂家
//=====================================
台达A2
【08】【00FF】【0000000011 80 7305】
//=========================
DLC=0x08
帧id=0xFF【0x80+127】//从站127故障
B1 B0=0x7305//寄存器 0x603F
B2=0x80     //寄存器 0x1001
B7 B3=0x11    //LED显示AL.011【位置检出故障,编码器线断】

远程帧

CAN 网络主站通过远程帧,可以检查每个节点的当前状态。

 用于呼叫从站,然后从站返回一个数据帧。

上位机发 SDO 读从站 0x1000 寄存器

从站回帧 580h+ 

PDO:批量读写寄存器

【08】【PDO码+从站id】【AAAAAAAAAAAAAAAA


TXPDO1(发送)   0011+7bit(从站id)    0x180+从站地址  (方向是:伺服发送到上位机)
RXPDO1(接收)   0100+7bit(从站id)    0x200+地址
 
TXPDO2(发送)   0101    0x280+地址
RXPDO2(接收)   0110    0x300+地址  (方向是:上位机发送到伺服)
TXPDO3(发送)   0111    0x380+地址
RXPDO3(接收)   1000    0x400+地址
TXPDO4(发送)   1001    0x480+地址
RXPDO4(接收)   1010    0x500+地址
————————————————
(帧id)11bit拆开使用(4bit+7bit)其中4bit表示功能码,7bit表示从站地址
版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cfqq1989/article/details/128009555

PDO是批量读写寄存器,有4路
PDO是需要先映射寄存器地址后,才能使用,对映射的寄存器批量读写。(PDO相当于快捷键)
去洗脚城点8号技师,(8号技师,可以是老板娘,也可以是小姨子,看寄存器指针怎么绑定)

// 写信(上位机到下位机)
                           参数开关                 地址绑定
rPDO1(200h+)(主→从伺服)   1400h                 1600h
rPDO2(300h+)               1401h                 1601h
rPDO3(400h+)               1402h                 1602h
rPDO4(500h+)               1403h                 1603h
 
注:1400-00【子索引个数】子索引总数5(只读5)
流程:
    1400-01【PDO配置】先关(1<<31),等1600配置好后,1400-01再开启(b31=0开;b30=1RTR强置关,b29=0标准帧11位,b10~0:标准帧id受控码样式200h+从站)
(注站2: 1400-01= 0x 40000202)//b31=0表示使能rPDO1功能,b30是RTR位
                        // b29位=1是扩展帧,b10~0位rPDO1功能码0x202 从站2(200h+2)
//============================
1400_【00】:[子索引个数]有5个子索引(只读5)
     【01】:[PDO配置]bit31(0开,1关rPDO1)
                    b30=1(RTR强置关,不应答远程帧)
                    bit28~0=    //有些伺服支持扩展帧,扩展帧29bit位
                    bit10~0=200h+从站id(帧id受控码样式200h+从站号)//对应11位标准帧id
                             例bit10~0=0x0203;表示站3伺服受控0x203功能码。
     【02】:[0~240](0~F0)在1个SYNC信号后,rPDO值才生效。
             [254或255]rPDO值立即生效(不需要sync同步信号)。
             //【241~251】保留【252~253】不受rPDO命令控制
                   
     【03】:[抑制时间,单位100us]连续2个PDO间隔最小时间(过滤干扰信号)12轴8.67ms
     【04】:(保留)【不使用】
     【05】:[事件定时器,单位1ms]0关,其他值:【定时时间】时间到,就生效rPDO设置的值。

1600_【00】有8个子索引:00:指针有效个数(把64bit分割给0~8个寄存器)
     【01】绑定指针1(一般绑 0x6040控制字)0x60400010
                        bit31~16寄存器编号(比如 6040h)
                        bit15~8 子索引 00h
                        bit7~0  寄存器类型bit(8h,10h,20h)8位,16位,32位
 
     【02】绑定指针2(0x607A目标位置)
     【03】绑定指针3(0x6060工作模式1位置3速度4扭矩6回零7补间位置)
     【04】绑定指针4
     【05】绑定指针5
     【06】绑定指针6
     【07】绑定指针7
     【08】绑定指针8
 
// 回信(下位机到上位机)
                          参数开关               地址绑定
tPDO1(180h+)(主←从)      1800h                 1A00h
tPDO2(280h+)              1801h                 1A01h
tPDO3(380h+)              1802h                 1A02h
tPDO4(480h+)              1803h                 1A03h
注:
1800_【00】:[子索引个数]有5个子索引(只读5)
     【01】:bit31(0开,1关tPDO)先关,绑好后再开
                        bit30(0rtr开,1关,远程帧)
                        bit28~0(扩展帧29位,回帧样式 180h+从站id)
                        bit10~0(标准帧11bit,回帧样式 180h+从站id)回帧id样式
                        //比如收到SYNC信号,伺服回帧 183h 【tPDO1站3】
     【02】:[0]值改变,且接收到一个同步帧,则发送该 tPDO;
             [1~240]个SYNC同步帧后上传回帧(tPDO帧)
             [254,255]值改变或定时器到就上传回帧(tPDO帧)

     【03】:帧间隔 0.1ms倍数(400是40ms)用于两个tPOD上传间隔时间
     【04】:保留
     【05】:[定时触发器,单位1ms] (0:关)触发tPDO回帧【只在02=254,255时有效】
                        【x1ms】u16,时间到就上传tPDO帧。如:5000是每5秒上传tPDO帧
 
1A00_【00】:1A00有8个子索引:00:指针有效个数(64bit中寄存器占用个数1~8)
     【01】:绑定指针1(一般绑 0x6041状态字)0x60410010;
                        bit31~16寄存器编号 6041h
                        bit15~8 子索引 00h
                        bit7~0  有效bit(8h,10h,20h)8位,16位,32位
 
     【02】:绑定指针2(0x6064PUU电机编码器回授位置)
     【03】:绑定指针3(0x6063回授命令位置)
     【04】:绑定指针4
     【05】:绑定指针5
     【06】:绑定指针6
     【07】:绑定指针7
     【08】:绑定指针8
 
异步通讯响应最快。(值改变就生效,立即回tPDO帧)

例子:寄存器0x1600子索引01赋值0x60400010;
状态流:
先设置 1400h-01=1<<31;// 关闭rPDO1
1600h-00h = 0;//关闭绑定
1600h-01h = 0x6040 00 10;//0x60400010;绑定寄存器0x6040子00长度bit16
RPDO1指针,指向寄存器0x6040,00子索引,10(16bit)
再设置 1400h-01=0<<31;// 使能rPDO1
 
08h(8bit)
10h(16bit)// 例子中 10 指 寄存器0x6040是16bit的
20h(32bit)// 0x20
 
例子2: 寄存器0x1A00子索引01赋值0x60410010;
 
0x1A00-01是伺服上传(TPDO1)
0x6041是把TPOD1和寄存器0x6041绑定
00是寄存器0x6041的子索引
10是寄存器的类型 16bit
作用:伺服上传 寄存器0x6041-00的数据
 
//========================
寄存器0x1400-01
bit31:  PDO1总开关(0开,1关)//进行设置的时候先关,再开。 1600也是如此(1600-00先设置0)
bit28:地址bit长度(0:11bit   1:29bit)
 
寄存器0x1400-02 //表示在下一个SYNC同步信号80h发生之后CANopen驱动器生效接收到的数据
0~0xF0 延时us   //雷赛用0x01us,0h立即,0xFE由SDO动作,0xFF事件立即响应

//========================================
              #region PDO1   // 6040和607A目标位置
                //写信
                PDO_Struct pdor = new PDO_Struct();
                pdor.rPDO命令触发器 = 240;//【0~240】在1个SYNC后,rPDO值才生效。【254或255】rPDO值立即生效(不需要SYNC信号)
                                     //【241~251】参数无效【252~253】rPDO命令失效(不受rPDO命令控制)
                pdor.rPDO禁止时间_100us = 10;// 1ms
                pdor.UseRegNum = 2;
                pdor.Reg1 = 0x60400010;// 16bit
                pdor.Reg2 = 0x607A0020;// 32bit
                PDO_Config(PDO_Type.配置rPDO1, pdor);
                //回信
                PDO_Struct pdot = new PDO_Struct();
                pdot.tPDO上传触发器 = 7;
                pdot.tPDO禁止时间_100us = 10000;//1秒
                pdot.tPDO计时触发器_1ms = 0;//【单位1ms】只在tPDO异步时生效【x1ms】(254,255)异步,定时上传u16【最大65535ms】
                pdot.UseRegNum = 2;//绑定2个寄存器
                pdot.Reg1 = 0x60410010;// 16bit
                pdot.Reg2 = 0x60640020;// 32bit
                PDO_Config(PDO_Type.配置tPDO1, pdot);

                #endregion

PDO配置源程序

//h1400-01定义
【d31 rPDO总开关 0开,1关】【d30 保留】【d29(0标准帧11bit),(1扩展帧29bit)】【d28~0 帧id】

//h1800-01定义
【h1800-01】定义【d31 tPDO 0开,1关】【d30 RTR 0开,1关】【d29 0标准帧,1扩展帧】【d28~0 帧id】
// RTR:远程帧回应



 //写信
 PDO_Struct pdor = new PDO_Struct();
 pdor.rPDO命令触发器 = 1;//【0~240】在1个SYNC后,rPDO值才生效。【254或255】rPDO值立即生效(不需要SYNC信号)
 //【241~251】参数无效【252~253】rPDO命令失效(不受rPDO命令控制)
 pdor.rPDO禁止时间_100us = 10; // 1ms内,不听命令

 pdor.UseRegNum = 2;
 pdor.Reg1 = 0x60400010;// 16bit
 pdor.Reg2 = 0x60FF0020;// 32bit     // 速度设定obj
 sta = PDO_Config(PDO_Type.配置rPDO1, pdor);

 //回信
 PDO_Struct pdot = new PDO_Struct();
 pdot.tPDO上传触发器 = (Byte)(Address * 2);// 错开上传帧【注意254/255】
 pdot.tPDO禁止时间_100us = (UInt16)(Address * 500);//6553.5ms   //65ms 【帧间隔】
 pdot.tPDO定时触发器_1ms = 10000;//.tPDO上传触发器= [254,255]时才有效

 pdot.UseRegNum = 2;
 pdot.Reg1 = 0x60410010;// 16bit
 pdot.Reg2 = 0x606C0020;// 32bit     //【速度值】
 sta &= PDO_Config(PDO_Type.配置tPDO1, pdot);
 return sta;


 #region  PDO配置: 指针绑定
        //========快捷指针寄存器受控码
        //rPDO1   200h+     tPDO1   180h+
        //rPDO2   300h+     tPDO1   280h+
        //rPDO3   400h+     tPDO1   380h+
        //rPDO4   500h+     tPDO1   480h+

        /// <summary>
        /// 2023.6.25 rPDO配置【1400控制,1600绑定】
        /// 祁成qq750273008
        /// SET到伺服8字节,这是寄存器快捷键1400h,1600h
        /// </summary>
        bool PDOr_Config(PDOtype PDOn, PDO_Struct obj)// PDO配置
        {
            bool sta = true;
            byte zu = (byte)(PDOn);
            short h1400 = (short)((zu - 1) + 0x1400);// 确定组队【队伍】
            short h1600 = (short)((zu - 1) + 0x1600);

            //1   PDO总开关:关闭  子索引5
//h1400-01定义【d31 rPDO总开关】【d30 保留】【d29(0标准帧11bit),(1扩展帧29bit)】【d28~0 帧id29bit】
            sta &= set_region(h1400, 0x01, 1 << 31);  // 关闭 rPDO 功能(原值: 0x4000 0200h + 从站地址)// 0x40000202

            //2  1400配置
            sta &= set_region(h1400, 0x02, obj.rPDO命令触发器); //【0~240】(第1次SYNC帧后,生效设定值)【原值:0xFF】254,255为事件,值不同立即生效
            sta &= set_region(h1400, 0x03, 0);//  (死区ms)接收PDO1命令过滤间隔,单位0.1ms         雷赛40ms(原值:0x00)
                                              //set_region(h1400, 0x04, 0);// 不存在
                                              //sta &= set_region(h1400, 0x05, 0);// 是定时触发器(原值:0x00)【不用于RPDO】

            //3  1600寄存器绑定
            sta &= set_region(h1600, 0x00, 0x00);  // 寄存器指针:关闭    (原值:0x02)

            if (obj.Reg1 != 0) sta &= set_region(h1600, 1, obj.Reg1); // 指针1  0x60400010   控制字u16
            if (obj.Reg2 != 0) sta &= set_region(h1600, 2, obj.Reg2); // 指针2  0x607A0020   目标位置u32    PDO4:0x60FF0020  目标速度u32
            if (obj.Reg3 != 0) sta &= set_region(h1600, 3, obj.Reg3); // 指针3
            if (obj.Reg4 != 0) sta &= set_region(h1600, 4, obj.Reg4); // 指针4
            if (obj.Reg5 != 0) sta &= set_region(h1600, 5, obj.Reg5); // 指针5
            if (obj.Reg6 != 0) sta &= set_region(h1600, 6, obj.Reg6); // 指针6
            if (obj.Reg7 != 0) sta &= set_region(h1600, 7, obj.Reg7); // 指针7
            if (obj.Reg8 != 0) sta &= set_region(h1600, 8, obj.Reg8); // 指针8
                                                                      // 所有寄存器加起来不能超过64bit
            if (obj.UseRegNum > 8) obj.UseRegNum = 8;// 不能超过64bit
            sta &= set_region(h1600, 0x00, obj.UseRegNum);  // 寄存器指针有效数量   

            //4   rPDO使能   bit31【PDO总开关,0:开】  bit10~0【帧id 11bit,受控码】
            sta &= set_region(h1400, 0x01, 0x40000000 + 0x100 + (0x100 * zu) + Address);  // 开启PDO1功能 // 0x40000202

            return sta;
        }
        /// <summary>
        ///  2023.6.25 tPDO配置【1800配置,1A00绑定】
        ///  祁成qq750273008
        /// </summary>
        /// <param name="PDOn"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        bool PDOt_Config(PDOtype PDOn, PDO_Struct obj)
        {
            bool sta = true;
            byte zu = (byte)(PDOn);
            short h1800 = (short)((zu - 1) + 0x1800);// 确定组队【队伍】
            short h1A00 = (short)((zu - 1) + 0x1A00);

            //1   PDO总开关:关闭  子索引5个
            sta &= set_region(h1800, 0x01, 1 << 31);  // 关闭 tPDO 功能(原值: 0x4000 0180h + 从站地址)// 0x40000182

            //2  1800配置
            sta &= set_region(h1800, 0x02, obj.tPDO上传触发器); //【0】非循环(值有变化,sync后上传),【1~240】SYNC到(设置值)次数后上传tPDO,【254或255】值变或计时到就上传。
            sta &= set_region(h1800, 0x03, obj.tPDO禁止时间_100us);//  (死区ms)回帧失败等待间隔,单位0.1ms         雷赛40ms(原值:0x00)
            //=set_region(h1400, 0x04, 0);// 不存在
            sta &= set_region(h1800, 0x05, obj.tPDO定时触发器_1ms);//【原值:0x00】//【tPDO上传触发器=255时】时间到就上传tPDO

            //3  1A00寄存器绑定
            sta &= set_region(h1A00, 0, 0x00);  // 寄存器指针:关闭    (原值:0x01)

            if (obj.Reg1 != 0) sta &= set_region(h1A00, 1, obj.Reg1); // 指针1  0x60410010  状态字u16
            if (obj.Reg2 != 0) sta &= set_region(h1A00, 2, obj.Reg2); // 指针2  0x60640020  电机位置u32
            if (obj.Reg3 != 0) sta &= set_region(h1A00, 3, obj.Reg3); // 指针3  0x606C0020  当前速度u32
            if (obj.Reg4 != 0) sta &= set_region(h1A00, 4, obj.Reg4); // 指针4
            if (obj.Reg5 != 0) sta &= set_region(h1A00, 5, obj.Reg5); // 指针5
            if (obj.Reg6 != 0) sta &= set_region(h1A00, 6, obj.Reg6); // 指针6
            if (obj.Reg7 != 0) sta &= set_region(h1A00, 7, obj.Reg7); // 指针7
            if (obj.Reg8 != 0) sta &= set_region(h1A00, 8, obj.Reg8); // 指针8
            // 所有寄存器加起来不能超过64bit
            if (obj.UseRegNum > 8) obj.UseRegNum = 8; //if (obj.UseNum > 8) obj.UseNum = 8;
            sta &= set_region(h1A00, 0, obj.UseRegNum);  // 寄存器指针有效数量   
            //4   PDO使能  40000182
            sta &= set_region(h1800, 1, 0x40000000 + 0x80 + (0x100 * zu) + Address);  // 开启tPDO功能 // 0x40000182
            return sta;
        }
        public bool PDO_Config(int Slave_id, PDO_Type type, PDO_Struct pdo)
        {
            Address = (byte)Slave_id;
            bool sta = true;
            switch (type)
            {
                case PDO_Type.配置rPDO1:// 命令下达寄存器(命令)
                    sta &= PDOr_Config(PDOtype.PDO1, pdo);
                    break;
                case PDO_Type.配置rPDO2:
                    sta &= PDOr_Config(PDOtype.PDO2, pdo);
                    break;
                case PDO_Type.配置rPDO3:
                    sta &= PDOr_Config(PDOtype.PDO3, pdo);
                    break;
                case PDO_Type.配置rPDO4:
                    sta &= PDOr_Config(PDOtype.PDO4, pdo);
                    break;
                case PDO_Type.配置tPDO1:// 状态回帧寄存器(回答)
                    sta &= PDOt_Config(PDOtype.PDO1, pdo);
                    break;
                case PDO_Type.配置tPDO2:
                    sta &= PDOt_Config(PDOtype.PDO2, pdo);
                    break;
                case PDO_Type.配置tPDO3:
                    sta &= PDOt_Config(PDOtype.PDO3, pdo);
                    break;
                case PDO_Type.配置tPDO4:
                    sta &= PDOt_Config(PDOtype.PDO4, pdo);
                    break;

                default:
                    break;
            }
            return sta;
        }
        public bool PDO_Config(PDO_Type type, PDO_Struct pdo)
        {

            写信
            //PDO_Struct pdor = new PDO_Struct();
            //pdor.rPDO命令触发器 = 255;//【0~240】在1个SYNC后,rPDO值才生效。【254或255】rPDO值立即生效(不需要SYNC信号)
            【241~251】参数无效【252~253】rPDO命令失效(不受rPDO命令控制)
            //pdor.rPDO禁止时间_100us = 10;//  1ms过滤

            //pdor.UseRegNum = 2;
            //pdor.Reg1 = 0x60400010;// 16bit
            //pdor.Reg2 = 0x607A0020;// 32bit
            //PDO_Config(PDO_Type.配置rPDO1, pdor);

            回信
            //PDO_Struct pdot = new PDO_Struct();
            //pdot.tPDO上传触发器 = 7;// 第n个SYNC帧后,触发tPDO帧
            //pdot.tPDO禁止时间_100us = 5000;// 【上传失败用】上传帧间隔
            //pdot.tPDO定时触发器_1ms = 0;//【tPDO上传触发器=255时】时间到就上传tPDO

            //pdot.UseRegNum = 2;
            //pdot.Reg1 = 0x60410010;// 16bit
            //pdot.Reg2 = 0x60640020;// 32bit
            //PDO_Config(PDO_Type.配置tPDO1, pdot);


            //=========================================
            return PDO_Config(Address, type, pdo);
        }

        #region 作废
        public void PDO_EN()
        {
            //6040
            //0 Switch on 转换开
            //1 Enable voltage电压开
            //2 Quick Stop快停
            //3 Enable operation 启用操作
            //4  pp新设定点(正触发)       hm回零操作开始(正触发)
            //5  pp立即更改集合
            //6  pp绝对(0)/相对(1)
            //7 Fault reset 故障复位
            //8 Halt 暂停                   hm回零停止运行(0x10F)

            //执行回零 bit4
            PDO(PDOtype.PDO1, 0x2F);// 准备回零    bit4 上升沿触发
            SYNC();
            PDO(PDOtype.PDO1, 0x3F);
            SYNC();
        }

        #endregion
        #endregion

SDO:读写寄存器设置(每次只能读写一个寄存器)

【08】【600+从站id】【DDDDDDDDCCBBBBAA

DLC=08,帧id=0x600+从站id

低字节先发。 AA操作码 ,BBBB寄存器地址,CC子索引,DDDDDDDD寄存器值


600h+    rSD0 (set到驱动器)
580h+    tSD0 (get回帧)
 
GOB_ID    B0             B1B2                  B3        B4B5B6B7
601(600h+从站地址)
 
          2F写  8bit     1032寄存器(0x3210)  10子索引  10 (0x10)设定值
          2B写 16bit     1032寄存器(0x3210)  10子索引  10 32 (0x3210)
          27写 24bit     1032寄存器(0x3210)  10子索引  10 32 54(0x543210)
          23写 32bit     1032寄存器(0x3210)  10子索引  10 32 54 76 (0x76543210)
           40读寄存器     1032寄存器(0x3210)  10子索引  空0x00000000
回帧0x60是成功,0x80是故障 
                         1032寄存器地址(0x3210)
                                              10子索引(0x10是16,一般是0~3)
                                                   //或错误码
                                                   数值10 32 54 76(0x76543210)
————————————————
版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cfqq1989/article/details/128009555

上位机到伺服:【写伺服】

伺服到上位机:【读伺服】

80h的 中止代码  【u32】

string get_Error_str( Int32 reg, Int32 Son, Int32 errcode)
{
  string errorString = string.Empty;
  string str1 = $"Reg{reg.ToString("X4")}h-{Son.ToString("X2")}= 0x{errcode.ToString("X8")};_";
  switch (errcode)
   {// 80h    b3b2b1b0
     case 0x05030000: errorString = str1 + "触发位没有交替变化! "; break;
     case 0x05040000: errorString = str1 + "SDO 协议超时! "; break;
     case 0x05040001: errorString = str1 + "无效/ 未知的命令字! "; break;
     case 0x05040002: errorString = str1 + "无效的块大小(仅块传输模式)! "; break;
     case 0x05040003: errorString = str1 + "无效的序号(仅块传输模式)! "; break;
     case 0x05040004: errorString = str1 + "CRC 错误(仅块传输模式)! "; break;
     case 0x05040005: errorString = str1 + "内存溢出! "; break;
     case 0x06010000: errorString = str1 + "对象不支持访问! "; break;
     case 0x06010001: errorString = str1 + "试图读只写对象! "; break;
     case 0x06010002: errorString = str1 + "试图写只读对象! "; break;
     case 0x06020000: errorString = str1 + "对象不存在! "; break;
     case 0x06040041: errorString = str1 + "对象不能映射到PDO内! "; break;
     case 0x06040042: errorString = str1 + "映射的对象的数目和长度超出 PDO 长度! "; break;
     case 0x06040043: errorString = str1 + "一般性参数不兼容! "; break;
     case 0x06040047: errorString = str1 + "一般性设备内部不兼容! "; break;
     case 0x06060000: errorString = str1 + "硬件错误导致对象访问失败! "; break;
     case 0x06060010: errorString = str1 + "数据类型不匹配,服务参数长度不匹配! "; break;
     case 0x06060012: errorString = str1 + "数据类型不匹配,服务参数长度太长! "; break;
     case 0x06060013: errorString = str1 + "数据类型不匹配,服务参数长度太短! "; break;
     case 0x06070010: errorString = str1 + "数据型态不符;参数长度不符! "; break;
     case 0x06090011: errorString = str1 + "子索引不存在! "; break;
     case 0x06090030: errorString = str1 + "超出参数的值范围(写访问时)! "; break;
     case 0x06090031: errorString = str1 + "写入参数数值太大! "; break;
     case 0x06090032: errorString = str1 + "写入参数数值太小! "; break;
     case 0x06090036: errorString = str1 + "最大值小于最小值! "; break;
     case 0x08000000: errorString = str1 + "一般性错误! "; break;
     case 0x080000A1: errorString = str1 + "读EEPROM错误! "; break;
     case 0x080000A2: errorString = str1 + "写EEPROM错误! "; break;
     case 0x080000A3: errorString = str1 + "存取EEPROM超出范围! "; break;
     case 0x080000A4: errorString = str1 + "存取EEPROM错误! "; break;
     case 0x080000A5: errorString = str1 + "密码错误! "; break;
     case 0x08000020: errorString = str1 + "数据不能传送或保存到应用! "; break;
     case 0x08000021: errorString = str1 + "由于本地控制导致数据不能传送或保存到应用! "; break;
     case 0x08000022: errorString = str1 + "对象使用中! "; break;
     case 0x08000023: errorString = str1 + "对象字典不存在! "; break;
     //0800 0023H
     //对象字典动态产生错误或对象字典不存在(例如通过文件生成对象字典,但由于文件
     //损坏导致错误产生)
     default:
             errorString = str1 + "从站错误。检查通讯? ";
             break;
   }
  return errorString;
}

数组:


上位机发 0x40读  寄存器地址  寄存器子索引
伺服回帧 0x41    寄存器地址  寄存器子索引  +  32bit【该寄存器值 b0在前..b4】

伺服回帧 0x80【操作失败】    寄存器地址  寄存器子索引  +  32bit【该寄存器值,故障码】
伺服回帧 0x60【操作成功】

while(条件)
{
    上位机发0x60继续
    伺服回帧0x00
        上位机发0x70继续
        伺服回帧0x10
}

案例参考:


    速率  与   距离
 1  Mbps      25 m 
750 Kbps      50 m 
500 Kbps     100 m 
250 Kbps     250 m 
125 Kbps     500 m 
台达A2-can伺服:上位机发帧id=0xff,从站会自动回复
6093-01齿轮比.分子(只适用于台达)//汇川0x6091
6093-02齿轮比.分母
关系 1280000/分母*分子    // 如果一圈是5万(分母128万,分子5万)

 //齿轮比
sta &= set_region(0x6093, 1, zi); //"P1.044"分子   乘以N ,齿轮 分zi(编码器最大值)
sta &= set_region(0x6093, 2, mu); //"P1.045"分母  
//速度
400转 x 编码器分辨率 / 60

扭矩模式:
0x6060=4;//扭矩模式
0x6061//当前伺服工作模式
0x6040//控制字
0x6041//状态字
0x6071//目标扭矩  -3000~3000
0x6074//最高扭矩
0x6075//电机额定电流
0x6077//回授:电机扭矩
0x6078//回授:电机电流mA
0x6087//扭矩斜率

定位模式:
6093-01分子(只适用于台达) 雷赛在(0x2001-00)
6093-02分母
0x6060=1;//1定位模式  3速度  4力矩  6回零  7插补
0x607A//目标位置
0x6081//目标速度
0x6082//起跳速度
0x6083//加速
0x6084//减速
0x6064//电机位置
0x6062//命令位置
0x6063//实际位置

0x6065//误差范围  //位置误差报警阀值 AL009 (60F4h当前位置误差值)
0x60F4//当前误差值
0x60FC//位置需求值-1
0x6067//定位完成,确认范围
0x6068//定位完成,时间范围

插补模式:
6060=0x07
周期写 60C1-01=绝对坐标

对象字典:

0000h            保留
0001h ~ 025Fh        数据类型定义
0260h ~ 0FFFh    保留
1000h ~ 1FFFh        通讯协议区【PDO,SDO,SYNC,NMT,TIME,EMCY等总线命令定义】
2000h ~ 5FFFh        设备参数区【设备厂家自定义】
6000h ~ 9FFFh        运动控制寄存器区【0x6040等】
A000h ~ AFFFh        网络变量【IEC61131-3】
B000h ~ BFFFh        路由器网关变量
C000h ~ FFFFh    保留


cia401 i/o模块行规
cia402 伺服运动控制
cia403 人机接口行规
cia404 测量设备与闭环控制行规
cia406 编码器行规
cia408 比例液压行规

 伺服恢复出厂保存


保存:
    0x1010-01=0x65766173;//【保存所有参数】保存参数
恢复:
    0x1011-01=0x64616F6C;//【恢复所有参数】雷赛不支持
    0x1011-02=0x64616F6C;//【恢复从1000h到1FFFh】雷赛,断电重启生效(不要执行保存)【禾川0x1011-02】
    0x1011-03=0x64616F6C;//【恢复从6000h到9FFFh】雷赛不支持
    0x1011-04=0x64616F6C;//【伺服厂家自定义】雷赛用4,断电重启生效(不要执行保存)

注:0x1011-04设置恢复后,拔掉电源,重新上电后,所有参数就复位了

PLC发的指令是 0x1011-02

1000h段定义:

CANopen协议,上位机开发(C#)_c# canopen_cfqq1989的博客-CSDN博客


1000h 【u32】驱动器机种码   0x0B02 0192  //0192伺服  0B02台达B3系列   04020192:A2
1001h 【u8】 错误寄存器  b4通讯 b3温度b2电压b1电流b0一般错误
1003h-00【u32】故障记录表(4组)
     -01        0x58120
     -02        0
     -03        0
     -04        0

1005h 【同步帧】功能码   【0x80】
1006h 【同步帧】循环周期us  【0】0关闭SYNC
1007h 【同步帧】同步窗口长度【0】
    1008h 设备名称【台达A2】
    1009h 硬件版本
    100Ah 软件版本
100Ch 【0】保护时间ms【100c乘以100d的值,构成看门狗时间】
100Dh 【0】寿命倍数,看门狗时间 100Ch的倍数(范例:若 OD 100Ch = 5 ms, OD 100Dh = 10,则寿命 Life time 等于 50 ms。)

1010h -00 【4】有4组
      -01 保存所有参数
      -02 保存通信参数1000h~1fffh
      -03 保存驱动器参数
      -04=0x65766173【必须看厂家手册】

1011h -00 【4】有4组
      -01 恢复所有默认段落。
      -02 恢复通讯命令参数。1000h~1fffh
      -03 恢复伺服出厂参数。
      -04=0x64616F6C【重置参数,厂家:出厂参数】

1014h EMCY故障帧   【80h+从站id】  如从站3是 0x83;

1016-01h 【u32】断网保护ms(b15~0 ms  b23~16 从站)AL180故障【监听上位机心跳】
    -02h    心跳消费
    -03h    消费者心跳时间
    -04h    消费者心跳时间
    -05h    消费者心跳时间

1017h 【u16】生产者心跳ms默认2000ms 【700h+从站id】如从站3【帧id=703h 包0f】

1018-00h    4
    -01h 【u32】辨识对象【厂家id,0x331
    -02h 【u32】                产品id,0x8200
    -03h 【u32】                       版本id,0x100
    -04h 【u32】                              序列号id】0x01

1200h SDO受控功能码 -01=【600h+从站id】  -02=【580h+从站id】

// PDO的配置【rPDO和tPDO】
1400h(主→从)rPDO1功能码-00h 【5】
                        -01h 【40000203】【u32】b31:rPDO1总开关(最高位0开)【帧id】b10~0:功能码(200h+从站地址)
                       -02【u8】【0~240】在1次SYNC后,生效rPDO设定值,【255】立即生效参数
                       -03【u16】命令过滤时间
                       -04 空
                       -05 【0】触发定时器
1401h(主→从)rPDO2功能码【配置】
1402h(主→从)rPDO3功能码
1403h(主→从)rPDO4功能码
1600h PDO1批量写寄存器(指针绑定寄存器数量)
1601h PDO2批量写寄存器【配置】
1602h PDO3批量写寄存器
1603h PDO4批量写寄存器

1800h(主←从)tPDO功能码-01【u32】b31:tPDO1总开关(最高位0开) b10~0:功能码(180h+从站地址)
                       -02 【0~240】在1~240次SYNC后,上传tPDO帧
                       -03 上传失败后,重发数据帧的间隔时间【0.1ms】
                       -04 空
                       -05 定时器(02=255时,值改变或定时时间到,上传tPDO帧)【1ms】
1801h(主←从)tPDO2功能码【配置】
1802h(主←从)tPDO3功能码
1803h(主←从)tPDO4功能码
1A00h tPDO1批量读寄存器(指针绑定寄存器数量)
1A01h tPDO2批量读寄存器
1A02h tPDO3批量读寄存器
1A03h tPDO4批量读寄存器
//汇川,公式
【200A+BB】//2000h段公式: H2.16对应【0x2002+ 子索引】 0x2002-16
//台达,公式
【200A+BB】//2000h段公式: P2.16对应【0x2002+ 子索引】0x2016

6000h段定义


603fh 最后错误码u16
6040h 控制字
      00关》06待机》07使能》0f工作》1f动作(b4上升沿,b5旧命令,b6 0绝对位置1相对位置)
            b0启动(上升沿)
            b1电压输出
            b2急停(0急停报警)
            b3命令(上升沿)
        //===B3~0伺服命令
        b4运动命令(上升沿)
        b5 0等待旧命令位置完成
        b6  0绝对位置           1相对位置【6F》SYNC》7F》SYNC】
        b7上升沿复位
           b8 1暂停    0继续(Halt)
           b9 1使用上次的旧速度
6040=相对运动6F》7F;    =绝对运动2F》3F;   =停止10F;
 (例如:速度模式0x6060=3;0x1400-02=240; 
                                            》0x6040=0x06;
                                            》SYNC();
                                            》0x6040=0x0f;
                                            》SYNC();
                                            )电机就转了
6041h 状态字 b0 准备启动  1637定位完成, 1237定位中
            b1 准备完成
            b2 伺服使能
            b3 异常
              b4 电压输出
              b5 急停
              b6 伺服关闭
              b7 警告
            b8 保留
            b9  远程控制
            b10 位置到达
            b11 内部限制激活(不支持)
              b12 pp:已收到位置命令
              b13 pp:跟踪误差
              b14 正转禁止
              b15 反转禁止
605Ah  急停时【刹车速度】
605Bh 停机下:0使能关闭 1开启动态刹车
605Eh 停机下:0使能关闭 1曲线刹车 2急刹
//===================================
6060h  伺服模式:0保留1位置3速度4扭矩6回零7补间位置8CSP位置(Ecat)9速度10转矩
6061h 当前伺服模式
6062h 命令位置
6063h 编码器位置(A2电机一圈 1280000 pulses,A3-B3 电机一圈 16777216 pulses)
6064h 电机位置
6065h 位置误差报警阀值 AL009 (60F4h当前位置误差值)
6067h 位置到达精度(位置小于精度,定位完成)
6068h 位置到达停留时间ms(超过停留时间,定位完成)
606Bh 命令速度
606ch 速度回授
606dh 速度到达范围
606eh 速度到达时间
606fh 零速准位
6071h 目标扭矩  -3000~3000
6072h 最大扭矩
6074h 内部扭矩值(千分比)
6075h 电机额定电流(mA)
6076h 电机额定扭矩
6077h 回授扭矩
6078h 回授电流
607Ah 目标位置  int32位
607Ch 原点偏移
607Dh 软限位 -01负限位(-2147483648) -02正限位
607Eh 指令极性
607fh 最高速度命令
6080h 电机最高速度
6081h 定位速度
6082h 起跳速度(冲击速度)【Jerk】
6083h 加速度
6084h 减速度
6085h 急停斜率
6087h 扭矩斜率
6091.01h 齿轮比分子
6091.02h 齿轮比分母
6093h 齿轮比 -01分母 -02分子   (适用于台达)
  6098h 回零模式 35当前位置为零点
  6099h 回零速度 -01高速(1~2000) -02低速(1~500)
  609Ah 回零加速度
60FCh位置需求值-1
60fdh DI输入信号(b0负 b1正 b2原点)
60feh DO输出信号(b16 DO1,b17 DO2)
60ffh 速度模式:目标速度

台达: P3.00对应 0x2300  // P1.44对应 0x212C  //  2000 + 3.00是 0x2300

//60B0h位置偏移-1
//60B1h速度偏移-1
//60B2h扭矩偏移-1
//60C0h插值子模式选择-1
//60C1h插值数据记录-1
//60C2h插值时间段-1
//60C5h最大加速度-1
//60C6h最大减速度-1
//60F2h定位选项代码-1
//60F4h跟随误差实际值0
//60FCh位置需求值-1
//set_region(0x2154, 0, 1);// 输入io电平逻辑 0正逻辑1反
//60FDh  输入 IO 状态0(bit0:负限位 bit1:正限位 bit2:原点信号 bit16:快速停止
//60FEh-01  物理输出控制
//60FEh-02  物理输出使能
//60FFh目标速度0
//6502h支持的驱动模式【1支持】d0轮廓位置pp,d1变频调速vl,d2轮廓速度pv,d3轮廓转矩,d5回零,d6插补模式ip,d7周期同步位置csp,d8周期同步速度csv,d9周期同步转矩cst

电机模式: 

动作激活  6040=  06》07》0F 》1F

输入IO状态  60FD+00【u32】  bit0:负限位 bit1:正限位 bit2:原点信号 bit16:快速停止
DO  60FE+01 
    60FE+02
当 2005+01/02 的功能设置为 bit4 时,IO 输
出为主站控制;bit16 对应 out1,bit17 对应
out2.必须当 60fe+01 和 60fe+02 进行的是
与操作

6060 =  // 【1位置 3速度 4扭矩 6回零 7补间
===========================【定位】=============
定位模式:6060=1
分子        6093-2
分母        6093-1
定位         607a
读电机位置    6064
定位时速度      6081
读电机速度      606c

起跳速度    6082    【模式 1 起跳速度和停止速度】
加速        6083    计算方式:【100转】 = (100 * 圈脉冲)
减速        6084
速度上限

位置超差    6065
当前超差    60f4
到位准位    6067【时间 6068】

============================【速度】=============
速度模式:6060=3
设置速度        60ff     【壹x圈脉冲】是每秒一转
读取速度        606c    
零速准位        606f

=============================【回零】============
回零模式:6060=6

回零方式        6098     35当前位置为零点
回零高速        6099h 回零速度 -01高速(1~2000) 
回零低速                      -02低速(1~500)
回零加/减速度    609A
原点偏置        607c







=============================【支持】============
1pp 2vl 3pv 4tq 6hm 7ip 8csp 9csv 10cst

6502h bit0轮廓pp  bit1变频vl  2轮廓速度pv  3轮廓转矩pt  4空  5回零hm  6插补ip  
         7同步位置csp   8同步速度csv   9同步力矩cst

USB_CAN 二次开发函数库

USB_CAN二次开发库源程序:


using help;
using Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

// 1先引用  CAN_namespace   用于参数类型 //  using USB_CAN;
// 2再引用 help的方法

namespace CAN_namespace
{
    #region 全局类型:通讯模块接口
    /// <summary>
    /// 全局类型:通讯模块接口
    /// </summary>
    public interface Iface_RTX
    {
        /// <summary>
        /// 连接
        /// </summary>
        /// <typeparam name="T">连接</typeparam>
        /// <param name="a">ip地址</param>
        /// <param name="b">端口</param>
        /// <returns></returns>
        bool connect<T>(T a, T b);


        /// <summary>
        /// 发送并接收
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cmd"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        bool sendAndReceive<T>(string cmd, ref T t);//万物皆文本


        /// <summary>
        /// 接收事件
        /// </summary>
        /// <returns></returns>
        int RXs(ref object obj);
        //int RXs<T>(ref T obj);//测试

        /// <summary>
        /// 只发送
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        bool TXs(string cmd);

        /// <summary>
        /// 断开
        /// </summary>
        /// <returns></returns>
        bool Close();

    }
    #endregion
    #region 对象
    public class Help_USB_CAN : Iface_RTX
    {//作者qq750273008 祁成  更新日期:2023.06.23
        //===============================================
        //[Browsable(true)]    //可浏览
        //[Category("自定义属性")]   // 属性分栏
        //[Description("属性的说明")]   //
        //================================================
        #region 全局
        //1申明委托》委托命令
        public delegate void WT_GET_Data(CiA402 ciA402);//↑ui显示发送命令
        public delegate void WT_SET_Data(byte[] butes, string data);//↓下执行对象

        #region CAN参数类型:全局;【设备类型,主板信息,单帧信息,配置表

        #region 备用接口声明
        public interface Iface_RTX
        {
            /// <summary>
            /// 连接
            /// </summary>
            /// <typeparam name="T">连接</typeparam>
            /// <param name="a">ip地址</param>
            /// <param name="b">端口</param>
            /// <returns></returns>
            bool connect<T>(T a, T b);


            /// <summary>
            /// 发和收
            /// </summary>
            /// <param name="cmd"></param>
            /// <returns></returns>
            bool sendAndReceive<T>(string cmd, ref T t);


            /// <summary>
            /// 接收事件
            /// </summary>
            /// <returns></returns>
            int RXs(ref object obj);

            /// <summary>
            /// 只发送
            /// </summary>
            /// <param name="cmd"></param>
            /// <returns></returns>
            bool TXs(string cmd);

            /// <summary>
            /// 断开
            /// </summary>
            /// <returns></returns>
            bool Close();



        }

        #endregion

        /*------------兼容ZLG的数据类型---------------------------------*/
        //1.设备类型
        /// <summary>
        /// 设备类型(单路,双路,以太网CAN)
        /// </summary>
        public enum DeviceType : byte   //设备类型【单路,双路,以太网can】
        {
            DEV_USBCAN = 3, // USB单路CAN
            DEV_USBCAN2 = 4,// USB双路CAN
            VCI_USBCAN1 = 3,
            VCI_USBCAN2 = 4,// 我买的属于这个(创新科技CAN-Pro)
            VCI_USBCAN2A = 4,
            VCI_USBCAN_E_U = 20,// 以太网单路CAN
            VCI_USBCAN_2E_U = 21// 以太网双路CAN
        }
        //设备类型
        public enum CAN设备类型 : int
        {
            DEV_USBCAN = 3, // USB单路CAN
            DEV_USBCAN2 = 4,// USB双路CAN
            VCI_USBCAN1 = 3,
            VCI_USBCAN2 = 4,
            VCI_USBCAN2A = 4,
            VCI_USBCAN_E_U = 20,// 以太网单路CAN
            VCI_USBCAN_2E_U = 21// 以太网双路CAN

        }

        //2.ZLGCAN系列接口卡信息的数据类型。
        /// <summary>
        /// 主板信息
        /// </summary>
        public struct VCI_BOARD_INFO //主板信息
        {
            public UInt16 hw_Version;// 硬件版本号,用16进制表示。比如0x0100表示V1.00。
            public UInt16 fw_Version;// 固件版本号, 
            public UInt16 dr_Version;// 驱动程序版本号,
            public UInt16 in_Version;// 接口库版本号,
            public UInt16 irq_Num;// 保留参数。
            public byte can_Num;// 表示有几路CAN通道。
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] str_Serial_Num;// 此板卡的序列号。
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
            public byte[] str_hw_Type;// 硬件类型,比如“USBCAN V1.00”(注意:包括字符串结束符’\0’)
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public byte[] Reserved;// 系统保留。
        }

        //2-1.USB-CAN总线适配器板卡信息的数据类型1,该类型为VCI_FindUsbDevice函数的返回参数。
        /// <summary>
        /// USB-CAN设备主板信息,该类型为VCI_FindUsbDevice函数的返回参数。
        /// </summary>
        public struct VCI_BOARD_INFO1 // 读取USB主板信息
        {//该类型为VCI_FindUsbDevice函数的返回参数。

            public UInt16 hw_Version;// 硬件版本号,用16进制表示。比如0x0100表示V1.00。
            public UInt16 fw_Version;// 固件版本号, 
            public UInt16 dr_Version;// 驱动程序版本号,
            public UInt16 in_Version;// 接口库版本号,
            public UInt16 irq_Num;// 保留参数。
            public byte can_Num;// 表示有几路CAN通道。
            public byte Reserved;// 保留。
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
            public byte[] str_Serial_Num;// 此板卡的序列号。
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public byte[] str_hw_Type;// 硬件类型,比如“USBCAN V1.00”(注意:包括字符串结束符’\0’)
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
            public byte[] str_Usb_Serial;// usb串口号?
        }
        //3.定义CAN每帧的数据类型。
        /// <summary>
        /// CAN的每一帧详细信息(每帧对象)
        /// </summary>
        public unsafe struct VCI_CAN_OBJ  //CAN每帧对象,
        {
            public uint ID;// 帧ID。        u32变量,数据格式为靠右对齐。 详情请参照: 《8.附件1: ID对齐方式.pdf》说明文档。
            public uint TimeStamp;        //u32,设备接收到某一帧的时间标识。 时间标示从CAN卡上电开始计时,计时单位为0.1ms。
            public byte TimeFlag;         //是否使用时间标识,=1时TimeStamp有效
            public byte SendType;         //发送类型。=0时为正常发送(发送失败会自动重发,重发超时时间为4秒, 4秒内没有发出则取消);=1时为单次发送
            public byte RemoteFlag;       //是否远程帧标志。 =0时为数据帧, =1时为远程帧(数据段空)。
            public byte ExternFlag;       //是否扩展帧标志。 =0时为标准帧(11位ID), =1时为扩展帧(29位ID)。
            public byte DataLen;          //数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中有效字节数。
            public fixed byte Data[8];    //如DataLen定义为3,即Data[0]、 Data[1]、 Data[2]是有效的。
            public fixed byte Reserved[3];//系统保留。
        }

        //4.定义配置CAN的数据类型
        /// <summary>
        /// USB-CAN2、配置表(参数表)
        /// </summary>
        public struct VCI_INIT_CONFIG // 配置参数
        {
            public UInt32 AccCode;// bit全部匹配,此帧才可以被接收。否则不能接收。(//标准帧右移21bit分析)
                                  // 相当于机械钥匙,凸起和凹槽必须匹配,才能开锁(接收)
                                  // b31对应帧id最高位,所以11bit要左移21bit(//分析标准帧时右移21bit)
            public UInt32 AccMask;// 屏蔽码。使AccCode的某个bit功能失效。左对齐,bit31~bit21置1为屏蔽AccCode的bit匹配功能,
                                  // 相当于机械钥匙的bit销位,是否失效(bit置1为失效)
                                  // 。屏蔽码推荐设置为0xFFFFFFFF,即全部接收。
            public UInt32 Reserved;// 保留。
            public byte Filter;   //0或1接收所有帧。2标准帧滤波,3是扩展帧滤波。
            public byte Timing0;  //波特率参数,具体配置,请查看二次开发库函数说明书。(1Mbps):《Timing0=0x00,Timing1=0x14》
            public byte Timing1;    // 0x14       波特率参数 1MHz(T0=0x00,T1=0x14)
            public byte Mode;     //模式,0表示正常模式,1表示只听模式,2自测模式
        }
        /*------------其他数据结构描述---------------------------------*/
        public struct CHGDESIPANDPORT // 扩展端口?
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] szpwd;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] szdesip;
            public Int32 desport;

            public void Init()
            {
                szpwd = new byte[10];
                szdesip = new byte[20];
            }
        }

        public struct VCI_FILTER_RECORD //过滤器配置表
        {
            UInt32 ExtFrame;// 过滤的帧类型标志,为1代表要过滤的为扩展帧,为0代表要过滤的为标准帧。
            UInt32 Start;//滤波范围的起始帧ID 
            UInt32 End;//滤波范围的结束帧ID 
        }

        /*------------数据结构描述完成---------------------------------*/



        #endregion

        #endregion
        #region 字段
        //private CancellationTokenSource cts;//线程令牌
        //private ManualResetEvent resetEvent = new ManualResetEvent(true);// 暂停业务
        //=====================================================
        Help_String help_String = new Help_String();//文本处理
        //设备类型
        public const int DEV_USBCAN = 3;// USB单路CAN
        public const int VCI_USBCAN2 = 4;// USB双路CAN
        public const int VCI_USBCAN2A = 4;// USB双路CAN
        // E表示以太网
        public const int VCI_USBCAN_E_U = 20;// 以太网单路CAN
        public const int VCI_USBCAN_2E_U = 21;// 以太网双路CAN

        public const uint CAN1 = 0;
        public const uint CAN2 = 1;
        uint CAN_inD = 0;// USB设备:编号   【第几个硬件(类似com3)



        #endregion
        #region 属性
        //public Iface_RTX help_rtx { get; set; }// 接口扩展
        /// <summary>
        /// ↑:byte包,文本
        /// </summary>
        static public WT_GET_Data Wt_get;//↑event委托=》呼叫上ui层  让上层显示:发送命令
        /// <summary>
        /// ↓:byte包,文本
        /// </summary>
        static public WT_SET_Data Wt_set;//↓委托=》呼叫下位机执行

        /// <summary>
        /// USB设备id
        /// </summary>
        public uint can_deviceID  { get => CAN_inD; private set => CAN_inD = value; }


        #endregion
        #region 构造

        #endregion
        #region 事件

        #endregion

        #region APP方法【打开,关闭,收,发,复位
        /// <summary>
        /// 打开CAN:设备类型,硬件id,CAN通道id,参数表
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="pInitConfig"></param>
        /// <returns></returns>
        public UInt32 set_Open()//(DeviceType deviceType, uint deviceID,uint canX, VCI_INIT_CONFIG Config)// 设备类型,设备id,通道,配置参数
        {//打开CAN:设备类型,硬件索引,CAN通道索引,初始化参数,

            UInt32 DeviceType = (uint)CAN设备类型.VCI_USBCAN2;// USB双路CAN
            UInt32 DeviceInd = 0;// 第0个设备

            VCI_INIT_CONFIG config = new VCI_INIT_CONFIG();// 参数表
            config.AccCode = 0;//bit钥匙匹配id,(右移21bit分析)b31对应帧id最高位
            config.AccMask = 0xffffffff;// 屏蔽码。使AccCode的某个bit功能失效。左对齐,bit31~bit21置1为屏蔽AccCode的bit匹配功能,
            config.Filter = 0;//0或1接收所有帧。2标准帧滤波,3是扩展帧滤波。
            config.Timing0 = 0x00;//波特率参数 1MHz     //波特率参数 1MHz(T0=0x00,T1=0x14)
            config.Timing1 = 0x14;
            config.Mode = 0;//模式,0表示正常模式,1表示只听模式,2自测模式

            UInt32 sta = 1;// 故障标记
            sta &= set_Close(DeviceType, DeviceInd);// 关
            sta &= VCI_使能硬件(DeviceType, DeviceInd);// 开

            sta &= VCI_初始化硬件(DeviceType, DeviceInd, CAN1, ref config);// CAN 1
            sta &= VCI_初始化硬件(DeviceType, DeviceInd, CAN2, ref config);// CAN 2

            sta &= VCI_CAN工作(DeviceType, DeviceInd, CAN1);// CAN 1
            sta &= VCI_CAN工作(DeviceType, DeviceInd, CAN2);// CAN 2

            return (uint)(sta == 1 ? 1 : 0);

            //================================
            //VCI_INIT_CONFIG config = new VCI_INIT_CONFIG();// 参数表
            //config.AccCode = 0;// bit全部匹配,此帧才可以被接收。否则不能接收。(//标准帧右移21bit分析)
            //config.AccMask = 0xffffffff;//全部接收  // 屏蔽码。使AccCode的某个bit功能失效。左对齐,bit31~bit21置1为屏蔽AccCode的bit匹配功能,
            //config.Filter = 0;//0或1接收所有帧。2标准帧滤波,3是扩展帧滤波。
            //config.Timing0 = 0x00;//波特率参数 1MHz(T0=0x00,T1=0x14)
            //config.Timing1 = 0x14;
            //config.Mode = 0;//模式,0表示正常模式,1表示只听模式,2自测模式

            //uint i = can.set_Open(Help_USB_CAN.VCI_USBCAN2, 0, 0, ref config);// CAN 1
            //i &= can.set_Open(Help_USB_CAN.VCI_USBCAN2, 0, 1, ref config);// CAN 2

        }
        /// <summary>
        /// 关闭CAN:设备类型,硬件id
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <returns></returns>
        public UInt32 set_Close(UInt32 DeviceType, UInt32 DeviceInd)// UInt32 DeviceType, UInt32 DeviceInd
        {
            UInt32 sta = 1;// 故障标记
            //UInt32 DeviceType = (uint)CAN设备类型.VCI_USBCAN2;// USB双路CAN

            sta &= VCI_CloseDevice(DeviceType, 0);
            VCI_UsbDeviceReset(DeviceType, DeviceInd, 0);
            return sta;
        }
        public UInt32 set_offLink(UInt32 DeviceType, UInt32 DeviceInd)
        {
            UInt32 sta = 1;// 故障标记
            VCI_UsbDeviceReset(DeviceType,DeviceInd,0);
            return sta;
        }
        /// <summary>
        /// 获取CAN设备厂家信息:设备类型,设备索引,参数表
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="pInfo"></param>
        /// <returns></returns>
        public UInt32 get_Board(UInt32 DeviceType, UInt32 DeviceInd, ref VCI_BOARD_INFO pInfo)
        {
            return VCI_ReadBoardInfo(DeviceType, DeviceInd, ref pInfo);
            //=======================================================
            //VCI_BOARD_INFO info = new VCI_BOARD_INFO();
            //uint i = can.get_Board(4, 0, ref info);

        }
        public UInt32 set_ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd)
        {
            uint sta = 1;
            sta &= VCI_ClearBuffer(DeviceType, DeviceInd, CANInd);
            //sta&= VCI_ClearBuffer(DeviceType, DeviceInd, 1);// CAN2
            return sta;
        }
        /// <summary>
        /// 接收:设备类型,设备索引,CAN通道索引,参数表,最大帧数2000,超时时间
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="pReceive"></param>
        /// <param name="Len">不大于2000帧</param>
        /// <param name="WaitTime">保留=0</param>
        /// <returns></returns>
        public Int32 get_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime)
        {//VCI_CAN_OBJ pReceive 设置为数组,2000帧
            return VCI_Receive(DeviceType, DeviceInd, CANInd, ref pReceive, Len, WaitTime);
        }
        /// <summary>
        /// 发送:设备类型,设备索引,CAN通道索引,参数表,要发帧数
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="pSend"></param>
        /// <param name="Len"></param>
        /// <returns></returns>
        public Int32 set_Send(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len)
        {
            return VCI_Transmit(DeviceType, DeviceInd, CANInd, ref pSend, Len);
        }

        public UInt32 set_ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd)
        {// 复位can通道
            return VCI_ResetCAN(DeviceType, DeviceInd, CANInd);
        }
        //==以下不再重要=======================================================================
        //==以下不再重要========




        /// <summary>
        /// 使能硬件:设备类型,设备id。
        /// </summary>
        /// <param name="DeviceType">Help_USB_CAN.VCI_USBCAN2 = 4;双路CAN</param>
        /// <param name="DeviceInd">第1个CAN通道是0</param>
        /// <param name="Reserved">备用=0</param>
        /// <returns></returns>
        public UInt32 VCI_使能硬件(UInt32 DeviceType, UInt32 DeviceInd)
        {// 相当于 插电
            return VCI_OpenDevice(DeviceType, DeviceInd, 0x00);
        }
        /// <summary>
        /// 初始化硬件:can设备类型,设备索引0,CAN通道索引0,VCI_INIT_CONFIG初始化参数结构
        /// </summary>
        /// <param name="DeviceType">can设备类型</param>
        /// <param name="DeviceInd">设备索引0</param>
        /// <param name="CANInd">CAN通道索引0</param>
        /// <param name="pInitConfig">VCI_INIT_CONFIG初始化参数结构</param>
        /// <returns></returns>
        public UInt32 VCI_初始化硬件(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_INIT_CONFIG pInitConfig)
        {
            return VCI_InitCAN(DeviceType, DeviceInd, CANInd, ref pInitConfig);
        }
        //设备类型。设备索引,CAN通道索引。初始化参数结构。

        /// <summary>
        /// CAN工作:设备类型,设备索引,CAN通道索引。(返回值=1,表示操作成功; =0表示操作失败; =-1表示USB-CAN设备不存在或USB掉线。)
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <returns></returns>
        public UInt32 VCI_CAN工作(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd)
        {
            return VCI_StartCAN(DeviceType, DeviceInd, CANInd);
        }

        /// <summary>
        /// CAN发送:设备类型,设备索引,CAN通道索引,VCI_CAN_OBJ数组的首指针,(帧数量 最大为10)
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="pSend"></param>
        /// <param name="Len"></param>
        /// <returns></returns>
        public Int32 VCI_set发送(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len)
        {
            return VCI_Transmit(DeviceType, DeviceInd, CANInd, ref pSend, Len);
        }

        public Int32 VCI_get缓存区帧数(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd)
        {
            return VCI_GetReceiveNum(DeviceType, DeviceInd, CANInd);// 缓存区,帧数
        }
        /// <summary>
        /// CAN接收:设备类型,设备id,CAN通道id,VCI_CAN_OBJ数组的首指针,封顶帧数 2000 ,超时时间ms
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="CANInd"></param>
        /// <param name="pReceive"></param>
        /// <param name="Len"></param>
        /// <param name="WaitTime"></param>
        /// <returns></returns>
        public Int32 VCI_get接收(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime)
        {
            //VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];
            //uint i = can.VCI_get接收(4, 0, 1, ref obj[0], 2000, 0);
            return VCI_Receive(DeviceType, DeviceInd, CANInd, ref pReceive, Len, WaitTime);
        }

        #endregion

        #region 后台方法
        #region 原始库方法

        //方法
        /*------------兼容ZLG的函数描述---------------------------------*/
        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved);
        //使能硬件//(通道不工作)
        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_CloseDevice(UInt32 DeviceType, UInt32 DeviceInd);
        //关闭硬件
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_InitCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_INIT_CONFIG pInitConfig);
        //初始化设备(参数配置)b31对应帧id最高位,所以11bit要左移21bit
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_ReadBoardInfo(UInt32 DeviceType, UInt32 DeviceInd, ref VCI_BOARD_INFO pInfo);
        //读取主板信息

        //EXTERNC DWORD __stdcall VCI_SetReference(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, DWORD RefType, void *pData);
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_SetReference(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, UInt32 RefType, ref VCI_FILTER_RECORD pData);
        //设置智能滤波表。(设备类型,设备id,通道id,参数类型,参数包)
        //RefType=1,添加滤波表 指向VCI_FILTER_RECORD结构的指针
        //RefType=2,可以启用滤波表
        //RefType=3,可以清除滤波表。

        [DllImport("controlcan.dll")]
        static extern Int32 VCI_GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        //缓存区可用帧数(<=2000帧)
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        //清空缓存区
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_StartCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        //CAN启动(启动can通道)
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        //CAN复位通道
        [DllImport("controlcan.dll")]
        static extern Int32 VCI_Transmit(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len);
        //CAN发送(单次<=10帧)
        [DllImport("controlcan.dll")]
        static extern Int32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime);
        //CAN接收(函数调用间隔至少应设置在5ms以上。)【Len为单次封顶帧数长度的数据被读出


        /*------------其他函数描述---------------------------------*/
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_ConnectDevice(UInt32 DevType, UInt32 DevIndex);
        //硬件线连接
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_UsbDeviceReset(UInt32 DevType, UInt32 DevIndex, UInt32 Reserved);
        //硬件线断开
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_FindUsbDevice(ref VCI_BOARD_INFO1 pInfo);
        //查找可用usb的设备
        //若计算机中插入多于4个适配器,则使用VCI_FindUsbDevice2函数,最大支持50个USB-CAN适配器。
        [DllImport("controlcan.dll")]
        static extern UInt32 VCI_FindUsbDevice2(ref VCI_BOARD_INFO pInfo);
        //如:VCI_BOARD_INFO pInfo[50]。 
        /*------------函数描述结束---------------------------------*/


        #endregion
        #endregion


        #region 窗体移动

        //private Point mouseOff;//鼠标移动位置变量
        //private bool leftFlag;//标签是否为左键
        //private void Frm_MouseDown(object sender, MouseEventArgs e)//鼠标按下
        //{
        //    if (e.Button == MouseButtons.Left)
        //    {
        //        mouseOff = new Point(-e.X, -e.Y); //得到变量的值
        //        leftFlag = true;                  //点击左键按下时标注为true;
        //    }
        //}
        //private void Frm_MouseMove(object sender, MouseEventArgs e)//鼠标移动
        //{
        //    if (leftFlag)
        //    {
        //        Point mouseSet = Control.MousePosition;
        //        mouseSet.Offset(mouseOff.X, mouseOff.Y);  //设置移动后的位置
        //                                                  //Location = mouseSet;//Form 窗体父类字段
        //    }
        //}
        //private void Frm_MouseUp(object sender, MouseEventArgs e)//鼠标松开
        //{
        //    if (leftFlag)
        //    {
        //        leftFlag = false;//释放鼠标后标注为false;
        //    }
        //}

        #endregion

        #region 错误
        //try
        //{
        //    byte[] buffer = Encoding.Default.GetBytes(data);//以计算机的编码发送
        //    serialPort.Write(buffer, 0, buffer.Length);//  开始发送
        //}
        //catch (Exception ex) { MessageBox.Show(ex.Message); }//显示错误
        #endregion

        #region 接口方法

        public bool connect<T>(T a, T b)
        {// 打开USB-CAN设备的can1通道

            //throw new NotImplementedException();

            // 配置表
            VCI_INIT_CONFIG config = new VCI_INIT_CONFIG();
            config.AccCode = 0;// bit的DNA全部匹配,此帧才可以被接收。否则不能接收。(//标准帧右移21bit分析)
            config.AccMask = 0xffffffff;// 屏蔽码。使AccCode的某个bit功能失效。左对齐,bit31~bit21置1为屏蔽AccCode的bit匹配功能,
            config.Filter = 0;//0或1接收所有帧。2标准帧滤波,3是扩展帧滤波。
            config.Timing0 = 0x00;//波特率参数 1MHz(T0=0x00,T1=0x14)
            config.Timing1 = 0x14;
            config.Mode = 0;//模式,0表示正常模式,1表示只听模式,2自测模式

            uint sta = 1;
            //sta = VCI_CloseDevice(VCI_USBCAN2, CAN_inD);//设备关
            sta &= VCI_OpenDevice(Help_USB_CAN.VCI_USBCAN2, CAN_inD, 0);//使能设备(开)
            sta &= VCI_InitCAN(Help_USB_CAN.VCI_USBCAN2, CAN_inD, CAN1, ref config);// CAN 1
            sta &= VCI_StartCAN(Help_USB_CAN.VCI_USBCAN2, CAN_inD, CAN1);// 通道(开)

            //sta &= VCI_InitCAN(Help_USB_CAN.VCI_USBCAN2, CAN_ind, CAN2, ref config);// CAN 2
            //sta &= VCI_StartCAN(Help_USB_CAN.VCI_USBCAN2, CAN_ind, CAN2);// 通道(开)
            return sta == 1 ? true : false; // 1完成,0故障
        }

        public unsafe object sendAndReceive(string stringcmd)
        {//"1803052000018705"
            //"04000006014000200000000000"
            //throw new NotImplementedException();
            stringcmd.Replace(" ", "");//去除空白
            byte[] buffercmd = help_String.StringsToHexBytes(stringcmd);//准备报文  //去除空白   StringsToHexBytes

            VCI_CAN_OBJ[] buffer = new VCI_CAN_OBJ[10];
            string id = stringcmd.Substring(7, 3);// 从站地址
            buffer[0].ID = Convert.ToUInt32(id, 16);//从站id
            buffer[0].SendType = 1;//发送帧类型。=0时为正常发送(发送失败会自动重发,重发超时时间为4秒, 4秒内没有发出则取消);=1时为单次发送
            buffer[0].DataLen = buffercmd[0];//数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节。

            buffer[0].Data[0] = buffercmd[5];
            buffer[0].Data[1] = buffercmd[6];
            buffer[0].Data[2] = buffercmd[7];
            buffer[0].Data[3] = buffercmd[8];
            buffer[0].Data[4] = buffercmd[9];
            buffer[0].Data[5] = buffercmd[10];
            buffer[0].Data[6] = buffercmd[11];
            buffer[0].Data[7] = buffercmd[12];

            Int32 ret = VCI_set发送(Help_USB_CAN.VCI_USBCAN2, 0, 0, ref buffer[0], 1);



            //byte[] buff = SendAndReceive(buffer, stringcmd);//  收发报文

            //if (buff == null) throw new Exception("ACK 未应答。。。");

            //if (buff != null)
            //{
            //    string str = help_String.BytesToHexStrings(buff);
            //    return str;
            //}
            return null;// 发送后未接收





        }

        //public unsafe object RXs()//  VCI_CAN_OBJ  //CAN帧参数
        //{
        //    //throw new NotImplementedException();

        //    // 缓存区可用帧数=============================
        //    uint i = VCI_GetReceiveNum(Help_USB_CAN.VCI_USBCAN2, 0, 0);// can1
        //    if (i > 0)// CAN1接收帧数
        //    {
        //        uint CAN1 = 0;
        //        VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];// 创新科技CAN分析仪最大支持2000帧接收
        //        i = VCI_get接收(4, 0, CAN1, ref obj[0], 1, 0);//  注意 CAN1  通道

        //        #region 取一帧
        //        //=================================================
        //        byte[] bytes = new byte[13];
        //        bytes[0] = obj[0].DataLen;   //obj[0].
        //        uint id = obj[0].ID; //       帧 id
        //        bytes[1] = (byte)(id >> 24);
        //        bytes[2] = (byte)(id >> 16);
        //        bytes[3] = (byte)(id >> 8);
        //        bytes[4] = (byte)(id & 0xff);

        //        fixed (VCI_CAN_OBJ* p_obj = &obj[0])// 8字节    第一帧
        //        {
        //            bytes[5] = p_obj->Data[0];
        //            bytes[6] = p_obj->Data[1];
        //            bytes[7] = p_obj->Data[2];
        //            bytes[8] = p_obj->Data[3];
        //            bytes[9] = p_obj->Data[4];
        //            bytes[10] = p_obj->Data[5];
        //            bytes[11] = p_obj->Data[6];
        //            bytes[12] = p_obj->Data[7];
        //        }
        //        return bytes;

        //        #endregion
        //        //return obj;
        //    }
        //    return null;
        //}

        //public unsafe object RXs()//  VCI_CAN_OBJ  //CAN帧参数
        //{// CAN1 接收

        //    //throw new NotImplementedException();

        //    // 缓存区可用帧数=============================
        //    //Help_Delay.delayTime(0.005);// usb大概3~5ms
        //    int i = VCI_GetReceiveNum(Help_USB_CAN.VCI_USBCAN2, CAN_inD, CAN1);// can1
        //    if (i > 0)// CAN1接收帧数
        //    {
        //        VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];// 创新科技CAN分析仪最大支持2000帧接收,间隔5ms以上
        //        i = VCI_get接收(4, 0, CAN1, ref obj[0], 2000, 50);//  注意 CAN1  通道

        //        #region 取一帧      【1   4    8】
        //        //=================================================
        //        //byte[] bytes = new byte[13];
        //        //bytes[0] = obj[0].DataLen;   //  【1】有效字节
        //        //uint id = obj[0].ID; //     帧 id【4】帧id
        //        //bytes[1] = (byte)(id >> 24);
        //        //bytes[2] = (byte)(id >> 16);
        //        //bytes[3] = (byte)(id >> 8);
        //        //bytes[4] = (byte)(id & 0xff);

        //        //fixed (VCI_CAN_OBJ* p_obj = &obj[0])// 8字节    第一帧
        //        //{
        //        //    bytes[5] = p_obj->Data[0];//  【8】包数据
        //        //    bytes[6] = p_obj->Data[1];
        //        //    bytes[7] = p_obj->Data[2];
        //        //    bytes[8] = p_obj->Data[3];
        //        //    bytes[9] = p_obj->Data[4];
        //        //    bytes[10] = p_obj->Data[5];
        //        //    bytes[11] = p_obj->Data[6];
        //        //    bytes[12] = p_obj->Data[7];
        //        //}
        //        //return bytes;

        //        #endregion
        //        return obj;
        //    }
        //    return null;
        //}
        //public unsafe object TXs(string stringcmd)// "04000006014000200000000000"
        //{//被CanOpen调用

        //    //throw new NotImplementedException();

        //    stringcmd.Replace(" ", "");//去除空白
        //    byte[] buffercmd = help_String.StringsToHexBytes(stringcmd);//准备报文  //去除空白   StringsToHexBytes

        //    VCI_CAN_OBJ[] buffer = new VCI_CAN_OBJ[10];
        //    string id = stringcmd.Substring(2, 8);// 从站地址  00000601
        //    buffer[0].ID = Convert.ToUInt32(id, 16);//从站id
        //    // 重发功能 0开,1关
        //    buffer[0].SendType = 0;//0(4秒内有重发)  1只发单次
        //    buffer[0].DataLen = buffercmd[0];//数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节。

        //    buffer[0].Data[0] = buffercmd[5];// byte8
        //    buffer[0].Data[1] = buffercmd[6];
        //    buffer[0].Data[2] = buffercmd[7];
        //    buffer[0].Data[3] = buffercmd[8];
        //    buffer[0].Data[4] = buffercmd[9];
        //    buffer[0].Data[5] = buffercmd[10];
        //    buffer[0].Data[6] = buffercmd[11];
        //    buffer[0].Data[7] = buffercmd[12];

        //    Int32 ret = VCI_set发送(Help_USB_CAN.VCI_USBCAN2, 0, CAN1, ref buffer[0], 1);// 库函数(发送TXs)
        //    return ret;
        //}

        //public int Close()
        //{
        //    //throw new NotImplementedException();
        //    return (int)set_Close((UInt32)CAN设备类型.VCI_USBCAN2, CAN_inD);
        //}

        public bool sendAndReceive<T>(string cmd, ref T t)
        {
            throw new NotImplementedException();
        }

        public int RXs(ref object Obj)
        {
            //throw new NotImplementedException();
            // 缓存区可用帧数=============================
            //Help_Delay.delayTime(0.005);// usb大概3~5ms
            //int i = VCI_GetReceiveNum(Help_USB_CAN.VCI_USBCAN2, CAN_inD, CAN1);// can1

            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];// 创新科技CAN分析仪,单次最大支持2000帧接收,间隔5ms以上
            int num = VCI_get接收(4, 0, CAN1, ref obj[0], 2000, 50);//  注意 CAN1  通道
            if (num > 0)// CAN1接收帧数
            {
                Obj = obj;
                return num;

                #region 取一帧      【1   4    8】
                //=================================================
                //byte[] bytes = new byte[13];
                //bytes[0] = obj[0].DataLen;   //  【1】有效字节
                //uint id = obj[0].ID; //     帧 id【4】帧id
                //bytes[1] = (byte)(id >> 24);
                //bytes[2] = (byte)(id >> 16);
                //bytes[3] = (byte)(id >> 8);
                //bytes[4] = (byte)(id & 0xff);

                //fixed (VCI_CAN_OBJ* p_obj = &obj[0])// 8字节    第一帧
                //{
                //    bytes[5] = p_obj->Data[0];//  【8】包数据
                //    bytes[6] = p_obj->Data[1];
                //    bytes[7] = p_obj->Data[2];
                //    bytes[8] = p_obj->Data[3];
                //    bytes[9] = p_obj->Data[4];
                //    bytes[10] = p_obj->Data[5];
                //    bytes[11] = p_obj->Data[6];
                //    bytes[12] = p_obj->Data[7];
                //}
                //return bytes;

                #endregion
               
            }

            return 0;
        }
        //public int RXs<T>(ref T obj)// 待测试
        //{
        //    //throw new NotImplementedException();
        //    VCI_CAN_OBJ[] obj2 = new VCI_CAN_OBJ[2000];// 创新科技CAN分析仪最大支持2000帧接收,间隔5ms以上
        //    int num = VCI_get接收(4, 0, CAN1, ref obj2[0], 2000, 50);
        //    if (num > 0)// CAN1接收帧数
        //    {
        //        //obj = obj2.ToList<T>();

        //        return num;
        //    }
        //    return 0;


        //    }
        unsafe bool CAN_namespace.Iface_RTX.TXs(string cmd)//  "08   00000602  4064600000000000"
        {// 1  4  8 (DLC字节,帧id,Byte8数据包)
            //throw new NotImplementedException();
            cmd.Replace(" ", "");//去除空白
            byte[] buffercmd = help_String.StringsToHexBytes(cmd);//准备报文  //去除空白   StringsToHexBytes
            VCI_CAN_OBJ[] buffer = new VCI_CAN_OBJ[10];
            string id = cmd.Substring(2, 8);// 从站地址  00000602
            // 4   
            buffer[0].ID = Convert.ToUInt32(id, 16);//从站id
            // 重发功能 0开,1关
            buffer[0].SendType = 0;//0(4秒内有重发)  1只发单次
            // 1      
            buffer[0].DataLen = buffercmd[0];//数据长度 DLC (<=8),即CAN帧Data有几个字节。约束了后面Data[8]中的有效字节。
            // 8           
            buffer[0].Data[0] = buffercmd[5];// byte8
            buffer[0].Data[1] = buffercmd[6];
            buffer[0].Data[2] = buffercmd[7];
            buffer[0].Data[3] = buffercmd[8];
            buffer[0].Data[4] = buffercmd[9];
            buffer[0].Data[5] = buffercmd[10];
            buffer[0].Data[6] = buffercmd[11];
            buffer[0].Data[7] = buffercmd[12];
            // 上车
            Int32 ret = VCI_set发送(Help_USB_CAN.VCI_USBCAN2, 0, CAN1, ref buffer[0], 1);// 库函数(发送TXs)
            if (ret == 1) return true;

            return false;
        }

        bool CAN_namespace.Iface_RTX.Close()
        {
            //throw new NotImplementedException();
            if (set_Close((UInt32)CAN设备类型.VCI_USBCAN2, CAN_inD) == 1)
            {
                return true;
            }
            return false;
        }

       


        #endregion

    }


    #endregion




}



CanOpen函数库

程序太长,在另一篇文章内。
Help_CANopen
Help_PLCopen

调试笔记:


1伺服恢复出厂   0x1011-04= 0x64616f6c;
要等待超过1秒才会有回帧。
子索引2有效:
子索引4有效:回帧60成功


2伺服参数保存   0x1010-01= 0x65766173;


//==================================
对象1011h:恢复默认参数【0x64616f6c】
•子索引00h包含支持的最高子索引。
•子索引01h是指可以恢复的所有参数。
•子索引02h是指与通信相关的参数(索引从1000h到1FFFh)。
•子索引03h是指与应用程序相关的参数(索引从6000h到9FFFh)。
•从04h到7Fh的子指数制造商可以恢复他们个人选择的参数。
•保留80h至FEh的子索引以备将来使用。

对象1010h:存储参数【0x65766173】
•子索引00h包含支持的最高子索引。
•子索引01h是指可能存储在CANopen设备上的所有参数。
•子索引02h是指与通信相关的参数(从1000h到1FFFh的索引)。
•子索引03h是指与应用程序相关的参数(索引从6000h到9FFFh)。
•从04h到7Fh的子索引制造商可以单独存储他们选择的参数。
•保留80h至FEh的子索引以备将来使用。

 广成io模块:

输出:

输入: 

1号表示第1片,9号表示第9片。

案例:

站号3,第9片,输出D7和D4【0x88】

数据发:

DLC=8
帧id=0x303
数据包= 0x88 0x00 0x00 0x00 0x00 0x00 0x00 0x00 【D0..D7】

雷赛io模块:

CAN总线:             EM32DX-C2

6100H   DI输入寄存器 16-bit (16位输入) 
        00H U8 子索引总数 
        01H Unsigned16 IN16bit(上传16位输入值)【读DI 15~0】
        02H Unsigned16 IN16bit(上传16位输入值)【读DI 31~16】
        03H Unsigned16 IN16bit(上传16位输入值)【读DI 47~32】
 
6300H   DO输出寄存器 16-bit 
        00H U8 子索引总数 
        01H Unsigned16 OUT16bit(设置16位输出值)【写DO 15~0】
        02H Unsigned16 OUT16bit(设置16位输出值)【写DO 31~16】
        03H Unsigned16 OUT16bit(设置16位输出值)【写DO 47~32】

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

ECAT总线:          EM32DX-E2

6000H   DI输入寄存器 16-bit (16位输入) 
        00H U8 子索引总数 
        01H Unsigned16 IN16bit(上传16位输入值)【读DI 15~0】
        02H Unsigned16 IN16bit(上传16位输入值)【读DI 31~16】
        03H Unsigned16 IN16bit(上传16位输入值)【读DI 47~32】
 
7000H   DO输出寄存器 16-bit 
        00H U8 子索引总数 
        01H Unsigned16 OUT16bit(设置16位输出值)【写DO 15~0】
        02H Unsigned16 OUT16bit(设置16位输出值)【写DO 31~16】
        03H Unsigned16 OUT16bit(设置16位输出值)【写DO 47~32】

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值