PC强实时运动控制(一):C#的EtherCAT总线初始化(上)

今天,正运动小助手给大家分享一下强实时运动控制内核MotionRT750下PC上位机的EtherCAT总线初始化。

**PC上位机EtherCAT总线的初始化方式主要分为两种:**纯上位机初始化和下载BASIC脚本初始化。本文将详细讲解如何通过纯上位机的方法来实现PC上位机EtherCAT总线初始化。

全国产强实时运动控制内核MotionRT750

MotionRT750是正运动技术首家自主自研的x86架构Windows系统或Linux系统下独占确定CPU的强实时运动控制内核。

该方案采用独占确定CPU内核技术实现超强性能的强实时运动控制。它将核心的运动控制、机器人算法、数控(CNC)及机器视觉等强实时的任务,集中运行在1-2个专用CPU核上。与此同时,其余CPU核则专注于处理Windows/Linux相关的非实时任务。

此外集成MotionRT750 Runtime实时层与操作系统非实时层,并利用高速共享内存进行数据交互,显著提升了运动控制与上层应用间的通信效率及函数执行速度,最终实现更稳定、更高效的智能装备控制,确保了运动控制任务的绝对实时性与系统稳定性,特别适用于半导体、电子装备等高速高精的应用场合。

MotionRT750应用优势:

1.跨平台兼容性:支持Windows/Linux系统,适配不同等级CPU。

2.开发灵活性:提供多语言编程接口,便于二次开发与功能定制。

3.实时性提升:通过CPU内核独占机制与高效LOCAL接口,实现2-3us指令交互周期,较传统PCI/PCIe方案提速近20倍。

4.扩展能力强化:多卡多EtherCAT通道架构支持254轴运动控制及500usEtherCAT周期。

5.系统稳定性:32轴125us EtherCAT冗余架构消除单点故障风险,保障连续生产。

6.安全可靠性:不惧Windows系统崩溃影响,蓝屏时仍可维持急停与安全停机功能有效,确保产线安全运行。

7.功能扩展性:实时内核支持C语言程序开发,方便功能拓展与实时代码提升效率。

0 1 C#语言进行PC上位机的EtherCAT总线初始化项目开发

1.在VS2019菜单"文件"→"新建"→"项目",启动创建项目向导。

2.选择开发语言为"C#"和Windows窗体应用程序,点击下一步。

3.配置好项目名称和位置,以及相应框架,点击创建。

4.找到厂家提供的光盘资料里面的C#函数库,路径如下(64位库为例)。

进入厂商提供的光盘资料,找到zauxdll.dll,zmotion.dll和Zmcaux.cs这三个库文件。库文件路径:【00光盘资料】→【04PC函数】→【01PC函数库V2.1】→【Windows平台】→【C#】→【64位】→【库文件】。

5.使用纯上位机总线初始化时需要找厂商提供ZMotionECAT.cs文件。

6.将厂商提供的C#的库文件以及相关文件复制到新建的项目中。

(1)将zmcaux.cs和ZMotionECAT.cs文件复制到新建的项目里面中。

(2)将zauxdll.dll和zmotion.dll文件放入bin\debug文件夹中。

(3)将Zmcaux.cs和ZMotionECAT.cs文件添加进项目中。右键项目名称,选择添加,再选择现有项,选择Zmcaux.cs和ZMotionECAT.cs文件。

7.双击Form1.cs里面的Form1,出现代码编辑界面,在文件开头写入using cszmcaux,并声明控制器句柄g_handle。

至此,项目新建完成,可进行C#项目开发。

0 2 相关PC函数介绍

PC函数手册可在光盘资料获取,具体路径如下:"00光盘资料\03编程手册\03ZMotion PC函数库编程手册"。

1.上位机连接MotionRT750的接口。

2.控制器回零模式。

3.驱动器回零模式。

0 3 PC纯上位机总线初始流程

总线初始化代码:

