欢迎来到老马的领地~ 这是“压风式散热底座”发明者的个人网站:) 本人QQ:80524554,用户群1:562279766
SOEM - Simple Open EtherCAT Master Library,一个开源的EtherCAT主站库.

以前只是使用基于EtherCAT的硬件来干活,这次也因为干活的因素,需要自己写主站,就买了个开发板,结果里面例程中是移植了它.

摸了一段时间了,大概性地总结一下这个库与从站的通讯流程吧,也许不对,但能工作,毕竟我这是初学lol.gif

初始化什么的就不说了,基本固定的代码,例程都是能跑通的.

重点是[如何配置从站,并成功通讯上,]我查到的资料说是要根据从站的设备描述文件,一个XML来定,这个没错,但这个XML怎么看,怎么用,找到的资料挺乱,所以自己总结并记录下.

这里第一个关键点就是PO2SOconfig回调,这个回调中主站会向从站发送配置项,以决定:

从站[将要收到什么样的数据 - output结构], 这是主站output给从站的数据块.

从站[将会发出什么样的数据 - input结构],这是主站在从站那里input回来的数据块.



可以注意到这里的input/output都是以[主站]的角度来说的.

PO2SOconfig回调中,调用ec_SDOwrite()来把对应的数据块描述写到从站,从站就知道[主站要什么],以及[给主站回复什么].


当这个回调完成后,就来到了第二个关键点:input/output结构.

这个结构,需要与上面的回调中写入的描述内容一致.

搞定这两点后,就是第三个关键点了:

定期调用ec_send_processdata()与ec_receive_processdata().


至此,已经能与从站通讯上了.

在PO2SOconfig回调里,还一个关键点,是第四个关键点,那就是[ec_SDOwrite()写入什么数据,向哪里写入.]

我这里给出一个自己总结出来的内容,不一定对,但在我手里的两个驱动器上是可以正常工作的.

这两个从站,一是步科的FD144S,伺服;二是东莞爱唯的EEDO-06-80,开环步进驱动器.

从两者的XML文件中来看,RxPdo都是0x1600索引下,TxPdo都是在0x1A00下.别的驱动器未知,我这里就这两个lol.giflol.gif

http://www.m5home.com/blog//uploadfiles/1_10239.png


至于这两者分配到哪,就要看SM2与SM3了,两个驱动器也一样,都是0x1C12与0x1C13:

http://www.m5home.com/blog//uploadfiles/2_13066.png


因此,ec_SDOwrite要先在0x1C12下写入RxPdo数量,我这里只有1组,就是0x1600,于是写[1].

然后写[0x1600].再在0x1600下面写我需要输出给从站的PDO对象,我这里只用了[控制字ControlWord-0x6040]与[目标位置TargetPosition-0x607A].

TxPdo组也是一样,1组,不过PDO对象多了几个:

状态字 StatusWord-0x6041
操作模式 ModesofOperation-0x6061
实际位置 PositionActualValue-0x6064
错误码 LastErrorCode-0x603F

所以最终的结构如下图:

http://www.m5home.com/blog//uploadfiles/3_87464.png


代码如下:

