一、实验前的准备工作
1.SSC软件
2.EtherCat从站开发板(淘宝-元杞科技-Ethercat从站开发板)STM32-LAN9252 通过SPI连接
3.电脑安装TWINCAT3
4.XMLNotepad 、XMLSpy
二、实验目的
- 从站向主站发送ADC1的值和ADC2的值(为了偷懒,ADC2=ADC1的值+1)
- 从站向主站发送Key1的值和Key2的值(Key1&Key2是uint16的变量,可以自定义值,并不是真实的按钮抬起或按下的状态)
- 主站向从站发送3个变量值 Voltage_control、Current_control、Power_control。
三、实验步骤
1.使用SSC文件生成对象字典
我在实验过程中发现——每次实验仅修改对象字典内容和更新xml文件即可,底层驱动和协议栈内容无需更改。所以,选择el9800模板即可,其他无需更改。

模板的保存-打开excel表格的步骤省略,表格如下:
我只填写了两种PDO ----> RXPDO & TXPDO
在填写时我遇到了两个问题:
问:可以使只使用0x6000一个Index吗?
答:可以但不合理,如果有不同类型参数,建议使用多个index来区分,index的累加值为0x10,例如,0x6000、0x6010等(参照ETG5001)。
问:我观察到很多例子中在DataType栏填写BOOLEAN添加了补位,我的理解是:3个boolbool类型不满16bit,所以要手动添加补位用的参数,直到16bit吗?
答:对象字典内容是需要按照2字节对齐的,常用的方法是定义uint16_t,不用的bit位预留,或者用BIT(X)【例:BIT2】来补齐,具体定义可以看ETG2000里面的对象字典数据类型)
2.生成对象字典并导入
在这一步,SSC会生成很多原文件,由于我们是在例程的基础上做修改,所以只需要关注生成的对象字典文件------>“SSC-DeviceObjects.h”如果有名字不同,那就找XXXXXXObjects.h这样的文件。
让我们打开这个文件,并同一时间打开例程的el9800.h文件。
根据el9800.h文件中的内容格式将SSC-DeviceObjects.h的内容复制到相应位置。
第一部分:结构体
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT32 SI1; /* Subindex1 - Reference to 0x7010.1 */
UINT32 SI2; /* Subindex2 - Reference to 0x7010.2 */
UINT32 SI3; /* Subindex3 - Reference to 0x7010.3 */
} OBJ_STRUCT_PACKED_END
TOBJ1601;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT32 SI1; /* Subindex1 - Reference to 0x6000.1 */
UINT32 SI2; /* Subindex2 - Reference to 0x6000.2 */
} OBJ_STRUCT_PACKED_END
TOBJ1A00;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT32 SI1; /* Subindex1 - Reference to 0x6020.1 */
UINT32 SI2; /* Subindex2 - Reference to 0x6020.2 */
} OBJ_STRUCT_PACKED_END
TOBJ1A02;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0; /**< \brief Subindex 0 */
UINT16 aEntries[1]; /**< \brief Subindex 1 - 1 */
} OBJ_STRUCT_PACKED_END
TOBJ1C12;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0; /**< \brief Subindex 0 */
UINT16 aEntries[2]; /**< \brief Subindex 1 - 2 */
} OBJ_STRUCT_PACKED_END
TOBJ1C13;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT16 Key1_value; /* Subindex1 - key1_value */
UINT16 Key2_value; /* Subindex2 - key2_value */
} OBJ_STRUCT_PACKED_END
TOBJ6000;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT16 Adc1_value; /* Subindex1 - adc1_value */
UINT16 Adc2_value; /* Subindex2 - adc2_value */
} OBJ_STRUCT_PACKED_END
TOBJ6020;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0;
UINT16 Voltage_control; /* Subindex1 - Voltage_control */
UINT16 Current_control; /* Subindex2 - Current_control */
UINT16 Power_control; /* Subindex3 - Power_control */
} OBJ_STRUCT_PACKED_END
TOBJ7010;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0; /**< \brief SubIndex0*/
UINT16 u16Moduleindexdistance; /**< \brief Module Index distance
*
* Index distance between two modules (maximum number of objects per module and area)<br>
* Default: 0x10*/
UINT16 u16Maximumnumberofmodules; /**< \brief Maximum number of modules*/
} OBJ_STRUCT_PACKED_END
TOBJF000;
typedef struct OBJ_STRUCT_PACKED_START {
UINT16 u16SubIndex0; /**< \brief SubIndex0*/
UINT32 aEntries[3]; /**< \brief Module profile information buffer
*
* Bit 0..15: Profile number of the module on position 1<br>
* Bit 16..31: Profile specific*/
} OBJ_STRUCT_PACKED_END
TOBJF010;
第二部分定义extern
#ifdef _EVALBOARD_
#define PROTO
#else
#define PROTO extern
#endif
第三部分定义index=0x1c12和index=0x1c13 共用的 Entry Description(入口描述)
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOAssignEntryDesc[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READ }};
#endif
第四部分 定义index=1601 的入口描述和aName,并定义TOBJ1601 结构体类型的sDORxPDOMap
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1601[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }, /* Subindex1 - Reference to 0x7010.1 */
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }, /* Subindex2 - Reference to 0x7010.2 */
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }}; /* Subindex3 - Reference to 0x7010.3 */
OBJCONST UCHAR OBJMEM aName0x1601[] = "Control_Para process data mapping\000"
"SubIndex 001\000"
"SubIndex 002\000"
"SubIndex 003\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ1601 sDORxPDOMap
#ifdef _EVALBOARD_
={3,0x70100110,0x70100210,0x70100310}
#endif
;
第五部分 定义index=1A00 的入口描述和aName,并定义TOBJ1A00结构体类型的sDITxPDOMap
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1A00[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }, /* Subindex1 - Reference to 0x6000.1 */
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }}; /* Subindex2 - Reference to 0x6000.2 */
OBJCONST UCHAR OBJMEM aName0x1A00[] = "KeyStatus process data mapping\000"
"SubIndex 001\000"
"SubIndex 002\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ1A00 sDITxPDOMap
#ifdef _EVALBOARD_
={2,0x60000110,0x60000210}
#endif
;
第六部分 定义index=1A02 的入口描述和aName,并定义TOBJ1A00结构体类型的sAITxPDOMap
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1A02[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }, /* Subindex1 - Reference to 0x6020.1 */
{ DEFTYPE_UNSIGNED32 , 0x20 , ACCESS_READ }}; /* Subindex2 - Reference to 0x6020.2 */
OBJCONST UCHAR OBJMEM aName0x1A02[] = "AdcValue process data mapping\000"
"SubIndex 001\000"
"SubIndex 002\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ1A02 sAITxPDOMap
#ifdef _EVALBOARD_
={2,0x60200110,0x60200210}
#endif
;
第七部分 定义index=1c12 和 index=1c13的入口描述和aName,并定义TOBJ1C12,TOBJ1C13 结构体类型的sRxPDOassign,sTxPDOassign
#ifdef _OBJD_
OBJCONST UCHAR OBJMEM aName0x1C12[] = "SyncManager 2 assignment\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ1C12 sRxPDOassign
#ifdef _EVALBOARD_
= {0x01, {0x1601}}
#endif
;
#ifdef _OBJD_
OBJCONST UCHAR OBJMEM aName0x1C13[] = "SyncManager 3 assignment\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ1C13 sTxPDOassign
#ifdef _EVALBOARD_
= {0x02, {0x1A00, 0x1A02}}
#endif
;
第八部分 定义index=6000 的入口描述和aName,并定义TOBJ6000结构体类型的sDIInputs
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x6000[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_TXPDOMAPPING }, /* Subindex1 - key1_value */
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_TXPDOMAPPING }}; /* Subindex2 - key2_value */
OBJCONST UCHAR OBJMEM aName0x6000[] = "KeyStatus\000"
"key1_value\000"
"key2_value\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ6000 sDIInputs
#ifdef _EVALBOARD_
={2,0,0}
#endif
;
第九部分 定义index=6020 的入口描述和aName,并定义TOBJ6020结构体类型的sAIInputs
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x6020[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_TXPDOMAPPING }, /* Subindex1 - adc1_value */
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_TXPDOMAPPING }}; /* Subindex2 - adc2_value */
OBJCONST UCHAR OBJMEM aName0x6020[] = "AdcValue\000"
"adc1_value\000"
"adc2_value\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ6020 sAIInputs
#ifdef _EVALBOARD_
={2,0,0}
#endif
;
第十部分 定义index=7010 的入口描述和aName,并定义TOBJ7010结构体类型的sDOOutputs
#ifdef _OBJD_
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x7010[] = {
{ DEFTYPE_UNSIGNED8 , 0x8 , ACCESS_READ },
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING }, /* Subindex1 - Voltage_control */
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING }, /* Subindex2 - Current_control */
{ DEFTYPE_UNSIGNED16 , 0x10 , ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING }}; /* Subindex3 - Power_control */
OBJCONST UCHAR OBJMEM aName0x7010[] = "Control_Para\000"
"Voltage_control\000"
"Current_control\000"
"Power_control\000\377";
#endif //#ifdef _OBJD_
PROTO TOBJ7010 sDOOutputs
#ifdef _EVALBOARD_
={3,0,0,0}
#endif
;
最后一分部,更改对象字典表
TOBJECT OBJMEM ApplicationObjDic[] = {
/* Enum 0x0800 */
// {NULL,NULL, 0x0800, {DEFTYPE_ENUM, 0x02 | (OBJCODE_REC << 8)}, asEntryDesc0x0800, 0, apEnum0800 },
/* Object 0x1601 */
{NULL,NULL, 0x1601, {DEFTYPE_PDOMAPPING, 3 | (OBJCODE_REC << 8)}, asEntryDesc0x1601, aName0x1601, &sDORxPDOMap, NULL, NULL, 0x0000 },
/* Object 0x1802 */
// {NULL,NULL, 0x1802, {DEFTYPE_RECORD, 9 | (OBJCODE_REC << 8)}, asEntryDesc0x1802, aName0x1802,&TxPDO1802Subindex0, ReadObject0x1802, NULL, 0x0000 },
/* Object 0x1A00 */
{NULL,NULL, 0x1A00, {DEFTYPE_PDOMAPPING, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x1A00, aName0x1A00, &sDITxPDOMap, NULL, NULL, 0x0000 },
/* Object 0x1A02 */
{NULL,NULL, 0x1A02, {DEFTYPE_PDOMAPPING, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x1A02, aName0x1A02, &sAITxPDOMap, NULL, NULL, 0x0000 },
/* Object 0x1C12 */
{NULL,NULL, 0x1C12, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)}, asPDOAssignEntryDesc, aName0x1C12, &sRxPDOassign, NULL, NULL, 0x0000 },
/* Object 0x1C13 */
{NULL,NULL, 0x1C13, {DEFTYPE_UNSIGNED16, 2 | (OBJCODE_ARR << 8)}, asPDOAssignEntryDesc, aName0x1C13, &sTxPDOassign, NULL, NULL, 0x0000 },
/* Object 0x6000 */
{NULL,NULL, 0x6000, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x6000, aName0x6000, &sDIInputs, NULL, NULL, 0x0000 },
/* Object 0x6020 */
{NULL,NULL, 0x6020, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x6020, aName0x6020, &sAIInputs, NULL, NULL, 0x0000 },
/* Object 0x7010 */
{NULL,NULL, 0x7010, {DEFTYPE_RECORD, 3 | (OBJCODE_REC << 8)}, asEntryDesc0x7010, aName0x7010, &sDOOutputs, NULL, NULL, 0x0000 },
/* Object 0x8020 */
// {NULL,NULL, 0x8020, {DEFTYPE_RECORD, 20 | (OBJCODE_REC << 8)}, asEntryDesc0x8020, aName0x8020, &sAISettings, NULL, NULL, 0x0008 },
/* Object 0xF000 */
// {NULL,NULL, 0xF000, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0xF000, aName0xF000, &sModulardeviceprofile, NULL, NULL, 0x0000 },
/* Object 0xF010 */
// {NULL,NULL, 0xF010, {DEFTYPE_UNSIGNED32, 3 | (OBJCODE_ARR << 8)}, asEntryDesc0xF010, aName0xF010, &sModulelist, NULL, NULL, 0x0000 },
{NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL}};
#endif //#ifdef _OBJD_
3.修改4个函数
void APPL_InputMapping(UINT16* pData)//将MCU本地的要发送的数据拷贝到LAN9252
void APPL_OutputMapping(UINT16* pData)//将lan9252收到的数据拷贝到MCU本地
void APPL_Application(void) //业务函数
APPL_StopOutputHandler()// 输出数值归零、输出使能关闭。
修改这四个函数
请看下文
void APPL_Application(void)
{
#if _STM32_IO4
UINT16 analogValue;
#endif
sAIInputs.Adc1_value = adcx;
sAIInputs.Adc2_value = adcx+1;
}
void APPL_OutputMapping(UINT16* pData)
{
UINT16 j = 0;
UINT16 *pTmpData = (UINT16 *)pData;
/* we go through all entries of the RxPDO Assign object to get the assigned RxPDOs */
for (j = 0; j < sRxPDOassign.u16SubIndex0; j++)
{
switch (sRxPDOassign.aEntries[j])
{
/* RxPDO 2 */
case 0x1601:
((UINT16 *) &sDOOutputs)[1] = SWAPWORD(*pTmpData++);
((UINT16 *) &sDOOutputs)[2] = SWAPWORD(*pTmpData++);
((UINT16 *) &sDOOutputs)[3] = SWAPWORD(*pTmpData++);
break;
}
}
}
void APPL_InputMapping(UINT16* pData)
{
UINT16 j = 0;
UINT16 *pTmpData = (UINT16 *)pData;
/* we go through all entries of the TxPDO Assign object to get the assigned TxPDOs */
for (j = 0; j < sTxPDOassign.u16SubIndex0; j++)
{
switch (sTxPDOassign.aEntries[j])
{
/* TxPDO 1 */
case 0x1A00:
*pTmpData++ = SWAPWORD(((UINT16 *) &sDIInputs)[1]);
*pTmpData++ = SWAPWORD(((UINT16 *) &sDIInputs)[2]);
break;
/* TxPDO 3 */
case 0x1A02:
*pTmpData++ = SWAPWORD(((UINT16 *) &sAIInputs)[1]);
*pTmpData++ = SWAPWORD(((UINT16 *) &sAIInputs)[2]);
break;
}
}
}
UINT16 APPL_StopOutputHandler(void)
{
sDOOutputs.Current_control = 0;
sDOOutputs.Power_control = 0;
sDOOutputs.Voltage_control = 0;
sAIInputs.Adc2_value = 0;
sAIInputs.Adc1_value = 0;
sDIInputs.Key1_value = 0;
sDIInputs.Key2_value = 0;
return ALSTATUSCODE_NOERROR;
}
4.注意要不要修改SSC生成的XML文件中E2PROM项目ConfigData字段
查询资料,图中的E2PROMconfigdata是例程中的。
链接: 资料

