以STM32F407+LAN9252为基础移植EtherCat从站试验记录

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

一、实验前的准备工作

1.SSC软件
2.EtherCat从站开发板(淘宝-元杞科技-Ethercat从站开发板)STM32-LAN9252 通过SPI连接
3.电脑安装TWINCAT3
4.XMLNotepad 、XMLSpy

二、实验目的

  1. 从站向主站发送ADC1的值和ADC2的值(为了偷懒,ADC2=ADC1的值+1)
  2. 从站向主站发送Key1的值和Key2的值(Key1&Key2是uint16的变量,可以自定义值,并不是真实的按钮抬起或按下的状态)
  3. 主站向从站发送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

本文是作者的经验总结,免费与大家分享,禁止任何形式转载!!!

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值