SylixOS EtherCAT 开发

更新时间:
2024-04-26
下载文档

SylixOS EtherCAT 开发

本节主要介绍在 IGC 系列控制器上如何使用 SylixOS-IgH 对 EtherCAT 伺服电机进行控制。

环境准备

设备列表

序号设备名称数量备注
1IGC1503 控制器1必选
2EtherCAT 伺服电机系统1必选[1 个伺控制器 + 1 个伺服电机]
3交换机1可选

说明:

  • 本节例程的代码适用于 IGC 系列控制器的 IGC1500、IGC1503。
  • EtherCAT 伺服电机系统,本例使用 1 套汇川 EtherCAT 伺服电机系统(1 个 SV660NS2R8I 型伺服控制器和 1 台 400W 伺服电机)。

软件列表

序号软件名称数量备注
1RealEvo-IDE1必选(版本需大于 5.0.4)
2PuTTY 终端工具下载1必选

示例工程列表

序号名称数量说明
1app_igh_user1SylixOS 使用开源 IgH EtherCAT 主站控制伺服电机运转。

下载列表

前提条件

通过“入门开发”中 SylixOS Modbus 开发 教程,熟练掌握 SylixOS 开发环境 RealEvo-IDE 的基本使用方法。如:新建工程,添加依赖文件及依赖路径,配置目标设备和工程编译运行等操作。

控制电机转动

案例使用 SylixOS-IgH 进行应用开发,对单个电机进行 CSP 模式控制,实际效果完成对单个 EtherCAT 伺服电机的匀速运动控制。

说明:

  • CSP 模式:Cyclic sync position mode 周期同步位置模式。
  • 周期旋转幅度与编码器位数相关,例程默认步进值为 30000。

步骤 1:连接硬件设备

  1. 准备一台 IGC1503 控制器,一根网线。网线一端接入电脑,另一端接入控制器 X1 网口,网口默认 IP 为 192.168.1.253。

  2. 准备一套 EtherCAT 伺服电机系统,一根网线或 EtherCAT 专用网线。网线一端接入 IGC1503 控制器网口 X2,另一端接入伺服控制器用于 EtherCAT 通讯的 IN 端口。

  3. IGC1503 控制器和伺服电机系统上电。

步骤 2:创建 Base 工程

参考“入门开发 > SylixOS Modbus 开发”中 步骤 4 操作,完成 Base 工程的创建。

步骤 3:创建 IgH 应用工程

  1. 打开 RealEvo-IDE 软件,选择主菜单 > File > New > SylixOS App,创建 app_igh_user 工程。在 Project name 中输入工程名称,如 app_igh_user,单击 Next

  2. 单击 Workspace 选择依赖的 base 工程,选择后单击 Finish 完成应用工程创建。

步骤 4:添加应用工程依赖

  1. 复制 libigh.raropen in new window 解压后的文件夹内容,放入新建的 app_igh_user 工程中。整体结构如图:

  2. 添加 IgH App 工程依赖头文件路径。

  3. 添加 IgH App 工程依赖库文件。

  4. 配置依赖库文件路径。

