EtherCAT学习之路——对象字典

首发与知乎

1.对象字典

CANopen 对象字典(OD: Object Dictionary)是 CANopen 协议最为核心的概念。所谓的对象字典就是一个有序的对象组,描述了对应 CANopen 节点的所有参数,包括通讯数据的存放位置也列入其索引,这个表变成可以传递形式就叫做 EDS 文件(电子数据文档Electronic Data Sheet)。对象字典,就像体检表,具备这个人每个功能的参数,便于用人单位(主站)进行合理分配工作。

这部分引用自周立功的《CANopen轻松入门》,对于CANopen协议不清楚的地方,可以看看这本书,加强理解。

图1-1 对象字典与体检表标题

对象字典中的每个对象都描述了它的功能、名字、索引、子索引、数据类型,以及这个对象是否必需、读写属性等等。下表为通用通讯对象的举例:

标图1-2 通用通讯对象题

每个对象采用一个 16 位的索引值来寻址,这个索引值通常被称为索引,其范围在 0x0000到 0xFFFF 之间。为了避免数据大量时无索引可分配,所以在某些索引下也定义了一个 8 位的索引值,这个索引值通常被称为子索引,其范围是 0x00 到 0xFF 之间。子索引可以进一步描述对象的参数和功能等信息。

标题图1-3 PDO对象

总结来说,对象字典即是一张表,表中记录了各种各样的对象,这些对象可以是int32,bool这种通用数据类型,也可以是自定义的结构体数据类型。对象字典内使用Index来对对象进行索引,对象中还可以存在Sub-Index进一步描述对象的参数和功能。

1.1 CoE(CANopen over EtherCAT)

EtherCAT协议在应用层支持CANopen协议,并作了相应的扩充。CoE协议完全遵从CANopen协议,其对对象字典的定义也相同,如表1-3所示。表1-4列出了CoE通讯数据对象。其中针对EtherCAT通讯扩展了相关通讯对象0x1C00~0x1C4F,用于设置存储同步管理器的类型,通讯参数和PDO数据分配。

标图1-4 CoE对象字典定义题

 

图1-5 CoE通讯对象定义标题

对于CoE对象字典,这里我们先不做具体的介绍,在后面的章节中会具体根据代码来具体说明。

2.EtherCAT Slave Information中的对象字典描述

2.1 EtherCAT Slave Information

在EtherCAT Slave Information(ESI)用于描述从站参数及特性,使用xml的形式进行描述。EtherCAT配置工具通过ESI文件生成EtherCAT Network Information(ENI)。

For each EtherCAT Slave a device description, the so called EtherCAT Slave Information (ESI) has tobe delivered. This is done in form of an XML file (eXtensible Markup Language). It describes EtherCAT specific as well as application specific features of the slave.The ESI file is used by an EtherCAT configuration tool to generate the EtherCAT Network Information(ENI).

事实上,目前我也还没弄明白ESI具体细节,特别是从站具体是如何使用ESI的还不是很清楚。不过,只要知道ESI中包含哪些内容,与EtherCAT协议是怎样的一种对应关系,做从站开发也足够了。ESI的具体内容,我会在其他的文章中介绍,这里就详细讲一下ESI中的对象字典。

ESI是用xml进行描述的,在例程中可以找到如AX58100-UC16-R1.xml的文件,用xmlspy打开如下图。

图2-1 对象字典标题

xmlspy将xml文件按照列表的形式进行排列,这里是ESI中Dictionary部分,也即是ESI中的对象字典描述。ESI中将对象字典分成了DataTypes和Objects两个部分,其中,Objects中描述了对象,DataTypes描述了对象的数据类型。

2.2 ESI中的对象

我所使用的例程中,共有23个对象,每一行即是一个对象的描述。包括对象的索引,名称,数据类型,位宽,还有一个Info域和Flags域。

图2-2 对象字典标题

Info域中包含了对象的子项,如对象0x7010就由9个子项构成,Sub-Index000和LED1~8。其中,Sub-Index000是对象中子索引的数目,0x7010中有LED1~8,所有其Sub-Index000就等于8。每一个有子项的对象的第一个子项都是Sub-Index000,且都表示该对象子索引的数目。

Flags域描述对象的读写限制。

2.3 对象的数据类型

对象的数据类型是多种多样的,可以是通用类型UDINT(即uint32),也可以是结构体类型。BIT2,BOOL,DINT这些数据类型都是在ETG.1020中定义的;DT1601,DT1C12等则是根据CoE协议进行定义的;DT6000,DT7010则是一些用户自定义类型。

图2-3 对象字典数据类型标题

可以看到0x7010这个对象的子项和其数据类型的子项是一一对应起来的,都是包含了一个Sub-Index000和LED1~8。同时数据类型的子项中进一步描述了对象子项的子索引,名称,类型,位宽,比特偏移等信息。

3.Slave Stack Code中的对象字典

Slave Stack Code(SSC)是倍福提供的从站协议栈代码,SSC的具体内容会在另一篇文章中介绍,这里具体关注SSC中的对象字典。

3.1 对象列表

/**
 *\brief EL9800 Application specific object dictionary
 * 
 */
TOBJECT    OBJMEM ApplicationObjDic[] = {
   /* Enum 0x0800 */
   {NULL,NULL, 0x0800, {DEFTYPE_ENUM, 0x02 | (OBJCODE_REC << 8)}, asEntryDesc0x0800, 0, apEnum0800 },
   /* Object 0x1601 */
   {NULL,NULL,  0x1601, {DEFTYPE_PDOMAPPING, 10 | (OBJCODE_REC << 8)}, asEntryDesc0x1601, aName0x1601, &sDORxPDOMap, NULL, NULL, 0x0000 },
    /* Object 0x1C12 */
   {NULL,NULL,   0x1C12, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)}, asPDOAssignEntryDesc, aName0x1C12, &sRxPDOassign, NULL, NULL, 0x0000 },
   /* Object 0x7010 */
   {NULL,NULL,   0x7010, {DEFTYPE_RECORD, 16 | (OBJCODE_REC << 8)}, asEntryDesc0x7010, aName0x7010, &sDOOutputs, NULL, NULL, 0x0000 },
   {NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL}};
