6. 应用程序
本章概述默认(示例)应用程序、应用程序接口,并提供自定义应用程序开发的入门指南。
6.1 应用程序设置
SSC 包含多个(示例)应用程序,可用于主站 / 从站测试或作为应用程序开发的基础。相关设置列于表 3 "应用程序相关设置"(位于 ecat_def.h 或 SSC 工具中)。
表 3:应用程序相关设置
| 定义 | 描述 |
|---|---|
| TEST_APPLICATION | 支持几乎所有 SSC 功能,可强制特定应用程序行为(见第 11 章)。注:不建议将此应用程序作为开发基础 |
| EL9800_APPLICATION | 基于 EL9800 EtherCAT 评估板的应用程序,包含 8 (4) 个 LED、8 (4) 个开关、16 位模拟输入 |
| CiA402_DEVICE | CiA402 驱动协议的示例实现,支持 2 个模块化轴,详见第 10 章 |
| SAMPLE_APPLICATION | 硬件无关应用程序,适用于目标平台无对应 SSC 工具配置的场景 |
| SAMPLE_APPLICATION_INTERFACE | 适用于 Win32 的示例应用程序,用于创建动态链接库 |
6.2 SSC 函数
这些函数由通用协议栈提供,需从应用程序层调用。
| 函数原型 | 参数 | 返回值 | 描述 |
|---|---|---|---|
| UINT16 MainInit(void) | 无 | 0:初始化成功;>0:初始化出错 | 初始化通用从站协议栈,需在平台(含操作系统和 ESC)就绪后调用 |
| void MainLoop(void) | 无 | 无 | 处理低优先级功能(如 EtherCAT 状态机、邮箱协议),未启用同步时还会处理应用程序。需由应用程序周期性调用 |
| void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode) | alStatus:请求的 AL 状态;alStatusCode:AL 状态码(非 0 时设置错误标志) | 无 | 应用程序发生错误或需完成挂起的状态转换时调用,错误解除后需再次调用。注:不允许请求高于当前状态的状态 |
6.3 接口函数
6.3.1 通用接口函数
| 函数原型 | 参数 | 返回值 | 描述 |
|---|---|---|---|
| void APPL_Application(void) | 无 | 无 | 启用同步时由同步 ISR 调用,未启用同步时由主循环调用 |
| UINT16 APPL_GetDeviceID (void) | 无 | 写入 AL 状态码寄存器的显式设备 ID | 主站请求显式设备 ID 时调用,仅当从站支持显式设备 ID 处理(EXPLICIT_DEVICE_ID)时需要 |
6.3.2 EtherCAT 状态机
每个 ESM 函数返回 16 位值,反映状态转换结果:
- 0:转换成功
- 0xFF:状态转换挂起(应用程序需调用 ECAT_StateChange 完成)
- 其他值:转换失败原因,有效返回码列表见 [2]
| 函数原型 | 参数 | 返回值 | 描述 |
|---|---|---|---|
| UINT16 APPL_StartMailboxHandler(void) | 无 | 见通用 ESM 返回码说明 | 状态从 INIT 转换到 PREOP 或 INIT 转换到 BOOT 时调用 |
| UINT16 APPL_StopMailboxHandler(void) | 无 | 见通用 ESM 返回码说明 | 状态从 PREOP 转换到 INIT 或 BOOT 转换到 INIT 时调用 |
| UINT16 APPL_StartInputHandler (UINT16 *pIntMask) | pIntMask:寄存器 0x204(AL 事件掩码)的值 | 见通用 ESM 返回码说明 | 状态从 PREOP 转换到 SAFEOP 时调用(即使无输入过程数据) |
| UINT16 APPL_StopInputHandler (void) | 无 | 见通用 ESM 返回码说明 | 状态从 SAFEOP 转换到 PREOP 时调用(即使无输入过程数据) |
| UINT16 APPL_StartOutputHandler (void) | 无 | 见通用 ESM 返回码说明 | 状态从 SAFEOP 转换到 OP 时调用(即使无输出过程数据) |
| UINT16 APPL_StopOutputHandler (void) | 无 | 见通用 ESM 返回码说明 | 状态从 OP 转换到 SAFEOP 时调用(即使无输出过程数据) |
| UINT16 APPL_GenerateMapping (UINT16 *pInputSize, UINT16 *pOutputSize) | pInputSize:输入过程数据大小(从站→主站);pOutputSize:输出过程数据大小(主站→从站) | 见通用 ESM 返回码说明 | 主站请求从 PREOP 转换到 SAFEOP 时调用,需计算过程数据的字节数,用于校验同步管理器设置和通用过程数据处理 |
| Void APPL_AckErrorInd(UINT16 stateTrans) | stateTrans:当前状态转换 | 无 | 主站确认错误时调用 |
6.3.3 过程数据处理
| 函数原型 | 参数 | 返回值 | 描述 |
|---|---|---|---|
| void APPL_InputMapping(UINT16 *pData) | pData:指向输入过程数据的指针 | 无 | 应用程序调用后执行,将输入过程数据映射到通用协议栈(通用协议栈会将数据复制到 SM 缓冲区) |
| void APPL_OutputMapping(UINT16 *pData) | pData:指向输出过程数据的指针 | 无 | 应用程序调用前执行,用于获取输出过程数据 |
6.4 接口变量
| 名称 | 类型 | 描述 |
|---|---|---|
| ApplicationObjDic | TOBJECT 结构数组(见 7.4 结构定义) | 仅当从站支持 CoE 时需要,定义在应用程序头文件中,包含应用程序特定对象,数组最后一个元素的索引为 0xFFFF |
| pEEPROM | UINT8 * | 指向 EEPROM 缓冲区的指针,仅当启用 EEPROM 仿真(ESC_EEPROM_EMULATION = 1)时需要。定义在 ecatappl.h 中,需由应用程序在启动时(调用 MainInit () 前)设置,缓冲区大小由 ESC_EEPROM_SIZE 定义(默认 2048 字节) |
6.5 创建应用程序
用户特定应用程序可从零开发,或基于现有示例应用程序修改(推荐后者)。
默认 SSC 示例应用程序见本章开头,其他厂商可能提供额外示例,可通过手动添加、应用程序厂商的补丁文件或 SSC 工具添加到从站项目,详见 6.5.1。
特殊配置说明
- 无邮箱支持:建议复杂 EtherCAT 从站至少支持 CoE 邮箱协议;若需禁用邮箱处理,需将所有协议设置设为 0(AOE_SUPPORTED、COE_SUPPORTED、EOE_SUPPORTED、FOE_SUPPORTED、SOE_SUPPRTED、VOE_SUPPORTED)。注:即使无邮箱支持,同步管理器 0 和 1 也需禁用并保留给邮箱通信,移除 SM 需适配 SSC。
- 仅输入 / 输出设备:创建仅输入 / 输出的 EtherCAT 从站时,需将 MAX_PD_INPUT_SIZE 或 MAX_PD_OUTPUT_SIZE 设为 0,否则需设为最大过程数据大小。注:未使用的过程数据同步管理器需禁用,移除 SM 需适配 SSC。
6.5.1 新增应用程序
- 手动添加:禁用所有默认示例应用程序(见 6.1),在 coeappl.c、ecatappl.c、ecatslv.c 文件中(参考 "APPLICATION_FILE" 注释)包含应用程序头文件(含函数定义),从零创建或修改现有 ESI 文件。
- 通过补丁文件添加:参考应用程序厂商说明,ESI 文件通常随应用程序源代码提供。
- 通过 SSC 工具添加:创建新项目,在弹出对话框中选择应用程序;若应用程序未列出,可导入对应的 SSC 工具配置文件(见 12.6);无配置文件时需联系应用程序厂商。SSC 工具会生成应用程序特定的源代码文件和对应的 ESI 文件。
6.5.2 示例
6.5.2.1 示例应用程序过程数据增强
本示例基于 SSC 5.01 版本,说明如何为默认示例应用程序(SAMPLE_APPLICATION)添加新过程数据。
原有过程数据
- 32 位输入计数器(0x6000)
- 32 位输出计数器(0x7010):输出计数器为 0 时,输入计数器每个应用周期加 1;否则输入计数器 = 输出计数器 + 1
新增后过程数据
- 32 位输入计数器(0x6000):每个应用周期自增 1
- 32 位结果(0x6010):0x7010.1 与 0x7010.2 之和
- 输出值(0x7010):包含两个 32 位值(SI1:Value1、SI2:Value2)
适配步骤
-
文件:sampleappl.h为 "32 位结果" 过程数据创建条目描述、对象名称和变量:
/* 对象 0x6010:结果对象 */ #ifdef _OBJD_ /* 条目描述 */ OBJCONST TSDOINFOENTRYDESC OBJMEM EntryDesc0x6010 = {DEFTYPE_UNSIGNED32, 0x10, ACCESS_READ | OBJACCESS_TXPDOMAPPING}; /* 对象名称 */ OBJCONST UCHAR OBJMEM aName0x6010[] = "Result"; #endif // #ifdef _OBJD_ /* 处理对象数据的变量 */ PROTO UINT32 ResultObj6010; -
文件:sampleappl.h修改现有对象 0x7010(32 位输出计数器)为记录对象,支持多个过程数据:
/* 对象 0x7010:输出值对象 */ #ifdef _OBJD_ OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x7010[] = { /* 子索引 0 的条目描述 */ {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, /* 第一个条目"Value1"的描述 */ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ | OBJACCESS_RXPDOMAPPING}, /* 第二个条目"Value2"的描述 */ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ | OBJACCESS_RXPDOMAPPING}}; /* 对象及条目的名称(每个子名称以"\000"结尾,整体以"\377"结尾) */ OBJCONST UCHAR OBJMEM aName0x7010[] = "Output values\000First value\000Second value\000\377"; #endif // #ifdef _OBJD_ /* 处理对象数据的结构 */ typedef struct OBJ_STRUCT_PACKED_START { UINT16 u16SubIndex0; UINT32 Value1; UINT32 Value2; } OBJ_STRUCT_PACKED_END TOBJ7010; PROTO TOBJ7010 OutputData #ifdef _SAMPLE_APPLICATION_ = {0x02, 0x00, 0x00} // 子索引 0 设为 2(表示 2 个条目) #endif -
文件:sampleappl.h更新 PDO 映射对象中的新过程数据信息(RxPDO 0x1601,处理输出过程数据):
/************************************************************************** * 对象 0x1601:RxPDO **************************************************************************/ #ifdef _OBJD_ OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1601[] = { {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ}, {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* 新过程数据的引用(描述不涉及过程数据本身类型,即使是 UNSIGNED8 也填 UNSIGNED32) */ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}}; /* 仅描述对象名称,条目自动命名为"SubIndex 000"(依次递增) */ OBJCONST UCHAR OBJMEM aName0x1601[] = "RxPDO-Map\000\377"; #endif // #ifdef _OBJD_ typedef struct OBJ_STRUCT_PACKED_START { UINT16 u16SubIndex0; /* 新增数组元素,用于引用新过程数据 */ UINT32 aEntries[2]; } OBJ_STRUCT_PACKED_END TOBJ1601; PROTO TOBJ1601 RxPDOMap #ifdef _SAMPLE_APPLICATION_ = {0x02, {0x70100120, 0x70100220}} // 引用对象 0x7010 子索引 1 和 2,32 位长度 #endif -
文件:sampleappl.h更新 PDO 映射对象中的新过程数据信息(TxPDO 0x1A00,处理输入过程数据):
/************************************************************************** * 对象 0x1A00:TxPDO **************************************************************************/ #ifdef _OBJD_ OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1A00[] = { {DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ}, {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* 新过程数据的引用(描述不涉及过程数据本身类型) */ {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}}; /* 仅描述对象名称,条目自动命名为"SubIndex 000"(依次递增) */ OBJCONST UCHAR OBJMEM aName0x1A00[] = "TxPDO-Map\000\377"; #endif // #ifdef _OBJD_ typedef struct OBJ_STRUCT_PACKED_START { UINT16 u16SubIndex0; /* 新增数组元素,用于引用新过程数据 */ UINT32 aEntries[2]; } OBJ_STRUCT_PACKED_END TOBJ1A00; PROTO TOBJ1A00 TxPDOMap #ifdef _SAMPLE_APPLICATION_ = {0x02, {0x60000020, 0x60100020}} // 引用对象 0x6000 子索引 0 和 0x6010 子索引 0,32 位长度 #endif -
文件:sampleappl.h更新对象字典中的引用:
TOBJECT OBJMEM ApplicationObjDic[] = { /* 对象 0x1601 */ {NULL, NULL, 0x1601, {DEFTYPE_PDOMAPPING, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x1601, aName0x1601, &RxPDOMap, NULL, NULL, 0x0000}, /* 对象 0x1A00 */ {NULL, NULL, 0x1A00, {DEFTYPE_PDOMAPPING, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x1A00, aName0x1A00, &TxPDOMap, NULL, NULL, 0x0000}, /* 对象 0x6010 */ {NULL, NULL, 0x6010, {DEFTYPE_UNSIGNED32, 0 | (OBJCODE_VAR << 8)}, &EntryDesc0x6010, aName0x6010, &ResultObj6010, NULL, NULL, 0x0000}, /* 对象 0x7010 */ {NULL, NULL, 0x7010, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x7010, aName0x7010, &OutputData, NULL, NULL, 0x0000}, {NULL, NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x0000}}; -
文件:sampleappl.c更新预期过程数据大小(需与 ESI 文件信息一致,否则从站从 PreOP 转换到 SafeOP 时会报错 0x1E 或 0x1D):
UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize) { /* 32 位循环计数器(0x6000)和 32 位结果(0x6010) */ *pInputSize = 8; /* 32 位 Value1(0x7010.1)和 32 位 Value2(0x7010.2) */ *pOutputSize = 8; return ALSTATUSCODE_NOERROR; } -
文件:sampleappl.c更新输入过程数据映射函数,复制新过程数据:
void APPL_InputMapping(UINT16* pData) { MEMCPY(pData, &InputCounter, SIZEOF(InputCounter)); /* 递增数据指针,写入下一个过程数据(pData 指向 SyncManager 3 控制的 ESC 内存缓冲区) */ pData += 2; /* 复制结果值 */ MEMCPY(pData, &ResultObj6010, SIZEOF(ResultObj6010)); } -
文件:sampleappl.c更新输出过程数据映射函数,更新输出过程数据变量:
void APPL_OutputMapping(UINT16* pData) { /* 更新变量"Value1" */ MEMCPY(&OutputData.Value1, pData, SIZEOF(OutputData.Value1)); /* 递增数据指针,读取下一个过程数据(pData 指向 SyncManager 3 控制的 ESC 内存缓冲区) */ pData += 2; /* 更新变量"Value2" */ MEMCPY(&OutputData.Value2, pData, SIZEOF(OutputData.Value2)); } -
文件:sampleappl.c更新应用程序逻辑:
void APPL_Application(void) { /* 硬件无关示例应用程序 */ ResultObj6010 = OutputData.Value1 + OutputData.Value2; InputCounter++; } -
文件:ESI 文件(XML 格式)根据步骤 3 和 4 更新 RxPdo 和 TxPdo 元素:
<RxPdo Mandatory="true" Fixed="true" Sm="2"> <Index>#x1601</Index> <Name>RxPDO</Name> <Entry> <Index>#x7010</Index> <SubIndex>1</SubIndex> <BitLen>32</BitLen> <Name>Value1</Name> <DataType>UDINT</DataType> </Entry> <Entry> <Index>#x7010</Index> <SubIndex>2</SubIndex> <BitLen>32</BitLen> <Name>Value2</Name> <DataType>UDINT</DataType> </Entry> </RxPdo> <TxPdo Mandatory="true" Fixed="true" Sm="3"> <Index>#x1a00</Index> <Name>TXPDO</Name> <Entry> <Index>#x6000</Index> <SubIndex>0</SubIndex> <BitLen>32</BitLen> <Name>32Bit Input</Name> <DataType>UDINT</DataType> </Entry> <Entry> <Index>#x6010</Index> <SubIndex>0</SubIndex> <BitLen>32</BitLen> <Name>Result</Name> <DataType>UDINT</DataType> </Entry> </TxPdo>主站将使用该信息计算同步管理器大小(需与步骤 6 中指定的大小一致)并向用户显示过程数据。
-
文件:ESI 文件根据 RxPdo/TxPdo 条目的总位大小,更新同步管理器的默认大小:
<Sm MinSize="34" MaxSize="192" DefaultSize="128" StartAddress="#x1000" ControlByte="#x26" Enable="1">MBoxOut</Sm> <Sm MinSize="34" MaxSize="192" DefaultSize="128" StartAddress="#x1400" ControlByte="#x22" Enable="1">MBoxIn</Sm> <Sm DefaultSize="8" StartAddress="#x1800" ControlByte="#x64" Enable="1">Outputs</Sm> <Sm DefaultSize="8" StartAddress="#x1c00" ControlByte="#x20" Enable="1">Inputs</Sm>
来源:AN_ET9300.pdf
用作个人学习和分享,侵删