CANopen库【CAN总线】

说明

 用于CANopen总线伺服的调试

目前主要用于:

                          雷赛DMA882-CAN

设计

伺服        x1轴 = new        伺服(站号1+);

x1轴.写参数(【参数地址】,【】);

//=================================

x1轴.电流(1);//【1A】

x1轴.齿轮比(分子,分母);//【圈脉冲】

x1轴.模式(定位);//【定位】【力矩】【速度】【回零】

x1轴.使能

x1轴.回零

x1轴.停止

x1轴.复位

x1轴.绝对

x1轴.相对

x1轴.力矩

x1轴.速度

x1轴.绝对(坐标,0);//        不立即执行

加速度

减速度

雷赛DMA882-CAN

更新24.5.6

增加oop概念。

 旧版本:

#define debug
using CAN_namespace;
using help;
using Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using static BLL.CANopen;
using static System.Net.Mime.MediaTypeNames;
//===============
#region Cia定义
//指数(十六进制)    目标

//0000               未使用

//0001 - 025F        数据类型

//0260 - 0FFF 保留供进一步使用

//1000-1FFF        》通信参数 区域 CiA301(DS301)

//2000-5FFF        》硬件参数 区域(生产制造商特定)

//6000-9FFF        》标准化,运动控制 区域 CiA402(DSP402)

//A000-AFFF        网络可变区域

//B000-BFFF        系统可变区

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

#endregion
#region Cia301_通讯层
//1000h 装置类型(硬件识别码0x40192)   04020192 h : A2 Series    06020192h : M Series
//1001h 错误寄存器4组
//1003h 预定义错误4组 (-01:58120)
//1005h  COB-ID SYNC码 0x80(同步码)
//1006h 通信周期0
//100Ch 保护时间0
//100Dh 寿命因数0
//1010h 参数保存4组(0x1010_0x04=0x65766173;回帧大于2秒)
//1011h 恢复默认参数4组(0x1011_4=0x64616F6C;不要保存,只要断电重启)
//1014h COB-ID EMCY(0xFF)急停码
//1016h 消费者心跳时间-1
//1017h 发生器心跳时间 2000ms
//1018h 标识对象4
//1029h 阵列错误行为-1
//1200h 第一台服务器SDO参数SDO参数RO(2)
//1400h~03h 接收PDO参数(开关配置)bit31总开关(0开)bit30RTR(0开)bit29标准帧(0标准帧1扩展帧)bit28~0 COB-ID码
//1600h~03h 接收PDO映射(寄存器指针)
//1800h~03h 发送PDO参数(开关配置)
//1A00h~03h 发送PDO映射(寄存器指针)

#endregion
#region Cia402_运动层
//603Fh 最新错误代码-1
//6040h 控制字==========0
//6041h 状态字==========
//605Ah 快速停止代码
//605Bh 停机选项代码-1
//605Eh 故障反应选项代码-1
//6060h 工作模式( 1位置 3速度 4扭矩 6回零 7补间)================
//6061h 显示驱动器的工作模式
//6062h 显示电机命令位置
//6063h 位置实际值-1
//6064h 显示电机实际位置0
//6065h 以下错误窗口-1
//6067h 位置窗口-1
//6068h 位置窗口时间-1
//606Bh 显示电机命令速度0
//606Ch 显示电机的实际速度0
//606Dh 速度窗口-1
//606Eh 速度窗口时间-1
//606Fh 速度阈值-1
//6071h 目标扭矩-1
//6074h 扭矩需求值-1
//6075h 电机额定电流-1
//6076h 电机额定扭矩-1
//6077h 扭矩实际值-1
//6078h 当前实际值-1
//607Ah 目标位置0
//607Ch 原点偏移0
//607Dh 软件位置限制-1
//607Fh 最大剖面速度-1
//6080h 最大电机转速-1
//6081h 位置模式的最大速度(每秒脉冲量)除以每圈脉冲  (每圈默认5万)
//6082h 起跳速度
//6083h 剖面加速度(默认50 0000
//6084h 剖面减速度(默认500 0000
//6085h 急停减速度(默认5000 0000
//6086h 运动剖面类型-1
//6087h 扭矩斜率-1
//6093h位置系数2
//6098h回零方式0
//6099h-01原点快速
//6099h-02原点慢速
//609Ah 回零加减速 5000000
//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  物理输出开启1
//60FEh-02  物理输出开启2
//60FFh目标速度0
//6502h支持的驱动模式-1

#endregion
namespace BLL
{
    public class CANopen//被调用版本必须低(调用者,只需要属性里有接口)
    {
        #region 全局
        //1申明委托》委托命令
        public delegate void WT_GET_Data(byte[] butes, string data);//↑ui显示发送命令
        public delegate void WT_SET_Data(byte[] butes, string data);//↓下执行对象

        #region 参数类型

        //public unsafe struct VCI_CAN_OBJ  //CAN帧参数,
        //{
        //    public uint ID;// 帧ID。 32位变量,数据格式为靠右对齐。 详情请参照: 《8.附件1: ID对齐方式.pdf》说明文档。
        //    public uint TimeStamp;        //设备接收到某一帧的时间标识。 时间标示从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];//系统保留。
        //}

        #endregion

        #endregion
        #region 字段
        Help_String helpstring = new Help_String();// 文本转换
        /// <summary>
        /// 设备类型
        /// </summary>
        uint CAN_DeviceType = (uint)Help_USB_CAN.VCI_USBCAN2;// 设备类型
        /// <summary>
        /// 硬件编号
        /// </summary>
        uint CAN_ind = 0;   // 硬件编号
        /// <summary>
        /// 通道编号
        /// </summary>
        const uint CAN1 = 0;// 通道编号
        const uint CAN2 = 1;



        Help_EightDiagrams diagrams;
        Help_USB_CAN can = new Help_USB_CAN();
        Help_USR_socket rj45 = new Help_USR_socket();
        int s剩余帧数;
        #region 内部
        static Int64 byte8 = 0;// 64bit  数据帧 包

        static volatile byte Region_Type;// 寄存器标记(读40h,写,成功60h,失败80h)
        bool debug = false;

        #endregion

        //private CancellationTokenSource cts;//线程令牌
        //private ManualResetEvent resetEvent = new ManualResetEvent(true);// 暂停业务

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

        public byte address { get; set; } = 127; // 从站1~127(不能为0,因为0站是广播)
        public Int32[] AXIS;// 轴坐标
        public Help_USR_socket RJ45 { set => rj45 = value; }// 以太网设备转CAN
        public Iface_RTX help_rtx { get; set; }// 接口扩展(用于通讯的收,发)
        public bool timeOutFun { get; set; } = false;// 通讯超时功能
        public int get排队帧 { get; set; } = 0;

        #endregion
        #region 构造
        public CANopen(Help_USR_socket rj45)
        {
            help_rtx = rj45;
            RJ45 = rj45;
            //diagrams = new Help_EightDiagrams();// 八卦炉
            //diagrams.令牌1 = true;// 总开关
            //diagrams.镇坛木1 = true;//  循环

            //====================
            debug = true;//调试
        }
        public CANopen(Iface_RTX iface)
        {
            help_rtx = iface;// 接口 Help_RTX

            //diagrams = new Help_EightDiagrams();// 八卦炉
            //diagrams.令牌1 = true;// 总开关
            //diagrams.镇坛木1 = true;//  循环

            //====================
            debug = true;//调试
        }
        #endregion

        #region 方法_APP
        #region 核心_延时
        /// <summary>
        /// Help_Delay.delayTime(0.123);//0.1秒
        /// </summary>
        /// <param name="secend"></param>
        void delaySecond(double secend)// 延时,秒
        {
            DateTime tempTime = DateTime.Now;
            while (tempTime.AddSeconds(secend).CompareTo(DateTime.Now) > 0)
            {
                //Application.DoEvents();
            }
        }
        #endregion
        #region 收发GET_SET
        public int Send(int hand8, int cobid, Int64 byte8)//  1  4  8
        {
            //int hand = 0x88;// 设备:USR-CANET200
            //int cobid = 0x12345678;// 11bit或29bit
            //Int64 byte8 = 0x0011223344556677;
            //wait_dataRX数据池();
            //can.send(hand8, cobid, byte8);
            //wait_dataRX数据池();

            string cmd = madeCode(hand8, cobid, byte8);// Code编码

            int sta = (int)help_rtx.TXs(cmd);// 接口功能
            if (sta < 1) throw new Exception("发送失败。检查主站状态!");
            return sta;
        }

        /// <summary>
        /// 伺服参数操作 0x40读 0x20+写
        /// </summary>
        /// <param name="byte8"></param>
        void SDO(Int64 byte8)// 600h+
        {// SDO操作  600h+
            Send(0x08, 0x600 + address, byte8);//  40  2000  00

            //===============================================
             code码(发送)===============================
            //byte by0 = (byte)regionGET.读;// 读 0x40
            //Int16 by1 = reg;// 寄存器
            //byte by2 = (byte)(Son);// 子索引
             b0  b1b2  b3   b4b5b6b7   // 编码
            //byte8 = by0 + (by1 << 8) + (by2 << 24); // 40 2000 00      

             3  准备发送==================================
            Send(0x04, 0x600 + address, byte8);//40 2000 00
            help_rtx.TXs(madeCode(0x04, 0x600 + address, byte8));
            //SDO(byte8);
        }

        public void SDO_set(regionSET regtype, short reg, int Son, int data)// 1操作类型,2寄存器编号,3子索引,4参数值
        {// SDO操作  600h+
            //========================================================
            // 准备发送
            byte by0 = (byte)regtype;//1 写 操作码
            Int16 by1 = reg;//2 寄存器
            byte by2 = (byte)(Son);//3 子索引
            Int64 by3 = data;//4设定值

            // b0  b1b2  b3   b4b5b6b7
            byte8 = by0 + (by1 << 8) + (by2 << 24) + (by3 << 32); // 23 2000 00      

            Send(0x08, 0x600 + address, byte8);//  40  2000  00

        }
        /// <summary>
        /// 可能返回null
        /// </summary>
        /// <returns></returns>
        public byte[] Receive()
        {//  rj45.socket
#if RJ45
            lock (this)  //加锁,防止冲突
            {
                byte[] buffer = new byte[13]; //定义一个接收的数组
                MemoryStream ms = new MemoryStream();//创建一个内存流,用来保存数据

                if (rj45.socket.Available > 0)
                {
                    //读取缓冲区的字节数
                    int count = rj45.socket.Receive(buffer, 13, SocketFlags.None);
                    //把缓冲区的数据写入内存流
                    ms.Write(buffer, 0, count);
                    //读取完毕,返回读取的数据
                    return ms.ToArray();
                }
                else
                {

                }

                return buffer;
#else

            byte[] rx13 = (byte[])help_rtx.RXs();//  提取数据帧
            return rx13;
#endif 

        }
        /// <summary>
        /// rx13[5] = 0x80;// 故障码
        /// </summary>
        /// <param name="ms"></param>
        /// <param name="WaitCount"></param>
        /// <returns></returns>
        byte[] wait_Receive(int ms, int WaitCount)
        {
            DateTime startTime = DateTime.Now;//记录开始时间
            byte[] rx13;// 提取数据帧
            while (true)
            {
                if (timeOutFun)
                {
                    if ((DateTime.Now - startTime).TotalMilliseconds > ms)// 超时
                    {
                        //Region_Type = 0x80;// 标记  读取失败
                        rx13 = new byte[13];
                        rx13[5] = 0x80;// 故障码
                        break;
                    }
                }// 超时

                rx13 = Receive();//回帧报文

                if (rx13 == null)
                {
                    if (WaitCount <= 0)// 超次数
                    {
                        rx13 = new byte[13];
                        rx13[5] = 0x80;// 故障码
                        break;
                    }
                    WaitCount--;
                    Help_Delay.delayTime(0.003);
                    continue;
                }// 超次
                else
                {// 非空
                    return rx13;
                }
            }
            return rx13;
        }


        #endregion
        #region 读写寄存器
        public unsafe int get_region(Int16 reg, int Son)//寄存器,子索引     //0x0000~0xFFFF
        {
            #region 清空缓存_---使发/收对应
            //#if debug
            //            s剩余帧数 = can.VCI_get缓存区帧数(Help_USB_CAN.VCI_USBCAN2, 0, CAN1);
            //#endif
            //            can.set_ClearBuffer(Help_USB_CAN.VCI_USBCAN2, 0, CAN1);// CAN1清空接收
            //#if debug
            //            s剩余帧数 = can.VCI_get缓存区帧数(Help_USB_CAN.VCI_USBCAN2, 0, CAN1);
            //#endif
            #endregion

            #region set_只为获取寄存器类型
            SDO_set(regionSET.读, reg, Son, 0);
            #endregion

            #region get_Value
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2500];// 创新科技CAN分析仪最大支持2000帧接收
            byte[] rx13 = new byte[13];// 提取数据帧

            Help_Delay.delayTime(0.005);//5ms   // USB速度3~5ms
            int num = can.get_Receive(CAN_DeviceType, CAN_ind, CAN1, ref obj[0], 2000, 50);
            if (num > 0)
            {
                for (int i = num - 1; i >= 0; i--)
                {
                    if (obj[i].ID > 0x80 && obj[i].ID < (0x80 + 127))// 从站故障 80h+ 从站
                    {
                        // 故障呼叫



                        //COB - ID            Byte 0:1        Byte 2              Byte 3:7
                        //0x080 + Node - ID     应急错误代码 错误寄存器(1001H)    厂商指定区域

                        //byte0~1:
                        //        应急错误代码
                        //        0000H 无错误
                        //        8110H CAN 溢出
                        //        8120H 错误被动模式(0411)
                        //        8130H 寿命保护/ 心跳错误
                        //        8140H 被迫离线恢复故障
                        //        8141H 被迫离线
                        //        8150H 发送 COB - ID 冲突
                        //        8210H PDO 长度错误未处理
                        //        8220H PDO 超过长度

                        //byte2:
                        //        (1001H)位定义
                        //        Bit0:generic error 一般错误
                        //        Bit1:current 电流
                        //        Bit2:voltage 电压 (04)
                        //        Bit3:temperature 温度

                        //————————————————
                        //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
                        //原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808
                    }// 故障(从站故障)80h + 从站
                    if (obj[i].ID > 0x580 && obj[i].ID < 0x600 && obj[i].Data[0] != 0x60)//  SDO 600h+
                    {
                        rx13[5] = obj[i].Data[0];
                        rx13[6] = obj[i].Data[1];
                        rx13[7] = obj[i].Data[2];
                        rx13[8] = obj[i].Data[3];
                        rx13[9] = obj[i].Data[4];
                        rx13[10] = obj[i].Data[5];
                        rx13[11] = obj[i].Data[6];
                        rx13[12] = obj[i].Data[7];
                        break;
                    }// SDO应答帧
                    else rx13[5] = 0x80;// 故障
                }
            }
            else
            {
                rx13[5] = 0x80;// 故障
            }
            // 回帧结果
            Region_Type = rx13[5];// 标记: 0x4b读
            int value = get_result(rx13);// 解析
            //  日志====================================
            if (Region_Type < 0x43 | Region_Type > 0x4F)// 读取失败
            {
                string error = getError(value, reg, Son, -1); // 故障码,寄存器,子,值
                if (Wt_get != null)
                {
                    Wt_get(new byte[] { 0x00 }, "读取错误:" + error);
                }
                throw new Exception("读取错误:" + error);// 提前终止
            }
            return value;
            #endregion
        }

        /// <summary>
        /// SET 寄存器
        /// </summary>
        /// <param name="reg">寄存器</param>
        /// <param name="Son">子索引</param>
        /// <param name="data">数值</param>
        /// <returns></returns>
        /// <exception cref="Exception">结果</exception>
        public unsafe int set_region(Int16 reg, byte Son, Int32 data, int waitTimeMs)
        {
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2500];// 创新科技CAN分析仪最大支持2000帧接收
            byte[] rx13 = new byte[13];// 提取数据帧
            //=======================================
            int valus = get_region(reg, Son);//用于 获知 寄存器类型
            //========================================================
            // 准备发送
            SDO_set((regionSET)Region_Type - 0x20, reg, Son, data);// 全局 Region_Type
            // 等待应答帧
            Help_Delay.delayTime(0.005 + (waitTimeMs / 1000));//5ms   // USB速度3~5ms
            int num = can.get_Receive(CAN_DeviceType, CAN_ind, CAN1, ref obj[0], 2000, 50);
            if (num > 0)
            {
                for (int i = num - 1; i >= 0; i--)
                {
                    if (obj[i].ID > 0x80 && obj[i].ID < (0x80 + 127))// 从站故障 80h+ 从站
                    {
                        // 故障呼叫



                        //COB - ID            Byte 0:1        Byte 2              Byte 3:7
                        //0x080 + Node - ID     应急错误代码 错误寄存器(1001H)    厂商指定区域

                        //byte0~1:
                        //        应急错误代码
                        //        0000H 无错误
                        //        8110H CAN 溢出
                        //        8120H 错误被动模式(0411)
                        //        8130H 寿命保护/ 心跳错误
                        //        8140H 被迫离线恢复故障
                        //        8141H 被迫离线
                        //        8150H 发送 COB - ID 冲突
                        //        8210H PDO 长度错误未处理
                        //        8220H PDO 超过长度

                        //byte2:
                        //        (1001H)位定义
                        //        Bit0:generic error 一般错误
                        //        Bit1:current 电流
                        //        Bit2:voltage 电压 (04)
                        //        Bit3:temperature 温度

                        //————————————————
                        //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
                        //原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808
                    }// 故障(从站故障)
                    if (obj[i].ID > 0x580 && obj[i].ID < 0x600 && (obj[i].Data[0] == 0x60 | obj[i].Data[0] == 0x80))//  SDO 600h+
                    {
                        rx13[5] = obj[i].Data[0];
                        rx13[6] = obj[i].Data[1];
                        rx13[7] = obj[i].Data[2];
                        rx13[8] = obj[i].Data[3];
                        rx13[9] = obj[i].Data[4];
                        rx13[10] = obj[i].Data[5];
                        rx13[11] = obj[i].Data[6];
                        rx13[12] = obj[i].Data[7];
                        break;
                    }// SDO应答帧
                    else rx13[5] = 0x80;// 故障
                }
            }// 提取数据
            else
            {
                rx13[5] = 0x80;// 故障
            }// 报错