#endif    //#ifdef _OBJD_
 

上面是我使用的例程中部分的对象定义,可以看到包含了对象的Index(如0x7010),对象的描述(如asEntryDesc0x7010),对象的名称(如aName0x7010),对象的实例(如sDOOutputs)。

需要注意的一点是,这里的ApplicationObjDic并不是对象字典,ApplicationObjDic只是简单的把对象罗列出来。因为各种对象的数据类型是不相同的,所以,无法简单地以数组的形式来实现对象字典,在SSC中使用链表的形式来实现对象字典。准确来讲ApplicationObjDic中的每一行并不是一个对象,而是一个对象字典入口(Object Dictionary Entry),除了对象本身的描述之外,还包含了一些链表指针。不过,为了简单理解,可以先暂时把ApplicationObjDic理解成对象字典。

3.2对象字典Entry

下面是对象字典Entry的结构体定义:

/**
 * \brief Object dictionary entry structure
 */
typedef struct OBJ_ENTRY
{
    struct OBJ_ENTRY                      *pPrev; /**< \brief Previous entry(object) in the object dictionary list*/
    struct OBJ_ENTRY                      *pNext; /**< \brief Next entry(object) in the object dictionary list*/

    UINT16                                Index; /**< \brief Object index*/
   TSDOINFOOBJDESC                       ObjDesc; /**< \brief Object access, type and code*/
   OBJCONST TSDOINFOENTRYDESC OBJMEM     *pEntryDesc; /**< \brief pointer to object entry descriptions*/
   OBJCONST UCHAR OBJMEM                 *pName; /**< \brief Pointer to object and entry names*/
   void MBXMEM                           *pVarPtr; /**< \brief Pointer to object buffer*/
   UINT8 (* Read)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to read function (if NULL default read function will be used)*/
   UINT8 (* Write)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to write function (if NULL default write function will be used)*/
   UINT16                                 NonVolatileOffset; /**< \brief Offset within the non volatile memory (need to be defined for backup objects)*/
}
TOBJECT;
  • *pPrev和*pNext是指向前链和后链的指针,通过*Prev和*pNext即可将各个对象链接起来,组成链表。

  • Index即是对象索引;

  • objDesc包含了“对象的数据类型”;

  • *pEntryDesc是对象entry的描述,包含了“entry的数据类型和读写限制”;

  • *pName是对象名称;

  • *pVarPtr是对象的实例。

objDesc中英文释义是object access,type and code。这里的object type与ESI中对象数据类型不是相同的东西。ESI中的对象数据类型是在*pEntryDesc中描述的。

3.3 对象数据类型(pEntryDesc)

下面以对象0x7010来介绍对象Entry描述:

/**
 * \brief Object 0x7010 (Digital output object) entry descriptions
 *
 * SubIndex 0 : read only<br>
 * SubIndex x : (One description for each led) read only and RxPdo mappable<br>
 *  (x > 0)
*/
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC    OBJMEM asEntryDesc0x7010[] = {
   {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, /* Subindex 000 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 001: LED 1 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 002: LED 2 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 003: LED 3 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 004: LED 4 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 005: LED 5 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 006: LED 6 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 007: LED 7 */
   {DEFTYPE_BOOLEAN, 0x01, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* SubIndex 008: LED 8 */
   {0x0000, 0x08, 0}
   }; /* Subindex 008 for align */

其中,第一列是数据类型,第二列是位宽,第三列是读写限制。

标图3-1 对象7010数据类型题

对象Entry描述即是ESI中的对象数据类型。

3.4对象实例(pVarPtr)

对象数据类型中描述了各个子项的数据类型,位宽,读写限制,还需要一个对象的实例来存储各子项的值。

/** \brief 0x7010 (Digital output object) data structure*/
typedef struct OBJ_STRUCT_PACKED_START {
   UINT16   u16SubIndex0; /**< \brief SubIndex 0*/
   BOOLEAN(bLED1); /**< \brief LED 1*/
   BOOLEAN(bLED2); /**< \brief LED 2*/
   BOOLEAN(bLED3); /**< \brief LED 3*/
   BOOLEAN(bLED4); /**< \brief LED 4*/
   BOOLEAN(bLED5); /**< \brief LED 5*/
   BOOLEAN(bLED6); /**< \brief LED 6*/
  BOOLEAN(bLED7); /**< \brief LED 7*/
  BOOLEAN(bLED8); /**< \brief LED 8*/
  ALIGN8(SubIndex008) /**< \brief 8Bit alignment*/
} OBJ_STRUCT_PACKED_END
TOBJ7010;

PROTO TOBJ7010 sDOOutputs
#ifdef _EVALBOARD_
= {8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0}
#endif
;

TOBJ7010定义了对象0x7010的数据结构,而sDOOutputs则是其实例。可以把这里的TOBJ7010视为ESI中的对象子项描述。

原本以为写博客是一件很简单的事情,亲自动手以后才发现这么耗时间。计划每天1小时,每周写2篇博客的,谁知道1周也写不完一篇。动手写的时候,才发现其实还有不少的内容自己没有理清楚,需要重新整理;还有不少的细节记不清,需要重新查证。因为个人的精力实在有限,所以,有些地方(比如ESI文件的调用)自己也还没完全搞清楚就先写出来了,不足之处望请谅解。

  • 27
    点赞
  • 130
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值