IgH EhterCat主站 EC Master 使用心得:SII,ENI和SM踩坑记录

声明一下:本文假设你对EtherCat有理解和对IgH有使用经验,遇到问题的时候来看最合适。假如你是初学者,类似帖子有很多的,也可以找ChatGPT对话学习效果更好。

2013年,我首次接触EtherCat通讯。初碰实时Linux+Xenomai2(Ubuntu10.04)的时候,有很多东西要做,根本没经历研究IgH的主站,直接选择acontis收费主站。

2017年,我与北京acontis总代的技术支持聊了一下才知道,原来以第一个带时钟的从站作为DC时钟时,acontis会新开一个实时线程来作通讯。这对于更低的通讯周期的要求的我来说,是很麻烦的事情。于是我转过头来奔向IgH。

此时我已经使用了Ubuntu14.04.5+Linux3.18.20,这个内核版本是Xenomai官方patch支持Linux3.x的最后一个版本。别问我为什么不选Linux3.14,问就是Uubntu14.04.5在Linux3.14下不稳定。而IgH的网卡驱动最接近这个内核版本的的就是Linux3.14。直接暴力手动patch移植intel网卡驱动。运气很好没怎么踩坑,经过无数次强制重启直到我开始怀疑我的硬盘还行不行的时候,我成功了 。这个时候我飘了,决定把我的Y430P笔记本电脑网卡的也移植一下。结果失败了,我猜想原因是出现在硬件联络层。这个时候我才想起来当初acontis技术说过的:事实上,Realtek网卡效果没有Intel网卡效果好的。后来我看了这两家驱动代码才有点明白,大概就是网卡厂商开放的功能是不同的,Intel的内核驱动代码量比Realtek多很多。这或许对实时数据收发是有益处的。

用过IgH的人都知道,IgH只需要配置从站的Process Data(SM2和SM3)就够了。其他配置直接从EEPROM读取的。做EtherCat产品嘛,TwinCat肯定是标杆,我就像模像样的写了一个解析acontis的ENI中的SM2和SM3的PDO部分配置的工具(其他的不用我管,不改源代码我也管不了),用来配置IgH。除了显得我们很专业,也为了防止用户自己换其他品牌驱动后出现生产或事故问题。

本以为事情就这样愉快的结束了。我可以一直陪甘雨老婆逛提瓦特大陆了。然而一年后,同事说:我们采购了一家I/O板卡,IgH切不到OP。What?Master代码我一共没看几行,直接凌乱了,甘雨老婆都不香了。我开始阅读和调试ioctl.c,master.c和slave.c。是的,你没有看错,我没读fsm*.c,我就是这么的无知。问题已经被我找到了,IgH读EEPROM的SII来配置SM0~SM3,但是没读到,或读到了错误的SII(这点我一直不理解,为什么SII和ESI会不一样),当时我定位到了slave部分(最外围嘛),SII不对,所以MailBox失败,COE失败,SM1到SM3就都没有配置。之前说了,我只配置了SM2和SM3。没有SM0和SM1的时候,IgH就把SM2和SM3配置到了SM0和SM1。而IgH是写死的,SM0和SM1走MailBox ,SM3和SM4走Process Date,通讯肯定有问题的。

既然如此,我从ENI读一下SM0和SM1,在ioctl.c加一个函数传递配置,slave.c加一个函数配置sync数组,搞定。甘雨老婆我来了!

    //伪代码,照这个生成SII数据
    uint8_t data[8 * EC_MAX_SYNC_MANAGERS] = {0}; 
    size_t size = sii_mb_sync_count * 8 + sii_pd_sync_count * 8;

    for(j = 0; j < sii_mb_sync_count; j++){  
        EC_WRITE_U16(data + j*8 + 0, sii_mb_sync[j].physical_start_address);
        EC_WRITE_U16(data + j*8 + 2, sii_mb_sync[j].default_length);
        EC_WRITE_U8(data  + j*8 + 4, sii_mb_sync[j].control_bytes);
        EC_WRITE_U8(data  + j*8 + 6, sii_mb_sync[j].enable);
    }
    for(k = 0; k < sii_pd_sync_count; k++){  
        EC_WRITE_U16(data + (j+k)*8 + 0, sii_pd_sync[k].physical_start_address);
        EC_WRITE_U16(data + (j+k)*8 + 2, sii_pd_sync[k].default_length);
        EC_WRITE_U8(data  + (j+k)*8 + 4, sii_pd_sync[k].control_bytes);
        EC_WRITE_U8(data  + (j+k)*8 + 6, sii_pd_sync[k].enable);
    }
            
    ecrt_master_set_sii_sync(master, Index, mailbox_protocols, data, size);
  