复制代码
/// /// EtherCat 总线扫描与初始化   该接口会阻塞线程,阻塞时长为超时时间
/// /// 句柄/// 槽位号,控制器的Ecat总线槽一般为0/// 总线初始化信息结构体,设置总线轴号分配情况,总线IO分配情况/// 超时时间/// Action logCallback = null回调函数 获取在线命令错误的返回字符串
/// 返回值,小于 0 表示错误,参考枚举 EcatInitErrCode,0 表示线程初始化成功,大于0:单个或多个zaux接口错误码的累加值public static int ZAux_BusCmd_EcatInit(IntPtr handle, int SlotId, EcatInitInfoSet EcatInfo, int ApiOutTime, Action logCallback = null)
{
    int Iresult = 0;                      // 接口返回值,错误码
    int OutTime = ApiOutTime;             // 保存接口的超时时间
    int EcatScanOutTime = ApiOutTime;     // 扫描超时时间
    int IdleOutTime = 100;                // 等待停止的超时时间
    float ScanNodeNum = 0;                // 扫描到的节点数量
    StringBuilder ReceBuff = new StringBuilder(256);
    byte[] cmdbuff = new byte[2048];
    int Drive_Vender, Drive_Device, Drive_Alias;    // 是否初始化 Ecat 初始化参数
    if (EcatInfo.InitStructFlag == 1)
    {
        // 1. 本地轴相关参数,用于指定本地轴的起始轴号和本地轴的数目
        EcatInfo.LocalAxisId = 0;    // 本地轴起始 ID[默认 0],若使用则从 0 开始
        EcatInfo.LocalAxisNum = 0;   // 本地轴使用的轴数量[默认 0],若使用则从 0 开始
        // 2. 驱动轴相关参数,用于指定总线驱动轴的起始轴号和总线轴的数目
        EcatInfo.DriveAxisStart = 0; // 驱动轴起始 ID[默认 0],第 1 个驱动轴为 DriveAxisStart,第 2 个为 DriveAxisStart + 1 以此类推
        EcatInfo.DriveAxisNum = -1;  // 驱动轴数量[默认 -1],-1 表示不指定驱动轴数量,若为 0 表示扫描到的轴数量,会检查扫描到的轴数量是否一致
        EcatInfo.DriveIoStara = 256; // 驱动 IO 映射到寄存器后的前一个 IO 起始地址
        EcatInfo.DriveIoSpa = 16;    // 一个驱动映射多少个 IO 端口数量
        for (int i = 0; i < 128; i++)
        {
            // PDO 模式数组,参考 RTBasic 手册 drive_profile 说明,默认值为 12 表示需要配置相关 PDO
            EcatInfo.DrivePdoMode[i] = 12;
        }
        EcatInfo.DriveEnable = 1;    //总线初始化时是否自动使能,1 自动使能,0 不使能
        // 3. EtherCAT总线节点使用参数,用于指定EtherCAT总线节点的数量和 IO扩展模块的起始地址
        EcatInfo.EcatNodeNum = -1;   // ECAT 总线从站的数量,-1 表示不指定从站数量,若为 0 表示扫描到的从站数量,会检查扫描到的从站数量是否一致
        for (int i = 0; i < 128; i++)
        {
            EcatInfo.NodeIoId[i] = 0;    // 节点 IO 起始地址,以节点 ID 形式
            EcatInfo.NodeAIoId[i] = 0;   // 节点 AIO 起始地址,以节点 ID 形式
        }
        // 4. DC 同步时钟和 DC 偏移补偿,用于指定系统时钟模式和 DC 偏移标志及时间
        EcatInfo.SysClockMode = 1;      // 系统时钟模式,1 开启 DC 同步时钟,0 关闭 DC 同步时钟
        for (int i = 0; i < 128; i++)
        {
            EcatInfo.DcOffsetFlag[i] = 0; // N 个节点对应的是否需要进行 DC 偏移补偿,0 关闭 DC 偏移,1 开启 DC 偏移,以节点 ID 形式
            EcatInfo.DcOffsetTime[i] = 0; // N 个节点对应的 DC 偏移时间,单位为微秒,0.5 表示 0.5 微秒,以节点 ID 形式
        }
    }
    //需要特殊处理的驱动器列表
    // 定义 Elmo 厂商和设备 ID
    int ElmoVender = 0x9a;
    // Elmo 驱动器的厂商 ID
    int ElmoDevice = 0x30924; // Elmo 驱动器的设备 ID    // 停止急停
    Iresult = zmcaux.ZAux_Direct_Rapidstop(handle, 2);
    //获取系统规格
    UInt16 VirtualAxiseNum = 0; // 虚拟轴数量
    uint8[] MotionAxisNum = new uint8[1];    // 运动轴数量
    uint8[] IoNum = new uint8[4]; // IO 数量
    zmcaux.ZAux_GetSysSpecification(handle, ref VirtualAxiseNum, MotionAxisNum, IoNum);
    // 1、初始化轴参数
    for (int i = 0; i < VirtualAxiseNum; i++)
    {
        Iresult += zmcaux.ZAux_Direct_SetAxisAddress(handle, i, 0);
        Iresult += zmcaux.ZAux_Direct_SetAxisEnable(handle, i, 0);
        Iresult += zmcaux.ZAux_Direct_SetAtype(handle, i, 0);
        // 等待停止
        int Idle = 0;
        while (IdleOutTime > 0)
        {
            Iresult += zmcaux.ZAux_Direct_GetIfIdle(handle, i, ref Idle);
            if (Idle == -1)
            {
                break;
            }
            MyDelayMs(10, ref OutTime);
            IdleOutTime = IdleOutTime - 10;
        }
    }
    // 2、本地轴号重映射
    for (int i = 0; i < EcatInfo.LocalAxisNum; i++)
    {
        Iresult += zmcaux.ZAux_Direct_SetAxisAddress(handle, EcatInfo.LocalAxisId + i, (-1 << 16) + i); // 轴 0 --> i 映射到 LocalAxisId --> LocalAxisId + i
        Iresult += zmcaux.ZAux_Direct_SetAtype(handle, EcatInfo.LocalAxisId + i, 1);
    }
    // 3、DC同步时钟的设置
    if (EcatInfo.SysClockMode == 1)
    {
        Iresult += zmcaux.ZAux_Execute(handle, "SYSTEM_ZSET = SET_BIT(7, SYSTEM_ZSET)", ReceBuff, 256);    //打开总线时钟优化
    }
    else
    {
        Iresult += zmcaux.ZAux_Execute(handle, "SYSTEM_ZSET = CLEAR_BIT(7, SYSTEM_ZSET)", ReceBuff, 256);   //关闭总线时钟优化
    }
    if (0 != Iresult)//阶段错误码拦截!
    {
        return Iresult;
    }
    // 用于保存 SlotScan 回调中的字符串
    string slotScanLog = null;
    string firstLine = null;
    // 自定义回调:捕获字符串,并可选地转发给外部
    Action captureCallback = (msg) =>
    {
        slotScanLog = msg;              // 捕获回调内容!
        logCallback?.Invoke(msg);       // 同时通知外部(保持原有行为)
    };
    // 4、如果前面的流程都正常,则进行第一次总线扫描,进行DC偏移时间的设置
    for (int ii = 0; ii < 3; ii++)
    {
        //停止总线扫描
        Iresult += ZAux_BusCmd_SlotStop(handle, SlotId);
        //等待200ms
        MyDelayMs(200, ref OutTime);
        //开始总线扫描
        if (ZAux_BusCmd_SlotScan(handle, SlotId, ref OutTime, captureCallback) == 1)
        {
            //DC偏移时间的设置
            string CmdBuff = string.Format("?NODE_COUNT({0})", SlotId);
            Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
            ScanNodeNum = int.Parse(ReceBuff.ToString());
            for (int j = 0; j < ScanNodeNum; j++)
            {
                //判断是否需要设置DC偏移时间
                if (EcatInfo.DcOffsetFlag[j] == 1)
                {
                    CmdBuff = string.Format("?NODE_INFO({0},{1}, 0)", SlotId, j);        //该节点的厂商ID
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    Drive_Vender = int.Parse(ReceBuff.ToString());
                    CmdBuff = string.Format("?NODE_INFO({0},{1}, 1)", SlotId, j);       //该节点的设备编号
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    Drive_Device = int.Parse(ReceBuff.ToString());                    //DC 偏移时间单位是ns
                    CmdBuff = string.Format("ZML_INFO(19, {0}, {1}) = SERVO_PERIOD * {2} * 1000", Drive_Vender, Drive_Device, EcatInfo.DcOffsetTime[j]);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                }
            }
            break;
        }
        //获取在线命令返回字符串传给主窗口
        firstLine = string.IsNullOrEmpty(slotScanLog)
        ? string.Empty
        : slotScanLog.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
        if (!string.IsNullOrEmpty(firstLine))
        {
            ZMotionECAT.Instance.DoWork(firstLine);
        }
    }
    //5、DC偏移时间设置后,需要再次扫描总线驱动器
    for (int ii = 0; ii < 3; ii++)
    {
        //停止总线扫描
        Iresult += ZAux_BusCmd_SlotStop(handle, SlotId);
        //等待200ms
        MyDelayMs(200, ref OutTime);
        //开始总线扫描
        if (ZAux_BusCmd_SlotScan(handle, SlotId, ref OutTime, captureCallback) == 1)
        {
            //【6、节点数目判断,看看是否少从站】
            String CmdBuff = string.Format("?NODE_COUNT({0})", SlotId);
            Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
            ScanNodeNum = int.Parse(ReceBuff.ToString());
            if (EcatInfo.EcatNodeNum >= 0)
            {
                //判断节点数目是否正确
                if ((int)ScanNodeNum != EcatInfo.EcatNodeNum)
                {
                    //节点数目不一致
                    return (int)EcatInitErrCode.WrongNodeNum;
                }
            }
            //【7、总线轴个数判断,看看轴数是否可以对上】
            int BusAxisNum = 0;       //总线轴个数
            int NodeAxisNum = 0;    //当前节点轴个数
            if (EcatInfo.DriveAxisNum >= 0)
            {
                for (int j = 0; j < ScanNodeNum; j++)
                {
                    CmdBuff = string.Format("?NODE_AXIS_COUNT({0},{1})", SlotId, j);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    NodeAxisNum = int.Parse(ReceBuff.ToString());
                    BusAxisNum = BusAxisNum + (int)NodeAxisNum;
                }
                //判断轴数目是否正确
                if (BusAxisNum != EcatInfo.DriveAxisNum)
                {
                    //驱动器轴数目不一致
                    return (int)EcatInitErrCode.WrongAxisNum;
                }
            }
            //【8、IO映射和轴映射】
            //总线轴总数,从0开始计数
            BusAxisNum = 0;
            int ServoPeriod = 0;
            Iresult += zmcaux.ZAux_Execute(handle, "?SERVO_PERIOD", ReceBuff, 256);//获取总线周期
            ServoPeriod = int.Parse(ReceBuff.ToString());
            //遍历节点
            for (int i = 0; i < ScanNodeNum; i++)
            {
                CmdBuff = string.Format("?NODE_AXIS_COUNT({0},{1})", SlotId, i);    //各个节点的轴数
                Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                NodeAxisNum = int.Parse(ReceBuff.ToString());
                CmdBuff = string.Format("?NODE_INFO({0},{1}, 0)", SlotId, i);       //该节点的厂商ID
                Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                Drive_Vender = int.Parse(ReceBuff.ToString());
                CmdBuff = string.Format("?NODE_INFO({0},{1}, 1)", SlotId, i);       //该节点的设备编号
                Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                Drive_Device = int.Parse(ReceBuff.ToString());
                CmdBuff = string.Format("?NODE_INFO({0},{1}, 3)", SlotId, i);        //该节点的设备拨码ID
                Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                Drive_Alias = int.Parse(ReceBuff.ToString());
                //【DC偏移设置成功校验】
                if (EcatInfo.DcOffsetFlag[i] == 1)
                {
                    int ZmlInfo, NodeInfo;
                    CmdBuff = string.Format("?ZML_INFO(19,{0},{1})", Drive_Vender, Drive_Device);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    ZmlInfo = int.Parse(ReceBuff.ToString());
                    CmdBuff = string.Format("?NODE_INFO({0},{1},19)", SlotId, i);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    NodeInfo = int.Parse(ReceBuff.ToString());
                    //DC偏移设置失败
                    if (((ServoPeriod * 1000 * EcatInfo.DcOffsetTime[i] - ZmlInfo) > 5) || (ZmlInfo != NodeInfo))
                    {
                        return (int)EcatInitErrCode.DcShiftSetFailu; //DC偏移设置失败
                    }
                }
                //遍历节点的各个电机
                for (int j = 0; j < NodeAxisNum; j++)
                {
                    //【轴号的映射与轴类型的设置】
                    Iresult += zmcaux.ZAux_Direct_SetAxisAddress(handle, EcatInfo.DriveAxisStart + BusAxisNum, BusAxisNum + 1);
                    Iresult += zmcaux.ZAux_Direct_SetAtype(handle, EcatInfo.DriveAxisStart + BusAxisNum, 65);
                    //【PDO的配置】
                    if ((Drive_Device == ElmoDevice) && (Drive_Vender == ElmoVender))
                    {
                        //如果是ELMO的驱动器的PDO配置
                        //ELMO的驱动器需要关闭总线时钟优化
                        Iresult += zmcaux.ZAux_Execute(handle, "SYSTEM_ZSET = CLEAR_BIT(7, SYSTEM_ZSET)", ReceBuff, 256);
                        CmdBuff = string.Format("DRIVE_PROFILE({0}) = -1", EcatInfo.DriveAxisStart + BusAxisNum);
                        Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                        CmdBuff = string.Format("NODE_PROFILE({0},{1}) = -1", SlotId, i);
                        Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                        //ELMO的驱动器需要自定义PDO
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c12, 0, 5, 0);//禁用RxPDO,禁用后才可以修改内容
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c1c, 0, 5, 0);//禁用TxPDO,禁用后才可以修改内容
                        MyDelayMs(50, ref OutTime);                        //更新TXPDO列表
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 0, 5, 0);//禁用0x1a07
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 1, 7, 0x60410010);//状态字
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 2, 7, 0x60770010);//当前力矩
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 3, 7, 0x60640020);//反馈位置
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 4, 7, 0x60fd0020);//驱动器输入
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 5, 7, 0x60b90010);//probe状态
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 6, 7, 0x60ba0020);//probe位置1
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 7, 7, 0x60bb0020);//probe位置2
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1a07, 0, 5, 0x7);//启用分配                                                                                                                 //更新RXPDO列表
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 0, 5, 0);//禁用0x1607
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 1, 7, 0x60400010);//控制字
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 2, 7, 0x60710010);//周期力矩
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 3, 7, 0x60ff0020);//周期速度
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 4, 7, 0x607a0020);//目标位置
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 5, 7, 0x60b80010);//probe设置
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 6, 7, 0x60720010);//力矩限制
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 7, 7, 0x60600008);//控制模式
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1607, 0, 5, 0x7);//启用分配                                                                         //1C12的配置
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c12, 0, 6, 0x1607);// RxPDO分配对象
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c12, 1, 5, 1);//启用分配 
                        //1C13的配置
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c13, 0, 6, 0x1a07);//TxPDO分配对象
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x1c13, 1, 5, 1);//启用分配                        //清除ELMO的驱动器报警
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x6040, 0, 6, 0);//状态初始化                        MyDelayMs(50, ref OutTime);
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x6040, 0, 6, 7);//伺服shutdown                        MyDelayMs(50, ref OutTime);
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x6040, 0, 6, 7);//伺服disable voltage                        MyDelayMs(50, ref OutTime);
                        Iresult += zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, 0x6040, 0, 6, 15);//伺服fault reset
                     }
                    else if ((Drive_Device == 0x1ab0) && (Drive_Vender == 0x41B))
                    {
                        //如果正运动的脉冲扩展卡
                        CmdBuff = string.Format("DRIVE_PROFILE({0}) = 0", EcatInfo.DriveAxisStart + BusAxisNum);
                        Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    }
                    else
                    {
                        //驱动器PDO设置,驱动器默认设置-- -1 位置模式--0  速度模式--20+  力矩模式--30+
                        CmdBuff = string.Format("DRIVE_PROFILE({0}) = {1}", EcatInfo.DriveAxisStart + BusAxisNum, EcatInfo.DrivePdoMode[BusAxisNum]);
                        Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                        //是否映射总线驱动器的硬件限位和原点
                        if ((4 == EcatInfo.DrivePdoMode[BusAxisNum]) || (5 == EcatInfo.DrivePdoMode[BusAxisNum]) || (12 == EcatInfo.DrivePdoMode[BusAxisNum]))
                        {
                            //设置总线驱动器的起始IO地址
                            int StartIdTemp = EcatInfo.DriveIoStara + EcatInfo.DriveIoSpa * (BusAxisNum);
                            CmdBuff = string.Format("DRIVE_IO({0}) = {1}", EcatInfo.DriveAxisStart + BusAxisNum, StartIdTemp);
                            Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                            //设置负限位
                            Iresult += zmcaux.ZAux_Direct_SetRevIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, StartIdTemp);
                            Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, StartIdTemp, 1);
                            //设置正限位
                            Iresult += zmcaux.ZAux_Direct_SetFwdIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, StartIdTemp + 1);
                            Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, StartIdTemp + 1, 1);
                            //设置负限位
                            Iresult += zmcaux.ZAux_Direct_SetDatumIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, StartIdTemp + 2);
                            Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, StartIdTemp + 2, 1);
                        }
                        else if (EcatInfo.DrivePdoMode[BusAxisNum] < 4)
                        {
                            int TempVar = 0;
                            //取消负限位的设置
                            Iresult += zmcaux.ZAux_Direct_GetRevIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, ref TempVar);
                            if (TempVar >= EcatInfo.DriveIoStara)
                            {
                                Iresult += zmcaux.ZAux_Direct_SetRevIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, -1);
                                Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, TempVar, 0);
                            }
                            //取消正限位的设置
                            Iresult += zmcaux.ZAux_Direct_GetFwdIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, ref TempVar);
                            if (TempVar >= EcatInfo.DriveIoStara)
                            {
                                Iresult += zmcaux.ZAux_Direct_SetFwdIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, -1);
                                Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, TempVar, 0);
                            }
                            //取消设置原点
                            Iresult += zmcaux.ZAux_Direct_GetDatumIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, ref TempVar);
                            if (TempVar >= EcatInfo.DriveIoStara)
                            {
                                Iresult += zmcaux.ZAux_Direct_SetDatumIn(handle, EcatInfo.DriveAxisStart + BusAxisNum, -1);
                                Iresult += zmcaux.ZAux_Direct_SetInvertIn(handle, TempVar, 0);
                            }
                        }
                    }
                    //每轴单独分组,轴报警只停自己
                    CmdBuff = string.Format("DISABLE_GROUP({0})", EcatInfo.DriveAxisStart + BusAxisNum);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    //总线轴计数+1
                    BusAxisNum = BusAxisNum + 1;
                }
                /*************************************************************************************** 
                   -------------------------------2:IO模块和AIO模块的设置------------------------------ 
                ***************************************************************************************/
                //ECAT节点数字量IO起始地址的映射
                if (EcatInfo.NodeIoId[i] >= 32)
                {
                    CmdBuff = string.Format("NODE_IO({0}, {1}) = {2}", SlotId, i, EcatInfo.NodeIoId[i]);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                }
                //ECAT节点模拟量IO起始地址的映射
                if (EcatInfo.NodeAIoId[i] > 0)
                {
                    CmdBuff = string.Format("NODE_AIO({0}, {1}) = {2}", SlotId, i, EcatInfo.NodeAIoId[i]);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                }
                /***************************************************************************************
                    --------------------------------3:特殊模块的设置----------------------------------
                ***************************************************************************************/
                //正运动EIO24088脉冲扩展轴和EIO16084脉冲扩展轴轴类型的设置与脉冲模式的设置
                if ((Drive_Device == 0x1AB0) && (Drive_Vender == 0x41B))
                {                    for (int k = 0; k < NodeAxisNum; k++)
                    {
                        //设置扩展脉冲轴ATYPE类型 ATYPE=1
                        zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, (uint)(0x6011 + k * 0x800), 0, 5, 1);
                        //设置扩展脉冲轴INVERT_STEP脉冲输出模式  INVERT_STEP=0
                        zmcaux.ZAux_BusCmd_SDOWrite(handle, (uint)SlotId, (uint)i, (uint)(0x6012 + k * 0x800), 0, 6, 0);
                    }
                }
            }
            //【4、总线开启】
            if (ZAux_BusCmd_SlotStart(handle, SlotId, ref OutTime, captureCallback) == 1)
            {
                MyDelayMs(3000, ref OutTime); //延迟3秒,等待驱动器时钟同步,不同驱动器时间不同,具体根据驱动器调整延时
                                              //"开始清除驱动器报警"
                for (int Drivei = EcatInfo.DriveAxisStart; Drivei < (EcatInfo.DriveAxisStart + BusAxisNum); Drivei++)
                {
                    //伺服错误清除
                    CmdBuff = string.Format("DRIVE_CONTROLWORD({0})=128", Drivei);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    MyDelayMs(10, ref OutTime);
                    //伺服shutdown
                    CmdBuff = string.Format("DRIVE_CONTROLWORD({0})=6", Drivei);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    MyDelayMs(10, ref OutTime);
                    //伺服disable voltage
                    //伺服fault reset
                    CmdBuff = string.Format("DRIVE_CONTROLWORD({0})=15", Drivei);
                    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
                    MyDelayMs(10, ref OutTime);
                }
                MyDelayMs(10, ref OutTime);
                //清除控制器所有轴的错误状态
                zmcaux.ZAux_Direct_Single_Datum(handle, 0, 0);
                MyDelayMs(200, ref OutTime);
                //打开总线轴使能总开关
                Iresult += zmcaux.ZAux_Execute(handle, "WDOG=1", ReceBuff, 256);
                //总线轴单轴上使能
                if (EcatInfo.DriveEnable == 1)
                {
                    for (int Drivei = EcatInfo.DriveAxisStart + BusAxisNum - 1; Drivei >= (EcatInfo.DriveAxisStart); Drivei--)
                    {
                        zmcaux.ZAux_Direct_SetAxisEnable(handle, Drivei, 1);  //总线轴通过这个指令上使能
                        MyDelayMs(10, ref OutTime);
                    }
                }
                return Iresult;
            }
            else
            {
                //获取在线命令返回字符串传给主窗口
                firstLine = string.IsNullOrEmpty(slotScanLog)
                ? string.Empty
                : slotScanLog.Split(new[] { '\r', '\n' },StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
                if (!string.IsNullOrEmpty(firstLine))
                {
                    ZMotionECAT.Instance.DoWork(firstLine);
                }
                return (int)EcatInitErrCode.EcatStartFailu;//总线开启失败
            }
        }
        //获取在线命令返回字符串传给主窗口
        firstLine = string.IsNullOrEmpty(slotScanLog)
        ? string.Empty
        : slotScanLog.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
        if (!string.IsNullOrEmpty(firstLine))
        {
            ZMotionECAT.Instance.DoWork(firstLine);
        }
    }
    return (int)EcatInitErrCode.NotScanNode;
}
/// /// 停止槽位号是SlotId的总线通讯
/// /// 句柄/// 返回值zaux接口错误码public static int ZAux_BusCmd_SlotStop(IntPtr handle, int SlotId)
{
    string CmdBuff = string.Format("SLOT_STOP({0})", SlotId);
    StringBuilder ReceBuff = new StringBuilder(256);
    return zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
}
/// /// 扫描控制器对应槽的总线设备
/// /// 句柄/// 返回值zaux接口错误码/// Action logCallback = null回调函数 获取在线命令错误的返回字符串
public static int ZAux_BusCmd_SlotScan(IntPtr handle, int SlotId, ref int OutTime, Action logCallback = null)
{
    int ScanOkFlag = 0;
    int Iresult = 0;
    int EcatScanOutTime;
    UInt32 puiread = 0;
    uint8 pbifExcuteDown = 0;
    byte[] ZmcReceBuff = new byte[256];
    EcatScanOutTime = OutTime;
    //总线扫描指令的拼接
    string CmdBuff = string.Format("SLOT_SCAN({0})  ?RETURN", SlotId);
    //定义接受缓冲器并清空
    StringBuilder ReceBuff = new StringBuilder(1024);
    ReceBuff.Remove(0, ReceBuff.Length);
    //开始扫描总线设备情况
    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 1024);
    // 通过回调通知外部
    string result = ReceBuff.ToString().Replace("-1\n", "");
    logCallback?.Invoke(result);
    //延时等待扫描结果
    if (ReceBuff.ToString() == "-1\n")
    {
        EcatScanOutTime = 0;
        ScanOkFlag = 1;
        return ScanOkFlag;
        //Console.WriteLine("直接获取-1,条件成功");
    }    else if (ReceBuff.Length > 0)      //如果有其他的返回值则报未扫描到设备
    {
        ScanOkFlag = 0;
        //Console.WriteLine("直接获取-1,条件失败");
        return ScanOkFlag;
    }
    else
    {
        MyDelayMs(500, ref OutTime);
        EcatScanOutTime = EcatScanOutTime - 500;
    }
    while (EcatScanOutTime > 0)
    {
        Array.Clear(ZmcReceBuff, 0, ZmcReceBuff.Length);
        //读取在线命令的应答, 对没有接收应答的命令有用
        Iresult += zmcaux.ZMC_ExecuteGetReceive(handle, ZmcReceBuff, 1000, ref puiread, ref pbifExcuteDown);
        if (ZmcReceBuff[0] != 0)
        {
            //返回负一表示有成功扫描到设备
            if ((ZmcReceBuff[0] == 45) && (ZmcReceBuff[1] == 49) && (ZmcReceBuff[2] == 10))
            {
                ScanOkFlag = 1;
                //Console.WriteLine("while获取-1,条件成功");
                break;
            }
            else
            {
                ScanOkFlag = 0;
                //Console.WriteLine("while获取-1,条件NG");
                break;
            }
        }
        MyDelayMs(50, ref OutTime);
        EcatScanOutTime = EcatScanOutTime - 50;
    }
    return ScanOkFlag;}