步骤 5:编写 IgH 控制代码

  1. 编写 app_igh_user 工程的 src 文件夹下的 app_igh_user.c。包含系统 C 库头文件、IgH 主站协议栈以及 ecatdc 同步库头文件。

     #include <sched.h>
     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
     #include <pthread.h>
     #include "ecrt.h"
     #include "ecatdc.h"
     #include "struct.h"
    
  2. app_igh_user.c:配置从站位置、偏移、厂商 ID、产品 ID。

    说明:

    • 由于只有一个电机,所以 alias,position 均为 0。
    • vendor_id 以及 product_id 每个电机不同,需要根据实际进行配置,此处使用的是汇川伺服 SV660NS2R8I 型号。
    /* 
     * ethercat slave info(alias, position) 
     */
    #define SLAVE_POS_1         0,0
    /* 
     * vendor_id  product_id 
     */
    #define VID_PID             0x00100000, 0x000c010d
    
  3. app_igh_user.c:配置实时任务绑核参数、电机运行模式以及步进值。

    • IGC1500 以及 IGC1503 均是四核 CPU,0 核默认为系统服务以及中断处理核,实时任务可以绑定到 1-3 核,此处为 3 核。

    • 不同电机支持的模式不同,但基本都支持 CSP 模式,此案例需要电机进入 CSP 模式。

    • 电机步进值,此处要结合电机编码器位数来实际编程,过大会导致电机转速过快报错。

    /* 
     * cycle task bind core num 
     */
    #define BIND_CORE  (3)
    /* 
     * Motor Operation mode define 
     */
    #define MOTOR_OPERATION_MODE  OP_CSP_MODE
    /* 
     * Motor move step 
     */
    #define MOTOR_INCREASE_STEP   (30000)
    /* 
     * max slave num 
     */
    #define SLAVE_MOTOR_POS_MAX   (1)
    

    在 app_igh_user 工程的 src 文件夹下新建 struct.h 头文件并对控制模式部分模块进行宏定义。

    #include <stdint.h>
    
    
    #define OP_PP_MODE         (0x01)  
    #define OP_PV_MODE         (0x03)  
    #define OP_TP_MODE         (0x04) 
    #define OP_CSP_MODE        (0x08)  
    #define OP_CSV_MODE        (0x09)  
    

    ECAT 标准文档《Cia-402-2》上关于控制模式配置字 0x6060 的模式配置说明:

  4. app_igh_user.c:定义所需要的 IgH 全局变量。

    /*
     *  EtherCAT
     *  igh ethercat master ptr
     */
    static ec_master_t              *master = NULL;
    
    /*
     *  domain data
     */
    static ec_master_state_t         master_state         = {};
    static ec_domain_t              *domain1_output       = NULL;
    static ec_domain_state_t         domain1_output_state = {};
    static ec_domain_t              *domain1_input        = NULL;
    static ec_domain_state_t         domain1_input_state  = {};
    static uint8_t                  *domain1_output_pd    = NULL;
    static uint8_t                  *domain1_input_pd     = NULL;
    
    /*
     *  slave config
     */
    static ec_slave_config_t        *sc[SLAVE_MOTOR_POS_MAX];
    static ec_slave_config_state_t   sc_state[SLAVE_MOTOR_POS_MAX] = {};
    /*
     *  offset info
     */
    static offset                    pdo_offset[SLAVE_MOTOR_POS_MAX];
    /*
     *  communication cycle
     */
    static unsigned int              cycle_ns       = 1000000; 
    /*
     *  user global var
     */
    static  volatile tTxPDO          driverData[SLAVE_MOTOR_POS_MAX];
    static  volatile uint32_t        uiDirection    = 0;
    static  volatile uint32_t        uiMoveStep     = MOTOR_INCREASE_STEP;
    static           BOOL            bRun           = TRUE;
    static           BOOL            bResetDone     = FALSE;
    static           uint32_t        uiResetCounter = 0;
    
  5. app_igh_user.c:配置 PDO 映射表。

    说明:

    这里只映射 ECAT 电机的基本条目,具体可参考电机使用手册或者 《Cia-402-2》。

    /*
    * RXPDO
    * index   sub index    bit size   describe
    * 0x607a    0x00          32      target position
    * 0x6040    0x00          16      control word
    * 0x6060    0x00           8      mode of operation
    *
    * TXPDO
    * index   sub index    bit size   describe
    * 0x6064    0x00          32      actual position
    * 0x6041    0x00          16      status word
    * 0x606c    0x00          32      actual velocity
    * 0x6061    0x00           8      sidplay mode of operation
    * 0x603f    0x00          16      error code
    * 0x6077    0x00          16      actual torque
    */
    static ec_pdo_entry_info_t device_pdo_entries[] = {
        {0x607a, 0x00, 32},             
        {0x6040, 0x00, 16},            
        {0x6060, 0x00, 8},              
        {0x6064, 0x00, 32},             
        {0x6041, 0x00, 16},            
        {0x606c, 0x00, 32},             
        {0x6061, 0x00, 8},              
        {0x603f, 0x00, 16},             
        {0x6077, 0x00, 16},             
    };
    
    static ec_pdo_info_t device_pdos[] = {
        {0x1600, 3, device_pdo_entries + 0},
        {0x1a00, 6, device_pdo_entries + 3},
    };
    
  6. struct.h: 定义本地 PDO 数据结构体以用于数据映射:

    说明:

    • #pragma pack() 使编译器取消对齐。
    • #pragma pack(1) 使编译器 1 字节对齐。
    /*
     *  Process Data,Offsets for PDO entries
     */
    #pragma pack(4)
    typedef struct {
        unsigned int  target_pos;            
        unsigned int  ctrl_word;             
        unsigned int  operation_mode;        
    
        unsigned int  actual_pos;            
        unsigned int  status_word;           
        unsigned int  actual_velocity;       
        unsigned int  display_mode;         
        unsigned int  error_code;            
        unsigned int  actual_torque;         
    }offset;
    #pragma pack()
    
    #pragma pack(1)
    typedef struct _TxPDO
    {
        int32_t   actual_pos;             
        uint16_t  status_word;            
        int32_t   actual_speed;           
        int8_t    display_mode;           
        uint16_t  error_code;             
        int16_t   actual_torque;          
    }tTxPDO;
    #pragma pack()
    
  7. app_igh_user.c:配置 Sync Manager 设备 PDO 信息。

    static ec_sync_info_t device_syncs[] = {
        {0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
        {1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},
        {2, EC_DIR_OUTPUT, 1, device_pdos + 0, EC_WD_ENABLE},
        {3, EC_DIR_INPUT, 1, device_pdos + 1, EC_WD_DISABLE},
        {0xff}
    };
    
  8. app_igh_user.c:进行 domain 数据域挂载,对设备端 PDO 以及本地内存进行映射。

    const static ec_pdo_entry_reg_t domain1_output_regs[] = {
        {SLAVE_POS_1, VID_PID, 0x607a, 0, &pdo_offset[0].target_pos},
        {SLAVE_POS_1, VID_PID, 0x6040, 0, &pdo_offset[0].ctrl_word},
        {SLAVE_POS_1, VID_PID, 0x6060, 0, &pdo_offset[0].operation_mode},
        {}
    };
    
    const static ec_pdo_entry_reg_t domain1_input_regs[] = {
        {SLAVE_POS_1, VID_PID, 0x6064, 0, &pdo_offset[0].actual_pos},
        {SLAVE_POS_1, VID_PID, 0x6041, 0, &pdo_offset[0].status_word},
        {SLAVE_POS_1, VID_PID, 0x606c, 0, &pdo_offset[0].actual_velocity},
        {SLAVE_POS_1, VID_PID, 0x6061, 0, &pdo_offset[0].display_mode},
        {SLAVE_POS_1, VID_PID, 0x603f, 0, &pdo_offset[0].error_code},
        {SLAVE_POS_1, VID_PID, 0x6077, 0, &pdo_offset[0].actual_torque},
        {}
    };
    
  9. app_igh_user.c:所使用到的函数提前声明,具体实现在后续步骤。

    /*
     *  function declarations
     */
    static void   exit_proc ();
    static int    ecMasterCycTaskCreate (void   *arg);
    static void  *taskCyclicRefSlave (VOID  *pArg);
    static void   check_domain_state (void);
    static void   check_master_state (void);
    static void   cyclic_task_data_proc (uint8_t  slave);
    static void   reset_task (uint8_t  slave);
    static void   cyclic_task (uint8_t  slave);
    
  10. app_igh_user.c:编写主站启动以及配置从站基本流程代码。

    int main(int  argc, char  **argv)
    {
        /* 
         *  set ctrl+C action: exit IgH master request 
         */
        atexit(exit_proc);
    
        /*
         *  request igh ethercat master instance
         */
        master = ecrt_request_master(0);
    
        if (!master) {
            printf("request master error\r\n");
            return  (-1);
        }
    
        /*
         *  get bus info
         */
        ec_master_info_t  ecMasterInfo;
    
        if ( 0 == ecrt_master(master, &ecMasterInfo) ){
            printf("Bus scan successful - %d slaves.\n", ecMasterInfo.slave_count);
        }
    
        /*
         *  show slave info
         */
        for (unsigned int i = 0; i < ecMasterInfo.slave_count; i++) {
            ec_slave_info_t slave_info;
            ecrt_master_get_slave ( master, 0, &slave_info );
            printf ( "slave[%d]-- vendor_id:0x%04x, product_code:0x%04x  \n",\
                    i,  slave_info.vendor_id, slave_info.product_code );
        }
    
        /*
         *  create output domain
         */
        domain1_output = ecrt_master_create_domain(master);
    
        if (!domain1_output) {
            printf("[%s]Failed to create domain1_output data pointer.\n" , __func__ );
            return  (-1);
        }
    
        /*
         *  create input domain
         */
        domain1_input = ecrt_master_create_domain(master);
    
        if (!domain1_input) {
            printf("[%s]Failed to create domain1_input data pointer.\n" , __func__ );
            return  (-1);
        }
    
        /*
         *  config slave
         */
        for(int i = 0; i < SLAVE_MOTOR_POS_MAX ; i++) {
            if (!(sc[i] = ecrt_master_slave_config(master, 0, i, VID_PID))) {
                printf("[%s]Failed to get slave configuration for slave %d!\n" , __func__ ,i );
                return  (-1);
            }
        }
    
        /*
         *  config slaves pdos
         */
        for (int i = 0; i < SLAVE_MOTOR_POS_MAX ; i++) {
            if (ecrt_slave_config_pdos(sc[i], EC_END, device_syncs)) {
                printf("[%s]Failed to configure slave PDOs,slave %d!\n" , __func__ ,i );
                return  (-1);
            }
        }
    
        /*
         *  register master output domain
         */
        if (ecrt_domain_reg_pdo_entry_list(domain1_output, domain1_output_regs)) {
            printf("[%s]output PDO entry registration failed!\n" , __func__ );
            return  (-1);
        }
    
        /*
         *  register master input domain
         */
        if (ecrt_domain_reg_pdo_entry_list(domain1_input, domain1_input_regs)) {
            printf("[%s]input PDO entry registration failed!\n" , __func__ );
            return  (-1);
        }
    
        /*
         *  dc sync init
         */
        setSlaveDcRef(master, sc[0]);
    
        for(int i = 0; i < SLAVE_MOTOR_POS_MAX ; i++) {
            ecrt_slave_config_dc(sc[i], 0x0300, cycle_ns , cycle_ns / 2, 0, 0);
        }
    
        /*
         *  activate igh ethercat master
         */
        if (ecrt_master_activate(master)) {
            return  (-1);
    
        } else {
            printf("Activating master succes!\n");
        }
    
        /*
         *  get domain memory address
         */
        if (!(domain1_output_pd = ecrt_domain_data(domain1_output))) {
            printf("[%s]Failed to get domain_output data pointer.\n" , __func__);
            return  (-1);
        }
    
        if (!(domain1_input_pd = ecrt_domain_data(domain1_input))) {
            printf("[%s]Failed to get domain_input data pointer.\n" , __func__);
            return  (-1);
        }
    
        /*
         *  reset user data
         */
        memset(driverData, 0, sizeof(driverData));
    
        /*
         *  create real time thread
         */
        ecMasterCycTaskCreate(NULL);
    
        while (bRun) {   
            /*
             *  print process status
             */
            volatile tTxPDO  *data        = &driverData[0];
            char              param[1024] = {0};
    
            sprintf(param, "status_word:%x mode:%x actual_pos:%d actual_speed %d error_code:%x actual_torque:%d\r\n",
                        data->status_word,
                        data->display_mode,
                        data->actual_pos,
                        data->actual_speed,
                        data->error_code,
                        data->actual_torque);
            printf(param);
            sleep(2);
        }
        ecrt_master_deactivate(master);
            
        return 0;
    }
    
  11. app_igh_user.c:退出函数 exit_proc 代码。

    说明:

    当用户使用 kill 方式或者 CTRL + C 方式退出程序时调用该接口进行释放主站资源。

    static void exit_proc()
    {
        bRun = 0;
        ecrt_master_deactivate(master);
        printf("exit!\n");
    }
    
  12. app_igh_user.c:创建 ECAT 实时收发任务。

    INT  ecMasterCycTaskCreate (void  *arg)
    {
        INT               iRet    = 0;
        pthread_attr_t    RtAttr  = {0};
        pthread_t         RtTid   = 0;
    
        pthread_attr_init(&RtAttr);
        RtAttr.schedparam.sched_priority = 255 - 15;
        pthread_attr_setname(&RtAttr, "ec_cyclic_task");
    
        iRet = pthread_create(&RtTid, &RtAttr, taskCyclicRefSlave, (void *)arg);
        if (iRet != ERROR_NONE) {
            return (PX_ERROR);
        }
    
        return (ERROR_NONE);
    }
    
    VOID  *taskCyclicRefSlave (VOID  *pArg)
    {
        printf("Cycle Thread Started.\n");
        (void)pArg;
    
        cpu_set_t       cpuset   =   {{0}};
        INT             iRet     =    0;
    
        /*
         *  设置线程CPU亲和性
         */
        CPU_ZERO(&cpuset);
        CPU_SET(BIND_CORE, &cpuset);
        iRet = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
        if (iRet != ERROR_NONE) {
            printf("cycecMastertask bind cpu core failed.\n");
            return  (NULL);
        }
    
        /*
         *  设置CPU强绑核
         */
        CPU_ZERO(&cpuset);
        if (sched_cpuaffinity_get_np(sizeof(LW_CLASS_CPUSET), &cpuset)) {
            printf("CPU strongly affinity schedule get fail: %s.\n",
                    lib_strerror(errno));
            return  (NULL);
        }
    
        if (!CPU_ISSET(BIND_CORE, &cpuset)) {
            CPU_SET(BIND_CORE, &cpuset);
            if (sched_cpuaffinity_set_np(sizeof(LW_CLASS_CPUSET), &cpuset)) {
                printf("CPU strongly affinity schedule set fail: %s.\n",
                        lib_strerror(errno));
                return  (NULL);
            }
        }
    
        /*
         *  设置周期任务启动时间
         */
        setDcStartTime(master, cycle_ns);
        printf("start run! Move step %d\n", uiMoveStep);
        while (bRun) {
            /*
             *  周期调度
             */
            waitPeriod(master, cycle_ns);
            ecrt_master_receive(master);
            ecrt_domain_process(domain1_output);
            ecrt_domain_process(domain1_input);
            check_domain_state();
            check_master_state();
        
            if (!bResetDone) {
                reset_task(0);
    
            } else {
                cyclic_task(0);
            }
        
            ecrt_domain_queue(domain1_output);
            ecrt_domain_queue(domain1_input);
            ecrt_master_async_data(master);
            syncDistributedClocks(master);
            ecrt_master_send(master);
            updateMasterClock();
        }
        
        printf("Cycle Thread Exit.\n");
    
        return  (NULL);
    }
    
  13. app_igh_user.c:Domain 状态监测函数 check_domain_state() 代码。

    static void  check_domain_state (void)
    {
        ec_domain_state_t  ds  = {};
        ec_domain_state_t  ds1 = {};
    
        ecrt_domain_state(domain1_output, &ds);
    
        if (ds.working_counter != domain1_output_state.working_counter) {
            printf("Domain1_output: WC %u.\n", ds.working_counter);
        }
    
        if (ds.wc_state != domain1_output_state.wc_state) {
            printf("Domain1_output: State %u.\n", ds.wc_state);
        }
    
        domain1_output_state = ds;
    
        ecrt_domain_state(domain1_input, &ds1);
    
        if (ds1.working_counter != domain1_input_state.working_counter) {
            printf("Domain1_input: WC %u.\n", ds.working_counter);
        }
    
        if (ds1.wc_state != domain1_input_state.wc_state) {
            printf("Domain1_input: State %u.\n", ds.wc_state);
        }
    
        domain1_input_state = ds1;
    }
    
  14. app_igh_user.c:主站状态检测函数 check_master_state() 代码。

    static void  check_master_state (void)
    {
        ec_master_state_t  ms;
    
        ecrt_master_state(master, &ms);
    
        if (ms.slaves_responding != master_state.slaves_responding) {
            printf("%u slave(s).\n", ms.slaves_responding);
        }
    
        if (ms.al_states != master_state.al_states) {
            printf("AL states: 0x%02X.\n", ms.al_states);
        }
    
        if (ms.link_up != master_state.link_up) {
            printf("Link is %s.\n", ms.link_up ? "up" : "down");
        }
    
        master_state = ms;
    }
    
  15. app_igh_user.c:周期拷贝过程数据函数 cyclic_task_data_proc() 代码。

    static void  cyclic_task_data_proc (uint8_t  slave)
    {
        tTxPDO                  *data  = &driverData[slave];
        static uint16_t          errorCode[SLAVE_MOTOR_POS_MAX];
    
        if(data == NULL)
            return ;
    
        memcpy((uint8_t *)data, domain1_input_pd, sizeof(tTxPDO));
    
        if(errorCode[slave] != EC_READ_U16(domain1_input_pd + pdo_offset[slave].error_code)) {
            printf("slave %d errorCode %X , last:%X...\r\n",slave , EC_READ_U16(domain1_input_pd + pdo_offset[slave].error_code),
                    errorCode[slave]);
            errorCode[slave] = EC_READ_U16(domain1_input_pd + pdo_offset[slave].error_code);
        }
    }
    
  16. app_igh_user.c:程序启动后,根据周期数来使用控制字对电机进行消除故障并复位,复位函数代码 reset_task() 如下所示。

    void  reset_task (uint8_t  slave)
    {
        int32_t         target_pos = 0;
        uint16_t        status     = 0;
    
        uiResetCounter++;
    
        /*
         *  根据时序周期进行复位电机并最终使能到 OP 状态
         */
        switch(uiResetCounter) {
        case RESETC_CLEAR :
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_STATUS_CLEAR);
            break;
        case RESETC_FAULT :
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_RESET_FAULT);
            break;
        case RESETC_SHUTDOWN :
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_SHUT_DOWN);
            break;
        case RESETC_SWITCHON :
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_SWITCH_ON);
            break;
        case RESETC_ENABLEOP :
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_ENABLE_OP);
            EC_WRITE_S8(domain1_output_pd + pdo_offset[slave].operation_mode, MOTOR_OPERATION_MODE);
            target_pos = EC_READ_S32(domain1_input_pd + pdo_offset[slave].actual_pos);
            EC_WRITE_S32(domain1_output_pd + pdo_offset[slave].target_pos, target_pos);
            break;
        case RESETC_DONE :
            status      = EC_READ_U16(domain1_input_pd + pdo_offset[slave].status_word);
            if ( (status & SW_OP_ENABLE) == SW_OP_ENABLE) {
                bResetDone = TRUE;
                printf("reset done !\n");
    
            } else {
                bResetDone    = FALSE;
                uiResetCounter = 0;
                printf("reset false , try to reset again !\n");
            }
            break;
        }
    }
    
  17. struct.h:复位周期点枚举类 ENUM_RESET 定义。

    typedef enum {
        RESETC_CLEAR    = 100,
        RESETC_FAULT    = 1000,
        RESETC_SHUTDOWN = 2000,
        RESETC_SWITCHON = 3000,
        RESETC_ENABLEOP = 4000,
        RESETC_DONE     = 4500,
    } ENUM_RESET;
    
  18. struct.h:控制字 0x6040 操作码宏定义。

    #define CW_STATUS_CLEAR     (0x00)
    #define CW_SHUT_DOWN        (0x06)
    #define CW_SWITCH_ON        (0x07)
    #define CW_ENABLE_OP        (0x0F)
    #define CW_RESET_FAULT      (0x80)
    

    ECAT 标准文档 《Cia-402-2》上关于控制字 0x6040 的操作说明:

  19. struct.h:状态字 0x6041 编码部分宏定义。

    #define SW_STATUS_MASK      (0XF)
    #define SW_SWITCHON_DISABLE (0X40)
    #define SW_OP_ENABLE        (0X27)
    #define SW_STATUS_FAULT     (0x8)
    

    ECAT 标准文档《Cia-402-2》上关于状态字 0x6041 的编码说明:

  20. app_igh_user.c:用户控制逻辑代码 cyclic_task() 编写。

    说明:

    控制程序每周期读取当前实际位置,并以此为基准增加固定的步进值 uiMoveStep,最后写入到目标位置 PDO 0x607a,实现控制电机 CSP 模式转动。

    void  cyclic_task (uint8_t  slave)
    {
        uint16_t status      = 0;
        int32_t  current_pos = 0, target_pos = 0;
    
        cyclic_task_data_proc(slave);
    
        status      = EC_READ_U16(domain1_input_pd + pdo_offset[slave].status_word);
        current_pos = EC_READ_S32(domain1_input_pd + pdo_offset[slave].actual_pos);
    
        if ( (status & SW_OP_ENABLE) == SW_OP_ENABLE) {
            switch (uiDirection) {
            case 0:
                target_pos = current_pos + uiMoveStep;
                break;
            case 1:
                target_pos = current_pos - uiMoveStep;
                break;
            default:
                break;
            }
            EC_WRITE_S32(domain1_output_pd + pdo_offset[slave].target_pos,  target_pos);
            
        } else {
            EC_WRITE_U16(domain1_output_pd + pdo_offset[slave].ctrl_word, CW_SHUT_DOWN);
            bResetDone = FALSE;
        }
    }
    
  21. 完整 app_igh_user 工程请参考 app_igh_useropen in new window

  22. 最终的工程文件结构