// lib/master.C下添加
int ecrt_master_set_sii_sync(ec_master_t *master, uint16_t slave_position, uint16_t mailbox_protocols,
        uint8_t *data, size_t size)
{
    ec_ioctl_slave_sii_sync_t sii;
    int ret;
    sii.mailbox_protocols = mailbox_protocols;
    sii.slave_position = slave_position;
    sii.size = size;
    sii.data = data;

    ret = ioctl(master->fd, EC_IOCTL_SLAVE_INIT_SII_SYNC, &sii);
    if (EC_IOCTL_IS_ERROR(ret)) {
        fprintf(stderr, "Failed to execute SDO download: %s\n",
                strerror(EC_IOCTL_ERRNO(ret)));
        return -EC_IOCTL_ERRNO(ret);
    }
    return 0;
}
//ioctl.h
typedef struct {
    // inputs
    uint16_t slave_position;
    uint16_t mailbox_protocols; /**< Supported mailbox protocols. */
    uint8_t *data; /**< Category data. */
    size_t size; /**< Number of bytes. */
} ec_ioctl_slave_sii_sync_t;
#define EC_IOCTL_SLAVE_INIT_SII_SYNC   EC_IOW(0x5a, ec_ioctl_slave_sii_sync_t)
//ioctl.c中添加,记得在EC_IOCTL中i调用这个函数
/*****************************************************************************/
static ATTRIBUTES int ec_ioctl_slave_init_sii_sync(
        ec_master_t *master, /**< EtherCAT master. */
        void *arg, /**< Userspace address to store the results. */
        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
        )
{
    //ec_slave_config_t *sc;
    ec_slave_t *slave;
    ec_ioctl_slave_sii_sync_t data;
    uint8_t *sii_data = NULL;
    int ret;

    if (unlikely(!ctx->requested))
        return -EPERM;

    if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
        return -EFAULT;

    if (!data.size)
        return -EINVAL;

    if (!(sii_data = kmalloc(data.size, GFP_KERNEL))) {
        return -ENOMEM;
    }

    if (copy_from_user(sii_data, (void __user *) data.data, data.size)) {
        kfree(sii_data);
        return -EFAULT;
    }

    if (down_interruptible(&master->master_sem)) {
        kfree(sii_data);
        return -EINTR;
    }

    if (!(slave = ec_master_find_slave(
                    master, 0, data.slave_position))) {
        up(&master->master_sem);
        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
                data.slave_position);
        kfree(sii_data);
        return -EINVAL;
    }
    /*
    if (!(sc = ec_master_get_config(master, data.slave_position))) {
        up(&master->master_sem);
        EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
                data.slave_position);
        kfree(sii_data);       
        return -ENOENT;
    }
    
    sc->use_eni_sii = true;
    sc->mailbox_protocols = data.mailbox_protocols;
    sc->sii_syncs_data = kmalloc(data.size, GFP_KERNEL);
    memcpy(sc->sii_syncs_data, sii_data, data.size);
    sc->sii_syncs_size = data.size;
    */
    //ec_slave_clear_sync_managers(slave);
    slave->sii.mailbox_protocols = data.mailbox_protocols;
    if(0 != (ret = ec_slave_init_sii_syncs(slave, sii_data, data.size))){
        EC_MASTER_ERR(master, "Slave %u fetch_sii_syncs Failed!\n",
                data.slave_position);
    }
    up(&master->master_sem); /** \todo sc could be invalidated */
    kfree(sii_data);
    return 0;
}
//slave.c中添加
int ec_slave_init_sii_syncs(
        ec_slave_t *slave, /**< EtherCAT slave. */
        const uint8_t *data, /**< Category data. */
        size_t data_size /**< Number of bytes. */
        )
{
    unsigned int i, count, total_count;
    ec_sync_t *sync;
    size_t memsize;
    ec_sync_t *syncs;
    uint8_t index;

    // one sync manager struct is 4 words long
    if (data_size % 8) {
        EC_SLAVE_ERR(slave, "Invalid SII sync manager category size %zu.\n",
                data_size);
        return -EINVAL;
    }

    count = data_size / 8;
    if (count) {
        slave->sii.sync_count = 0;

        total_count = count + slave->sii.sync_count;
        if (total_count > EC_MAX_SYNC_MANAGERS) {
            EC_SLAVE_ERR(slave, "Exceeded maximum number of"
                    " sync managers!\n");
            return -EOVERFLOW;
        }
        memsize = sizeof(ec_sync_t) * total_count;
        if (!(syncs = kmalloc(memsize, GFP_KERNEL))) {
            EC_SLAVE_ERR(slave, "Failed to allocate %zu bytes"
                    " for sync managers.\n", memsize);
            return -ENOMEM;
        }

        for (i = 0; i < slave->sii.sync_count; i++)
            ec_sync_init_copy(syncs + i, slave->sii.syncs + i);

        // initialize new sync managers
        for (i = 0; i < count; i++, data += 8) {
            index = i + slave->sii.sync_count;
            sync = &syncs[index];

            ec_sync_init(sync, slave);
            sync->physical_start_address = EC_READ_U16(data);
            sync->default_length = EC_READ_U16(data + 2);
            sync->control_register = EC_READ_U8(data + 4);
            sync->enable = EC_READ_U8(data + 6);
            EC_SLAVE_DBG(slave, 1, "index = %d, start_addr = %d, def_len = %d, control_reg = %d, enable = %d\n",
                index, sync->physical_start_address, sync->default_length, sync->control_register, sync->enable);
        }

        if (slave->sii.syncs)
            kfree(slave->sii.syncs);
        slave->sii.syncs = syncs;
        slave->sii.sync_count = total_count;
    }

    return 0;
}