// /// 打开总线
/// /// 句柄/// 返回值zaux接口错误码/// Action logCallback = null回调函数 获取在线命令错误的返回字符串
public static int ZAux_BusCmd_SlotStart(IntPtr handle, int SlotId, ref int OutTime, Action logCallback = null)
{
    int StartOkFlag = 0;
    int Iresult = 0;
    int EcatScanOutTime;
    StringBuilder ReceBuff = new StringBuilder(1024);
    UInt32 puiread = 0;
    uint8 pbifExcuteDown = 0;
    byte[] ZmcReceBuff = new byte[256];
    EcatScanOutTime = OutTime;
    MyDelayMs(100, ref OutTime);
    string CmdBuff = string.Format("SLOT_START({0}, 4)", SlotId);
    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
    // 通过回调通知外部
    string result = ReceBuff.ToString().Replace("-1\n", "");
    logCallback?.Invoke(result);
    MyDelayMs(1000, ref OutTime);
    CmdBuff = string.Format("SLOT_START({0}, 8) ?Return", SlotId);
    Iresult += zmcaux.ZAux_Execute(handle, CmdBuff, ReceBuff, 256);
    //延时等待扫描结果    if (ReceBuff.ToString() == "-1\n")
    {
        EcatScanOutTime = 0;
        StartOkFlag = 1;
        return StartOkFlag;
        //Console.WriteLine("直接获取-1,条件成功");
    }
    else if (ReceBuff.Length > 0)      //如果有其他的返回值则报未扫描到设备
    {
        StartOkFlag = 0;
        //Console.WriteLine("直接获取-1,条件失败");
        return StartOkFlag;
    }
    else
    {
        MyDelayMs(500, ref OutTime);
        EcatScanOutTime = EcatScanOutTime - 500;
    }
    while (EcatScanOutTime > 0)
    {
        Array.Clear(ZmcReceBuff, 0, ZmcReceBuff.Length);
        //读取在线命令的应答, 对没有接收应答的命令有用
        Iresult += zmcaux.ZMC_ExecuteGetReceive(handle, ZmcReceBuff, 1000, ref puiread, ref pbifExcuteDown);
        if (ZmcReceBuff[0] != 0)
        {
            //返回负一表示有成功扫描到设备
            if ((ZmcReceBuff[0] == 45) && (ZmcReceBuff[1] == 49) && (ZmcReceBuff[2] == 10))
            {
                StartOkFlag = 1;
                //Console.WriteLine("while获取-1,条件成功");
                break;
            }
            else
            {
                StartOkFlag = 0;
                //Console.WriteLine("while获取-1,条件NG");
                break;
            }
        }
        MyDelayMs(50, ref OutTime);
        EcatScanOutTime = EcatScanOutTime - 50;
    }
    return StartOkFlag;
}