步骤 6:下载部署应用程序

上传到设备端的文件清单如下:

上传文件说明上传路径
/libigh/ec_master.koECAT 主站协议栈/lib/modules/ec_master.ko
/libigh/ethercatECAT 命令行工具/usr/bin/ethercat
/libigh/rt_netcard.koECAT 实时网卡驱动/lib/modules/rt_netcard.ko
/Release/strip/app_igh_userECAT 应用程序/apps/app_igh_user/app_igh_user (app 工程创建默认已添加)
  1. 添加上述列表上传路径,最终如图所示:

  2. 右击 app_igh_user 工程,选择 SylixOS > Upload,上传工程文件。

步骤 7:配置 IgH 实时网卡

  1. 打开 PuTTY 工具,单击 Session 选项,选中 Telnet,输入 IP,输入端口,单击 Open,登录 IGC1503 控制器。

  2. 使用 ifconfig 命令查看当前板卡网络接口配置。

    [root@sylixos:/etc]# ifconfig
    _X2       Link encap: Ethernet HWaddr: 02:81:01:6e:98:96
            Dev: gmac Ifidx: 3 DHCP: D4 D6 Spd: N/A
            inet addr: 10.4.0.230 netmask: 255.255.0.0
            gateway: 10.4.0.1 broadcast: 10.4.255.255
            inet6 addr: fe80::81:1ff:fe6e:9896 Scope:Link<T0>
            UP BROADCAST MULTICAST  MTU:1500  Metric:1
            collisions:0 txqueue:0 tcpaf:2 tcpwnd:65535
            RX ucast packets:0 nucast packets:0 dropped:0
            TX ucast packets:0 nucast packets:0 dropped:0
            RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    
    _X1       Link encap: Ethernet HWaddr: 02:82:01:6e:98:96
            Dev: emac Ifidx: 2 DHCP: D4 D6 Spd: 100 Mbps
            inet addr: 192.168.1.253 netmask: 255.255.255.0
            gateway: 192.168.1.1 broadcast: 192.168.1.255
            inet6 addr: fe80::82:1ff:fe6e:9896 Scope:Link
            UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
            collisions:0 txqueue:0 tcpaf:2 tcpwnd:65535
            RX ucast packets:77 nucast packets:30049 dropped:0
            TX ucast packets:63 nucast packets:10 dropped:0
            RX bytes:2428933 (2.3 MB)  TX bytes:7192 (7.0 KB)
    
    lo0       Link encap: Local Loopback
            Dev: N/A Ifidx: 1 DHCP: D4 D6 Spd: N/A
            inet addr: 127.0.0.1 netmask: 255.0.0.0
            P-to-P: 127.0.0.1 broadcast: N/A
            inet6 addr: ::1 Scope:Loopback
            UP LOOPBACK RUNNING  MTU:0  Metric:1
            collisions:0 txqueue:0 tcpaf:2 tcpwnd:262140
            RX ucast packets:8 nucast packets:0 dropped:0
            TX ucast packets:8 nucast packets:0 dropped:0
            RX bytes:468 (468.0 B)  TX bytes:468 (468.0 B)
    
    dns0: 0.0.0.0
    dns1: 0.0.0.0
    dns2: 0.0.0.0
    dns3: 0.0.0.0
    default device is: null
    list net interface: 3
    
  3. 配置 /etc/ethercat_cfg.ini 文件,明确 IgH 使用的通讯网口。

    说明:

    • Dev 可以配置的网络设备名为 ifconfig 命令显示的 _X2:Dev:gmac _X1:Dev:emac。
    • 这里请配置使用 gmac 口作为 ECAT 通讯口。
    [ethercat]
    Dev=gmac
    