又过差不多一年,同事说,客户要求,加工过程中,从站要经常断电,从新上电以后切不到OP!要知道,不管是机器人还是机床,基本都是要么一起通电要么一起断电的。所以这个真没考虑过。这回直接查到fsm*.c了, 当网络拓扑结构发生变化,slaves被从新扫描,我之前配置的slave部分丢了。断电以后没有人去配置slave的sync部分了,因此切不到OP。一番debug后,最新的配置方式是把SM信息写到master结构的slave中,在fsm阶段替换slave的SII中的sync部分,这样SM部分彻底告别SII了。SM的部分我想基本完事了吧。

    //伪代码,照这个生成SII数据
    uint8_t data[8 * EC_MAX_SYNC_MANAGERS] = {0}; 
    size_t size = sii_mb_sync_count * 8 + sii_pd_sync_count * 8;

    for(j = 0; j < sii_mb_sync_count; j++){  
        EC_WRITE_U16(data + j*8 + 0, sii_mb_sync[j].physical_start_address);
        EC_WRITE_U16(data + j*8 + 2, sii_mb_sync[j].default_length);
        EC_WRITE_U8(data  + j*8 + 4, sii_mb_sync[j].control_bytes);
        EC_WRITE_U8(data  + j*8 + 6, sii_mb_sync[j].enable);
    }
    for(k = 0; k < sii_pd_sync_count; k++){  
        EC_WRITE_U16(data + (j+k)*8 + 0, sii_pd_sync[k].physical_start_address);
        EC_WRITE_U16(data + (j+k)*8 + 2, sii_pd_sync[k].default_length);
        EC_WRITE_U8(data  + (j+k)*8 + 4, sii_pd_sync[k].control_bytes);
        EC_WRITE_U8(data  + (j+k)*8 + 6, sii_pd_sync[k].enable);
    }
              
    ecrt_slave_config_sii_sync(slave_config, mailbox_protocols, data, size);