0 4 C#总线初始化功能展示例程介绍

一、选择连接方式为LOCAL连接,点击【连接】。弹出连接成功提示框后,配置总线轴地址参数和本地脉冲轴地址参数后,点击【总线初始化】,会在界面表格中显示总线相关信息。

1.若无法成功总线初始化,命令与输出窗口会打印相关错误信息。如:Unknown device man XXX Slot return error:3205.

报错信息如下图,则需要先询问对应驱动器厂家提供XML文件。

解决方法:将驱动器厂家提供的XML文件,添加到RTSys项目的配置文件中。

(1)打开RTSys软件,点击【文件】,选择【新建工程】。

(2)选择文件路径,输入文件名,然后点击【保存】。

(3)右键点击【配置文件】,选择【添加到配置文件】,选中要添加的XML文件后点击【打开】。

(4)点击菜单栏中的【控制器】,接着点击【生成ZAR文件】,随后点击【浏览】来选择生成ZAR文件的路径。

(5)将生成的ZAR文件放在项目的ZAR文件夹中,随后进行总线初始化。

具体代码如下:

复制代码
// 获取解决方案根目录
string solutionDir = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, @"..\..\..\"));
string zarPath = Path.Combine(solutionDir, "ZAR");
// 获取所有文件的完整路径string[] fileNames = Directory.GetFiles(zarPath).Select(Path.GetFileName).ToArray();
foreach(string fileName in fileNames)
{
    //Init.zar为下载的ZAR文件名
    if (fileName == "Init.zar")
    {
        string path = Path.Combine(zarPath, "Init.zar");
        zmcaux.ZAux_ZarDown(g_handle, path, 0);
        AppendTextOut("ZAR文件下载成功!!!", Color.DarkGoldenrod);
    }
}