步骤 8:加载 IgH 驱动模块

  1. 加载 IgH 主站协议栈模块。

    [root@sylixos:/]# insmod /lib/modules/ec_master.ko
    use MAC addr: 02:81:01:6e:98:96
    module /lib/modules/ec_master.ko register ok, handle: 0x43ea0d10
    
  2. 加载 IgH 实时网卡驱动模块。

    [root@sylixos:/]# insmod /lib/modules/rt_netcard.ko
    use Dev: gmac EtherCAT port.
    module /lib/modules/rt_netcard.ko register ok, handle: 0x4400f050
    
  3. 使用 ethercat 命令行工具查看主站状态。

    [root@sylixos:/]# ethercat master
    Master0
    Phase: Idle
    Active: no
    Slaves: 1
    Ethernet devices:
        Main: 02:81:01:6e:98:96 (attached)
        Link: UP
        Tx frames:   922981
        Tx bytes:    78044260
        Rx frames:   922980
        Rx bytes:    78044200
        Tx errors:   7998
        Tx frame rate [1/s]:   1000   1000   1000
        Tx rate [KByte/s]:     58.6   69.3   80.1
        Rx frame rate [1/s]:   1000   1000   1000
        Rx rate [KByte/s]:     58.6   69.3   80.1
        Common:
        Tx frames:   922981
        Tx bytes:    78044260
        Rx frames:   922980
        Rx bytes:    78044200
        Lost frames: 0
        Tx frame rate [1/s]:   1000   1000   1000
        Tx rate [KByte/s]:     58.6   69.3   80.1
        Rx frame rate [1/s]:   1000   1000   1000
        Rx rate [KByte/s]:     58.6   69.3   80.1
        Loss rate [1/s]:          0      0      0
        Frame loss [%]:         0.0    0.0    0.0
    Distributed clocks:
        Reference clock:   Slave 0
        DC reference time: 0
        Application time:  0
                        2000-01-01 00:00:00.000000000
    
  4. 使用 ethercat 命令行工具查看从站状态及设备名。

    [root@sylixos:/]# ethercat slaves
    0  0:0  PREOP  +  SV660_1Axis_00913
    