// lib/slave_config.c
/*****************************************************************************/
int ecrt_slave_config_sii_sync(ec_slave_config_t *sc, uint16_t mailbox_protocols,
        uint8_t *data, size_t size)
{
    ec_ioctl_config_sii_sync_t sii;
    int ret;
    sii.config_index = sc->index;
    sii.mailbox_protocols = mailbox_protocols;
    sii.size = size;
    sii.data = data;

    ret = ioctl(sc->master->fd, EC_IOCTL_SC_INIT_SII_SYNC, &sii);
    if (EC_IOCTL_IS_ERROR(ret)) {
        fprintf(stderr, "Failed to execute SDO download: %s\n",
                strerror(EC_IOCTL_ERRNO(ret)));
        return -EC_IOCTL_ERRNO(ret);
    }
    return 0;
}
//ioctl.h
typedef struct {
    // inputs
    uint32_t config_index;
    uint16_t mailbox_protocols; /**< Supported mailbox protocols. */
    uint8_t *data; /**< Category data. */
    size_t size; /**< Number of bytes. */
} ec_ioctl_config_sii_sync_t;
#define EC_IOCTL_SC_INIT_SII_SYNC      EC_IOW(0x5b, ec_ioctl_config_sii_sync_t)

//ioctl.c中添加,记得在EC_IOCTL中i调用这个函数
/*****************************************************************************/