2.如需将对应总线节点的IO状态信息或模拟量信息映射到控制器上对应起始地址查看,则修改对应节点IO或模拟量的起始地址,修改完成后,点击【保存】,再点击【总线初始化】。

3.通过RTSys软件查看IO或模拟量映射的地址成功与否。

成功连接控制器后,在菜单栏中找到【控制器】→【控制器状态】→【槽位0节点】,可以看到所有节点对应的IO或模拟量起始地址。可以在总线初始化完成后观看此界面确认IO或模拟量起始地址映射正常。

相关代码介绍:

(1)上位机如何链接上MotionRT750。

复制代码
if (Controler_IP.Text != null && Controler_IP.Text != "")
{
    int ret = -1;
    switch (Connect_ModeIP.Text)
    {
        case "IP":
        //控制器IP地址连接
        ret = zmcaux.ZAux_FastOpen(2, Controler_IP.Text.Trim(), 5000, out g_handle);
        break;
        case "COM":
        //控制器COM串口号连接
        ret = zmcaux.ZAux_OpenCom(uint.Parse(Controler_IP.Text), out g_handle);
        break;
        case "LOCAL":
        //LOCAL接口连接RT750
        ret = zmcaux.ZAux_FastOpen(5, Controler_IP.Text.Trim(), 5000, out g_handle);
        break;
        case "PCI":
        //插入卡槽卡号连接PCI卡
        ret = zmcaux.ZAux_FastOpen(4, Controler_IP.Text.Trim(), 5000, out g_handle);
        break;
    }
    if (ret != 0)
    {
        MessageBox.Show("IP链接失败,报错码: " + ret.ToString());
    }
    else
    {
        if (g_handle != (IntPtr)0)
        {
            //连接成功后停止所有轴
            ret = zmcaux.ZAux_Direct_Rapidstop(g_handle, 2);
            MessageBox.Show("链接成功!");
            this.Text = "已连接";
            timer1.Enabled = true;
        }
    }
}