步骤 9:查看运行效果

进入“/apps/app_igh_user”目录,输入“./app_igh_user”命令运行程序。当程序正常运行,周期打印状态时,可以看到电机正在匀速同向转动。

注意:

如果电机未正常转动,请确认连接的电机设备 PID、VID 是否和代码中设置的一致。 程序运行后,终端会通过“vendor_id:0x1xxxxx,product_code:0xxxxx”打印信息显示实际连接电机的 PID、VID。用户需暂停并退出当前运行程序,将真实的电机 PID 和 VID 配置到 步骤 5 第 2 步的“VID_PID”宏参。之后,再次将工程重新编译、上传,执行运行命令。

[root@sylixos:/]# cd /apps/app_igh_user
[root@sylixos:/apps/app_igh_user]# ./app_igh_user
Bus scan successful - 1 slaves.
slave[0]-- vendor_id:0x100000, product_code:0xc010d  
EtherCAT WARNING 0: 2 datagrams UNMATCHED!
Activating master succes!
status_word:0 mode:0 actual_pos:0 actual_speed 0 error_code:0 actual_torque:0
Cycle Thread Started.
dc ref time 59153390712788 
start run! Move step 30000
1 slave(s).
AL states: 0x02.
Link is up.
First master diff: 24759.
AL states: 0x01.
AL states: 0x02.
Domain1_output: WC 1.
Domain1_output: State 2.
AL states: 0x04.
Domain1_input: WC 1.
Domain1_input: State 2.
AL states: 0x08.
status_word:0 mode:0 actual_pos:0 actual_speed 0 error_code:0 actual_torque:0
status_word:0 mode:0 actual_pos:0 actual_speed 0 error_code:0 actual_torque:0
reset done !
status_word:1637 mode:8 actual_pos:1272238852 actual_speed 1408000 error_code:0 actual_torque:-1
status_word:1637 mode:8 actual_pos:1275071660 actual_speed 1472000 error_code:0 actual_torque:-1
status_word:1637 mode:8 actual_pos:1277902500 actual_speed 1472000 error_code:0 actual_torque:-1
文档内容是否对您有所帮助?
有帮助
没帮助