static ATTRIBUTES int ec_ioctl_config_init_sii_sync(
        ec_master_t *master, /**< EtherCAT master. */
        void *arg, /**< ioctl() argument. */
        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
        )
{
    ec_ioctl_config_sii_sync_t data;
    ec_slave_config_t *sc;
    uint8_t *sii_data = NULL;

    if (unlikely(!ctx->requested))
        return -EPERM;

    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
        return -EFAULT;
    }
    if (!data.size)
        return -EINVAL;

    if (!(sii_data = kmalloc(data.size, GFP_KERNEL))) {
        return -ENOMEM;
    }

    if (copy_from_user(sii_data, (void __user *) data.data, data.size)) {
        kfree(sii_data);
        return -EFAULT;
    }

    if (down_interruptible(&master->master_sem)) {
        kfree(sii_data);
        return -EINTR;
    }

    if (!(sc = ec_master_get_config(
                    master, data.config_index))) {
        up(&master->master_sem);
        EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
                data.config_index);
        kfree(sii_data);
        return -EINVAL;
    }

    sc->use_eni_sii = true;
    sc->mailbox_protocols = data.mailbox_protocols;
    sc->sii_syncs_data = kmalloc(data.size, GFP_KERNEL);
    memcpy(sc->sii_syncs_data, sii_data, data.size);
    sc->sii_syncs_size = data.size;

    up(&master->master_sem); /** \todo sc could be invalidated */
    kfree(sii_data);

    return 0;
}
//fsm_slave_config.c 中的函数,自己对比修改一下 #if 1 部分
void ec_fsm_slave_config_enter_mbox_sync(
        ec_fsm_slave_config_t *fsm /**< slave state machine */
        )
{
    ec_slave_t *slave = fsm->slave;
    ec_datagram_t *datagram = fsm->datagram;
#if 1
    ec_slave_config_t *sc = slave->config;
    ec_master_t *master = slave->master;
#endif
    unsigned int i;

    // slave is now in INIT
    if (slave->current_state == slave->requested_state) {
        fsm->state = ec_fsm_slave_config_state_end; // successful
        EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
        return;
    }
#if 1
    if(master->active && sc && sc->use_eni_sii){
        if(0 != (ec_slave_init_sii_syncs(slave, sc->sii_syncs_data, sc->sii_syncs_size)) ){
            EC_SLAVE_ERR(slave, "init_sii_syncs Failed!\n");
        }
        slave->sii.mailbox_protocols = sc->mailbox_protocols;
    }
#endif
    if (!slave->sii.mailbox_protocols) {
        // no mailbox protocols supported
        EC_SLAVE_DBG(slave, 1, "Slave does not support"
                " mailbox communication.\n");
#ifdef EC_SII_ASSIGN
        ec_fsm_slave_config_enter_assign_pdi(fsm);
#else
        ec_fsm_slave_config_enter_boot_preop(fsm);
#endif
        return;
    }

    EC_SLAVE_DBG(slave, 1, "Configuring mailbox sync managers...\n");

    if (slave->requested_state == EC_SLAVE_STATE_BOOT) {
        ec_sync_t sync;

        ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
                EC_SYNC_PAGE_SIZE * 2);
        ec_datagram_zero(datagram);

        ec_sync_init(&sync, slave);
        sync.physical_start_address = slave->sii.boot_rx_mailbox_offset;
        sync.control_register = 0x26;
        sync.enable = 1;
        ec_sync_page(&sync, 0, slave->sii.boot_rx_mailbox_size,
                EC_DIR_INVALID, // use default direction
                0, // no PDO xfer
                datagram->data);
        slave->configured_rx_mailbox_offset =
            slave->sii.boot_rx_mailbox_offset;
        slave->configured_rx_mailbox_size =
            slave->sii.boot_rx_mailbox_size;

        ec_sync_init(&sync, slave);
        sync.physical_start_address = slave->sii.boot_tx_mailbox_offset;
        sync.control_register = 0x22;
        sync.enable = 1;
        ec_sync_page(&sync, 1, slave->sii.boot_tx_mailbox_size,
                EC_DIR_INVALID, // use default direction
                0, // no PDO xfer
                datagram->data + EC_SYNC_PAGE_SIZE);
        slave->configured_tx_mailbox_offset =
            slave->sii.boot_tx_mailbox_offset;
        slave->configured_tx_mailbox_size =
            slave->sii.boot_tx_mailbox_size;

    } else if (slave->sii.sync_count >= 2) { // mailbox configuration provided
        ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
                EC_SYNC_PAGE_SIZE * slave->sii.sync_count);
        ec_datagram_zero(datagram);

        for (i = 0; i < 2; i++) {
            ec_sync_page(&slave->sii.syncs[i], i,
                    slave->sii.syncs[i].default_length,
                    NULL, // use default sync manager configuration
                    0, // no PDO xfer
                    datagram->data + EC_SYNC_PAGE_SIZE * i);
        }

        slave->configured_rx_mailbox_offset =
            slave->sii.syncs[0].physical_start_address;
        slave->configured_rx_mailbox_size =
            slave->sii.syncs[0].default_length;
        slave->configured_tx_mailbox_offset =
            slave->sii.syncs[1].physical_start_address;
        slave->configured_tx_mailbox_size =
            slave->sii.syncs[1].default_length;
    } else { // no mailbox sync manager configurations provided
        ec_sync_t sync;

        EC_SLAVE_DBG(slave, 1, "Slave does not provide"
                " mailbox sync manager configurations.\n");

        ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
                EC_SYNC_PAGE_SIZE * 2);
        ec_datagram_zero(datagram);

        ec_sync_init(&sync, slave);
        sync.physical_start_address = slave->sii.std_rx_mailbox_offset;
        sync.control_register = 0x26;
        sync.enable = 1;
        ec_sync_page(&sync, 0, slave->sii.std_rx_mailbox_size,
                NULL, // use default sync manager configuration
                0, // no PDO xfer
                datagram->data);
        slave->configured_rx_mailbox_offset =
            slave->sii.std_rx_mailbox_offset;
        slave->configured_rx_mailbox_size =
            slave->sii.std_rx_mailbox_size;

        ec_sync_init(&sync, slave);
        sync.physical_start_address = slave->sii.std_tx_mailbox_offset;
        sync.control_register = 0x22;
        sync.enable = 1;
        ec_sync_page(&sync, 1, slave->sii.std_tx_mailbox_size,
                NULL, // use default sync manager configuration
                0, // no PDO xfer
                datagram->data + EC_SYNC_PAGE_SIZE);
        slave->configured_tx_mailbox_offset =
            slave->sii.std_tx_mailbox_offset;
        slave->configured_tx_mailbox_size =
            slave->sii.std_tx_mailbox_size;
    }

    fsm->take_time = 1;

    fsm->retries = EC_FSM_RETRIES;
    fsm->state = ec_fsm_slave_config_state_mbox_sync;
}

总结一句:ENI很重要,ENI很重要,ENI很重要。

唠叨一句:大多数Slave厂商用TwinCat配ESI跑测试,他才不管从站的SII对不对呢。