(2)总线初始化结构体定义,自定义初始化所需的变量,例如LocalAxisId(本地轴起始ID)和LocalAxisNum(本地轴使用的轴数量)。

具体变量信息可以在解决方案中点击ZMotionEcat.cs文件进行查看。

其中,DrivePdoMode用于设置每个轴的发送PDO和接收PDO的配置选择。不同模式对应的PDO数据各不相同。

例如,若总线轴需切换至EtherCAT周期速度模式或EtherCAT周期力矩模式,则相应的PDO列表必须包含对应数据,如模式17和18支持三种总线模式。

具体列表数据请参考RTBasic手册中的drive_profile说明。

复制代码
if (textBox1.Text == "" || textBox3.Text == "")
{
    Console.WriteLine("总线参数不能为空");
    return;
}
//定义总线初始化的信息结构体个数,最多可以支持128个节点MyEcatInit.EcatInitInfoSet ecatInitInfo = new 
MyEcatInit.EcatInitInfoSet(int.Parse(textBox3.Text));
​ecatInitInfo.InitStructFlag = 0;                            //是否自定义初始化参数,设置1的话使用默认的总线初始化参数,设置成0则需要自定义总线初始化相关参数
if (ecatInitInfo.InitStructFlag != 1)
{
    // 1. 本地轴参数,用于指定本地轴的起始 ID 和使用的轴数量
    ecatInitInfo.LocalAxisId = int.Parse(textBox2.Text);    // 本地轴起始 ID
    ecatInitInfo.LocalAxisNum = int.Parse(textBox4.Text);   // 本地轴使用的轴数量
    // 2. 驱动轴相关参数,用于指定驱动轴的起始 ID 和数量
    ecatInitInfo.DriveAxisStart = int.Parse(textBox1.Text); // 驱动轴起始 ID,第 1 个驱动轴为 DriveAxisStart,第 2 个为 DriveAxisStart + 1 以此类推
    ecatInitInfo.DriveAxisNum = -1;                         // 驱动轴数量[默认 -1],-1 表示总线初始化的时候不判断驱动的轴数量是否对上,若为 0 表示扫描到的轴数量,会检查扫描到的轴数量是否一致
    ecatInitInfo.EcatNodeNum = int.Parse(textBox3.Text);    // 总线节点数目[默认 -1],-1 表示总线初始化的时候不判断节点个数是否对上
    for (int i = 0; i < ecatInitInfo.EcatNodeNum; i++)
    {
        ecatInitInfo.DrivePdoMode[i] = 12;                  // PDO 模式数组,参考 RTBasic 手册 drive_profile 说明,默认值为 12 表示需要配置相关 PDO
    }
    ecatInitInfo.DriveEnable = 1;                           // 总线初始化时是否自动使能,1 驱动器自动使能,0 不使能驱动器
    ecatInitInfo.DriveIoSpa = 16;                           // 一个驱动映射多少个 IO 端口数量    
    // 3.DC 同步时钟和 DC 偏移补偿,用于指定系统时钟模式和 DC 偏移标志及时间
    ecatInitInfo.SysClockMode = 1;                          // 系统时钟模式,1 开启 DC 同步时钟,0 关闭 DC 同步时钟
    for (int i = 0; i < ecatInitInfo.EcatNodeNum; i++)
    {
        ecatInitInfo.DcOffsetFlag[i] = 0;                   // N 个节点对应的是否需要进行 DC 偏移补偿,0 关闭 DC 偏移,1 开启 DC 偏移,以节点 ID 形式
        ecatInitInfo.DcOffsetTime[i] = 0;                   // N 个节点对应的 DC 偏移时间,单位为微秒,0.5 表示 0.5 微秒,以节点 ID 形式
    }
}