            // 回帧结果
            Region_Type = rx13[5];// 标记: 结果
            int end = get_result(rx13);// 解析数值
            //  日志====================================
            if (Region_Type != 0x60)// 写入失败
            {
                string error = getError(end, reg, Son, data); // 故障码,寄存器,子,值
                if (Wt_get != null)
                {
                    Wt_get(new byte[] { 0x00 }, "写失败:" + error);//  错误  上传
                }
                throw new Exception("寄存器写入失败!");
            }
            if (Region_Type == 0x60) return 1;//成功
            return end;

        }//长时间set寄存器

        public unsafe int set_region(Int16 reg, byte Son, Int32 data)
        {
            return set_region(reg, Son, data, 0);
        }

        /// <summary>
        /// 故障码,寄存器,子,值
        /// </summary>
        /// <param name="errcode"></param>
        /// <param name="reg"></param>
        /// <param name="Son"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        string getError(Int32 errcode, Int16 reg, Int32 Son, Int32 data)
        {
            string errorString = string.Empty;
            string str1 = $"Reg{reg.ToString("X4")}h-{Son.ToString("X2")}={data.ToString()};";
            switch (errcode)
            {
                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 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;
        }

        #endregion
        #region 协议can
        /// <summary>
        /// 心跳包0x1017(16bit)
        /// </summary>
        /// <param name="address"></param>
        public void SYNC_close(byte address)
        {//关闭 心跳 0x1017=2000;ms
            Send(0x08, 0x600 + address, 0x00000000010172b);// 寄存器 0x1017,操作码 0x2b (16bit)
            //delaySecond(2.0);//延时2秒
            Help_Delay.delayTime(0.123);//0.1秒
        }
        public void SYNC_open(byte address, int time)
        {//心跳 0x1017 = 2000; ms
            set_region(0x1017, 0x00, 2000);// 0x7d0  2000ms
            Help_Delay.delayTime(2.123);//2.1秒
        }
        /// <summary>
        /// 用于同步信号(广播帧)留20ms电机响应
        /// </summary>
        public void SYNC()
        {// SYNC同步心跳  80h+
            Send(0x08, 0x80, 0x00);// id=0x80
            // 等待同步完成
            Help_Delay.delayTime(0.020);//0.02秒
        }
        /// <summary>
        /// 总线管理(命令,从站对象);cob_id=0x00
        /// 启动从站 = 0x01,
        ///    停止从站 = 0x02,
        ///    预运行从站 = 0x80,
        ///    重启从站 = 0x81,
        ///    通讯重连 = 0x82
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="address"></param>
        public void NMT(NMTe modecmd, byte address)
        {// NMT总线管理操作  00h+
            byte by0 = (byte)modecmd;
            byte by1 = address;

            // b0  b1b2  b3   b4b5b6b7
            byte8 = by0 + (by1 << 8);
            //接收负载=0

            Send(0x08, 0x00, byte8);

            //延时 等待生效75ms
            DateTime startTime = DateTime.Now;//记录开始时间
            do
            {
                if ((DateTime.Now - startTime).TotalMilliseconds > 75)// 75ms
                {
                    break;   // 超时   毫秒
                }
            } while (true);

        }
        /// <summary>
        /// 时间广播
        /// </summary>
        /// <param name="byte8"></param>
        public void TIME(Int64 byte8)
        {// TIME操作  100h+
            Send(0x08, 0x100, byte8);
        }
        public void set_PDO(PDOtype pdo, int reg1, int bita, int reg2)
        {
            Int64 a = reg1;// 6040h  控制字
            Int64 b = reg2;// 607Ah  目标位置
            //int bits = 16;//reg1长度
            Int64 byte8;

            //a &= 0xFF;// bit4(准备上升沿)   1111 1111
            b = b << bita;
            byte8 = a + b;// 6040绑定在(数据帧低字节)
            // 200h+
            Send(0x08, 0x100 + (int)pdo * 0x100 + address, byte8);// 生效了

        }
        public void set_PDO(PDOtype pdo, int reg1, int bita, int reg2, int bitb, int reg3)
        {
            Int64 a = reg1;// 6040h  控制字    16bit
            Int64 b = reg2;// 607Ah  目标位置  32bit
            Int64 c = reg3;// 6081h  速度      16bit
            int bits = 16;//reg1长度
            Int64 byte8;

            a &= 0xFF;// bit4(准备上升沿)   1111 1111
            b = b << bits;// 16bit
            c = c << 48;// 16bit +  32bit
            byte8 = a + b + c;// 6040绑定在(数据帧低字节)
            Send(0x08, 0x100 + (int)pdo * 0x100 + address, byte8);// 生效了

        }

        public void PDO(CMDtype cmd, Int32[] AXIS)// PDO 快捷命令
        {// PDO1操作  200h+
         //  6040  607a
         //  16     32 
         // 1122-33445566-7788 (数据帧:低字节先出)
         // a<<6字节
         // b<<2字节
            byte at = address;//标记
                              //6040
                              //0 Switch on 转换开
                              //1 Enable voltage电压开
                              //2 Quick Stop快停
                              //3 Enable operation 启用操作
                              //4   hm回零操作开始(正触发)  pp新设定点(正触发)       
                              //5                             pp立即更改集合
                              //6                             pp绝对(0)/相对(1)
                              //7   Fault reset 故障复位      ==
                              //8   Halt 暂停                 ==
                              //9                             Change of Set Point
            if (cmd == CMDtype.回零)// bit4
            {
                for (int i = 127; i >= 1; i--)
                {
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x0F, 16, 0);//  命令
                }
                SYNC();
                for (int i = 127; i >= 1; i--)
                {
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x1F, 16, 0);//  命令
                }
                SYNC();
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.绝对定位)
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x2F, 16, 0);//  6040命令
                    set_PDO(PDOtype.PDO2, AXIS[i], 32, AXIS[0]);// 轴坐标 + 速度
                }
                SYNC();// 同步帧:准备
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x3F, 16, AXIS[i]);
                }
                SYNC();// 同步帧:开始
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.相对定位)
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x6F, 16, 0);//  6040命令
                    set_PDO(PDOtype.PDO2, AXIS[i], 32, AXIS[0]);// 轴坐标 + 速度
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x7F, 16, 0);
                }
                SYNC();
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.补间)
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x6F, 16, 0);//  命令
                    set_PDO(PDOtype.PDO2, AXIS[i], 32, AXIS[0]);// 轴坐标 + 速度
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x7F, 16, AXIS[i]);
                }
                SYNC();
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.速度 | cmd == CMDtype.力矩)
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x07, 16, 0);//  6040命令
                    set_PDO(PDOtype.PDO2, AXIS[i], 32, AXIS[0]);// 速度 + 起步速度
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x0F, 16, 0);
                }
                SYNC();
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.复位)// bit7
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x0F, 16, 0);//  命令
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x8F, 16, 0);
                }
                SYNC();// 同步帧
                address = at;// 还原
                return;
            }
            if (cmd == CMDtype.暂停)// bit8
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x0F, 16, 0);//  命令
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x10F, 16, 0);
                }
                SYNC();// 同步帧
                address = at;// 还原
                return;
            }



            //Int64 a = reg1;// 6040h  控制字
            //Int64 b = reg2;// 607Ah  目标位置
            //int bits = 16;//reg1长度
            //Int64 byte8;

            //if (reg1 == 0x10F)//停止运行
            //{
            //    b = b << bits;
            //    byte8 = a + b;// 6040绑定在(数据帧低字节)
            //    Send(0x08, 0x200 + address, byte8);
            //    SYNC();// 同步信号
            //    return;
            //}
            //a &= 0xEF;// bit4(准备上升沿)
            //b = b << bits;
            //byte8 = a + b;// 6040绑定在(数据帧低字节)
            //Send(0x08, 0x200 + address, byte8);// 生效了
            //SYNC();// 同步信号,用于寄存器生效
            ===============================
            Help_Delay.delayTime(0.040); // 超过 命令滤波 时间 1400h-02
            ===============================
            下一帧(上升沿)
            //a |= 1 << 4;// 上升沿
            //byte8 = a + b;// 6040绑定在(数据帧低字节)
            //Send(0x08, 0x200 + address, byte8);
            //SYNC();// 同步信号,用于激活旋转
        }

        public unsafe void gCode(CMDtype cmd, Int32[] AXIS)// G0 X123.123 F1000
        {
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2500];// 创新科技CAN分析仪最大支持2000帧接收
            byte[] rx13 = new byte[13];// 提取数据帧
            can.set_ClearBuffer(4, 0, 0);
            PDO(cmd, AXIS);
            do
            {
                set_PDO(PDOtype.PDO1, 0, 0, 0);//
                
                Help_Delay.delayTime(0.005);//5ms   // USB速度3~5ms
                int num = can.get_Receive(CAN_DeviceType, CAN_ind, CAN1, ref obj[0], 2000, 50);
                if (num > 0)
                {
                    for (int i = num - 1; i >= 0; i--)
                    {
                        if (obj[i].ID > 0x80 && obj[i].ID < (0x80 + 127))// 从站故障 80h+ 从站
                        {
                            // 故障呼叫



                            //COB - ID            Byte 0:1        Byte 2              Byte 3:7
                            //0x080 + Node - ID     应急错误代码 错误寄存器(1001H)    厂商指定区域

                            //byte0~1:
                            //        应急错误代码
                            //        0000H 无错误
                            //        8110H CAN 溢出
                            //        8120H 错误被动模式(0411)
                            //        8130H 寿命保护/ 心跳错误
                            //        8140H 被迫离线恢复故障
                            //        8141H 被迫离线
                            //        8150H 发送 COB - ID 冲突
                            //        8210H PDO 长度错误未处理
                            //        8220H PDO 超过长度

                            //byte2:
                            //        (1001H)位定义
                            //        Bit0:generic error 一般错误
                            //        Bit1:current 电流
                            //        Bit2:voltage 电压 (04)
                            //        Bit3:temperature 温度

                            //————————————————
                            //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
                            //原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808
                        }// 故障(从站故障)80h + 从站
                        // 等待轴定位完成
                        if (obj[i].ID > 0x180 && obj[i].ID <= 0x180+127)//  PDO1  200h+
                        {
                            rx13[5] = obj[i].Data[0];
                            rx13[6] = obj[i].Data[1];
                            rx13[7] = obj[i].Data[2];
                            rx13[8] = obj[i].Data[3];
                            rx13[9] = obj[i].Data[4];
                            rx13[10] = obj[i].Data[5];
                            rx13[11] = obj[i].Data[6];
                            rx13[12] = obj[i].Data[7];
                            if ((rx13[6]&0x16) == 0x16)// bit10
                            {
                                return;
                            }
                           
                        }// SDO应答帧
                        
                    }
                }
                else
                {
                    Help_Delay.delayTime(0.205);//
                }

            } while (true);
        }

        /// <summary>
        /// 移动控制6040h(控制字),607A(目标位置)
        /// </summary>
        /// <param name="byte8"></param>
        public void PDO1(bool en, Int32[] AXIS)// 6040  607A 目标位置
        {// PDO1操作  200h+
         //  6040  607a
         //  16     32 
         // 1122-33445566-7788 (数据帧:低字节先出)
         // a<<6字节
         // b<<2字节
            byte at = address;//标记
            if (!en)
            {
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x0F, 16, 0);//  命令
                    set_PDO(PDOtype.PDO2, AXIS[i], 32, AXIS[0]);// 轴坐标 + 速度
                }
                SYNC();// 同步帧
                for (int i = 127; i >= 1; i--)
                {
                    if (AXIS[i] == -1) continue;
                    address = (byte)i;// 指向从站
                    set_PDO(PDOtype.PDO1, 0x1F, 16, AXIS[i]);
                }
            }
            else
            {
                SYNC();
            }
            address = at;// 还原




            //Int64 a = reg1;// 6040h  控制字
            //Int64 b = reg2;// 607Ah  目标位置
            //int bits = 16;//reg1长度
            //Int64 byte8;

            //if (reg1 == 0x10F)//停止运行
            //{
            //    b = b << bits;
            //    byte8 = a + b;// 6040绑定在(数据帧低字节)
            //    Send(0x08, 0x200 + address, byte8);
            //    SYNC();// 同步信号
            //    return;
            //}
            //a &= 0xEF;// bit4(准备上升沿)
            //b = b << bits;
            //byte8 = a + b;// 6040绑定在(数据帧低字节)
            //Send(0x08, 0x200 + address, byte8);// 生效了
            //SYNC();// 同步信号,用于寄存器生效
            ===============================
            Help_Delay.delayTime(0.040); // 超过 命令滤波 时间 1400h-02
            ===============================
            下一帧(上升沿)
            //a |= 1 << 4;// 上升沿
            //byte8 = a + b;// 6040绑定在(数据帧低字节)
            //Send(0x08, 0x200 + address, byte8);
            //SYNC();// 同步信号,用于激活旋转
        }

        public void PDO2(int reg1, int bits, int reg2)//相对运动
        {// PDO2操作  300h+
         //  6040  607a
         //  16     32 
         // 1122-33445566-7788 (数据帧:低字节先出)
         // a<<6字节
         // b<<2字节
            Int64 a = reg1;
            Int64 b = reg2;
            Int64 byte8;

            if (reg1 == 0x10F)//停止运行
            {
                b = b << bits;
                byte8 = a + b;// 6040绑定在(数据帧低字节)
                Send(0x08, 0x200 + address, byte8);
                SYNC();// 同步信号
                return;
            }

            a |= 0x4F;
            a &= 0xEF;// (准备上升沿)  4F 》 5F
            b = b << bits;

            byte8 = a + b;// 6040绑定在(数据帧低字节)

            Send(0x08, 0x300 + address, byte8);
            SYNC();// 同步信号
            //===============================
            Help_Delay.delayTime(0.040); // 超过 命令滤波 时间 1400h-02
            //下一帧(上升沿)
            //===============================
            a |= 0x5F;// 上升沿
            byte8 = a + b;// 6040绑定在(数据帧低字节)

            Send(0x08, 0x300 + address, byte8);
            SYNC();// 同步信号

        }
        public void PDO3(Int64 byte8)
        {// PDO3操作  400h+
            Send(0x08, 0x400 + address, byte8);
        }
        public void PDO4(Int64 byte8)
        {// PDO4操作  500h+
            Send(0x08, 0x500 + address, byte8);
        }
        /// <summary>
        /// SET到伺服8字节,这是寄存器快捷键1400h,1600h
        /// </summary>
        /// <param name="zu">组#</param>
        /// <param name="reg1">指针1</param>
        /// <param name="bita">位数</param>
        /// <param name="reg2">指针2</param>
        /// <param name="bitb">位数</param>
        /// <exception cref="Exception"></exception>
        public void PDOr_Config(PDOtype PDOn, PDOregion regs)// 1400,1600
        {// 1400-00=5
            // 1400-01=4000027F (0x4000 0200h+从站地址127) // 200h+7F      bit31是PDO总开关(0开)
            //                                                               bit30  0:PDO上允许RTR
            //                                                               bit29  0:标准11bit帧(CAN 2.0A)    1:扩展帧29bit(CAN 2.0B)
            //                                                               bit28~0 :COB-ID码
            //

            // 1400-02=0xFF   //  0~240us 时,只要接收到一个同步帧则将该 RPDO 最新的数据更新到应用,255新数据立即生效
            // 1400=03=0x00   // 禁止时间: rPDO1命令过滤间隔,单位0.1ms 
            // 1400-04=不存在
            // 1400-05=0    //事件计时器
            // 1600-00=2    //寄存器指针数
            // 1600-01=60400010  // 6060h=1默认定位模式
            // 1600-02=607A0020  // 607A是目标位置
            // 1600-03=0

            Int32 regValue;

            //1400-01:总开关
            //1600-00:绑定数量
            //1600-01:第一绑定寄存器    6040油门离合
            //1600-02:第二绑定寄存器    607A目标地址
            // 2个寄存器,6040h,607Ah
            // 0x6040   b7  b3b2b1b0

            //          b7  ↑故障复位
            //         0x06  关闭
            //         0x07  打开
            //         0x0F  打开+启用操作
            //         0<<1  快速停止
            //         0x07  禁用操作
            //         0x0F  启用操作

            //===============================
            short zu = (short)(PDOn);
            short h1400 = (short)((zu - 1) + 0x1400);
            short h1600 = (short)((zu - 1) + 0x1600);
            try
            {
                //解锁 //开始设置 , 从机响应配置
                set_region(h1400, 0x01, 1 << 31);  // 关闭 rPDO 功能(原值: 0x4000 0200h + 从站地址)
                set_region(h1400, 0x02, 234); //接收类型: 生效rPDO数据的延迟 us(0~240us) (原值:0x000000FF)255为事件
                set_region(h1400, 0x03, 0);//  接收PDO1命令过滤间隔,单位0.1ms         雷赛40ms(原值:0x00000000)

                set_region(h1600, 0x00, 0x00);  // 寄存器指针:关闭   (原值:0x00000002)

                for (int i = 0; i < regs.num; i++)
                {//绑定地址
                    regValue = regs.regName[i];
                    regValue <<= 16;//固定
                    regValue += regs.regBit[i];// 寄存器bit
                    set_region(h1600, (byte)(1 + i), regValue);// 指针1》绑定6040   0x60400010     (原值:0x60400010)16bit
                }

                // 上锁
                set_region(h1600, 0x00, regs.num); // 绑定的寄存器 生效       (原值:0x00000002)
                int pdo = 0x100 + (zu * 0x100) + address;  //bit31(0开PDO功能)bit30(0开RTR)bit29(0标准帧)bit28~0(COB-ID码)
                //  4000 027F 
                set_region(h1400, 0x01, pdo);  // 开启PDO1功能       (原值:0x4000 0201)
                                               //                     bit31是PDO总开关(0开)
                                               //                     bit30  0:PDO上允许RTR(RTR是强制显性1bit)
                                               //                     bit29  0:标准11bit帧(CAN 2.0A)    1:扩展帧29bit(CAN 2.0B)
                                               //                     bit28~0 :COB-ID码
            }
            catch (Exception ex)
            {
                throw ex;
            }
            //regs.num = 1;
            //regs.regName[0] = 0x6040;// 控制字
            //regs.regBit[0] = 16;
        }

        /// <summary>
        /// 控制字,定位,速度
        /// </summary>
        /// <param name="PDOn"></param>
        /// <param name="reg1"></param>
        /// <param name="bita"></param>
        /// <param name="reg2"></param>
        /// <param name="bitb"></param>
        /// <param name="reg3"></param>
        /// <param name="bitc"></param>
        public void PDOr_Config(PDOtype PDOn, Int16 reg1, int bita, Int16 reg2, int bitb, Int16 reg3, int bitc)// 1400,1600
        {// 1400-00=5
            // 1400-01=4000027F (0x4000 0200h+从站地址127) // 200h+7F      bit31是PDO总开关(0开)
            //                                                               bit30  0:PDO上允许RTR
            //                                                               bit29  0:标准11bit帧(CAN 2.0A)    1:扩展帧29bit(CAN 2.0B)
            //                                                               bit28~0 :COB-ID码
            //

            // 1400-02=0xFF   //  0~240us 时,只要接收到一个同步帧则将该 RPDO 最新的数据更新到应用,255新数据立即生效
            // 1400=03=0x00   // 禁止时间: rPDO1命令过滤间隔,单位0.1ms 
            // 1400-04=不存在
            // 1400-05=0    //事件计时器
            // 1600-00=2    //寄存器指针数
            // 1600-01=60400010  // 6060h=1默认定位模式
            // 1600-02=607A0020  // 607A是目标位置
            // 1600-03=0

            Int32 regValue;
            int regNum = 1;//绑定寄存器数量
            //1400-01:总开关
            //1600-00:绑定数量
            //1600-01:第一绑定寄存器    6040油门离合
            //1600-02:第二绑定寄存器    607A目标地址
            // 2个寄存器,6040h,607Ah
            // 0x6040   b7  b3b2b1b0

            //          b7  ↑故障复位
            //         0x06  关闭
            //         0x07  打开
            //         0x0F  打开+启用操作
            //         0<<1  快速停止
            //         0x07  禁用操作
            //         0x0F  启用操作

            //===============================
            short zu = (short)(PDOn);
            short h1400 = (short)((zu - 1) + 0x1400);
            short h1600 = (short)((zu - 1) + 0x1600);
            try
            {
                //解锁 //开始设置 , 从机响应配置
                set_region(h1400, 0x01, 1 << 31);  // 关闭 rPDO 功能(原值: 0x4000 0200h + 从站地址)
                set_region(h1400, 0x02, 234); //接收类型: 生效rPDO数据的延迟 us(0~240us) (原值:0x000000FF)255为事件
                set_region(h1400, 0x03, 0);//  接收PDO1命令过滤间隔,单位0.1ms         雷赛40ms(原值:0x00000000)

                set_region(h1600, 0x00, 0x00);  // 寄存器指针   关闭   (原值:0x00000002)
                regValue = reg1;
                regValue <<= 16;//固定
                regValue += bita;// 寄存器bit
                set_region(h1600, 0x01, regValue);// 指针1》绑定6040   0x60400010     (原值:0x60400010)16bit
                if (reg2 != 0x00)
                {
                    regNum = 2;// 又加一个
                    regValue = reg2;
                    regValue <<= 16; //固定
                    regValue += bitb;//寄存器bit   32bit(0x20)
                    set_region(h1600, 0x02, regValue);// 绑定607A    32bit   0x607A0020
                }
                if (reg3 != 0x00)
                {
                    regNum = 3;// 又加一个
                    regValue = reg3;
                    regValue <<= 16; //固定
                    regValue += bitc;//寄存器bit   32bit(0x20)
                    set_region(h1600, 0x03, regValue);// 速度
                }

                // 上锁
                set_region(h1600, 0x00, regNum); // 绑定的寄存器 生效       (原值:0x00000002)
                int pdo = 0x100 + (zu * 0x100) + address;  //bit31(0开PDO功能)bit30(0开RTR)bit29(0标准帧)bit28~0(COB-ID码)
                //  4000 0200h + 7F 
                set_region(h1400, 0x01, pdo);  // 开启PDO1功能       (原值:0x4000 0201)
                                               //                     bit31是PDO总开关(0开)
                                               //                     bit30  0:PDO上允许RTR(RTR是强制显性1bit)
                                               //                     bit29  0:标准11bit帧(CAN 2.0A)    1:扩展帧29bit(CAN 2.0B)
                                               //                     bit28~0 :COB-ID码
            }
            catch (Exception ex)
            {
                throw ex;
            }


        }
        public void PDOr_Config(PDOtype PDOn, Int16 reg1, int bita, Int16 reg2, int bitb)// 1400,1600
        {
            PDOr_Config(PDOn, reg1, bita, reg2, bitb, 0, 0);
            // 1400-00=5
            // 1400-01=4000027F (0x4000 0200h+从站地址127) // 200h+7F      bit31是PDO总开关(0开)
            //                                                               bit30  0:PDO上允许RTR
            //                                                               bit29  0:标准11bit帧(CAN 2.0A)    1:扩展帧29bit(CAN 2.0B)
            //                                                               bit28~0 :COB-ID码
            //

            // 1400-02=0xFF   //  0~240us 时,只要接收到一个同步帧则将该 RPDO 最新的数据更新到应用,255新数据立即生效
            // 1400=03=0x00   // 禁止时间: rPDO1命令过滤间隔,单位0.1ms 
            // 1400-04=不存在
            // 1400-05=0    //事件计时器
            // 1600-00=2    //寄存器指针数
            // 1600-01=60400010  // 6060h=1默认定位模式
            // 1600-02=607A0020  // 607A是目标位置
            // 1600-03=0

            //1400-01:总开关
            //1600-00:绑定数量
            //1600-01:第一绑定寄存器    6040油门离合
            //1600-02:第二绑定寄存器    607A目标地址
            // 2个寄存器,6040h,607Ah
            // 0x6040   b7  b3b2b1b0

            //          b7  ↑故障复位
            //         0x06  关闭
            //         0x07  打开
            //         0x0F  打开+启用操作
            //         0<<1  快速停止
            //         0x07  禁用操作
            //         0x0F  启用操作

            //===============================


        }


        /// <summary>
        /// GET上传    1800h,1A00h
        /// </summary>
        /// <param name="reg1"></param>
        /// <param name="bita"></param>
        /// <param name="reg2"></param>
        /// <param name="bitb"></param>
        public void PDOt_Config(PDOtype PDOn, Int16 reg1, int bita, Int16 reg2, int bitb)//1800,1A00
        {// 1800-00=5           子索引数量
         // 1800-01=400001FF    回帧功能码   0x4000 0180h+从站号   7F(127)  // 最高bit位是总开关(1<<31是关)
         // 1800-02=0xFF        当 TPDO 传输类型为 0 时,如果映射对象的数据发生改变,且接收到一个同步帧,则发送该 TPDO;
         //                        收到1~240个同步帧后,发送tPDO帧
         //                        0xFF异步是指数据一旦发生变化马上,发送该 TPDO
         //                        实时性要求不高的参数用同步 PDO 的方式,实时性高的参数用异步 PDO 的传输方式,但
         //                        要注意配置禁止时间,以保护网络负荷不受冲击
         // 1800-03=0           抑制时间,发送失败间隔,单位0.1ms 
         // 1800-04=不存在
         // 1800-05=0           事件计时器,如果在计时器运行周期内出现了数据改变等其它事件,TPDO 也会触发,且事件计数器会被立即复位。
         //                     0是关闭,  bit15~0:周期性触发时间,单位毫秒ms
         // 1A00-00=1           寄存器指针数
         // 1A00-01=60410010    // 0x6041是状态字
         // 1A00-02=0
            Int32 reg;
            int regs = 1;
            //===============================
            short zu = (short)(PDOn);
            short h1800 = (short)((zu - 1) + 0x1800);
            short h1A00 = (short)((zu - 1) + 0x1A00);
            try
            {
                //解锁 //开始设置   上传
                set_region(h1800, 0x01, 1 << 31);  // 关闭tPDO功能  (原值: 0x4000 0180h+从站地址)
                set_region(h1800, 0x02, 0xFF); // 收到1~240个同步帧就发送tPDO,(原值:0x000000FF)255数据变化就立即发送tPDO
                set_region(h1800, 0x03, 400);// 抑制时间// 发送失败间隔,tPDO 间隔  单位0.1ms    (40ms)设置400

                set_region(h1A00, 0x00, 0x00);  //    关闭 PDO1 寄存器指针 (原值:0x00000002)
                reg = reg1;
                reg <<= 16;//固定
                reg += bita;// 寄存器bit
                set_region(h1A00, 0x01, reg);// 指针1》绑定6041   0x60410010     (原值:0x60410010)16bit
                if (reg2 != 0x00)
                {
                    regs = 2;// 又加一个
                    reg = reg2;
                    reg <<= 16; //固定
                    reg += bitb;//寄存器bit   32bit(0x20)
                    set_region(h1A00, 0x02, reg);// 绑定607A    32bit   0x607A0020
                }

                // 上锁
                set_region(h1A00, 0x00, regs); // 寄存器指针   生效       (原值:0x00000001)
                int pdo = (0 << 30) + 0x80 + (zu * 0x100) + address;  //b31总开关(0开)b30强制RTR显性开关(0开)b29~0帧id码
                //  0x 0000 0180h + 从站地址
                set_region(h1800, 0x01, pdo); // 开启 tPDO1    回帧  08(8字节)  0000 0181   寄存器A   寄存器B
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion
        #region 伺服配置
        /// <summary>
        /// 初始化,从站
        /// </summary>
        /// <param name="id"></param>
        public void setMode_Init(int id)// 初始化
        {// 初始化从站
            try
            {
                this.address = (byte)id;// 从站地址
                SYNC();// 0x80帧,网络同步 ( 回帧  281,381,481)
                NMT(NMTe.重启从站, address);
                Help_Delay.delayTime(0.123);//0.1秒
                NMT(NMTe.启动从站, 0x00);// 所有,从站
                                     //===================
                                     //SYNC_close(address);//关闭从站广播
                                     //===================


                #region 设备电机参数
                if (address <= 127  | address >= 122) // 轴127 — 122
                {
                    setMode_Init雷赛();// 电机初始化
                }


                #endregion

                //使能电机
                PowerEN();// 6>7>F
            }
            catch (Exception ex)
            {
                throw ex;
                // 批量修改寄存器,必须用try,遇到错误能及时退出操作!!!!!!!
            }
            //=====rPDO1配置
            //0x80                  网络同步信号
            //0x00  81 01           NMT 重启01站
            //0x00  01 00           NMT 启动从站  0所有
            //=========ACK 700h+
            // 1400-01=0x8000000        rPDO1 bit31:1关闭PDO1,bit28:0是11bit,+CANcobid功能码  0x200+本站地址
            //                           1400.1=0x40000201                 (1400=5,,1400.2=0xff,1400.3=0,1400.4=0,1400.5=0)
            // 1600-00=0                rPDO1绑定  0失效     (2个)

            // 1400-02=0x01            同步的延迟1us(1~240us)
            // 1400-03=0x190            rPDO1  40ms

            // 1600-01=0x60ff 00 20     32bit     ( 6040 00 10)
            // 1600-02=0x6040 00 10     16bit     ( 607A 00 20)
            // 1600-00=0x02             绑定2个寄存器生效
            // 1400-01=0x4000 0201       rPDO1 开      ( 4000 0201)

            //======tPDO1配置
            // 0x00   81 01          NMT 重启01站
            // 1800-01=0x80000000    tPDO1关        ( 4000 0181 )
            // 1A00-00=0x00          0绑定失效     (1)
            // 1800-02=0xff          延迟255(0x01同步)  (0xff事件)
            // 1800-03=0x0190          上传间隔40ms
            // 
            // 1A00-01=0x6041 00 10   16bit   (6040 00 10)
            // 1A00-02=0x3010 00 10              (0)
            // 1A00-00=0x02            生效  6041,3010
            // 1800-01=0x4000 0181    tPDO1开    ( 4000 0181)

            //保存参数
            // 1010-01=0x6576 6173



            //PDOr_Config(1, 0x6040, 16, 0x607A, 32);//  PDO1     // 控制字,目标位置
            //PDOt_Config(1, 0x6041, 16, 0x6064, 32);// 第2寄存器绑 (状态字,位置反馈)
        }
        void setMode_Init雷赛()//0x2001一圈脉冲200,0x60ff 一秒脉冲量200  这样一秒一圈
        {// 只要电流保护
            // ===============设备厂家参数==============
            set_region(0x2000, 0, 3000); // 电流 (默认3200mA) 最大 8200mA
                                         // 2005-01 输出口1功能设置  bit0:报警输出  Bit2:到位输出  Bit4:主站控制输出
                                         // 2005-02 输出口1功能设置  bit0:报警输出  Bit2:到位输出  Bit4:主站控制输出
                                         // 2008-00 输出口逻辑方向   位定义:bit0 对应 out1 以此类推
                                         // 2051-00 电机运行方向
                                         // 2152-01 输入数字 IO 口1 功能选择  1:原点信号; 2:正限位;4:负限位; 8:快速停止;16:自定义;32:Probe1 功能(暂无该功能);64:Probe2功能(暂无该功能)
            set_region(0x2005, 1, 1);// io输出1 故障灯 ( bit0:报警输出  Bit2:到位输出  Bit4:主站控制输出
            set_region(0x2005, 2, 2);// io输出2 刹车阀
            //set_region(0x2154, 0, 1);// 输入io电平逻辑 0正逻辑1反
            set_region(0x2152, 1, 1);// io输入1 原点(1原点信号 2正限位 4负限位 8快速停止 16自定义 32Probe1 功能(暂无该功能);64:Probe2功能(暂无该功能)
            set_region(0x2152, 2, 2);// io输入2 正限
            set_region(0x2152, 3, 4);// io输入3 负限
            set_region(0x2152, 4, 8);// io输入4  //急停
                                     //60FEh-01  物理输出开启1 
                                     //60FEh-02  物理输出开启2 (刹车阀线圈
            //60FDh  输入 IO状态  (bit0:负限位 bit1:正限位 bit2:原点信号 bit16:快速停止
        }
        /// <summary>
        /// 回零模式:Home
        /// </summary>
        /// <param name="id"></param>
        /// <param name="Puls">一圈脉冲数</param>
        /// <param name="homeMode"></param>
        /// <param name="Hspeed"></param>
        /// <param name="Lspeed"></param>
        /// <param name="AThome"></param>
        public void setMode_HM(byte id, GoHome homeMode, int Hspeed, int Lspeed, int AThome)//  Home
        {// 回零模式:Home
         //6040h  b4:Homing operation start (positive trigger)
         //         回零操作开始 (正触发)
         //60FDh数字输入0

            address = id;// 重新绑定从站
            try
            {

                // HM回零模式
                set_region(0x6060, 0, 6); //设置为回零模式  ( 1位置 3速度 4扭矩 6回零 7补间)
                if (get_region(0x6061, 0) != 6) throw new Exception("回零模式:设置失败!");

                // 速度基准 (步进电机57 速度250r/mm
                set_region(0x2001, 0, 200); // 一圈脉冲量,(200~51200), 》》》》》这个影响转速
                                            //60ff 一秒脉冲量:一秒一圈(一秒脉冲量等于一圈脉冲量)
                set_region(0x6081, 0, 200 * 250 / 60); // 最高速度(每秒脉冲量
                set_region(0x6082, 0, 200 * 100 / 60); //起跳速度
                set_region(0x6083, 0, 200 * 250 / 1); // 加速度  1秒
                set_region(0x6084, 0, 200 * 250 / 1); // 减速度
                set_region(0x6085, 0, 200 * 2500); // 急停减速度 (额定转速除以0.1秒


                // 回零配置
                set_region(0x607C, 0, AThome); // 原点偏移
                set_region(0x6098, 0, (byte)homeMode); // 回零方式选择(1~35)   35当前位置强制零点
                set_region(0x6099, 1, Hspeed); // 回零 高速
                set_region(0x6099, 2, Lspeed); // 回零 低速(找Z点速度)
                set_region(0x609A, 0, 3000); // 回零加减速

                PDOr_Config(PDOtype.PDO1, 0x6040, 16, 0, 0);

                //执行回零 bit4
                set_PDO(PDOtype.PDO1, 0x0f, 16, 0);// 准备回零
                SYNC();
                set_PDO(PDOtype.PDO1, 0x1f, 16, 0);
                SYNC();                            // 触发回零
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 定位模式(齿轮分子,齿轮分母)
        /// </summary>
        public void setMode_PP(int zi, int mu)// 定位
        {
            // 目标位置607A-00
            // 目标速度6081-00
            // 实际位置6064-00
            // 实际速度606C-00
            // 数字输入60FD-00
            // 最新错误603F-00
            // 定位模式:6060
            // 加速 6083
            // 减速 6084
            // 电机位置6064
            //控制字6040
            //状态字6041
            //读取当前模式6061
            // ,读实际位置6063,读电机位置6064,位置容忍误差6065,读追随误差60F4,到位范围值6067,到位时间值6068
            try
            {
                // PP定位模式
                set_region(0x6060, 0, 1); //设置为轮廓位置模式  ( 1位置 3速度 4扭矩 6回零 7补间)
                if (get_region(0x6061, 0) != 1) throw new Exception("定位模式:设置失败!");

                //==========受限于单帧8字节===========
                //齿轮比
                set_region(0x6093, 1, zi); //乘以N ,齿轮 分母(编码器最大值)
                set_region(0x6093, 2, mu); //除以N ,齿轮 分子(一圈脉冲数)200

                // 第1组  绝对定位
                PDOr_Config(PDOtype.PDO1, 0x6040, 16, 0, 0);//set 控制字
                PDOt_Config(PDOtype.PDO1, 0x6041, 16, 0x6064, 32);//get 位置反馈(32bit)、、注意6041的bit10
                // 第2组
                PDOr_Config(PDOtype.PDO2, 0x607A, 32, 0x6081, 32);// 目标位置,目标速度

                //PDOt_Config(PDO.PDO2, 0x6041, 16, 0x6064, 32);//位置反馈(32bit)

            }
            catch (Exception ex)
            {
                throw ex;
                //PDOr_Config(PDO.PDO1, 0x6040, 16, 0x6081, 32);//目标位置
                //PDOr_Config(PDO.PDO1, 0x6040, 16, 0x607A, 32, 0x6081, 32);//目标位置,速度
            }
            //6061读电机当前模式
            //6062读命令位置
            //6063读位置
            //6064当前电机位置
            //set_region(0x6065, 0, 12345);// 位置容忍误差
            //set_region(0x6067, 0, 12345);// 到位确认,范围值
            //set_region(0x6068, 0, 12345);// 到位确认,时间范围值
            //607A设置目标位置

            // 速度
            //set_region(0x6081, 0, 100); //定位速度;(受齿轮比影响)
            //set_region(0x6082, 0, 10); // 默认0, 启停速度
            //set_region(0x6083, 0, 3000);// 加速
            //set_region(0x6084, 0, 3000);// 减速
            //set_region(0x6085, 0, 50000000); // 停止减速度 50w(2FAF080)  

            //60F4h跟随误差实际值
            //60FCh位置需求值





            //set_region(0x6060, 0, 0x01); // 
            //set_region(0x6060, 0, 0x01); // 

            //            设置运动轨迹参数
            //603 8 23 81 60 00 20 4E 00 00 ‘设置速度为20000 step/ s
            //603 8 23 83 60 00 40 9C 00 00 ‘设置加速度为40000 step/ s2
            //603 8 23 84 60 00 40 9C 00 00 ‘设置减速度为40000 step/ s2
            //绝对运动
            //603 8 23 7A 60 00 40 0D 03 00 ‘设置目标位置为200000 steps
            //603 8 2B 40 60 00 1F 00 00 00 ‘设置New Set Point位为1(电机开始绝对运动)
            //603 8 2B 40 60 00 0F 00 00 00 ‘清除New Set Point
            //相对运动
            //603 8 23 7A 60 00 40 0D 03 00 ‘设置目标位置为200000 steps
            //603 8 2B 40 60 00 5F 00 00 00 ‘设置New Set Point位为1(电机开始相对运动)
            //603 8 2B 40 60 00 4F 00 00 00 ‘清除New Set Point



            //   607A  目标位置
            //   607E  数据取反
            //   6081   轮廓速度
            //  607F    最大速度
            //  6083    轮廓加速度
            //  6084    轮廓减速度
            //  6085    快速停止速度
            //  605A    快速停止模式
            //  6062   取得內部位置命令。 (unit: PUU)
            // 6063h   取得實際位置。 (unit: increments)
            // 6065h】設定為實際位置和位置命令的容忍誤差值。
            // 60F4h】,取得追隨誤差的實際數值。
            // 6067h】,定義距離目標位置的允許正負誤差範圍
            //6068h】,設置當停留於 Position window 內多少時 間時 ,可判定為位置到達(unit: millisecond)。

            // 0x6040控制字
            //0x6041状态值
            //0x6060操作模式
            //0x6061操作模式显示( 1位置 3速度 6回零 )
            //0x607A目标位置
            //0x6081剖面速度
            //0x6083配置文件加速
            //0x6084轮廓减速
            //0x6085急停减速
            //0x605A快速停止代码

            //===========================



        }
        /// <summary>
        /// 补间模式
        /// </summary>
        public void setMode_IP()// 补间
        {//60C0h插值子模式选择INTEGER16 RW
         //60C1h插值数据记录ARRAY RW
            try
            {
                set_region(0x6060, 0, 7); //设置为轮廓位置模式  ( 1位置 3速度 4扭矩 6回零 7补间)
                if (get_region(0x6061, 0) != 7) throw new Exception("补间模式:设置失败!");
                //齿轮比
                set_region(0x6093, 1, 1);//乘以N , 齿轮 分子1
                set_region(0x6093, 2, 1);//除以N , 齿轮 分母1



            }
            catch (Exception ex)
            {
                throw ex;
            }


        }
        /// <summary>
        /// 速度模式:目标速度0x60FF,速度反馈0x606C
        /// </summary>
        public void setMode_PV()// 速度Speed  200转(200*100)填 2万
        {// 目标速度 60FF-00
            try
            {
                // 速度
                set_region(0x6060, 0, 3); //设置为轮廓位置模式  ( 1位置 3速度 4扭矩 6回零 7补间)
                if (get_region(0x6061, 0) != 3) throw new Exception("速度模式:设置失败!");
                // 第一组
                PDOr_Config(PDOtype.PDO1, 0x6040, 16, 0, 0);//6040控制字
                PDOt_Config(PDOtype.PDO1, 0x6041, 16, 0x606C, 32);// 状态字 + 速度反馈
                // 第二组
                PDOr_Config(PDOtype.PDO2, 0x60ff, 32, 0, 0);// 目标速度

            }
            catch (Exception ex)
            {
                throw ex;
                //2001 一圈脉冲      200
                //60ff 一秒脉冲量200    这样一秒一圈

            }

        }
        public void setMode_PT(byte id)// 扭矩t 模式
        {
            try
            {
                // 扭矩
                set_region(0x6060, 0, 4); //设置为轮廓位置模式  ( 1位置 3速度 4扭矩 6回零 7补间)
                if (get_region(0x6061, 0) != 4) throw new Exception("扭矩控制模式:设置失败!");

                PDOr_Config(PDOtype.PDO1, 0x6040, 16, 0, 0);//6040控制字
                PDOt_Config(PDOtype.PDO1, 0x6041, 16, 0x6077, 32);//扭矩反馈

                PDOr_Config(PDOtype.PDO2, 0x6071, 16, 0, 0);//目标扭矩
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        //===============================================
        /// <summary>
        /// 0x1010_0x04=0x65766173;回帧大于2秒
        /// </summary>
        /// <returns></returns>
        public int set_Save()// 1010h_4个子项
        {
            int sta = 1;
            //byte by0 = (byte)regionSET.写b32;// 写32bit
            //Int16 by1 = 0x1010;//      寄存器 0x1010
            //byte by2 = (byte)(0x01);// 子索引 0x01
            //Int64 by3 = 0x65766173;
             b0  b1b2  b3   b4b5b6b7
            //byte8 = by0 + (by1 << 8) + (by2 << 24) + (by3 << 32);
            //SDO(byte8);

            sta &= set_region(0x1010, 0x01, 0x65766173, 2000);// 保存
            return sta;
        }
        /// <summary>
        /// 0x1011_4=0x64616F6C;不要保存,只要断电重启
        /// </summary>
        /// <returns></returns>
        public int set_Rest恢复出厂()//4个子项  //  回帧大于1秒
        {
            int sta = 1;
            //byte by0 = (byte)regionSET.写b32;// 写32bit
            //Int16 by1 = 0x1011;//      寄存器 0x1011
            //byte by2 = (byte)(0x01);// 子索引 0x01
            //Int64 by3 = 0x64616F6C;
             b0  b1b2  b3   b4b5b6b7
            //byte8 = by0 + (by1 << 8) + (by2 << 24) + (by3 << 32);
            //SDO(byte8);
            //set_Save();// 保存参数,需要断电生效

            sta &= set_region(0x1011, 0x04, 0x64616F6C, 1000);// 恢复出厂,回帧大于1秒 (雷赛只能子索引4)
            return sta;

        }

        /// <summary>
        /// 高位地址(N*32+SW=127)32的倍数
        /// SW为off时,逻辑为1
        /// </summary>
        /// <param name="address"></param>
        public void set_address_Hight(addressH address)
        {//恢复出厂,保存后,高地址为空
            byte add = (byte)address;
            set_region(0x2150, 0, add);//高位地址
            set_Save();
        }

        public void PowerEN()// 使能电机
        {
            //address = id;// 重新绑定从站
            try
            {
                set_region(0x6040, 0x00, 0x06);// 脱机
                set_region(0x6040, 0x00, 0x07);// 准备使能
                                               //Help_Delay.delayTime(5);
                set_region(0x6040, 0x00, 0x0F);// 进入使能(上升沿 触发)
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public void PowerOFF()// 使能电机
        {
            set_region(0x6040, 0x00, 0x06);// 脱机

        }





        #endregion
        #endregion

        #region 后台方法
        /// <summary>
        /// 制作Code编码   1  4  8
        /// </summary>
        /// <param name="hend"></param>
        /// <param name="id"></param>
        /// <param name="byte8"></param>
        /// <returns></returns>
        string madeCode(int hend, int id, Int64 byte8)// 1表示帧类型,4字节是帧id,8表示帧数据包
        {
            string data8 = string.Empty;
            for (int i = 0; i < 8; i++)
            {
                data8 += ((byte8 >> (i * 8)) & 0xFF).ToString("X2");
            }
            // 单帧
            string cmd = hend.ToString("X2") + id.ToString("X8") + data8;// 1 4 8   配置数据单帧
            byte[] buffer = helpstring.StringsToHexBytes(cmd);// 单帧 13字节
            string data = helpstring.BytesToHexStrings(buffer);// 等效编码
            return data;
        }
        /// <summary>
        /// 6041h状态
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        public string get_6041h(byte byte0, byte byte1)
        {
            string str0 = Convert.ToString(byte0, 16);
            string str1 = Convert.ToString(byte1, 16);
            int state = Convert.ToInt32(str1 + str0, 16);

            string end = string.Empty;
            switch (state & 0x6F)
            {
                case 0x00: end = "未准备启动 "; break;
                case 0x40: end = "取消启动 "; break;
                case 0x21: end = "准备启动 "; break;
                case 0x23: end = "启动 "; break;
                case 0x27: end = "允许操作 "; break;
                case 0x07: end = "快速停止激活 "; break;
                case 0x0F: end = "故障效应激活 "; break;
                case 0x08: end = "故障 "; break;

                default:
                    break;
            }
            return end;
        }

        //private void delayTime(double secend)
        //{
        //    DateTime tempTime = DateTime.Now;
        //    while (tempTime.AddSeconds(secend).CompareTo(DateTime.Now) > 0)
        //        Application.DoEvents();
        //}


        Int32 get_result(List<string> getend)//   "46s8821:08000005814B002000E8030000"
        {//回帧结果
            string str = string.Empty;
            string obj = string.Empty;//寄存器标记
            for (int i = 0; i < getend.Count; i++)
            {
                string str1 = getend[i];// 提取
                if (str1.Length < 26) throw new Exception("长度不够 ");//长度不够
                string str2 = str1.Substring(str1.Length - 16); // 截取 8字节
                obj = str2.Substring(0, 2);// 寄存器 类型
                                           //  1111    1011   0111   0011
                if (obj == "4F" | obj == "4B" | obj == "47" | obj == "43" | obj == "80")// 1字节,2字节,3字节,4字节,错误码
                {
                    Region_Type = Convert.ToByte(obj, 16);// 标记寄存器 类型
                    //if (obj == "4F") Region = 8;
                    //if (obj == "4B") Region = 16;
                    //if (obj == "47") Region = 24;
                    //if (obj == "43") Region = 32;
                    string str3 = str1.Substring(str1.Length - 8);
                    for (int j = 0; j < 4; j++)
                    {
                        string num = str3.Substring(str3.Length - (2 * j) - 2, 2);
                        str += num;
                    }
                    return Convert.ToInt32(str, 16);// 成功
                }
                //下一轮数据帧
            }
            if (obj == "60") Region_Type = 0x60; return 0;// 写成功

            //return -1;// 读取失败
        }
        Int32 get_result(byte[] getend)//   "46s8821:08000005814B002000E8030000"
        {//回帧结果
            return getend[9] + (getend[10] << 8) + (getend[11] << 16) + (getend[12] << 24);
        }

        void Log_ol(byte errcode, short reg, int Son)
        {
            //  日志====================================
            if (Region_Type < 0x43 | Region_Type > 0x4F)// 读取失败
            {
                string error = getError(errcode, reg, Son, 0x40); // 故障码,寄存器,子,值
                if (Wt_get != null)
                {
                    Wt_get(new byte[] { 0x00 }, error);
                    //throw new Exception(error);
                }
            }

        }

        //void wait_dataRX数据池()
        //{
        //    do
        //    {
        //        if (rj45.rx数据池字节 != 0)
        //        {
        //            int i = 0;// 调试 断点
        //            i++;
        //        }
        //        if (rj45.rx数据池字节 == 0)
        //        {
        //            break;
        //        }
        //    } while (true);
        //}


        void throw_bytes()
        {
            Thread.Sleep(20);
            if (rj45.socket.Available > 0)
            {
                byte[] bytes = new byte[rj45.socket.Available];

                int count = rj45.socket.Receive(bytes, bytes.Length, SocketFlags.None);

            }

        }


        #endregion
        #region 模块方法
        public enum regionSET : byte
        {
            写b8 = 0x2F,
            写b16 = 0x2B,
            写b24 = 0x27,
            写b32 = 0x23,
            读 = 0x40, // 成功0x60

        }
        enum regionGET : byte
        {
            读 = 0x40,
            b8 = 0x4f,
            b16 = 0x4b,
            b24 = 0x47,
            b32 = 0x43

        }

        public enum NMTe : byte
        {
            启动从站 = 0x01,
            停止从站 = 0x02,
            预运行从站 = 0x80,
            重启从站 = 0x81,
            通讯重连 = 0x82
        }
        public enum NMTsalve : byte
        {
            心跳 = 0x00,
            未连接 = 0x01,
            连接 = 0x02,
            预备 = 0x03,
            从站停止 = 0x04,
            从站运行 = 0x05,
            从站预操作 = 0x7F // PDO禁用状态
        }

        public enum POWER : byte
        {
            //          b7  ↑故障复位
            //         0x06  关闭
            //         0x07  打开
            //         0x0F  打开+启用操作
            //         0<<1  快速停止
            //         0x07  禁用操作
            //         0x0F  启用操作
            完全关闭 = 0x06,
            快速停止 = 0x00,
            禁用操作 = 0x07,
            启用操作 = 0x0F //要先0x06完全关闭

        }
        public enum PDOtype : byte
        {
            PDO1 = 1,
            PDO2,
            PDO3,
            PDO4,
        }
        public enum GoHome : byte
        {//6098
            方法17 = 17,
            方法18 = 18,
            方法19 = 19,
            方法20 = 20,
            方法21 = 21,
            方法22 = 22,
            方法23 = 23,
            方法24 = 24,
            方法25 = 25,
            方法26 = 26,
            方法27 = 27,
            方法28 = 28,
            方法29 = 29,
            方法30 = 30,
            方法31 = 31,
            方法32 = 32,
            方法33 = 33,
            方法34 = 34,

            方法35 = 35,
            零点直设 = 37
        }

        public enum cia402 : ushort//  16bit
        {
            故障码 = 0x603F,
            控制字 = 0x6040,// 油门,刹车   06h关闭》07h伺服使能(命令关)》0Fh命令生效           00h急停
                         // 0 启动                1伺服开,0伺服关
                         // 1 电压输出            1电压输出,0关
                         // 2 急停                1不报警
                         // 3 命令(上升沿)     01命令上升沿生效    07h》0Fh
                         //======================================
                         // 4 运动指令(上升沿)    该位由0跳变为1将触发新的位置指令
                         // 5 0等待旧命令位置完成        1立刻更新并执行新的位置指令动作
                         // 6 0绝对位置           1相对位置
                         // 7 复位               01上升沿,故障复位
                         // 8 1暂停             0继续运行
                         // 9  速度             1使用上次的旧速度
                         // 10~15 空
            状态字 = 0x6041,// 汽车仪表(状态)
                         // 0 
                         // 1
                         // 2
                         // 3
                         // 4
                         // 5
                         // 6
                         // 7
                         // 8   0: 目标位置到达 (bit10为1)
                         // 9
                         // 10  1
                         // 11
                         // 12   当前位置正在缓存, 新的位置将被接受
                         // 13   跟随错误
                         // 14
                         // 15

            快停 = 0x605A,
            //定位模式
            工作模式 = 0x6060, // 01h定位模式
            目标位置 = 0x607A,
            额定速度 = 0x6081,// 2w
            加速度 = 0x6083,// 5w
            减速度 = 0x6084,// 50000
                         // 绝对0x6040=1Fh     停 0Fh
                         // 相对0x6040=5Fh     停 4Fh

            //速度模式
            目标速度 = 0x60FF,
            速度反馈 = 0x606C,

            //力矩模式

            //回零模式
            回零方式 = 0x6098,

            回零速度 = 0x6099,//-01回零高速,-02回零低速
            回零加速 = 0x609A,

            零点偏置 = 0x607C,




            //0x603F-错误代码UINT16 RO YES
            //0x6040-控制字UINT16 WO是
            //0x6041-状态剑UINT16 RO是
            //0x605A-快速停止选项代码INT16 RW NO
            //0x6060-操作模式INT8 WO是
            //0x6061-操作模式显示INT8 RO YES
            //0x6064-位置实际值INT32 RO是
            //0x6065-以下错误窗口UINT32 RW NO
            //0x606C-速度实际值INT32 RO是
            //0x6071-目标扭矩INT16 RW是
            //0x6073-最大电流UINT16 RW是
            //0x6074-扭矩需求INT16 RO是
            //0x6078-当前实际值INT16 RO是
            //0x607A-目标位置INT32 RW是
            //0x607C-起始偏移INT32 RW是
            //0x607E-极性UINT8 RW是
            //0x607F-最大剖面速度UINT32 RW是
            //0x6081-剖面速度UINT32 RW是
            //0x6083-配置文件加速UINT32 RW是
            //0x6084-轮廓减速UINT32 RW是
            //0x6085-快速停止减速UINT32 RW是
            //0x6087-扭矩斜率UINT32 RW是
            //0x6098-归位方法INT8 RW是
            //0x6099 2归位速度阵列-否
            //0x609A-归位加速度UINT32 RW是
            //0x60F4-跟随错误实际值INT32 RO YES
            //0x60FD-数字输入UINT32 RW是
            //0x60FE 2数字输出阵列-否
            //0x60FF-目标速度INT32 RW是
            //0x6502-支持的驱动器模式UINT32 RO N


            //0x603F - Error code UINT16 RO YES
            //0x6040 - Controlword UINT16 WO YES
            //0x6041 - Statusword UINT16 RO YES
            //0x605A - Quick stop option code INT16 RW NO
            //0x6060 - Modes of operation INT8 WO YES
            //0x6061 - Modes of operation display INT8 RO YES
            //0x6064 - Position actual value INT32 RO YES
            //0x6065 - Following error window UINT32 RW NO
            //0x606C - Velocity actual value INT32 RO YES
            //0x6071 - Target torque INT16 RW YES
            //0x6073 - Max current UINT16 RW YES
            //0x6074 - Torque demand INT16 RO YES
            //0x6078 - Current actual value INT16 RO YES
            //0x607A - Target position INT32 RW YES
            //0x607C - Home offset INT32 RW YES
            //0x607E - Polarity UINT8 RW YES
            //0x607F - Max profile velocity UINT32 RW YES
            //0x6081 - Profile velocity UINT32 RW YES
            //0x6083 - Profile acceleration UINT32 RW YES
            //0x6084 - Profile deceleration UINT32 RW YES
            //0x6085 - Quick stop deceleration UINT32 RW YES
            //0x6087 - Torque slope UINT32 RW YES
            //0x6098 - Homing method INT8 RW YES
            //0x6099 2 Homing speed ARRAY - NO
            //0x609A - Homing acceleration UINT32 RW YES
            //0x60F4 - Following error actual value INT32 RO YES
            //0x60FD - Digital inputs UINT32 RW YES
            //0x60FE 2 Digital outputs ARRAY - NO
            //0x60FF - Target velocity INT32 RW YES
            //0x6502 - Supported drive modes UINT32 RO N




        }

        public enum addressH : byte
        {
            add_0 = 0,
            add_32 = 1,
            add_64 = 2,
            add_96 = 3,// 96+31=127
        }

        public class PDOregion
        {
            public int num;
            public int[] regName { get; set; }//PDO待绑定寄存器

            public byte[] regBit { get; set; }// 寄存器bit长度

        }
        public enum CMDtype
        {
            回零,
            绝对定位,
            相对定位,
            补间,
            速度,
            力矩,
            复位,// bit7
            暂停,// bit8

        }

        #endregion


        #region 委托

        #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); }//显示错误





        //================================================
        // 读取回帧-------0x40 结果
        //int WaitCount = 0;//接收超次数
        //DateTime startTime = DateTime.Now;//记录开始时间
        // 4  等待接收
        #region MyRegion
        //while (true)
        //{// 08 00000081 20811104 00000000
        //    // 超时
        //    if (timeOutFun)
        //    {
        //        if ((DateTime.Now - startTime).TotalMilliseconds > 300)// 超时
        //        {
        //            Region_Type = 0x80;// 标记  读取失败
        //            throw new Exception("(超时)读寄存器失败!");   // 超时   毫秒
        //        }
        //    }// 超时
        //    rx13 = Receive();//回帧报文
        //    if (rx13 == null)
        //    {
        //        ++WaitCount;
        //        if (WaitCount > 20)// 超次数
        //        {
        //            rx13 = new byte[13];
        //            rx13[5] = 0x80;// 故障码
        //            break;
        //        }
        //        continue;
        //    }// 超次
        #endregion





        #endregion

        #region 接口方法

        #endregion

        #region 参考

        //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)

        //6041         上电50》启动250》06码231》07码233》0F码c237

        //0准备打开=========================7
        //1打开
        //2操作已启用(伺服开启状态)
        //3故障(驱动器将伺服关闭)
        //   4电压==========================3
        //   5快速停止
        //   6关闭
        //   7警告(驱动器仍然打开伺服)
        //8非正常停止===========================2
        //9远程控制
        //10位置到达(到位置1)
        //11内部位置超限(不支持)

        //12  pp设定点 承认    HM回原点完成    ipIP模式激活  pv零速
        //13  pp以下错误       HM归零错误
        //
        //
        //定位中   1237
        //定位完成 1637
        //



        #endregion
    }
}

更新24.3.13


using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
//====================================
using help;             //【文本转换】
using static CAN_namespace.Help_CAN_ZHCX;// 创芯CAN 【缓冲区2000帧】        珠海创芯科技
//using static CAN_namespace.Help_CAN_SYGC;// 广成CAN  【5帧,一坨屎】       沈阳广成
using static System.Net.Mime.MediaTypeNames;

//===============【使用方法】标准PLCopen方法
//CTRL + M, CTRL + O折叠代码定义
//CTRL + M, CTRL + L展开代码定义
#region Cia定义
// canopen协议: https://blog.csdn.net/cfqq1989/article/details/128707894
//指数(十六进制)    目标
//0000               未使用
//0001 - 025F        数据类型
//0260 - 0FFF       保留供进一步使用

//1000-1FFF        》通信参数 区域 CiA301(DS301)总线命令HMT,SDO,PDO配置
//2000-5FFF        》设备参数(按伺服品牌厂家生产制造商特定)
//6000-9FFF        》标准化,运动控制 区域 CiA402(DSP402)
//A000-AFFF        网络可变区域
//B000-BFFF        系统可变区
//C000-CFFF        保留供进一步使用
//————————————————
//版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
//原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808

#endregion
#region Cia301_通讯层
//1000h 硬件类型(硬件识别码0x40192)   04020192 h : A2 Series    06020192h : M Series
//1001h 错误寄存器4组
//1003h 预定义错误4组 (-01:58120)
//1005h  COB-ID SYNC码 0x80(同步码)
//1006h 通信周期0
//100Ch 保护时间0
//100Dh 寿命因数0
//1010h 参数保存4组(0x1010_0x04=0x65766173;回帧大于2秒)
//1011h 恢复默认参数4组(0x1011_4=0x64616F6C;不要保存,只要断电重启)
//1014h COB-ID EMCY(0xFF)急停码
//1016h 消费者心跳时间-1
//1017h 发生器心跳时间 2000ms
//1018h 标识对象4
//1029h 阵列错误行为-1
//1200h 第一台服务器SDO参数SDO参数RO(2)
//1400h~03h 接收rPDO控制参数(开关配置)bit31总开关(0开)bit30RTR(0开)bit29标准帧(0标准帧1扩展帧)bit10~0 COB-ID码(帧id:11bit)
//1600h~03h 接收rPDO映射(寄存器指针)
//1800h~03h 发送tPDO控制参数(开关配置)
//1A00h~03h 发送tPDO映射(寄存器指针)

#endregion
#region Cia402_运动层
//603Fh 最后错误代码-1
//6040h 控制字==========【0】【6】【7】【F】
//6041h 状态字==========【0x250】
//605Ah 快速停止代码
//605Bh 停机选项代码-1
//605Eh 故障反应选项代码-1
//6060h 工作模式          【1位置 3速度 4扭矩 6回零 7补间 8Ecat:CSP】================
//6061h 驱动器当前工作模式【1位置 3速度 4扭矩 6回零 7补间 8Ecat:CSP】
//6062h 显示电机命令位置
//6063h 位置实际值-1
//6064h 显示电机实际位置0
//6065h 以下错误窗口-1
//6067h 位置窗口-1
//6068h 位置窗口时间-1
//606Bh 显示电机命令速度0
//606Ch 显示电机的实际速度0
//606Dh 速度窗口-1
//606Eh 速度窗口时间-1
//606Fh 速度阈值-1
//6071h 目标扭矩-1
//6074h 扭矩需求值-1
//6075h 电机额定电流-1
//6076h 电机额定扭矩-1
//6077h 扭矩实际值-1
//6078h 当前实际值-1
//607Ah 目标位置0
//607Ch 原点偏移0
//607Dh 软件位置限制-1
//607Fh 最大剖面速度-1
//6080h 最大电机转速-1
//6081h 位置模式的最大速度(每秒脉冲量)除以每圈脉冲  (每圈默认5万)
//6082h 起跳速度
//6083h 加速度【单圈脉冲量】乘以【速度/秒】
//6084h 减速度【单圈脉冲量】乘以【速度/秒】
//6085h 急停减速度(默认5000 0000
//6086h 运动剖面类型-1
//6087h 扭矩斜率-1
//6093h位置系数2
//6098h回零方式-------35和37是当前位置设零点
//6099h-01原点快速
//6099h-02原点慢速
//609Ah 回零加减速 5000000
//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  物理输出开启1
//60FEh-02  物理输出开启2
//60FFh目标速度0
//6502h支持的驱动模式-1

#endregion
#region 类型
// AXIS_REF_HC_CO  伺服can轴类型 【禾川CAN轴】

#endregion
namespace CAN_namespace
{
    public class Help_CANopen
    {//作者qq750273008 祁成  更新日期:24/3/13          2023.11.28

        // CANopen【指令说明】
        //NMT总线命令、SYNC同步、SDO读写、PDO批量读写、EMCY故障

        //NMT(NMT总线命令,从站id); //控制从站失效和上线
        //SYNC();同步命令// 用于PDO命令生效
        //SDO();读写寄存器// 每次读写单个寄存器
        //PDO();批量读写寄存器//一次可读写64bit长度寄存器组

        #region 全局
        //1申明委托》委托命令
        public delegate void WT_GET_Data(byte[] butes, string data);//↑ui显示发送命令
        public delegate void WT_SET_Data(byte[] butes, string data);//↓下执行对象

        #region CanOpen【数据类型】:全局

        //public unsafe struct VCI_CAN_OBJ  //CAN数据帧 【单帧对象】
        //{
        //    public uint ID;// 帧ID。      【11bit】数据右对齐。 详情请参照: 《8.附件1: ID对齐方式.pdf》说明文档。
        //    public uint TimeStamp;        //设备接收到某一帧的时间标识。 时间标示从CAN卡上电开始计时,计时单位为0.1ms。
        //    public byte TimeFlag;         //时间标识,为1时TimeStamp有效
        //    public byte SendType;         //发送帧类型。=0时为正常发送(发送失败会自动重发,重发超时时间为4秒, 4秒内没有发出则取消);=1时为单次发送
        //    public byte RemoteFlag;       //是否是远程帧【RTR】。 =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];//系统保留。
        //}

        public enum Region_Type : byte 
        {
            读 = 0x40, // 回帧0x60成功
            成功 = 0x60,
            失败 = 0x80,
            写b8 = 0x2F,
            写b16 = 0x2B,
            写b24 = 0x27,
            写b32 = 0x23,
            //寄存器bit
            b8 = 0x4F,//回帧:4F 寄存器有效长度 8bit
            b16 = 0x4B,
            b24 = 0x47,
            b32 = 0x43
        }//【寄存器bit】

        public enum NMT_CMD : byte
        {//NMT总线命令;重连,重启,停止,预备,启动
            /// <summary>
            /// 【只有工作模式后,才能定位,回零,速度】
            /// </summary>
            从站工作 = 0x01,//回帧 05  PDO有效(可以运动控制)
            从站停止 = 0x02,//回帧 04  运动命令失效【PDO命令失效】
            /// <summary>
            /// 【伺服待机,配参】
            /// </summary>
            从站预操作 = 0x80,  //回帧 0F  SDO命令有效,PDO命令失效(用于伺服配置参数阶段)【伺服配参】
            从站重启 = 0x81,    //回帧 0F  SDO命令有效,PDO命令失效
            从站通讯重连 = 0x82,//回帧 0F  SDO命令有效,PDO命令失效
        }//【总线,命令】

        public enum Status_Slave : byte
        {//从站状态
            正在初始化 = 0x00,
            未连接 = 0x01,
            连接 = 0x02,
            预备 = 0x03,
            停止 = 0x04,  //停止状态,NMT有效
            运行 = 0x05,  //PDO有效
            预操作 = 0x7F //SDO有效, PDO失效    // 伺服配置参数状态
        }//【从站,状态】

        public enum PDO_Type
        {
            PDO1cmd = 1,    //   寄存器呼叫
            PDO2cmd,
            PDO3cmd,
            PDO4cmd,
            配置rPDO1,// 上位机到终端 数据帧
            配置rPDO2,
            配置rPDO3,
            配置rPDO4,
            配置tPDO1,//  回帧  (终端到上位机)
            配置tPDO2,
            配置tPDO3,
            配置tPDO4,
        }

        public struct PDO_Slave
        {
            public byte regUSE;
            public Int32[] regNO;
            public Int32[] regValue;
        }

        public struct AXIS_OBJ
        {
            public bool isError;
            public Int64 Error;
            public byte h700;
            public PDO_Slave PDO1;
            public PDO_Slave PDO2;
            public PDO_Slave PDO3;
            public PDO_Slave PDO4;
            public Int64 PDO1_Int64;
            public Int64 PDO2_Int64;
            public Int64 PDO3_Int64;
            public Int64 PDO4_Int64;
        }

        public struct AXIS_OBJ3
        {
            public byte h700;
            public Int64 PDO1_Int64;
            public int[][] reg;//  PDO1_reg1;
            public int[] PDO_reg_num;//  
            public int PDO1_num;
            public int PDO1_reg1;
            public int PDO1_reg2;
            public int PDO1_reg3;
            public int PDO1_reg4;
            public int PDO1_reg5;
            public int PDO1_reg6;
            public int PDO1_reg7;
            public int PDO1_reg8;
            public int PDO1_reg1_value;
            public int PDO1_reg2_value;
            public int PDO1_reg3_value;
            public int PDO1_reg4_value;
            public int PDO1_reg5_value;
            public int PDO1_reg6_value;
            public int PDO1_reg7_value;
            public int PDO1_reg8_value;

            public Int64 PDO2_Int64;
            public int PDO2_num;
            public int PDO2_reg1;
            public int PDO2_reg2;
            public int PDO2_reg3;
            public int PDO2_reg4;
            public int PDO2_reg5;
            public int PDO2_reg6;
            public int PDO2_reg7;
            public int PDO2_reg8;
            public int PDO2_reg1_value;
            public int PDO2_reg2_value;
            public int PDO2_reg3_value;
            public int PDO2_reg4_value;
            public int PDO2_reg5_value;
            public int PDO2_reg6_value;
            public int PDO2_reg7_value;
            public int PDO2_reg8_value;

            public Int64 PDO3_Int64;
            public int PDO3_num;
            public int PDO3_reg1;
            public int PDO3_reg2;
            public int PDO3_reg3;
            public int PDO3_reg4;
            public int PDO3_reg5;
            public int PDO3_reg6;
            public int PDO3_reg7;
            public int PDO3_reg8;
            public int PDO3_reg1_value;
            public int PDO3_reg2_value;
            public int PDO3_reg3_value;
            public int PDO3_reg4_value;
            public int PDO3_reg5_value;
            public int PDO3_reg6_value;
            public int PDO3_reg7_value;
            public int PDO3_reg8_value;

            public Int64 PDO4_Int64;
            public int PDO4_num;
            public int PDO4_reg1;
            public int PDO4_reg2;
            public int PDO4_reg3;
            public int PDO4_reg4;
            public int PDO4_reg5;
            public int PDO4_reg6;
            public int PDO4_reg7;
            public int PDO4_reg8;
            public int PDO4_reg1_value;
            public int PDO4_reg2_value;
            public int PDO4_reg3_value;
            public int PDO4_reg4_value;
            public int PDO4_reg5_value;
            public int PDO4_reg6_value;
            public int PDO4_reg7_value;
            public int PDO4_reg8_value;

        }
      
        public struct PDO_Config_Struct // PDO的配置表
        { //从站id,PDO配置,指针表
            //public Int32[] Regs;//=new UInt16[8];//= 0x60400010;    //8张表
            public Byte UseRegNum;  // 寄存器指针使用数量
            public Int32 Reg1;  // 寄存器指针1
            public Int32 Reg2;  // 寄存器指针2
            public Int32 Reg3;
            public Int32 Reg4;
            public Int32 Reg5;
            public Int32 Reg6;
            public Int32 Reg7;
            public Int32 Reg8;
            //======【写信】===================================================================
            /// <summary>
            /// 【0~240】在1个SYNC信号后,rPDO值才生效。【254或255】rPDO值立即生效。(不需要SYNC信号)
            /// 【241~251】保留。【252~253】不受rPDO命令控制
            /// </summary>
            public Byte rPDO命令触发器;  //【伺服接收命令】
            /// <summary>
            /// 命令死区时间【x0.1ms倍数】2个命令最小间隔
            /// </summary>
            public UInt16 rPDO禁止时间_100us;
            //======【回信】===================================================================
            /// <summary>
            /// 【0】值有变化,1次sync后上传tPDO【1~240】1~240次SYNC后上传,【254或255】值变或定时到就上传
            /// </summary>
            public Byte tPDO上传触发器;//【伺服上传状态】
            /// <summary>
            /// 上传间隔时间【x0.1ms倍数】
            /// </summary>
            public UInt16 tPDO禁止时间_100us;
            /// <summary>
            /// 只在tPDO异步时生效【x1ms】(254,255)异步,【u16】定时上传tPDO帧,65535ms
            /// </summary>
            public UInt16 tPDO定时触发器_1ms;
        }

        #endregion  【数据类型】:全局

        #endregion  全局

        #region 字段
        //private CancellationTokenSource cts;//线程令牌
        //private ManualResetEvent resetEvent = new ManualResetEvent(true);// 暂停业务
        Help_String helpstring;// 文本转换

        static UInt32 address;//标记从站地址【1~127】
        Int32 error = 0;// CAN总线故障标记

        /// <summary>
        /// 【1 4 8】字节
        /// </summary>
        static byte[] Rx13;// 1 4 8     //1:DLC   4:帧id   8:byte8
        static Int64 Byte8;// 64bit  数据帧 包
        static volatile byte Region_Obj;// 寄存器标记(读40h,写,成功60h,失败80h)参考 enum Region_Type
        /// <summary>
        /// 【忙碌状态】
        /// </summary>
        int busy;
        volatile static AXIS_OBJ[] axis_obj;//【单例】 static 实例化不会创建副本,不会加速读写性能

        #endregion  字段

        #region 属性
        /// <summary>
        /// ↑:byte包,文本
        /// </summary>
        static public WT_GET_Data Wt_get;//↑event委托=》呼叫上ui层  让上层显示:发送命令
        /// <summary>
        /// ↓:byte包,文本
        /// </summary>
        static public WT_SET_Data Wt_set;//↓委托=》呼叫下位机执行
        //===============================================================
        public UInt32 Address { get => address; set => address = value; } // 从站1~127(不能为0,因为帧id=0是NMT广播)

        public Int32 IsError { get => error; }
        
        /// <summary>
        /// 接口【收/发】
        /// </summary>
        public IFace_RTX Help_rtx { get; set; }// 接口【收/发】
        /// <summary>
        /// 忙碌状态
        /// </summary>
        public Int32 Busy { get => busy; }// 【/ˈbɪzi/】忙碌

        /// <summary>
        /// 现场总线刷新的对象
        /// </summary>
        public AXIS_OBJ[] GetAxis { get => axis_obj; }


        #endregion  属性

        #region 构造
        /// <summary>
        /// 【CAN】接口
        /// </summary>
        /// <param name="iface"></param>
        public Help_CANopen(CAN_namespace.IFace_RTX iface) // usb转CAN
        {
            Help_rtx = iface;// 接口 Help_RTX
            //================================
            Rx13 = new byte[13];// 提取数据帧
            Byte8 = 0;          // 64bit,数据帧的【数据包】
            Region_Obj = 0x80;  // 寄存器标记(读40h,写,成功60h,失败80h)参考 enum Region_Type
            busy = 0;
            helpstring = new Help_String();// 文本转换
            axis_obj = new AXIS_OBJ[128]; //127轴
            for (int i = 0; i < 127; i++)   // PDO4组 ,reg指针数量
            {
                axis_obj[i].PDO1 = new PDO_Slave();
                axis_obj[i].PDO1.regNO=new int[9];
                axis_obj[i].PDO1.regValue=new int[9];

                axis_obj[i].PDO2 = new PDO_Slave();
                axis_obj[i].PDO2.regNO = new int[9];
                axis_obj[i].PDO2.regValue = new int[9];

                axis_obj[i].PDO3 = new PDO_Slave();
                axis_obj[i].PDO3.regNO = new int[9];
                axis_obj[i].PDO3.regValue = new int[9];

                axis_obj[i].PDO4 = new PDO_Slave();
                axis_obj[i].PDO4.regNO = new int[9];
                axis_obj[i].PDO4.regValue = new int[9];
            }// axis_obj[address].PDO1.regNO[1] = obj.Reg1;

            //===【调试】===================================
            
            address = 3;// 默认 从站id






        }

        #endregion  构造

        //NMT、SYNC、EMCY、TIME、PDO、SDO。
        #region 方法_API  【 NMT总线命令、SYNC同步、SDO读写、EMCY故障、TIME时间广播、PDO批量读写。

        #region 标准CAN总线指令
        #region NMT
        /// <summary>
        /// NMT【can总线指令,从站id】0x00指所有从站
        /// </summary>
        /// <param name="Slave_id">从站id【0x00全部】 </param>
        /// <param name="nMT_Type">通讯复位,重启,停止,配参0x80,工作0x01</param>
        /// <returns></returns>
        public void NMT(NMT_CMD nMT_CMD, int Slave_id)// 【总线命令】
        {// 从站id,NMT总线命令
            NMT(nMT_CMD, Slave_id, 0.075);// 【延时,秒】
        }
        public void NMT(NMT_CMD nMT_Type)// 【总线命令】
        {// 从站id,NMT总线命令
            NMT(nMT_Type, (byte)Address, 0.075);// 【延时,秒】
        }

        #endregion

        #region SYNC
        /// <summary>
        /// 主站同步帧
        /// </summary>
        public void SYNC_Set_OutOne()//【单个同步帧】
        {
            SYNC(0.020);

        }
        /// <summary>
        /// Set从站心跳时间ms【0x1017 = 0ms为关】
        /// </summary>
        /// <param name="Slave_id"></param>
        /// <param name="type"></param>
        /// <param name="time_ms"></param>
        public void SYNC_Set_UpTime(int Slave_id, int time_ms)// 0ms为关
        {//  0x1017

            // SDO(0x1017,0x00,time_ms);
            SYNC(Slave_id, time_ms);
        }

        #endregion

        #region SDO【伺服参数配置】
        /// <summary>
        /// 【SDO】读_寄存器
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <returns></returns>
        public Int32 SDO(int reg, int son)//【读】
        {
            Int32 i32 = SDO(reg, son, 0.0);
            return i32;
        }

        /// <summary>
        /// 【SDO】写_寄存器
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SDO(int reg, int son, int value)//【写】
        {// 重启伺服操作需要延时

            bool sta = SDO(reg, son, value, 0.0);
            return sta;
        }


        #endregion

        #region PDO【批量命令】
        /// <summary>
        /// 从站,PDO组,配置表
        /// </summary>
        /// <param name="Slave_id"></param>
        /// <param name="type"></param>
        /// <param name="pdo"></param>
        /// <returns></returns>
        public bool PDO_Set_Config(PDO_Type type, PDO_Config_Struct pdo)
        {
            return PDO_Config(type, pdo);
        }

        public bool PDO_Set默认配置()
        {
            bool sta;
            //===================
            // 2批量PDO配置=========================
            #region 写信
            PDO_Config_Struct rPDO = new PDO_Config_Struct();
            rPDO.rPDO命令触发器 = 240; //【0~240】收到1个SYNC信号,rPDO值才生效【255】rPDO值立即生效(不需要SYNC帧)
            rPDO.rPDO禁止时间_100us = 10;// 1ms
            rPDO.UseRegNum = 2;
            rPDO.Reg1 = 0x60400010;   //控制字u16    //bit64分配
            rPDO.Reg2 = 0x607A0020;    //目标位置u32
            rPDO.Reg3 = 0;
            rPDO.Reg4 = 0;
            rPDO.Reg5 = 0;
            rPDO.Reg6 = 0;
            rPDO.Reg7 = 0;
            rPDO.Reg8 = 0;
            //  0x60400010, 0x607A0020 
            //PDO1
            sta = PDO_Config(PDO_Type.配置rPDO1, rPDO);// 批量:命令帧
            //PDO2
            rPDO.UseRegNum = 0;
            sta &= PDO_Config(PDO_Type.配置rPDO2, rPDO);// 关
            //PDO3
            sta &= PDO_Config(PDO_Type.配置rPDO3, rPDO);// 关
            //PDO4
            rPDO.UseRegNum = 1;
            sta &= PDO_Config(PDO_Type.配置rPDO4, rPDO);// //调试用  PDO4 【6040】

            #endregion

            #region 回信
            PDO_Config_Struct tPDO = new PDO_Config_Struct();
            tPDO.tPDO上传触发器 = 7;
            tPDO.tPDO禁止时间_100us = 5000;//500ms
            tPDO.tPDO定时触发器_1ms = 0;
            tPDO.UseRegNum = 2;
            tPDO.Reg1 = 0x60410010;    //状态字u16    //bit64分配
            tPDO.Reg2 = 0x60640020;    //电机位置u32
            tPDO.Reg3 = 0;
            tPDO.Reg4 = 0;
            tPDO.Reg5 = 0;
            tPDO.Reg6 = 0;
            tPDO.Reg7 = 0;
            tPDO.Reg8 = 0;
            //  0x60410010, 0x60640020 
            //【状态】【位置】
            //PDO1
            sta &= PDO_Config(PDO_Type.配置tPDO1, tPDO);// 批量:回帧
            //PDO2
            tPDO.UseRegNum = 0;
            sta &= PDO_Config(PDO_Type.配置tPDO2, tPDO);// 关
            //PDO3
            sta &= PDO_Config(PDO_Type.配置tPDO3, tPDO);// 关
            //PDO4
            //【定时tPDO】
            tPDO.UseRegNum = 2;
            tPDO.tPDO上传触发器 = 255;// 异步
            tPDO.tPDO定时触发器_1ms = 5000;//5秒上传
            sta &= PDO_Config(PDO_Type.配置tPDO4, tPDO);// 批量:回帧

            #endregion

            return sta;
        }

        /// <summary>
        /// PDO命令:批量读写寄存器【必须在从站工作总线命令下】
        /// 相对运动6F》7F,绝对2F》3F
        /// </summary>
        /// <param name="Slave_id"></param>
        /// <param name="type"></param>
        /// <param name="CMD"></param>
        public void PDO(PDO_Type type, Int64 CMD)
        {
            switch (type)
            {
                case PDO_Type.PDO1cmd:
                    PDO1(CMD);
                    break;
                case PDO_Type.PDO2cmd:
                    PDO2(CMD);
                    break;
                case PDO_Type.PDO3cmd:
                    PDO3(CMD);
                    break;
                case PDO_Type.PDO4cmd:
                    PDO4(CMD);
                    break;
                default:
                    break;
            }
        }

        #endregion

        #region EMCY
        public void EMCY_Out(int Slave_id, Int16 H603f, byte H1001, Int32 Motor)// 测试
        {//下位机故障 80h+从站id
            EMCY(Slave_id, H603f, H1001, Motor);// H603f记录在 h1003-01
        }

        #endregion

        #region Time【48bit】
        /// <summary>
        /// 【6字节】
        /// </summary>
        /// <param name="time"></param>
        public void TIME_Set_Time(Int64 time)
        {// 固定6字节长度
            if (time < 10000) TIME(time);
            else
            {
                DateTime dateTime = DateTime.Now;// 系统时间
                Int64 now = dateTime.ToBinary();
                //string str = now.ToString("D-HH-MM-SS");// 
                TIME(now);
            }
        }

        public DateTime GetTimer(byte[] data)
        {
            DateTime time = Convert.ToDateTime(data);
            return time;

        }


        #endregion


      
        #endregion

        /// <summary>
        /// 【1】所有【2】从1000h到1FFFh 【3】从6000h到9FFFh 【4】厂家
        /// </summary>
        /// <param name="Level4"></param>
        /// <returns></returns>
        public bool Set_Device_Reset(int Level4)//【设备恢复出厂】
        {
            const int key出厂 = 0x64616F6C;//   key出厂 = 0x64616F6C;
            //if (SDO(0x1011, d等级1_4级, key出厂, 3.0)) return true;
            if (set_region(0x1011, Level4, key出厂, 3.0)) return true;
            return false;

            //            恢复:
            //            0x1011 - 01 = 0x64616F6C;//【恢复所有参数】雷赛不支持
            //            0x1011 - 02 = 0x64616F6C;//【恢复从1000h到1FFFh】雷赛,断电重启生效(不要执行保存)【禾川0x1011-02】
            //            0x1011 - 03 = 0x64616F6C;//【恢复从6000h到9FFFh】雷赛不支持
            //            0x1011 - 04 = 0x64616F6C;//【厂家自定义】雷赛,断电重启生效(不要执行保存)
            //————————————————
            //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
            //原文链接:https://blog.csdn.net/cfqq1989/article/details/128707894
        }
        /// <summary>
        /// 【1】所有【2】从1000h到1FFFh 【3】从6000h到9FFFh 【4】厂家
        /// </summary>
        /// <param name="Level4"></param>
        /// <returns></returns>
        public bool Set_Device_Save(int Level4)//【设备参数保存】
        {
            const int key保存 = 0x65766173; //   key保存 = 0x65766173;
            //if (SDO(0x1010, d等级1_4级, key保存, 3.0)) return true;
            if (set_region(0x1010, Level4, key保存, 3.0)) return true;
            return false;

            //            对象1010h:存储参数【0x65766173】
            //•子索引00h包含支持的最高子索引。
            //•子索引01h是指可能存储在CANopen设备上的所有参数。
            //•子索引02h是指与通信相关的参数(从1000h到1FFFh的索引)。
            //•子索引03h是指与应用程序相关的参数(索引从6000h到9FFFh)。
            //•从04h到7Fh的子索引制造商可以单独存储他们选择的参数。
            //•保留80h至FEh的子索引以备将来使用。
            //————————————————
            //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
            //原文链接:https://blog.csdn.net/cfqq1989/article/details/128707894
        }



        #endregion  标准CAN总线指令


        //==以下不再重要===========================================================

        #region 核心:延时
        /// <summary>
        /// Help_Delay.delayTime(0.123);//0.1秒
        /// </summary>
        /// <param name="secend"></param>
        public void DelaySecond(double secend)// 延时,秒
        {
            DateTime tempTime = DateTime.Now;
            while (tempTime.AddSeconds(secend).CompareTo(DateTime.Now) > 0)
            {
                //Application.DoEvents();
            }
        }
        #endregion
        #region 核心:收发GET_SET 【SDO】

        unsafe public int Get_Receive(ref VCI_CAN_OBJ[] obj)
        {
            //===1 等待加载=================
            DelaySecond(0.005);//5ms   // USB速度3~5ms

            //===2 接口接收=================
            object obj2 = new object();//对象
            int num = Help_rtx.RXD(ref obj2);//  can.get_Receive(CAN_DeviceType, CAN_ind, CAN1, ref obj[0], 2000, 50);
            if (num < 1) return 0;// 空退

            //===3 取出帧,并且分发=========
            obj = (VCI_CAN_OBJ[])obj2;

            //===4 刷新从站状态
            int i = 0;
            foreach (var item in obj)
            {
                if (i >= num) break;
                if (item.ID >= 0x81 && item.ID <= 0x1FF)//  回信【80h+】故障
                {
                    axis_obj[item.ID - 0x80].isError = true;
                    axis_obj[item.ID - 0x80].Error = get_Value_Int64(item.Data);// 从站轴
                }

                if (item.ID >= 0x181 && item.ID <= 0x1FF)//  回信【180h+】
                { axis_obj[item.ID - 0x180].PDO1_Int64 = get_Value_Int64(item.Data); }// 从站轴

                if (item.ID >= 0x281 && item.ID <= 0x2FF)//  回信【280h+】
                { axis_obj[item.ID - 0x280].PDO2_Int64 = get_Value_Int64(item.Data); }

                if (item.ID >= 0x381 && item.ID <= 0x3FF)//  回信【380h+】
                { axis_obj[item.ID - 0x380].PDO3_Int64 = get_Value_Int64(item.Data); }

                if (item.ID >= 0x481 && item.ID <= 0x4FF)//  回信【480h+】
                { axis_obj[item.ID - 0x480].PDO4_Int64 = get_Value_Int64(item.Data); }

                if (item.ID >= 0x701 && item.ID <= 0x77F)//  回信【700h+】状态
                { axis_obj[item.ID - 0x700].h700 = (byte)get_Value_Int64(item.Data); }
                i++;


            }

            //===5 界面显示=================
            // 分发数据帧,UI层显示
            //if (Wt_get != null)
            //{// 数组是引用类型
            //VCI_CAN_OBJ[] uiObj = new VCI_CAN_OBJ[num];
            //uiObj = obj;
            //UIshow(num, uiObj);


            //COB - ID              Byte 0:1              Byte 2               Byte 3:7
            //0x080 + Node - ID     应急错误代码(603FH)   错误寄存器(1001H)    厂商指定区域

            //byte0~1:  8120  【0x603F】
            //        应急错误代码
            //        0000H 无错误
            //        8110H CAN 溢出
            //        8120H 错误被动模式(0411)
            //        8130H 寿命保护/ 心跳错误
            //        8140H 被迫离线恢复故障
            //        8141H 被迫离线
            //        8150H 发送 COB - ID 冲突
            //        8210H PDO 长度错误未处理
            //        8220H PDO 超过长度

            //byte2:   11
            //        (1001H)位定义
            //         Bit0:generic error 一般错误
            //        Bit1:current 电流
            //        Bit2:voltage 电压 (04)
            //        Bit3:temperature 温度
            //         Bit4:通讯
            //        Bit5:协议402错误
            //        Bit6:保留
            //        Bit7:伺服厂家


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




            //if (item.ID > 0x700 && item.ID < (0x700 + 127)) //从站状态
            //{
            //    if (Wt_get != null)
            //    {
            //        NMTslave slave = (NMTslave)p[0];
            //        string sta = Enum.GetName(typeof(NMTslave), slave);
            //        //string str = $"从站:{item.ID - 0x700}状态{p[0].ToString("X2")}; ";
            //        string str = $"从站:{item.ID - 0x700}_{sta}; ";
            //        Wt_get(null, str);
            //    }
            //}





            return num;
        }

        /// <summary>
        /// 【1 4 8】DLC长度,帧id,数据包64bit【小端】
        /// </summary>
        /// <param name="DLC"></param>
        /// <param name="CANid"></param>
        /// <param name="byte8"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool Set_Send(Byte DLC, UInt32 CANid, Int64 byte8)//  1  4  8
        {
            //int hand = 0x88;// 设备:USR-CANET200
            //int cobid = 0x12345678;// 11bit或29bit
            //Int64 byte8 = 0x0011223344556677;
            //wait_dataRX数据池();
            //can.send(hand8, cobid, byte8);
            //wait_dataRX数据池();
            if (DLC > 8) DLC = 8;// <=8
            string cmd = madeCode((byte)DLC, (UInt32)CANid, byte8);// Code编码  "08   00000602    2B 5021 00 00000000"

            if (Help_rtx.TXD(cmd) == 1)// 接口功能  "08000006022B50210000000000" // 单帧 13字节( 1 4 8 在接口解码发送)
            {
                return true;
            }
            else throw new Exception("发送失败。检查主站硬件状态(是否开启?)!");
            //return false;
        }

        /// <summary>
        /// [-1层] SDO指令
        /// </summary>
        /// <param name="regtype"></param>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="value"></param>
        void SDO(Region_Type regtype, UInt16 reg, Byte son, Int32 value)// 1操作类型,2寄存器编号,3子索引,4参数值
        {// SDO操作  600h+
            //========================================================
            // 准备发送
            Byte by0 = (Byte)regtype;//1 操作码【u8】
            UInt16 by1 = (UInt16)reg;//2 寄存器【u16】
            Byte by2 = (Byte)son;//3 子索引【u8】
            Int64 by3 = (Int32)value;//4设定值【u32】

            // b0  b1b2  b3   b4b5b6b7
            Byte8 = by0 + (by1 << 8) + (by2 << 24) + (by3 << 32); // 23 2000 00 11223344     

            Set_Send(0x08, 0x600 + Address, Byte8);//  40  2000  00  11223344

        }

        /// <summary>
        /// [0层]【读】寄存器
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="WaitTime_S"></param>
        /// <returns></returns>
        unsafe public Int32 SDO(UInt16 reg, Byte son, double WaitTime_S)// 【读】寄存器,子索引【0x0000~0xFFFF】
        {// 读
            busy = -1;// 忙
            //===1 get_数据类型=============
            SDO(Region_Type.读, reg, son, 0);//读

            //===2 get_帧===================
            #region get_Value
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];// 创芯科技CAN分析仪最大支持2000帧接收,广成科技缓存10239帧

            DelaySecond(WaitTime_S);//10ms   // USB速度3~5ms

            int num = Get_Receive(ref obj); //  【读取数据帧】
            busy = +1;
            if (num > 0)// 数据帧
            {
                for (int i = num - 1; i >= 0; i--)
                {
                    if (obj[i].ID == 0x580 + Address && obj[i].Data[0] != 0x60)//  SDO 600h+的回信【580h+】
                    {// 580h+从站地址   回帧
                        //=====580h  寄存器类型====================
                        Rx13[5] = obj[i].Data[0];// 2001.00=50000; // 4B,01 20,00, 50 C3 00 00  // 
                        // u16寄存器
                        Rx13[6] = obj[i].Data[1];// 寄存器4B(b16位)  2001-00= 0x 0000C350
                        Rx13[7] = obj[i].Data[2];
                        // u8子索引
                        Rx13[8] = obj[i].Data[3];
                        // int32数据值
                        Rx13[9] = obj[i].Data[4];// 小端
                        Rx13[10] = obj[i].Data[5];
                        Rx13[11] = obj[i].Data[6];
                        Rx13[12] = obj[i].Data[7];
                        //=====2======================
                        //VCI_CAN_OBJ one = obj[i]; //
                        break;
                    }// SDO应答帧(580h+)
                }
            }
            else
            {
                Region_Obj = 0x80;// 故障
                return -1;
            }
            // 回帧结果
            Region_Obj = Rx13[5];// 标记: b8 = 0x4F,   b16 = 0x4B,    b24 = 0x47,    b32 = 0x43    //0x6040是16bit

            //===3 get_Value===================
            int value = get_Value_Int32(Rx13);// 解析   43,4060,00,0000 0000
            //  日志====================================
            if (Region_Obj < 0x43 | Region_Obj > 0x4F)// u8~32 读取失败 // 参考 enum region_Type  //| Region_Type>(int)region_Type.b32
            {//  0x05030000  80 
                string error = get_Error_str(reg, son, value); // 故障码,寄存器,子,值
                if (Wt_get != null)
                {
                    Wt_get(new byte[] { 0x00 }, "读取寄存器错误:" + error);
                }
                // throw new Exception("读取错误:" + error);// 提前终止
            }//错误
            else
            {
                //成功
                return value;
            }
            #endregion
            return -1;
        }

        /// <summary>
        /// [0层]【写】寄存器
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="value"></param>
        /// <param name="WaitTime_S"></param>
        /// <returns></returns>
        unsafe public bool SDO(UInt16 reg, Byte son, Int32 value, double WaitTime_S)//【写】
        {// 写
            busy = -1;
            //===1 get寄存器【数据类型,32/24/16/8bit】=============
            Int32 data = SDO(reg, son);//读

            //===2 set_Value【设置值】===================
            if (Region_Obj >= 0x43 || Region_Obj <= 0x4F)
            {
                SDO((Region_Type)Region_Obj - 0x20, reg, son, value);
            }
            else { return false; }// 【写】失败
            //===3 get_帧【确认结果】===================
            #region get_Value
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2000];// 创芯科技CAN分析仪最大支持2000帧接收,广成10239帧

            DelaySecond(WaitTime_S);//10ms   // USB速度3~5ms

            int num = Get_Receive(ref obj); //  【读取数据帧】
            busy = +1;
            if (num > 0)// 数据帧
            {
                for (int i = num - 1; i >= 0; i--)// 【倒查数据帧】
                {
                    int end_sta = obj[i].Data[0];// 0x80是失败
                    if (obj[i].ID == 0x580 + Address && obj[i].Data[0] == 0x60)//  SDO 600h+的回信【580h+】
                    {// 580h+从站地址   回帧
                        return true;
                    }// SDO应答帧(580h+)
                }
            }
            #endregion
            Region_Obj = 0x80;// 故障
            return false;
        }


        unsafe void UIshow(int num, VCI_CAN_OBJ[] obj)
        {
            //====3界面显示================================================================
            // 分发数据帧,UI层显示

            int len = 0;
            foreach (var item in obj)
            {
                if (len++ >= num)
                {
                    return;
                }
                byte* p = item.Data;//  8字节数据包
                byte b0 = p[0];// 4f       4f  1400.02=05
                byte b1 = p[1];// 00
                byte b2 = p[2];// 14
                byte b3 = p[3];// 02
                byte b4 = p[4];// 05
                byte b5 = p[5];
                byte b6 = p[6];
                byte b7 = p[7];
                // 40 00 02 02  01 14 00 43


                //===每帧==============
                if (item.ID > 0x80 && item.ID < 0x80 + 128)// EMCY故障【从站故障】 // 要忽略0站
                {
                    string str1 = $"【{item.DataLen:X2}】故障从站:{(item.ID - 0x80).ToString("X4")}={b7:X2}{b6:X2}{b5:X2}{b4:X2}{b3:X2}【{b2:X2}】【{b1:X2}{b0:X2}】;";
                    Wt_get(null, str1);
                }
                if (item.ID > 0x580 - 1 && item.ID < 0x580 + 128)//  0~127站SDO回帧
                {
                    string reg = $"%{b0:X2}:{b2:X2}{b1:X2}-{p[3]}=0x"; // %43:6064-00=0x

                    Wt_get(null, $"【{item.DataLen:X2}】寄存器{reg}{b7:X2}{b6:X2}{b5:X2}{b4:X2};");
                    continue;
                }// SDO回帧

                //  1  4  8    
                string str = item.DataLen.ToString("X2") + "/" + item.ID.ToString("X4") + "/"
                 + $"{b7:X2}{b6:X2}{b5:X2}{b4:X2}{b3:X2}{b2:X2}{b1:X2}{b0:X2};";
                Wt_get(null, str);

            }
        }

        #endregion
        #region 核心:读写寄存器【region】
        public Int32 get_region(int reg, int Son)
        {
            return SDO(reg, Son);
        }
        public Int32 get_region(int reg, int Son, double WaitTime_S)
        {
            return SDO(reg, Son, WaitTime_S);
        }
        public bool get_region(int reg, int Son, ref int data)
        {
            data = SDO(reg, Son);
            if (Region_Obj > 0x43 | Region_Obj < 0x4F) return true;
            return false;

        }

        /// <summary>
        /// 【读参数】适合台达
        /// </summary>
        /// <param name="PxXXX"></param>
        /// <param name="data值"></param>
        /// <returns></returns>
        public int get_region(string PxXXX, Int32 data值) // "P1.45"
        {// 0x2000 + Pn + 常数
            //寄存器地址
            UInt16 reg_address = get_reg_获取寄存器地址(PxXXX);
            return get_region(reg_address, 0x00);
        }
        //=========================================================================
        public bool set_region(int reg, int Son, int data)
        {
            return SDO(reg, Son, data, 0.0);
        }
        public bool set_region(int reg, int Son, int data, double WaitTime_S)
        {
            return SDO(reg, Son, data, WaitTime_S);
        }

        public bool set_region(int reg, int Son, ref Int32 data)
        {
            if (SDO(reg, Son, data))
            {
                data = SDO(reg, Son);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 【写参数】适合台达
        /// </summary>
        /// <param name="PxXXX"></param>
        /// <param name="data值"></param>
        /// <returns></returns>
        public bool set_region(string PxXXX, Int32 data值) // "P1.45"
        {// 0x2000 + Pn + 常数
            return Write_reg32bit(PxXXX, data值);// 应答帧  "1890295C19"h
        }
        public bool Write_reg32bit(string PxXXX, Int32 data)// 10h码
        {// 0x2000 + Pn + 常数
            //寄存器地址
            UInt16 reg_address = get_reg_获取寄存器地址(PxXXX);
            //设定值
            //string start = modbus.Write_Reg32bit(reg_address, data);// "7F10040E00022B25"
            bool start = set_region((short)reg_address, 0, data); // 寄存器,子索引, Value
            return start;
        }
        private ushort get_reg_获取寄存器地址(string PxXXX)//  "P4.007"
        {// 0x2000 + Pn + 常数
            //if (reg<0x07C8)//最大寄存器
            if (PxXXX.IndexOf(".") > 0) //  "P5.016"  伺服电机坐标
            {
                string hi = PxXXX.Substring(PxXXX.IndexOf(".") - 1, 1); // 组 4
                string li = PxXXX.Substring(PxXXX.IndexOf(".") + 1);  //   编号 007
                int hv = int.Parse(hi) * 0x0100;  // 0x400
                int lv = int.Parse(li);   //32   0x07
                ushort reg_NO = (ushort)(hv + lv);  //   0x0407
                return (ushort)(0x2000 + reg_NO); // can在 0x2000+ 位置
            }
            else
            {
                return ushort.Parse(PxXXX);//  标准寄存器号
            }

        }

        /// <summary>
        /// 故障码,寄存器,子索引,值
        /// // 80     b3b2b1b0
        /// </summary>
        /// <param name="errcode"></param>
        /// <param name="reg"></param>
        /// <param name="Son"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        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;
        }

        #endregion
        #region 协议can    CANopen 通訊協定:NMT、SYNC、SDO、PDO、EMCY、TIME。【总线命令,同步帧,寄存器读写,寄存器批量读写,故障帧】
        #region SDO
        //【操作核心】勿动。  作者:QQ750273008 祁成  2023/11/26更新
        /// <summary>
        /// 【读】
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="WaitTime_S"></param>
        /// <returns></returns>
        public int SDO(int reg, int son, double WaitTime_S)// 读
        {
            return SDO((UInt16)reg, (Byte)son, 0.0);
        }
        /// <summary>
        /// 【写】
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="son"></param>
        /// <param name="data"></param>
        /// <param name="WaitTime_S"></param>
        /// <returns></returns>
        public bool SDO(int reg, int son, int data, double WaitTime_S)// 写
        {// 进行重载调用
            bool sta = SDO((UInt16)reg, (Byte)son, (Int32)data, WaitTime_S);
            return sta;
        }

        #endregion
        #region NMT
        /// <summary>
        /// 总线管理(命令,从站id);帧_id=0x00,全站00
        /// 【启动从站 = 0x01,
        ///    停止从站 = 0x02,
        ///    预运行从站 = 0x80,
        ///    重启从站 = 0x81,
        ///    通讯重连 = 0x82
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="address"></param>
        public void NMT(NMT_CMD modecmd, int address, double WaitTime_s)// 总线指令,并留时间,等待从站响应
        {// NMT总线管理操作  00h+
            byte by0 = (byte)modecmd;//总线命令
            byte by1 = (byte)address;// 从站id

            // b0  b1b2  b3   b4b5b6b7
            Byte8 = by0 + (by1 << 8);

            Set_Send(0x02, 0x00, Byte8);// 1  4  8
            DelaySecond(WaitTime_s);
            延时 等待生效75ms
            //DateTime startTime = DateTime.Now;//记录开始时间
            //do
            //{
            //    if ((DateTime.Now - startTime).TotalMilliseconds > 75)// 75ms
            //    {
            //        break;   // 超时   毫秒
            //    }
            //} while (true);

        }// 总线命令
        #endregion
        #region SYNC
        /// <summary>
        /// 用于同步信号(广播帧)留20ms电机响应
        /// </summary>
        public void SYNC()
        {// SYNC同步帧  【80h】
            Set_Send(0x00, 0x80, 0x00);// id=0x80
        }//同步帧

        /// <summary>
        /// (S)double等待秒
        /// </summary>
        /// <param name="WaitTime_s"></param>
        public void SYNC(double WaitTime_s)
        {
            SYNC();
            DelaySecond(WaitTime_s);
        }

        /// <summary>
        /// 从站心跳ms:0x1017(16bit)
        /// </summary>
        /// <param name="address"></param>
        public void SYNC_Set_Close()
        {//关闭 心跳 0x1017=2000;ms
            //【0x 00000000  00  1017  2B】
            Set_Send(0x08, 0x600 + Address, 0x00000000010172b);// 寄存器 0x1017-00=0x00000000,操作码 0x2b (16bit)
                                                               //delaySecond(2.0);//延时2秒
                                                               //Help_Delay.delayTime(1.123);//0.1秒
            DelaySecond(1.123);
        }


        /// <summary>
        /// 从站id,设置心跳时间【回帧700h+,时间ms】
        /// </summary>
        /// <param name="address"></param>
        /// <param name="type"></param>
        /// <param name="time_ms"></param>
        public void SYNC(Int32 Slave_id, int time_ms = 2000)
        {
            Address = (UInt32)Slave_id;
            //心跳: 0x1017 = 2000; ms
            set_region(0x1017, 0x00, time_ms);// 0x7d0  9000ms

        }
        #endregion
        #region EMCY
        /// <summary>
        /// 0x【厂家错误码,H1001,应急错误码】
        /// </summary>
        /// <param name="Slave_id"></param>
        /// <param name="H603f"></param>
        /// <param name="H1001"></param>
        /// <param name="Motor厂家"></param>
        public void EMCY(int Slave_id, Int16 H603f, byte H1001, Int32 Motor厂家)// 测试
        {//下位机故障 80h+从站id

            Int64 error = (Int16)H603f;// 16bit 应急错误码【b1b0】 0x1013-01
            Int64 h1001 = (byte)H1001;// 寄存器0x1001【b2】
            Int64 motor = Motor厂家;// 伺服厂家错误码 5字节
            Int64 cmd = error + (H1001 << 16) + (motor << 24);// u32 =【b7-b4】【b2】【b1b0】
                                                              //      【厂家】【h1001】【h603f】

            Set_Send(0x08, (uint)0x80 + (Byte)Slave_id, cmd);
        }
        #endregion
        #region TIME
        /// <summary>
        /// 时间广播bit48
        /// </summary>
        /// <param name="byte8"></param>
        public void TIME(Int64 byte8)
        {// TIME操作  100h+ 
            Set_Send(0x06, 0x100, byte8);// 【6】固定6字节时间
        }// 时钟
        #endregion
        #region PDO
        #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_Config_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 &= SDO(h1400, 0x01, 1 << 31);//【关闭 rPDO】(原值: 0x4000 0200h + 从站【帧id29】)// 0x40000202

            //2  1400配置
            sta &= set_region(h1400, 0x02, obj.rPDO命令触发器); //【0~240】(第1次SYNC帧后,生效设定值)【原值:0xFF】254,255为事件,值不同立即生效
            sta &= set_region(h1400, 0x03, obj.rPDO禁止时间_100us);//  (死区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,受控码】
            //h1400-01定义【d31 rPDO总开关】【d30 保留】【d29(0标准帧11bit),(1扩展帧29bit)】【d28~0 帧id29bit】
            sta &= set_region(h1400, 0x01, (int)(0x40000000 + 0x100 + (0x100 * zu) + Address));  // 开启PDO1功能 // 0x40000202

            //5 轴对象标记
            //Axis_Config_start(PDOn, obj);

            return sta;
        }
        // 轴配置记录
        void Axis_Config_start(PDOtype PDOn, PDO_Config_Struct obj)
        {
            byte zu = (byte)(PDOn);
            if(zu==1)
            {
                axis_obj[address].PDO1.regUSE = obj.UseRegNum;
                axis_obj[address].PDO1.regNO[1] = obj.Reg1;
                axis_obj[address].PDO1.regNO[2] = obj.Reg2;
                axis_obj[address].PDO1.regNO[3] = obj.Reg3;
                axis_obj[address].PDO1.regNO[4] = obj.Reg4;
                axis_obj[address].PDO1.regNO[5] = obj.Reg5;
                axis_obj[address].PDO1.regNO[6] = obj.Reg6;
                axis_obj[address].PDO1.regNO[7] = obj.Reg7;
                axis_obj[address].PDO1.regNO[8] = obj.Reg8;
            }
            if (zu == 2)
            {
                axis_obj[address].PDO2.regUSE = obj.UseRegNum;
                axis_obj[address].PDO2.regNO[1] = obj.Reg1;
                axis_obj[address].PDO2.regNO[2] = obj.Reg2;
                axis_obj[address].PDO2.regNO[3] = obj.Reg3;
                axis_obj[address].PDO2.regNO[4] = obj.Reg4;
                axis_obj[address].PDO2.regNO[5] = obj.Reg5;
                axis_obj[address].PDO2.regNO[6] = obj.Reg6;
                axis_obj[address].PDO2.regNO[7] = obj.Reg7;
                axis_obj[address].PDO2.regNO[8] = obj.Reg8;
            }
            if (zu == 3)
            {
                axis_obj[address].PDO3.regUSE = obj.UseRegNum;
                axis_obj[address].PDO3.regNO[1] = obj.Reg1;
                axis_obj[address].PDO3.regNO[2] = obj.Reg2;
                axis_obj[address].PDO3.regNO[3] = obj.Reg3;
                axis_obj[address].PDO3.regNO[4] = obj.Reg4;
                axis_obj[address].PDO3.regNO[5] = obj.Reg5;
                axis_obj[address].PDO3.regNO[6] = obj.Reg6;
                axis_obj[address].PDO3.regNO[7] = obj.Reg7;
                axis_obj[address].PDO3.regNO[8] = obj.Reg8;
            }
            if (zu == 4)
            {
                axis_obj[address].PDO4.regUSE = obj.UseRegNum;
                axis_obj[address].PDO4.regNO[1] = obj.Reg1;
                axis_obj[address].PDO4.regNO[2] = obj.Reg2;
                axis_obj[address].PDO4.regNO[3] = obj.Reg3;
                axis_obj[address].PDO4.regNO[4] = obj.Reg4;
                axis_obj[address].PDO4.regNO[5] = obj.Reg5;
                axis_obj[address].PDO4.regNO[6] = obj.Reg6;
                axis_obj[address].PDO4.regNO[7] = obj.Reg7;
                axis_obj[address].PDO4.regNO[8] = obj.Reg8;
            }

        }


        /// <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_Config_Struct obj)
        {
            bool sta = true;
            byte zu = (byte)(PDOn);
            short h1800 = (short)((zu - 1) + 0x1800);// 确定组队【队伍】
            short h1A00 = (short)((zu - 1) + 0x1A00);

            //1   PDO总开关:关闭  子索引5个
            //【h1800-01】定义【d31 tPDO 0开,1关】【d30 RTR 0开,1关】【d29 0标准帧,1扩展帧】【d28~0 帧id】
            // RTR:远程帧回应
            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, (int)(0x40000000 + 0x80 + (0x100 * zu) + Address));  // 开启tPDO功能 // 0x40000182

            //5 轴对象标记
            Axis_Config_start(PDOn, obj);

            return sta;
        }
        public bool PDO_Config(uint Slave_id, PDO_Type type, PDO_Config_Struct pdo)
        {
            Address = 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:
                    sta = false;
                    break;
            }
            return sta;
        }
        public bool PDO_Config(PDO_Type type, PDO_Config_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);
        }

        #endregion
        #region 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

        /// <summary>
        /// 批量写寄存器【数据包64bit】
        /// </summary>
        /// <param name="pdo"></param>
        /// <param name="byte8"></param>
        public bool PDO(PDOtype pdo, Int64 byte8)// 批量写寄存器【数据包64bit】
        {
            return Set_Send(0x08, 0x100 + (uint)pdo * 0x100 + Address, byte8);// 
        }// PDO指针寄存器
        public bool PDO1(Int64 byte8)// 6040  607A 目标位置
        {// PDO1操作  200h+
            return Set_Send(0x08, 0x200 + Address, byte8);
        }
        public bool PDO2(Int64 byte8)
        {// PDO2操作  300h+
            return Set_Send(0x08, 0x300 + Address, byte8);
        }
        public bool PDO3(Int64 byte8)
        {// PDO3操作  400h+
            return Set_Send(0x08, 0x400 + Address, byte8);
        }
        public bool PDO4(Int64 byte8)
        {// PDO4操作  500h+
            return Set_Send(0x08, 0x500 + Address, byte8);
        }
        #endregion
        #endregion

        #endregion



        #region 后台方法【数据帧处理】
        /// <summary>
        /// 制作Code编码   1  4  8
        /// </summary>
        /// <param name="hend"></param>
        /// <param name="id"></param>
        /// <param name="byte8"></param>
        /// <returns></returns>
        string madeCode(byte hend, UInt32 id, Int64 byte8)// 1表示帧类型,4字节是帧id,8表示帧数据包
        {
            string data8 = string.Empty;
            for (int i = 0; i < 8; i++)
            {
                data8 += ((byte8 >> (i * 8)) & 0xFF).ToString("X2");// 低字节先出
            }
            // 单帧
            string cmd = hend.ToString("X2") + id.ToString("X8") + data8;// 1 4 8   配置数据单帧
            byte[] buffer = helpstring.StringsToHexBytes(cmd);// 单帧 13字节( 1 4 8 在接口解码发送)
            string data = helpstring.BytesToHexStrings(buffer);// 等效编码
            return data;
        }

        double getValue(char key, string data)// 获取字母后的小数
        {//G0X9.896Y10.158Z1.000F10000
            string data1, data2;
            if (data.IndexOf(key) != -1)// 确定 字母
            {
                data1 = data.Split(key)[1];//去头
                data2 = get_去尾(data1);//去尾
                try
                {
                    return double.Parse(data2);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            return -1.0;
        }
        static string get_去尾(string str)
        {
            for (int i = 0; i < str.Length; i++)
            {
                string num = str.Substring(i, 1);
                if (num == "-" || num == "." ||
                     num == "0" || num == "1" || num == "2" || num == "3" || num == "4" ||
                     num == "5" || num == "6" || num == "7" || num == "8" || num == "9") { }
                else
                {
                    return str.Substring(0, i);
                }
            }
            return str;
        }

        unsafe void wait_AXIS(byte MAX, byte small)
        {
            VCI_CAN_OBJ[] obj = new VCI_CAN_OBJ[2500];// 创新科技CAN分析仪最大支持2000帧接收
            byte[] rx13 = new byte[13];// 提取数据帧
            byte at = (byte)Address;//标记
            Address = MAX;
            int num;

            //延时 等待生效75ms
            DateTime startTime = DateTime.Now;//记录开始时间

            do
            {
                //if ((DateTime.Now - startTime).TotalMilliseconds > 3000) break;
                // can.set_ClearBuffer(4, 0, 0);
                num = Get_Receive(ref obj);

                SYNC();
                DelaySecond(0.005);//5ms   // USB速度3~5ms
                                   //Help_Delay.delayTime(0.005);//5ms   // USB速度3~5ms
                num = Get_Receive(ref obj);
                //return;
                if (num > 0)
                {
                    for (int i = num - 1; i >= 0; i--)
                    {
                        if (obj[i].ID > 0x80 && obj[i].ID < (0x80 + 127))// 从站故障 80h+ 从站
                        {
                            // 故障呼叫



                            //COB - ID            Byte 0:1        Byte 2              Byte 3:7
                            //0x080 + Node - ID     应急错误代码 错误寄存器(1001H)    厂商指定区域

                            //byte0~1:
                            //        应急错误代码
                            //        0000H 无错误
                            //        8110H CAN 溢出
                            //        8120H 错误被动模式(0411)
                            //        8130H 寿命保护/ 心跳错误
                            //        8140H 被迫离线恢复故障
                            //        8141H 被迫离线
                            //        8150H 发送 COB - ID 冲突
                            //        8210H PDO 长度错误未处理
                            //        8220H PDO 超过长度

                            //byte2:
                            //        (1001H)位定义
                            //        Bit0:generic error 一般错误
                            //        Bit1:current 电流
                            //        Bit2:voltage 电压 (04)
                            //        Bit3:temperature 温度

                            //————————————————
                            //版权声明:本文为CSDN博主「cfqq1989」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。
                            //原文链接:https://blog.csdn.net/cfqq1989/article/details/128308808
                        }// 故障(从站故障)80h + 从站
                        // 等待轴定位完成
                        if (obj[i].ID == 0x180 + Address)//  PDO1  200h+
                        {
                            rx13[5] = obj[i].Data[0];// 37
                            rx13[6] = obj[i].Data[1];// 16
                            rx13[7] = obj[i].Data[2];
                            rx13[8] = obj[i].Data[3];
                            rx13[9] = obj[i].Data[4];
                            rx13[10] = obj[i].Data[5];
                            rx13[11] = obj[i].Data[6];
                            rx13[12] = obj[i].Data[7];
                            if ((rx13[6] & 0x16) == 0x16)// bit10    0x1237   0x1637
                            {
                                Address--;//定位完成
                                if (Address < small)
                                {
                                    Address = at;// 还原
                                    return;
                                }
                            }

                        }// SDO应答帧

                    }
                }
                else
                {
                    DelaySecond(0.205);//
                    //Help_Delay.delayTime(0.205);//
                }

            } while (true);

        }
        unsafe void wait_AXIS(byte addr)
        {
            wait_AXIS(addr, addr);
        }

        /// <summary>
        /// 6041h状态
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        public string get_6041h(byte byte0, byte byte1)
        {
            string str0 = Convert.ToString(byte0, 16);
            string str1 = Convert.ToString(byte1, 16);
            int state = Convert.ToInt32(str1 + str0, 16);

            string end = string.Empty;
            switch (state & 0x6F)
            {
                case 0x00: end = "未准备启动 "; break;
                case 0x40: end = "取消启动 "; break;
                case 0x21: end = "准备启动 "; break;
                case 0x23: end = "启动 "; break;
                case 0x27: end = "允许操作 "; break;
                case 0x07: end = "快速停止激活 "; break;
                case 0x0F: end = "故障效应激活 "; break;
                case 0x08: end = "故障 "; break;

                default:
                    break;
            }
            return end;
        }

        //private void delayTime(double secend)
        //{
        //    DateTime tempTime = DateTime.Now;
        //    while (tempTime.AddSeconds(secend).CompareTo(DateTime.Now) > 0)
        //        Application.DoEvents();
        //}


        Int32 get_result(List<string> getend)//   "46s8821:08000005814B002000E8030000"
        {//回帧结果
            string str = string.Empty;
            string obj = string.Empty;//寄存器标记
            for (int i = 0; i < getend.Count; i++)
            {
                string str1 = getend[i];// 提取
                if (str1.Length < 26) throw new Exception("长度不够 ");//长度不够
                string str2 = str1.Substring(str1.Length - 16); // 截取 8字节
                obj = str2.Substring(0, 2);// 寄存器 类型
                                           //  1111    1011   0111   0011
                if (obj == "4F" | obj == "4B" | obj == "47" | obj == "43" | obj == "80")// 1字节,2字节,3字节,4字节,错误码
                {
                    Region_Obj = Convert.ToByte(obj, 16);// 标记寄存器 类型
                    //if (obj == "4F") Region = 8;
                    //if (obj == "4B") Region = 16;
                    //if (obj == "47") Region = 24;
                    //if (obj == "43") Region = 32;
                    string str3 = str1.Substring(str1.Length - 8);
                    for (int j = 0; j < 4; j++)
                    {
                        string num = str3.Substring(str3.Length - (2 * j) - 2, 2);
                        str += num;
                    }
                    return Convert.ToInt32(str, 16);// 成功
                }
                //下一轮数据帧
            }
            if (obj == "60") Region_Obj = 0x60; return 0;// 写成功

            //return -1;// 读取失败
        }
        /// <summary>
        /// 解析数据帧:get_Value(u32)
        /// </summary>
        /// <param name="getend"></param>
        /// <returns></returns>
        Int32 get_Value_Int32(byte[] getend)//   "46s8821:08000005814B002000E8030000"
        {//回帧结果  08 00000581 4B002000E8030000
            return getend[9] + (getend[10] << 8) + (getend[11] << 16) + (getend[12] << 24);
            // 解析   43,4060,00,0000 0000    // 寄存器0x6040-00=0x00000000;
            //                E8 索引9
            //             03    索引10
            //           00      索引11
            //         00        索引12   结果  0000 03E8
        }

        unsafe Int64 get_Value_Int64(byte* getend)//  数据帧data
        {//
            return getend[0] + (getend[1] << 8) + (getend[2] << 16) + (getend[3] << 24) +
                getend[4] << 32 + (getend[5] << 40) + (getend[6] << 48) + (getend[7] << 58);
        }

        //void wait_dataRX数据池()
        //{
        //    do
        //    {
        //        if (rj45.rx数据池字节 != 0)
        //        {
        //            int i = 0;// 调试 断点
        //            i++;
        //        }
        //        if (rj45.rx数据池字节 == 0)
        //        {
        //            break;
        //        }
        //    } while (true);
        //}


        //void throw_bytes()
        //{
        //    Thread.Sleep(20);
        //    if (rj45.socket.Available > 0)
        //    {
        //        byte[] bytes = new byte[rj45.socket.Available];

        //        int count = rj45.socket.Receive(bytes, bytes.Length, SocketFlags.None);

        //    }

        //}


        #endregion
        #region 模块方法




        public enum POWER : byte
        {
            //          b7  ↑故障复位
            //         0x06  关闭
            //         0x07  打开
            //         0x0F  打开+启用操作
            //         0<<1  快速停止
            //         0x07  禁用操作
            //         0x0F  启用操作
            完全关闭 = 0x06,
            快速停止 = 0x00,
            禁用操作 = 0x07,
            启用操作 = 0x0F //要先0x06完全关闭

        }
        public enum PDOtype : byte
        {
            PDO1 = 1,
            PDO2,
            PDO3,
            PDO4,
        }
        public enum GoHome_Type : byte
        {//6098
            方法17 = 17,
            方法18 = 18,
            方法19 = 19,
            方法20 = 20,
            方法21 = 21,
            方法22 = 22,
            方法23 = 23,
            方法24 = 24,
            方法25 = 25,
            方法26 = 26,
            方法27 = 27,
            方法28 = 28,
            方法29 = 29,
            方法30 = 30,
            方法31 = 31,
            方法32 = 32,
            方法33 = 33,
            方法34 = 34,

            方法35 = 35,//零点直设
            零点直设 = 37//零点直设
        }

        public enum cia402 : ushort//  16bit
        {
            故障码 = 0x603F,
            控制字 = 0x6040,// 油门,刹车   06h关闭》07h伺服使能(命令关)》0Fh命令生效           00h急停
                         // 0 启动                1伺服开,0伺服关
                         // 1 电压输出            1电压输出,0关
                         // 2 急停                1不报警
                         // 3 命令(上升沿)     01命令上升沿生效    07h》0Fh
                         //======================================
                         // 4 运动指令(上升沿)    该位由0跳变为1将触发新的位置指令
                         // 5 0等待旧命令位置完成        1立刻更新并执行新的位置指令动作
                         // 6 0绝对位置           1相对位置
                         // 7 复位               01上升沿,故障复位
                         // 8 1暂停             0继续运行
                         // 9  速度             1使用上次的旧速度
                         // 10~15 空
            状态字 = 0x6041,// 汽车仪表(状态)
                         // 0 
                         // 1
                         // 2
                         // 3
                         // 4
                         // 5
                         // 6
                         // 7
                         // 8   0: 目标位置到达 (bit10为1)
                         // 9
                         // 10  1
                         // 11
                         // 12   当前位置正在缓存, 新的位置将被接受
                         // 13   跟随错误
                         // 14
                         // 15

            快停 = 0x605A,
            //定位模式
            工作模式 = 0x6060, // 01h定位模式
            目标位置 = 0x607A,
            额定速度 = 0x6081,// 2w
            加速度 = 0x6083,// 5w
            减速度 = 0x6084,// 50000
                         // 绝对0x6040=1Fh     停 0Fh
                         // 相对0x6040=5Fh     停 4Fh

            //速度模式
            目标速度 = 0x60FF,
            速度反馈 = 0x606C,

            //力矩模式

            //回零模式
            回零方式 = 0x6098,

            回零速度 = 0x6099,//-01回零高速,-02回零低速
            回零加速 = 0x609A,

            零点偏置 = 0x607C,




            //0x603F-错误代码UINT16 RO YES
            //0x6040-控制字UINT16 WO是
            //0x6041-状态剑UINT16 RO是
            //0x605A-快速停止选项代码INT16 RW NO
            //0x6060-操作模式INT8 WO是
            //0x6061-操作模式显示INT8 RO YES
            //0x6064-位置实际值INT32 RO是
            //0x6065-以下错误窗口UINT32 RW NO
            //0x606C-速度实际值INT32 RO是
            //0x6071-目标扭矩INT16 RW是
            //0x6073-最大电流UINT16 RW是
            //0x6074-扭矩需求INT16 RO是
            //0x6078-当前实际值INT16 RO是
            //0x607A-目标位置INT32 RW是
            //0x607C-起始偏移INT32 RW是
            //0x607E-极性UINT8 RW是
            //0x607F-最大剖面速度UINT32 RW是
            //0x6081-剖面速度UINT32 RW是
            //0x6083-配置文件加速UINT32 RW是
            //0x6084-轮廓减速UINT32 RW是
            //0x6085-快速停止减速UINT32 RW是
            //0x6087-扭矩斜率UINT32 RW是
            //0x6098-归位方法INT8 RW是
            //0x6099 2归位速度阵列-否
            //0x609A-归位加速度UINT32 RW是
            //0x60F4-跟随错误实际值INT32 RO YES
            //0x60FD-数字输入UINT32 RW是
            //0x60FE 2数字输出阵列-否
            //0x60FF-目标速度INT32 RW是
            //0x6502-支持的驱动器模式UINT32 RO N


            //0x603F - Error code UINT16 RO YES
            //0x6040 - Controlword UINT16 WO YES
            //0x6041 - Statusword UINT16 RO YES
            //0x605A - Quick stop option code INT16 RW NO
            //0x6060 - Modes of operation INT8 WO YES
            //0x6061 - Modes of operation display INT8 RO YES
            //0x6064 - Position actual value INT32 RO YES
            //0x6065 - Following error window UINT32 RW NO
            //0x606C - Velocity actual value INT32 RO YES
            //0x6071 - Target torque INT16 RW YES
            //0x6073 - Max current UINT16 RW YES
            //0x6074 - Torque demand INT16 RO YES
            //0x6078 - Current actual value INT16 RO YES
            //0x607A - Target position INT32 RW YES
            //0x607C - Home offset INT32 RW YES
            //0x607E - Polarity UINT8 RW YES
            //0x607F - Max profile velocity UINT32 RW YES
            //0x6081 - Profile velocity UINT32 RW YES
            //0x6083 - Profile acceleration UINT32 RW YES
            //0x6084 - Profile deceleration UINT32 RW YES
            //0x6085 - Quick stop deceleration UINT32 RW YES
            //0x6087 - Torque slope UINT32 RW YES
            //0x6098 - Homing method INT8 RW YES
            //0x6099 2 Homing speed ARRAY - NO
            //0x609A - Homing acceleration UINT32 RW YES
            //0x60F4 - Following error actual value INT32 RO YES
            //0x60FD - Digital inputs UINT32 RW YES
            //0x60FE 2 Digital outputs ARRAY - NO
            //0x60FF - Target velocity INT32 RW YES
            //0x6502 - Supported drive modes UINT32 RO N




        }

        public enum addressH : byte
        {
            add_0 = 0,
            add_32 = 1,
            add_64 = 2,
            add_96 = 3,// 96+31=127
        }

        public class PDOregion
        {
            public int num;
            public int[] regName { get; set; }//PDO待绑定寄存器

            public byte[] regBit { get; set; }// 寄存器bit长度

        }
        public enum CMDtype
        {
            回零,
            绝对定位,
            相对定位,
            补间,
            速度,
            力矩,
            复位,// bit7
            暂停,// bit8

        }

        public enum AXIS_type
        {
            A = 65,
            B,
            C,
            D,
            E,
            F,
            G,
            H,
            I,
            J,
            K,
            L,
            M,
            N,
            O,
            P,
            Q,
            R,
            S,
            T,
            U,
            V,
            W,

            X = 127,
            Y = 126,
            Z = 125,
            X1 = 124,
            Y1 = 123,
            Z1 = 122,

        }

        public enum Motor_type
        {//  【1位置 3速度 4扭矩 6回零 7补间
            位置1 = 1,
            速度3 = 3,
            扭矩4 = 4,
            回零6 = 6,
            补间7 = 7,



        }




        #endregion

        #region 委托

        #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); }//显示错误





        //================================================
        // 读取回帧-------0x40 结果
        //int WaitCount = 0;//接收超次数
        //DateTime startTime = DateTime.Now;//记录开始时间
        // 4  等待接收
        #region MyRegion
        //while (true)
        //{// 08 00000081 20811104 00000000
        //    // 超时
        //    if (timeOutFun)
        //    {
        //        if ((DateTime.Now - startTime).TotalMilliseconds > 300)// 超时
        //        {
        //            Region_Type = 0x80;// 标记  读取失败
        //            throw new Exception("(超时)读寄存器失败!");   // 超时   毫秒
        //        }
        //    }// 超时
        //    rx13 = Receive();//回帧报文
        //    if (rx13 == null)
        //    {
        //        ++WaitCount;
        //        if (WaitCount > 20)// 超次数
        //        {
        //            rx13 = new byte[13];
        //            rx13[5] = 0x80;// 故障码
        //            break;
        //        }
        //        continue;
        //    }// 超次
        #endregion





        #endregion



        #region 参考

        //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)

        //6041         上电50》启动250》06码231》07码233》0F码c237
        //定位中=0x1237   定位完成=0x1637
        //0准备打开=========================7
        //1打开
        //2操作已启用(伺服开启状态)
        //3故障(驱动器将伺服关闭)
        //   4电压==========================3
        //   5快速停止
        //   6关闭
        //   7警告(驱动器仍然打开伺服)
        //8非正常停止===========================2
        //9远程控制
        //10位置到达(到位置1)
        //11内部位置超限(不支持)

        //12  pp设定点 承认    HM回原点完成    ipIP模式激活  pv零速
        //13  pp以下错误       HM归零错误
        //
        //
        //定位中   1237
        //定位完成 1637
        //



        #endregion
    }
}





  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值