打开主站,烧录新的xml,扫描从站,成功。

四、高级玩法,实验心得
1.ssc可以导入LAN9252的模板,可以登录microchip搜索LAN9252 Ethercat SDK

2.利用下面的图,来判断你的某个变量应该填写在0x6xxx区域还是0x7xxx区域。
3.ssc表格中的数据类型都是什么意思?
RECORD:结构体
VAR:变量
ARRAY:数组
4.定义结构体时,结构体里都有一个subIndex0 这个是用来表示有多少个其他变量(这里有3个)。

index1601映射的对象是index7010,如下。1601结构体中的SI1、SI2、SI3是用来存放subindex入口地址的。比如定义sDORxPDOMap时,内容如下。
0x701000110(代表index=0x7010,subindex=001,长度等于0x10bit->也就是16bit)
PROTO TOBJ1601 sDORxPDOMap
#ifdef _EVALBOARD_
={3,0x70100110,0x70100210,0x70100310}
#endif
;
index7010里面存放的才是真正的变量名称。但是也有一个subindex0表示有三个变量

以上,移植就成功了,如果有其他失败的地方,xml文件一般不会有问题,问题会现在从站,根据例程一步一步做修改,不要全部将el9800.h删个干净再把SSC-DeviceObjects.h内容一股脑复制进去,而是根据例程el9800.h中的结构内容分批复制。没有用上的结构体,入口描述,aName就保留着,然后将最后面的对象字典中没有用上的都注释掉,这样比较稳妥。
博主也是ethercat新人,ethercat协议的原理还在进一步研究中。
感谢你阅读到这里,觉得文章写的不错,请点赞加收藏吧!再见!
项目源码丢在这里了,https://download.csdn.net/download/Armind/90086321
本文是作者的经验总结,免费与大家分享,禁止任何形式转载!!!

2149

被折叠的 条评论
为什么被折叠?