二、点击【轴参数初始化】进入轴控制界面,开始总线轴的手动调试运动。

1.轴的使能控制。首先将轴类型切换到总线轴,再观察轴使能状态指示灯的颜色,可以准确判断轴的使能情况:绿色代表已使能,红色则表示未使能。点击图案即可切换轴的使能状态。

2.配置好轴参数、轴号及相关参数后,通过左转右转手动调试轴运动。

建议将【脉冲当量】设置为机台运动1mm所需的脉冲数**。**若按此推荐设置,【速度】的单位为mm/s,【加速度】和【减速度】的单位则为mm/s²。

通常,【加速度】和【减速度】设置为速度的 10倍。

3.若在调试过程中不确定机台运动1mm所需的脉冲数,可将【脉冲当量】设为1。

为安全起见,【速度】可先设为10,此时左右整体速度为1*10=10个脉冲每秒,【加速度】和【减速度】均设为1000。随后手动运动轴,观察轴是否运动及【反馈位置】是否有变化。

若手动运动时【反馈位置】数据变化但实际轴未动,可逐步将【脉冲当量】以10倍递增,每次调整后反复手动运动,检查轴是否运动。

【反馈位置】由编码器反馈,若其数据变化,轴必定在运动;若肉眼难以察觉,可能是机台运动较慢。

