阜新市网站建设_网站建设公司_SSL证书_seo优化
2026/1/18 19:18:06 网站建设 项目流程
///////////////////////////////////////////////////////////////////////////////////////// /** * \brief CoE (CANopen over EtherCAT) 服务主处理函数。 * \details 此函数在后台被调用,用于处理挂起的SDO(服务数据对象)读写请求。 * 其执行不影响同步模式下的ECAT_Application,保证了实时过程数据交换的优先级。 * \note 该函数仅在定义了 SDO_RES_INTERFACE 时被编译,是 CoE 邮箱协议异步处理的关键部分。 */ ///////////////////////////////////////////////////////////////////////////////////////// void COE_Main(void) { #if SDO_RES_INTERFACE // 条件编译:仅在启用SDO响应接口时包含此部分代码 /* ECATCHANGE_START(V5.01) SDO6*/ // 版本变更标记:V5.01版本中SDO模块的修改起点 UINT8 abort = 0; // SDO中止代码,默认为0(无错误) // 检查是否存在一个由应用程序注册的、用于处理耗时SDO请求的挂起回调函数 if(pSdoPendFunc != NULL) { // 调用应用程序的回调函数处理挂起的SDO请求,并获取处理结果(中止码) abort = pSdoPendFunc(u16StoreIndex, u8StoreSubindex, u32StoreDataSize, pStoreData, bStoreCompleteAccess); // 判断SDO处理是否已完成(ABORTIDX_WORKING 表示仍在处理中,需要等待) if(abort != ABORTIDX_WORKING) { // 根据挂起的SDO操作类型,进行相应的结束处理 switch(u8PendingSdo) { #if SEGMENTED_SDO_SUPPORTED // 条件编译:仅在支持分段SDO传输时处理分段相关清理 case SDO_PENDING_SEG_WRITE: // 挂起的操作是分段下载(主站写从站) if(pSdoSegData) // 安全检查:确保分段数据缓冲区指针有效 { /* 释放为分段传输动态分配的内存缓冲区 */ FREEMEM( (UINT16 VARMEM *) pSdoSegData ); pSdoSegData = NULL; // 将指针置空,防止野指针 } // 注意:此处没有break,会继续执行 SDO_PENDING_WRITE 的case,发送最终响应 #endif case SDO_PENDING_WRITE: // 挂起的操作是普通下载(主站写从站) /* 发送SDO下载响应给主站,通知写入操作已完成(成功或失败) */ SDOS_SdoRes(abort, 0, NULL); // 对于写操作,数据长度为0,数据指针为NULL break; case SDO_PENDING_SEG_READ: // 挂起的操作是分段上传(主站读从站) case SDO_PENDING_READ: // 挂起的操作是普通上传(主站读从站) /* 发送SDO上传响应给主站,并返回请求的数据 */ SDOS_SdoRes(abort, u32StoreDataSize, pStoreData); break; } // 无论成功或失败,请求处理完毕,清空所有挂起请求的状态变量 u8PendingSdo = 0; // 清除挂起操作类型 u16StoreIndex = 0; // 清除对象索引 u8StoreSubindex = 0; // 清除对象子索引 u32StoreDataSize = 0; // 清除数据大小 pStoreData = NULL; // 清除数据指针 bStoreCompleteAccess = 0; // 清除“完全访问”标志 pSdoPendFunc = NULL; // 清除回调函数指针 } // 如果 abort == ABORTIDX_WORKING,则回调函数指示操作仍在进行, // 本次 COE_Main 调用不发送响应,等待下次调用。 } /* ECATCHANGE_END(V5.01) SDO6*/ // 版本变更标记:V5.01版本中SDO模块的修改结束 #endif // 结束 #if SDO_RES_INTERFACE 条件编译块 } /** @} */ // Doxygen文档分组结束标记 #endif // 结束 #if COE_SUPPORTED 条件编译块(文件级别) ///////////////////////////////////////////////////////////////////////////////////////// /** * \brief 构造并发送SDO响应报文。 * \details 此函数在SDO请求(读或写)被处理完毕后调用,负责组装符合CoE协议的响应帧, * 并通过邮箱发送给EtherCAT主站。它处理普通响应、加急响应和分段响应。 * \param abort SDO访问结果(中止码)。0表示成功,非零值表示错误(符合CANopen中止代码标准)。 * \param objLength 请求的对象数据总长度(字节数)。对于写操作,此参数通常为0。 * \param pData 指向待响应数据的指针。对于读操作,指向要返回的数据; * 对于写操作或错误响应,此参数可能为NULL。 */ ///////////////////////////////////////////////////////////////////////////////////////// void SDOS_SdoRes(UINT8 abort, UINT32 objLength, UINT16 MBXMEM *pData) { UINT16 dataSize = 0; // 用于计算当前邮箱帧可容纳的数据大小,主要用于分段传输 // 检查是否确实有一个SDO请求正在处理中(即主站正在等待响应) if (bSdoInWork) { /* 主站期望收到SDO响应,开始构建响应帧 */ // 从已存储的请求头中提取命令类型和访问模式 UINT8 command = pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] & SDOHEADER_COMMAND; UINT8 completeAccess = pSdoResStored->SdoHeader.Sdo[SDOHEADER_COMMANDOFFSET] & SDOHEADER_COMPLETEACCESS; // 判断请求类型:如果是“初始化上传请求”(主站想读数据) if ( command == SDOSERVICE_INITIATEUPLOADREQ ) { #if SEGMENTED_SDO_SUPPORTED // 条件编译:仅在支持分段传输时处理相关逻辑 /* 计算当前邮箱缓冲区一次能发送多少数据 */ // 邮箱总大小减去邮箱头大小和普通上传响应帧的固定头大小 dataSize = u16SendMbxSize - MBX_HEADER_SIZE - UPLOAD_NORM_RES_SIZE; // 核心判断:如果请求的数据对象总长度大于单次可发送量,则需要启动分段传输 if ( dataSize < objLength ) { /* 设置为分段上传模式,并初始化分段传输所需的全局控制变量 */ bSdoSegFollows = TRUE; // 标志位:表示后续还有分段 bSdoSegLastToggle = 1; // 切换位(Toggle Bit)初始值,用于帧序列控制 #if COMPLETE_ACCESS_SUPPORTED bSdoSegAccess = completeAccess; // 保存“完全访问”标志,用于整个分段过程 #endif nSdoSegCompleteSize = objLength; // 保存需要传输的数据总长度 nSdoSegService = SDOSERVICE_UPLOADSEGMENTREQ; // 将后续服务类型设置为“上传段请求” pSdoSegData = (UINT16 VARMEM *) pData; // 保存指向完整数据的指针,供后续分段使用 /* 准备第一段数据:将第一个数据块拷贝到准备好的响应缓冲区中 */ MBXMEMCPY(((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data, pData, dataSize); /* ECATCHANGE_START(V5.01) SDO6*/ // 版本变更标记 nSdoSegBytesToHandle = dataSize; // V5.01新增:记录本次已处理/准备发送的字节数 /* ECATCHANGE_END(V5.01) SDO6*/ } else // 如果数据可以一次性发完,则进入普通或加急响应流程 #endif // 结束 #if SEGMENTED_SDO_SUPPORTED // 判断是否使用“加急响应”(Expedited Transfer):数据长度<=4字节且大于0 if ( (objLength <= 4) && (objLength > 0) ) { /* 构建加急响应帧:将数据直接嵌入到响应报文的命令头中 */ // 安全检查:确保数据不在目标缓冲区时再进行拷贝(避免内存重叠或冗余拷贝) if ( pData != ((TINITSDOUPLOADEXPRESMBX MBXMEM *) pSdoResStored)->Data ) { MBXMEMCPY(((TINITSDOUPLOADEXPRESMBX MBXMEM *) pSdoResStored)->Data, pData, objLength); } } else // 数据长度>4字节,使用“普通响应”(Normal Transfer) { /* 构建普通响应帧:数据跟在响应报文头后面 */ // 同样的安全检查 if ( pData != ((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data ) { MBXMEMCPY(((TINITSDOUPLOADNORMRESMBX MBXMEM *) pSdoResStored)->Data, pData, objLength); } } } // 结束 if ( command == SDOSERVICE_INITIATEUPLOADREQ ) /* 当前SDO访问处理完毕,准备发送响应 */ bSdoInWork = FALSE; // 清除“SDO在处理中”标志,允许接收新的SDO请求 // 调用底层响应发送函数,传入所有必要参数 SdoRes(abort, command, completeAccess, dataSize, objLength, pSdoResStored); } // 结束 if (bSdoInWork) } #endif // 结束 #if SDO_RES_INTERFACE 条件编译块

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询