Ehercat代码解析中文摘录<2>

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)
适配步骤
  1. 文件: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;
  2. 文件: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
  3. 文件: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
  4. 文件: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
  5. 文件: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}};
  6. 文件: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;
    }
  7. 文件:sampleappl.c更新输入过程数据映射函数,复制新过程数据:

    复制代码
    void APPL_InputMapping(UINT16* pData) {
        MEMCPY(pData, &InputCounter, SIZEOF(InputCounter));
        /* 递增数据指针,写入下一个过程数据(pData 指向 SyncManager 3 控制的 ESC 内存缓冲区) */
        pData += 2;
        /* 复制结果值 */
        MEMCPY(pData, &ResultObj6010, SIZEOF(ResultObj6010));
    }
  8. 文件: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));
    }
  9. 文件:sampleappl.c更新应用程序逻辑:

    复制代码
    void APPL_Application(void) {
        /* 硬件无关示例应用程序 */
        ResultObj6010 = OutputData.Value1 + OutputData.Value2;
        InputCounter++;
    }
  10. 文件: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 中指定的大小一致)并向用户显示过程数据。

  11. 文件: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

用作个人学习和分享,侵删

相关推荐
YJlio2 小时前
PsPing 学习笔记(14.1):ICMP Ping 进阶——替代系统 ping 的正确姿势
windows·笔记·学习
阿豪只会阿巴2 小时前
【多喝热水系列】从零开始的ROS2之旅——Day3
linux·笔记·ubuntu·ros2
郑泰科技2 小时前
mstsc 频繁断开是由svchost 策略引起的吗?
windows·笔记·负载均衡
鲨莎分不晴2 小时前
优化的基石:深度解析“凸集”的数学美感与工程价值
笔记
雍凉明月夜3 小时前
深度学习网络笔记Ⅲ(注意力机制)
笔记·深度学习·神经网络·分类
Ahtacca4 小时前
Linux环境下前后端分离项目(Spring Boot + Vue)手动部署全流程指南
linux·运维·服务器·vue.js·spring boot·笔记
polarislove02145 小时前
9.6 [定时器]超声波测距实验-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
chushiyunen5 小时前
快慢双指针算法笔记
数据结构·笔记·算法
临风小红楼5 小时前
别了2025,你好2026
笔记