之前工作有接触到Ethercat 并学习实践了IghMaster库,现在不做这方面工作了把代码开源出来,希望对大家有用。 代码使用Source Insight 3.5编辑的,用其它编辑器可能会出现乱码。 压缩包中包含了 1.基于Ighmaster 1.5.2驱动Sanyo,泰科伺服电机做力矩模式运动的代码。包含电机使能/禁止使能,设置运行模式,控制模式,发送力矩控制命令,读取力矩反馈等功能。基于SDO方式实现的代码。 2. 基于Ighmaster 1.5.2 驱动Et100 Io板代码,基于PDO,SDO方式的Io控制及温湿度读取 3. 测试例子包括了介于原生驱动接口ioctl方式控制码基于libethercat库的驱动代码。具体见源代码。 文档目录结构及简要说明 0.Ethercat调试记录.txt 自己调试Ethercat过程中碰到的问题及解决办法 1. libethercat\std 目录 编译Igh生成的ethecat库 2. libethercat\include Igh Master Ethercat库包含的头文件 3. scripts/ 自动生成从站信息的相关脚本文件,要生成从站信息,将该文件夹复制到开发板运行GenerateSlavesConfig.sh。 默认生成ec_common_configs_define.hec_common_configs_define.c文件,这两个文件会在libethercat\ec_common\ecat_common.c,libethercat\ec_common\ecat_common.h相关接口使用。 4. libethercat\ec_common 4.0 ec_common_configs_define.h ec_common_configs_define.c 由脚本scripts/GenerateSlavesConfig.sh自动生成的从站信息,包括从站PDO,SDO设置等,更具体的可以参照scripts/README.txt 4.1 ecat_common.h ecat_common.c 基于ibethercat\std 中的库的二次封装库,简化了PDO,SDO等操作 4.2 ecat_common_intermediate_interface.h ecat_common_intermediate_interface.c 与具体厂商相关的接口库,基于ecat_common.c中接口的实现,不同设备修改这两个文件中的接口进行适配。 目前该文件实现的山羊电机接口。主要实现的PDO接口的电机状态查询,上电开机,关机,操作模式设置,力矩设置等接口,不同电机的类似接口可以参照该文件实现,接口作用见名知意。 封装接口的关键是对象字典的操作,根据手册设置对象字典即可,标准的Ethercat接口伺服电机一般来说对象字典定义基本是一样的,可能稍微有差别 5. 标准Igh Master接口的使用例子 mytest目录 mytest/test_torque_sanyo_ioctl 基于ioctl接口的sanyo电机测试例子 mytest/test_torque_tec_ioctl 基于ioctl接口的泰科电机电机测试例子 mytest/test_torque_tec_lib 基于libethercat\std库接口的电机测试例子 mytest/test_torque2 基于libethercat\std库接口的电机测试例子2 mytest/test_io_board ET1100,IO板的测试例子 基于标准的Igh 库及iotcl接口可以参照该文件夹例子 6.二次封装接口库ecat_common_intermediate_interface.c的使用说明 使用步骤 (1)将编译生成的Igh库文件替换libethercat\std文件, 文件名可能要改成,或者不该也行,自己写Makefile时匹配库名称就行 (2)将 scripts/ 目录复制到开发运行GenerateSlavesConfig.sh脚本,将脚本生成的ec_common_configs_define.h ec_common_configs_define.h 复制到libethercat\ec_common 文件夹 (3)基于二次封装接口的电机一般操作参照demo.c 参照demo.c中的ethercat_init() 进行从站初始化 初始化后就可以调用ecat_common_intermediate_interface.c中接口对从站进行操作 比如 interpolation_2_ecat_set_slave_pwr_on()接口使能电机, interpolation_2_ecat_set_slave_pwr_off() 关闭电机 interpolation_2_ecat_set_slave_target_pos() 设置目标位置,对应位置模式操作 其它接口作用见参照具体实现 7.关于轴操作的几点说明 (1)每个轴对应一个从站,由alias,position确定,一般来说从站不多时alias=0固定不变,对不同轴根据positon确定。 例如使能关闭不同的轴 int interpolation_2_ecat_set_slave_pwr_on(MasterSpecifiedInfo_T *master_specified_info, int slave_pos); int interpolation_2_ecat_set_slave_pwr_off(MasterSpecifiedInfo_T *master_specified_info, int slave_pos); 中slave_pos参数就对应不同的轴,slave_pos=0,axis1 slave_pos=1,axis2... (2)设置不同轴的操作模式,位置,力矩,速度模式 可以参照这个接口 int interpolation_2_ecat_set_slave_operation_model(MasterSpecifiedInfo_T *master_specified_info, int slave_pos, unsigned char operation_model) 代码已经托管在云上,可以直接通过git下载: https://gitee.com/wllw7176/MyEthercat-IGH-1.5.2.git
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱上思念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值