手动运动和寸动代码如下:

复制代码
//参数同步
zmcaux.ZAux_Direct_SetAtype(g_handle, axis[select_axis], int.Parse(atype_s[select_axis]));           //设置轴类型
zmcaux.ZAux_Direct_SetUnits(g_handle, axis[select_axis], units[select_axis]);                        //设置脉冲当量
zmcaux.ZAux_Direct_SetAccel(g_handle, axis[select_axis], accel[select_axis]);                        //设置加速度
zmcaux.ZAux_Direct_SetDecel(g_handle, axis[select_axis], decel[select_axis]);                        //设置减速度
zmcaux.ZAux_Direct_SetSpeed(g_handle, axis[select_axis], speed[select_axis]);                        //设置运行速度
zmcaux.ZAux_Direct_SetFsLimit(g_handle, axis[select_axis], Fslimit[select_axis]);                    //设置正向软限位
zmcaux.ZAux_Direct_SetRsLimit(g_handle, axis[select_axis],Rslimit[select_axis]);                     //设置负向软限位
zmcaux.ZAux_Direct_SetFastDec(g_handle, axis[select_axis], speed[select_axis]* accel[select_axis]);  //设置快减减速度,达到限位或异常停止时自动采用
Thread.Sleep(1);
if (param < 5)//左转
{
    zmcaux.ZAux_Direct_Single_MoveAbs(g_handle, axis[select_axis], Rslimit[select_axis]);
}
else if (5 <= param && param  9)//运动
{
    if (mode[select_axis])//绝对运动
    {
        zmcaux.ZAux_Direct_Single_MoveAbs(g_handle, axis[select_axis], move_dpos[select_axis]);
    }
    else //相对运动
    {
        zmcaux.ZAux_Direct_Single_Move(g_handle, axis[select_axis], move_dpos[select_axis]);
    }
}

三、在确保手动运动正常且方向无误的前提下,我们可以继续测试轴的回零功能。

若未勾选【启用控制器回零方式】,则默认采用总线驱动器回零(精度更高,推荐使用,驱动器回零参考驱动器手册)。

此时,需参照总线驱动器手册中对回零模式的说明,并将原点传感器连接至驱动器的原点输入口。若勾选【启用控制器回零方式】,则使用控制器回零,具体回零方式可参考下拉框中的介绍。

在实际项目中,仍建议优先选择总线驱动器回零。回零测试步骤如下:

1.选择回零轴号,配置轴的正限位信号、负限位信号及原点信号后,点击【配置轴IO】。若轴状态显示"30h",则勾选【反转】,并再次点击【配置轴IO】。

2.配置速度、爬行速度及回零模式后,点击【启动回零】。若出现方向错误或速度过快的情况,请立即点击【紧急停止】,并重新进行调试。

3.若轴状态显示"8h",则表示驱动器出现报错。以下是遇到驱动器报警后的处理方法。

(1)检查驱动器上的LED面板是否显示报错信息,报错时将呈现相应的错误码。

常见的错误,如驱动器过载和转矩饱和异常等,均可通过【清除报警】按钮清除。

具体代码如下:

复制代码
int position = 3;                                                               // 获取第3位
int value = 0;
zmcaux.ZAux_BusCmd_DriveClear(g_handle, (uint)param, 0);                        //清除总线伺服轴报警
Thread.Sleep(5000);                                                             //等待报警清除
zmcaux.ZAux_BusCmd_SDOReadAxis(g_handle, (uint)param, 0x6041, 0, 6, ref value); //读取驱动器状态字对应数据字典6041
if (((value >> position) & 1) == 0)
{
    zmcaux.ZAux_Direct_Single_Datum(g_handle, param, 0);                        //清除控制器报警
    Thread.Sleep(100);
    zmcaux.ZAux_Direct_SetAxisEnable(g_handle, param, 1);                       //重新上使能
    MessageBox.Show("报警清除成功!!!");
}
else{MessageBox.Show("报警清除失败,重新清除或查找报错原因!!!");}

(2)如果通过代码无法清除报警,绝大多数驱动器报警问题可以通过断电并重启驱动器来解决。若断电重启后报警依然存在,则需参照驱动器手册进行错误排查,并可咨询驱动器技术人员以获取解决方案。

教学视频:

实时在线变速实现多段速的软着陆

相关推荐
d111111111d2 小时前
在STM32中,中断服务函数的命名有什么要求?
笔记·stm32·单片机·嵌入式硬件·学习·c#
极客智造4 小时前
深入解析 C# Type 类:解锁反射与动态编程的核心
c#·反射
SmoothSailingT4 小时前
C#——textBox控件(1)
开发语言·c#
superman超哥4 小时前
仓颉语言中并发集合的实现深度剖析与高性能实践
开发语言·后端·python·c#·仓颉
工程师0075 小时前
C#中的服务注册剖析
c#·服务注册
张人玉5 小时前
c#DataTable类
数据库·c#
缺点内向5 小时前
如何在 C# .NET 中将 Markdown 转换为 PDF 和 Excel:完整指南
pdf·c#·.net·excel
CodeCraft Studio5 小时前
Excel处理控件Aspose.Cells教程:使用C#在Excel中创建旭日图
c#·excel·aspose·excel旭日图·excel库·excel开发控件·excel api库
民乐团扒谱机5 小时前
【微实验】仿AU音频编辑器开发实践:从零构建音频可视化工具
算法·c#·仿真·audio·fft·频谱