/// @brief EEDO-06-80, Man: 0000004b ID: 00000641 Rev: 00000001
/// @param slave 
/// @return 
int PO2SOconfig_EEDO_06_80(uint16 slave)
{
    int retval, iRet, i;
    uint8 u8val;
    uint16 u16val, _index;
    uint32 u32val;

    // 0x1c12 = SM2, 0x1600 = RxPDO, 从站的接收内容配置
    uint16 map_1c12[2] = {0x0001, 0x1600};
    // RxPDO组下面的PDO对象
    uint32 map_1600[3] = {0x0002,     //数量与下面的PDO对象数量相等
        0x60400010,     // Controlword       //0x6040 = 索引, 00 = 子索引, 10 = 16BIT
        0x607a0020,     // Target position
    };

    // 0x1c13 = SM3, 0x1A00 = TxPDO, 从站的发送内容配置
    uint16 map_1c13[2] = {0x0001, 0x1a00};
    // TxPDO组下面的PDO对象
    uint32 map_1a00[5] = {0x0004,      //数量与下面的PDO对象数量相等
        0x60410010,     // Statusword
        0x60610008,     // Modes of operation display
        0x60640020,     // Position actual value
        0x603f0010,     // Error Code
    };

    retval = 0;
    iRet = 0;

        // *********************************** 0x1c12 = RxPDO分配
    u8val = 0;
    retval += ec_SDOwrite(slave, 0x1c12, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);
    u16val = 0x1600;
    retval += ec_SDOwrite(slave, 0x1c12, 0x01, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTRXM);
    u8val = 1;
    retval += ec_SDOwrite(slave, 0x1c12, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);

    u8val = 0;
    _index = 0x1600;
    retval += ec_SDOwrite(slave, _index, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);

    for (i = 1; i <= map_1600[0]; i++)
    {
        u32val = map_1600[i];
        u8val = i;
        iRet = ec_SDOwrite(slave, _index, i, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTRXM);
        retval += iRet;
        printf("    >ec_SDOwrite(0x%X) = %d.\r\n", u32val, iRet);
    }    
    retval += ec_SDOwrite(slave, _index, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);
        // *********************************** 0x1c12 = RxPDO分配 End

        // *********************************** 0x1c13 = TxPDO分配
    u8val = 0;
    retval += ec_SDOwrite(slave, 0x1c13, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);
    u16val = 0x1a00;
    retval += ec_SDOwrite(slave, 0x1c13, 0x01, FALSE, sizeof(u16val), &u16val, EC_TIMEOUTRXM);
    u8val = 1;
    retval += ec_SDOwrite(slave, 0x1c13, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);

    u8val = 0;
    _index = 0x1A00;
    retval += ec_SDOwrite(slave, _index, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);

    for (i = 1; i <= map_1a00[0]; i++)
    {
        u32val = map_1a00[i];
        u8val = i;
        iRet = ec_SDOwrite(slave, _index, i, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTRXM);
        retval += iRet;
        printf("    >ec_SDOwrite(0x%X) = %d.\r\n", u32val, iRet);
    }    
    retval += ec_SDOwrite(slave, _index, 0x00, FALSE, sizeof(u8val), &u8val, EC_TIMEOUTRXM);
        // *********************************** 0x1c13 = TxPDO分配 End

    // 以下是对驱动器其它参数的初始化
    // 每转脉冲数
    u32val = 6400;
    retval += ec_SDOwrite(slave, 0x6092, 0x01, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTSAFE);

    // 梯形速度
    u32val = 500;
    retval += ec_SDOwrite(slave, 0x6081, 0x00, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTSAFE);
    // Accel
    u32val = 100;
    retval += ec_SDOwrite(slave, 0x6083, 0x00, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTSAFE);
    // Decel
    u32val = 100;
    retval += ec_SDOwrite(slave, 0x6084, 0x00, FALSE, sizeof(u32val), &u32val, EC_TIMEOUTSAFE);

    while(EcatError) printf("%s", ec_elist2string());

    printf("EEDO-06-80 slave %d set, retval = %d\n", slave, retval);
    return 1;
}


要添加更多的PDO对象,只需要修改map_1600[]与map_1a00[]数组就好.

而对应上面代码中两个数组内定义好的RxPDO与TxPDO对象组的结构如下:

// ************************************************* EEDO-06-80
// 0x60400010,     // Controlword
// 0x607a0020,     // Target position
typedef struct
{
    uint16      ControlWord;                //0x6040
    int32       TargetPos;                //0x607A
} __attribute__((packed)) PDO_Output_EEDO;

// 0x60410010,     // Statusword
// 0x60610008,     // Modes of operation display
// 0x60640020,     // Position actual value
// 0x603f0010,     // Error Code
typedef struct
{
    uint16      StatusWord;
    uint8       CurrentMode;
    int32       CurrentPos;
    uint16      ErrorCode;
} __attribute__((packed)) PDO_Input_EEDO;
// ************************************************* EEDO-06-80 End


再定义两个上面结构的指针变量:

PDO_Output_EEDO *outputs_EEDO;
PDO_Input_EEDO *inputs_EEDO;


这样就完成了从站的配置,并且也能与从站通讯了.

所以实际的通讯流程就是:


向outputs_EEDO->xxxxxx写入数据
从inputs_EEDO->xxxxxx读取数据


上面所有的铺垫,都是为了这两位师傅能正常运货smilie66.png

再结合驱动器的说明书,里面有讲都支持什么运动模式,如何进入这些模式,要设置些啥值(还是利用ec_SDOwrite()),基本就行了.

记录一下,免得以后自己忘了.

PS:

还是要吐槽一下.

这个项目本来控制系统是用一个支持脉冲电机的PLC完成的,是做好了的,直接就可以工作的.

项目中使用了一个伺服电机来开关舱门,并在开关过程中控制扭矩,以防夹人,此电机仅有这作用.

本来我寻思着,加个扭矩传感器,做成恒扭矩闭环控制就好,这多简单是吧?但是----------------

合作伙伴的项目负责人[听供应商说脉冲控制的电机已经过时了,都不怎么生产了,价格与总线电机差不多,不如用总线电机],于是就想当然地在这[只有一个电机的项目中使用了一台EtherCAT总线电机].

最终导致我这边必须把整套控制方案重做,要么换成支持EtherCAT的PLC,要么换成对应的板子.

哼哼,我也是有私心的,既然你们要听供应商的来折腾我,还不听劝(我两个月前就劝过了,[不听不听我不听,供应商说总线电机好]),那我就报了一个比较长的周期,然后选了自己开发EtherCAT主站的方案,让公司买了开发板,我自己也顺便学习学习不是?smilie56.pngsmilie56.png

http://www.m5home.com/blog//uploadfiles/6af97736175d6cf2b9b8f3341b48dc38_99211.jpg


SO,以上.
添加评论

昵称 *

E-mail