EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析
EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析
- 一 ethercatconfig.h/c文件功能预览:
- 二 ethercatconfig.h/c 中主要函数的作用:
- 2.1.1 `ec_config_init(uint8 usetable)` 和 `ecx_config_init(ecx_contextt *context, uint8 usetable)`:
- 2.1.2 `ec_config_map(void *pIOmap)`:
- 2.1.3 `ec_config_overlap_map(void *pIOmap)` 和 `ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)`:
- 2.1.4 `ec_config(uint8 usetable, void *pIOmap)` 和 `ecx_config_init(ecx_contextt *context, uint8 usetable)`:
- 2.1.5 `ec_recover_slave(uint16 slave, int timeout)` 和 `ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)`:
- 2.1.6 `ec_reconfig_slave(uint16 slave, int timeout)` 和 `ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)`:
- 三 ethercatconfig.c 原文件的中文注释
- 该文档修改记录:
- 总结
一 ethercatconfig.h/c文件功能预览:
函数 | 功能和用途 | 使用场景 |
---|---|---|
ec_config_init (仅适用于EC_VER1 ) | 初始化EtherCAT主站配置,用于主站初始化。 | 应用程序启动时进行EtherCAT主站的初始化。 |
ecx_config_init | 初始化EtherCAT主站配置,用于主站初始化。 | 应用程序启动时进行EtherCAT主站的初始化。 |
ec_config_map_group (仅适用于EC_VER1 ) | 映射EtherCAT从站数据到主站,配置数据映射到指定组。 | 初始化后,配置数据映射到指定的数据组。 |
ecx_config_map_group | 映射EtherCAT从站数据到主站,配置数据映射到指定组。 | 初始化后,配置数据映射到指定的数据组。 |
ec_config_overlap_map_group (仅适用于EC_VER1 ) | 映射EtherCAT从站数据到主站,处理重叠数据映射。 | 处理重叠数据映射配置时,将多个数据组映射到主站。 |
ecx_config_overlap_map_group | 映射EtherCAT从站数据到主站,处理重叠数据映射。 | 处理重叠数据映射配置时,将多个数据组映射到主站。 |
ec_recover_slave (仅适用于EC_VER1 ) | 尝试从站的恢复,用于从站恢复。 | 当从站失去连接或通信出现问题时,尝试从站的恢复。 |
ecx_recover_slave | 尝试从站的恢复,用于从站恢复。 | 当从站失去连接或通信出现问题时,尝试从站的恢复。 |
ec_reconfig_slave (仅适用于EC_VER1 ) | 重新配置从站,用于运行时更改从站配置。 | 应用程序需要在运行时更改从站配置时使用。 |
ecx_reconfig_slave | 重新配置从站,用于运行时更改从站配置。 | 应用程序需要在运行时更改从站配置时使用。 |
二 ethercatconfig.h/c 中主要函数的作用:
ethercatconfig.h 文件通常包含了用户或开发者定义的配置参数和宏定义,用于指定 EtherCAT 网络的特性,如周期时间、数据对象的大小、网络拓扑结构等。开发者可以根据其应用程序的需求,在这个文件中进行定制。以下是一些可能包含在 ethercatconfig.h 文件中的配置选项:
-
通信周期:定义 EtherCAT 网络的通信周期,这对于实时性能至关重要。
-
从站数量:指定在网络中的 EtherCAT 从站数量。
-
从站配置:每个从站的配置参数,包括地址、数据对象大小等。
-
网络拓扑:定义网络中不同从站的连接方式和物理拓扑。
-
数据对象定义:定义 EtherCAT 数据对象(PDO)的结构和映射。
ethercatconfig.c 文件通常包含 SOEM 库的初始化代码,它使用 ethercatconfig.h 中的配置参数来初始化 EtherCAT 主站并设置网络。这个文件负责执行以下操作:
-
初始化 EtherCAT 主站:配置主站并初始化必要的数据结构。
-
从站配置:为每个从站设置相应的参数,以确保网络中的从站能够正常工作。
-
网络初始化:设置 EtherCAT 网络的物理连接、通信周期等参数。
-
启动 EtherCAT 通信:启动 EtherCAT 通信,使网络中的从站可以开始交换数据。
总之,ethercatconfig.h 和 ethercatconfig.c 文件是 SOEM 库中用于配置和初始化 EtherCAT 网络的关键文件。
2.1.1 ec_config_init(uint8 usetable)
和 ecx_config_init(ecx_contextt *context, uint8 usetable)
:
- 这些函数用于初始化EtherCAT主站的配置。
- 参数
usetable
是一个标志,用于指示是否使用表格(table)来配置主站。如果设置为1,表示使用表格;如果设置为0,表示手动配置。 - 在初始化过程中,这些函数通常会设置网络接口、配置Slave设备的数量、数据映射表等。如果使用表格,则初始化会包括加载表格信息。
2.1.2 ec_config_map(void *pIOmap)
:
- 此函数用于将Slave设备的输入和输出数据映射到主站的过程数据区域。
- 参数
pIOmap
是指向数据映射的指针,它包含了关于Slave设备数据映射的信息。 - 主站需要知道如何读取和写入每个Slave设备的数据,这是通过数据映射来实现的。这个函数负责确保正确的数据映射。
2.1.3 ec_config_overlap_map(void *pIOmap)
和 ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
:
- 这些函数用于处理数据映射中的重叠问题,以确保数据传输不会发生冲突。
- 参数
pIOmap
是指向数据映射的指针。 - 参数
group
可用于指定数据映射到特定的数据组。在某些情况下,数据映射可能会重叠,这可能导致数据混淆。这些函数处理这些重叠,以确保数据正常传输。
2.1.4 ec_config(uint8 usetable, void *pIOmap)
和 ecx_config_init(ecx_contextt *context, uint8 usetable)
:
- 这些函数将配置的初始化和数据映射组合在一起,以便主站的整体配置。
- 参数
usetable
指示是否使用表格(table)来配置主站。 - 参数
pIOmap
是指向数据映射的指针。 - 这些函数在主站应用程序中通常用于一次性配置整个EtherCAT主站,包括初始化和数据映射。
2.1.5 ec_recover_slave(uint16 slave, int timeout)
和 ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)
:
- 这些函数用于尝试从错误状态中恢复指定的Slave设备。
- 参数
slave
指定要恢复的Slave设备。 - 参数
timeout
指定恢复操作的超时时间。如果Slave设备发生错误状态,这些函数可以尝试将其恢复到正常状态。
2.1.6 ec_reconfig_slave(uint16 slave, int timeout)
和 ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
:
- 这些函数用于重新配置指定的Slave设备,通常在网络配置发生更改时使用。
- 参数
slave
指定要重新配置的Slave设备。 - 参数
timeout
指定重新配置操作的超时时间。当需要更改Slave设备的配置时,这些函数可用于重新配置它们,确保网络正常运行。
三 ethercatconfig.c 原文件的中文注释
/** 根据带有异常的GNU通用公共许可证第2版许可。详细的许可信息请参见项目根目录中的LICENSE文件*//** \file* \brief* 用于EtherCAT主站的配置模块。** 在成功使用ec_init()或ec_init_redundant()进行初始化后,可以使用此模块自动配置从站。*/#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "ethercattype.h"
#include "ethercatbase.h"
#include "ethercatmain.h"
#include "ethercatcoe.h"
#include "ethercatsoe.h"
#include "ethercatconfig.h"typedef struct
{int thread_n;int running;ecx_contextt *context;uint16 slave;
} ecx_mapt_t;ecx_mapt_t ecx_mapt[EC_MAX_MAPT];
#if EC_MAX_MAPT > 1
OSAL_THREAD_HANDLE ecx_threadh[EC_MAX_MAPT];
#endif#ifdef EC_VER1
/** 从站配置结构 */
typedef const struct
{/** 从站制造商代码 */uint32 man;/** 从站ID */uint32 id;/** 可读的名称 */char name[EC_MAXNAME + 1];/** 数据类型 */uint8 Dtype;/** 输入位数 */uint16 Ibits;/** 输出位数 */uint16 Obits;/** SyncManager 2地址 */uint16 SM2a;/** SyncManager 2标志 */uint32 SM2f;/** SyncManager 3地址 */uint16 SM3a;/** SyncManager 3标志 */uint32 SM3f;/** FMMU 0激活 */uint8 FM0ac;/** FMMU 1激活 */uint8 FM1ac;
} ec_configlist_t;#include "ethercatconfiglist.h"
#endif/** 用于邮箱从站的标准SM0标志配置 */
#define EC_DEFAULTMBXSM0 0x00010026
/** 用于邮箱从站的标准SM1标志配置 */
#define EC_DEFAULTMBXSM1 0x00010022
/** 用于数字输出从站的标准SM0标志配置 */
#define EC_DEFAULTDOSM0 0x00010044#ifdef EC_VER1
/** 在标准配置列表ec_configlist[]中查找从站** @param[in] man = 制造商* @param[in] id = ID* @return 在ec_configlist[]中找到时的索引,否则返回0*/
int ec_findconfig( uint32 man, uint32 id)
{int i = 0;do{i++;} while ( (ec_configlist[i].man != EC_CONFIGEND) &&((ec_configlist[i].man != man) || (ec_configlist[i].id != id)) );if (ec_configlist[i].man == EC_CONFIGEND){i = 0;}return i;
}
#endifvoid ecx_init_context(ecx_contextt *context)
{int lp;*(context->slavecount) = 0;/* 清除ec_slave数组 */memset(context->slavelist, 0x00, sizeof(ec_slavet) * context->maxslave);memset(context->grouplist, 0x00, sizeof(ec_groupt) * context->maxgroup);/* 清除从站EEPROM缓存,实际上不会读取任何EEPROM */ecx_siigetbyte(context, 0, EC_MAXEEPBUF);for(lp = 0; lp < context->maxgroup; lp++){/* 每个组条目的默认起始地址 */context->grouplist[lp].logstartaddr = lp << EC_LOGGROUPOFFSET;}
}int ecx_detect_slaves(ecx_contextt *context)
{uint8 b;uint16 w;int wkc;/* 进行特殊的预初始化寄存器写操作,以启用老netX100从站的MAC[1]本地管理位设置 */b = 0x00;ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS, sizeof(b), &b, EC_TIMEOUTRET3); /* 忽略Alias寄存器 */b = EC_STATE_INIT | EC_STATE_ACK;ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3); /* 将所有从站重置为Init状态 *//* 现在netX100应该正常工作 */ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL, sizeof(b), &b, EC_TIMEOUTRET3); /* 将所有从站重置为Init状态 */wkc = ecx_BRD(context->port, 0x0000, ECT_REG_TYPE, sizeof(w), &w, EC_TIMEOUTSAFE); /* 检测从站数量 */if (wkc > 0){/* 这是严格的“小于”关系,因为主站是“从站0” */if (wkc < EC_MAXSLAVE){*(context->slavecount) = wkc;}else{EC_PRINT("错误:网络上从站太多:num_slaves=%d,EC_MAXSLAVE=%d\n",wkc, EC_MAXSLAVE);return EC_SLAVECOUNTEXCEEDED;}}return wkc;
}static void ecx_set_slaves_to_default(ecx_contextt *context)
{uint8 b;uint16 w;uint8 zbuf[64];memset(&zbuf, 0x00, sizeof(zbuf));b = 0x00;ecx_BWR(context->port, 0x0000, ECT_REG_DLPORT , sizeof(b) , &b, EC_TIMEOUTRET3); /* 停用循环手动 */w = htoes(0x0004);ecx_BWR(context->port, 0x0000, ECT_REG_IRQMASK , sizeof(w) , &w, EC_TIMEOUTRET3); /* 设置中断掩码 */ecx_BWR(context->port, 0x0000, ECT_REG_RXERR , 8 , &zbuf, EC_TIMEOUTRET3); /* 重置CRC计数器 */ecx_BWR(context->port, 0x0000, ECT_REG_FMMU0 , 16 * 3 , &zbuf, EC_TIMEOUTRET3); /* 重置FMMU */ecx_BWR(context->port, 0x0000, ECT_REG_SM0 , 8 * 4 , &zbuf, EC_TIMEOUTRET3); /* 重置SyncM */b = 0x00;ecx_BWR(context->port, 0x0000, ECT_REG_DCSYNCACT , sizeof(b) , &b, EC_TIMEOUTRET3); /* 重置激活寄存器 */ecx_BWR(context->port, 0x0000, ECT_REG_DCSYSTIME , 4 , &zbuf, EC_TIMEOUTRET3); /* 重置系统时间+偏移 */w = htoes(0x1000);ecx_BWR(context->port, 0x0000, ECT_REG_DCSPEEDCNT , sizeof(w) , &w, EC_TIMEOUTRET3); /* DC速度启动 */w = htoes(0x0c00);ecx_BWR(context->port, 0x0000, ECT_REG_DCTIMEFILT , sizeof(w) , &w, EC_TIMEOUTRET3); /* DC滤波表达式 */b = 0x00;ecx_BWR(context->port, 0x0000, ECT_REG_DLALIAS , sizeof(b) , &b, EC_TIMEOUTRET3); /* 忽略Alias寄存器 */b = EC_STATE_INIT | EC_STATE_ACK;ecx_BWR(context->port, 0x0000, ECT_REG_ALCTL , sizeof(b) , &b, EC_TIMEOUTRET3); /* 将所有从站重置为Init */b = 2;ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG , sizeof(b) , &b, EC_TIMEOUTRET3); /* 强制从PDI读取EEPROM */b = 0;ecx_BWR(context->port, 0x0000, ECT_REG_EEPCFG , sizeof(b) , &b, EC_TIMEOUTRET3); /* 将EEPROM设置为主站 */
}#ifdef EC_VER1
static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
{int cindex;ec_slavet *csl;csl = &(context->slavelist[slave]);cindex = ec_findconfig( csl->eep_man, csl->eep_id );csl->configindex= cindex;/* slave found in configuration table ? */if (cindex){csl->Dtype = ec_configlist[cindex].Dtype;strcpy(csl->name ,ec_configlist[cindex].name);csl->Ibits = ec_configlist[cindex].Ibits;csl->Obits = ec_configlist[cindex].Obits;if (csl->Obits){csl->FMMU0func = 1;}if (csl->Ibits){csl->FMMU1func = 2;}csl->FMMU[0].FMMUactive = ec_configlist[cindex].FM0ac;csl->FMMU[1].FMMUactive = ec_configlist[cindex].FM1ac;csl->SM[2].StartAddr = htoes(ec_configlist[cindex].SM2a);csl->SM[2].SMflags = htoel(ec_configlist[cindex].SM2f);/* simple (no mailbox) output slave found ? */if (csl->Obits && !csl->SM[2].StartAddr){csl->SM[0].StartAddr = htoes(0x0f00);csl->SM[0].SMlength = htoes((csl->Obits + 7) / 8);csl->SM[0].SMflags = htoel(EC_DEFAULTDOSM0);csl->FMMU[0].FMMUactive = 1;csl->FMMU[0].FMMUtype = 2;csl->SMtype[0] = 3;}/* complex output slave */else{csl->SM[2].SMlength = htoes((csl->Obits + 7) / 8);csl->SMtype[2] = 3;}csl->SM[3].StartAddr = htoes(ec_configlist[cindex].SM3a);csl->SM[3].SMflags = htoel(ec_configlist[cindex].SM3f);/* simple (no mailbox) input slave found ? */if (csl->Ibits && !csl->SM[3].StartAddr){csl->SM[1].StartAddr = htoes(0x1000);csl->SM[1].SMlength = htoes((csl->Ibits + 7) / 8);csl->SM[1].SMflags = htoel(0x00000000);csl->FMMU[1].FMMUactive = 1;csl->FMMU[1].FMMUtype = 1;csl->SMtype[1] = 4;}/* complex input slave */else{csl->SM[3].SMlength = htoes((csl->Ibits + 7) / 8);csl->SMtype[3] = 4;}}return cindex;
}
#else
static int ecx_config_from_table(ecx_contextt *context, uint16 slave)
{(void)context;(void)slave;return 0;
}
#endif/* If slave has SII and same slave ID done before, use previous data.* This is safe because SII is constant for same slave ID.*/
static int ecx_lookup_prev_sii(ecx_contextt *context, uint16 slave)
{int i, nSM;if ((slave > 1) && (*(context->slavecount) > 0)){i = 1;while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||(context->slavelist[i].eep_id != context->slavelist[slave].eep_id ) ||(context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&(i < slave)){i++;}if(i < slave){context->slavelist[slave].CoEdetails = context->slavelist[i].CoEdetails;context->slavelist[slave].FoEdetails = context->slavelist[i].FoEdetails;context->slavelist[slave].EoEdetails = context->slavelist[i].EoEdetails;context->slavelist[slave].SoEdetails = context->slavelist[i].SoEdetails;if(context->slavelist[i].blockLRW > 0){context->slavelist[slave].blockLRW = 1;context->slavelist[0].blockLRW++;}context->slavelist[slave].Ebuscurrent = context->slavelist[i].Ebuscurrent;context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;memcpy(context->slavelist[slave].name, context->slavelist[i].name, EC_MAXNAME + 1);for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ){context->slavelist[slave].SM[nSM].StartAddr = context->slavelist[i].SM[nSM].StartAddr;context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;context->slavelist[slave].SM[nSM].SMflags = context->slavelist[i].SM[nSM].SMflags;}context->slavelist[slave].FMMU0func = context->slavelist[i].FMMU0func;context->slavelist[slave].FMMU1func = context->slavelist[i].FMMU1func;context->slavelist[slave].FMMU2func = context->slavelist[i].FMMU2func;context->slavelist[slave].FMMU3func = context->slavelist[i].FMMU3func;EC_PRINT("Copy SII slave %d from %d.\n", slave, i);return 1;}}return 0;
}/** 枚举和初始化所有从站。** @param[in] context = 上下文结构* @param[in] usetable = 使用配置表来初始化从站时为TRUE,否则为FALSE* @return 从站发现数据报的工作计数 = 找到的从站数量*/
int ecx_config_init(ecx_contextt *context, uint8 usetable)
{uint16 slave, ADPh, configadr, ssigen;uint16 topology, estat;int16 topoc, slavec, aliasadr;uint8 b, h;uint8 SMc;uint32 eedat;int wkc, cindex, nSM;uint16 val16;EC_PRINT("ec_config_init %d\n", usetable);ecx_init_context(context);wkc = ecx_detect_slaves(context);if (wkc > 0){ecx_set_slaves_to_default(context);for (slave = 1; slave <= *(context->slavecount); slave++){ADPh = (uint16)(1 - slave);val16 = ecx_APRDw(context->port, ADPh, ECT_REG_PDICTL, EC_TIMEOUTRET3); /* 读取从站的接口类型 */context->slavelist[slave].Itype = etohs(val16);/* 为了提高网络帧的可读性,使用节点偏移 *//* 这不会影响可寻址从站的数量(自动循环) */ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(slave + EC_NODEOFFSET), EC_TIMEOUTRET3); /* 设置从站的节点地址 */if (slave == 1){b = 1; /* 对于第一个从站,禁用非EtherCAT帧 */}else{b = 0; /* 对于后续从站,允许所有帧通过 */}ecx_APWRw(context->port, ADPh, ECT_REG_DLCTL, htoes(b), EC_TIMEOUTRET3); /* 设置非EtherCAT帧的行为 */configadr = ecx_APRDw(context->port, ADPh, ECT_REG_STADR, EC_TIMEOUTRET3);configadr = etohs(configadr);context->slavelist[slave].configadr = configadr;ecx_FPRD(context->port, configadr, ECT_REG_ALIAS, sizeof(aliasadr), &aliasadr, EC_TIMEOUTRET3);context->slavelist[slave].aliasadr = etohs(aliasadr);ecx_FPRD(context->port, configadr, ECT_REG_EEPSTAT, sizeof(estat), &estat, EC_TIMEOUTRET3);estat = etohs(estat);if (estat & EC_ESTAT_R64) /* 检查从站是否支持读取8字节数据块 */{context->slavelist[slave].eep_8byte = 1;}ecx_readeeprom1(context, slave, ECT_SII_MANUF); /* Manuf */}for (slave = 1; slave <= *(context->slavecount); slave++){eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* Manuf */context->slavelist[slave].eep_man = etohl(eedat);ecx_readeeprom1(context, slave, ECT_SII_ID); /* ID */}for (slave = 1; slave <= *(context->slavecount); slave++){eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* ID */context->slavelist[slave].eep_id = etohl(eedat);ecx_readeeprom1(context, slave, ECT_SII_REV); /* revision */}for (slave = 1; slave <= *(context->slavecount); slave++){eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* revision */context->slavelist[slave].eep_rev = etohl(eedat);ecx_readeeprom1(context, slave, ECT_SII_RXMBXADR); /* write mailbox address + mailboxsize */}for (slave = 1; slave <= *(context->slavecount); slave++){eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* write mailbox address and mailboxsize */context->slavelist[slave].mbx_wo = (uint16)LO_WORD(etohl(eedat));context->slavelist[slave].mbx_l = (uint16)HI_WORD(etohl(eedat));if (context->slavelist[slave].mbx_l > 0){ecx_readeeprom1(context, slave, ECT_SII_TXMBXADR); /* read mailbox offset */}}for (slave = 1; slave <= *(context->slavecount); slave++){if (context->slavelist[slave].mbx_l > 0){eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP); /* read mailbox offset */context->slavelist[slave].mbx_ro = (uint16)LO_WORD(etohl(eedat)); /* read mailbox offset */context->slavelist[slave].mbx_rl = (uint16)HI_WORD(etohl(eedat)); /* read mailbox length */if (context->slavelist[slave].mbx_rl == 0){context->slavelist[slave].mbx_rl = context->slavelist[slave].mbx_l;}ecx_readeeprom1(context, slave, ECT_SII_MBXPROTO);}configadr = context->slavelist[slave].configadr;val16 = ecx_FPRDw(context->port, configadr, ECT_REG_ESCSUP, EC_TIMEOUTRET3);if ((etohs(val16) & 0x04) > 0) /* 支持DC? */{context->slavelist[slave].hasdc = TRUE;}else{context->slavelist[slave].hasdc = FALSE;}topology = ecx_FPRDw(context->port, configadr, ECT_REG_DLSTAT, EC_TIMEOUTRET3); /* 从DL状态中提取拓扑信息 */topology = etohs(topology);h = 0;b = 0;if ((topology & 0x0300) == 0x0200) /* 端口0打开并已建立通信 */{h++;b |= 0x01;}if ((topology & 0x0c00) == 0x0800) /* 端口1打开并已建立通信 */{h++;b |= 0x02;}if ((topology & 0x3000) == 0x2000) /* 端口2打{h++;b |= 0x04;}if ((topology & 0xc000) == 0x8000) /* 端口3打开并已建立通信 */{h++;b |= 0x08;}/* ptype = 物理类型 */val16 = ecx_FPRDw(context->port, configadr, ECT_REG_PORTDES, EC_TIMEOUTRET3);context->slavelist[slave].ptype = LO_BYTE(etohs(val16));context->slavelist[slave].topology = h;context->slavelist[slave].activeports = b;/* 0=没有链接,不可能 *//* 1=1个链接,线尾 *//* 2=2个链接,前后各一个 *//* 3=3个链接,分割点 *//* 4=4个链接,交叉点 *//* 搜索父节点 */context->slavelist[slave].parent = 0; /* 父节点是主站 */if (slave > 1){topoc = 0;slavec = slave - 1;do{topology = context->slavelist[slavec].topology;if (topology == 1){topoc--; /* 找到终端 */}if (topology == 3){topoc++; /* 找到分割点 */}if (topology == 4){topoc += 2; /* 找到交叉点 */}if (((topoc >= 0) && (topology > 1)) ||(slavec == 1)) /* 找到父节点 */{context->slavelist[slave].parent = slavec;slavec = 1;}slavec--;}while (slavec > 0);}(void)ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE); /* 检查状态变化Init *//* 如果从站具有邮箱,设置默认邮箱配置 */if (context->slavelist[slave].mbx_l > 0){context->slavelist[slave].SMtype[0] = 1;context->slavelist[slave].SMtype[1] = 2;context->slavelist[slave].SMtype[2] = 3;context->slavelist[slave].SMtype[3] = 4;context->slavelist[slave].SM[0].StartAddr = htoes(context->slavelist[slave].mbx_wo);context->slavelist[slave].SM[0].SMlength = htoes(context->slavelist[slave].mbx_l);context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);context->slavelist[slave].SM[1].StartAddr = htoes(context->slavelist[slave].mbx_ro);context->slavelist[slave].SM[1].SMlength = htoes(context->slavelist[slave].mbx_rl);context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);eedat = ecx_readeeprom2(context, slave, EC_TIMEOUTEEP);context->slavelist[slave].mbx_proto = (uint16)etohl(eedat);}cindex = 0;/* 使用配置表吗? */if (usetable == 1){cindex = ecx_config_from_table(context, slave);}/* 如果从站不在配置表中,则通过SII查找 */if (!cindex && !ecx_lookup_prev_sii(context, slave)){ssigen = ecx_siifind(context, slave, ECT_SII_GENERAL);/* SII通用部分 */if (ssigen){context->slavelist[slave].CoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x07);context->slavelist[slave].FoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x08);context->slavelist[slave].EoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x09);context->slavelist[slave].SoEdetails = ecx_siigetbyte(context, slave, ssigen + 0x0a);if ((ecx_siigetbyte(context, slave, ssigen + 0x0d) & 0x02) > 0){context->slavelist[slave].blockLRW = 1;context->slavelist[0].blockLRW++;}context->slavelist[slave].Ebuscurrent = ecx_siigetbyte(context, slave, ssigen + 0x0e);context->slavelist[slave].Ebuscurrent += ecx_siigetbyte(context, slave, ssigen + 0x0f) << 8;context->slavelist[0].Ebuscurrent += context->slavelist[slave].Ebuscurrent;}/* SII字符串部分 */if (ecx_siifind(context, slave, ECT_SII_STRING) > 0){ecx_siistring(context, context->slavelist[slave].name, slave, 1);}/* 没有找到从站的名称,使用构造的名称 */else{sprintf(context->slavelist[slave].name, "? M:%8.8x I:%8.8x",(unsigned int)context->slavelist[slave].eep_man,(unsigned int)context->slavelist[slave].eep_id);}/* SII SM部分 */nSM = ecx_siiSM(context, slave, context->eepSM);if (nSM > 0){context->slavelist[slave].SM[0].StartAddr = htoes(context->eepSM->PhStart);context->slavelist[slave].SM[0].SMlength = htoes(context->eepSM->Plength);context->slavelist[slave].SM[0].SMflags =htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));SMc = 1;while ((SMc < EC_MAXSM) && ecx_siiSMnext(context, slave, context->eepSM, SMc)){context->slavelist[slave].SM[SMc].StartAddr = htoes(context->eepSM->PhStart);context->slavelist[slave].SM[SMc].SMlength = htoes(context->eepSM->Plength);context->slavelist[slave].SM[SMc].SMflags =htoel((context->eepSM->Creg) + (context->eepSM->Activate << 16));SMc++;}}/* SII FMMU部分 */if (ecx_siiFMMU(context, slave, context->eepFMMU)){if (context->eepFMMU->FMMU0 !=0xff){context->slavelist[slave].FMMU0func = context->eepFMMU->FMMU0;}if (context->eepFMMU->FMMU1 !=0xff){context->slavelist[slave].FMMU1func = context->eepFMMU->FMMU1;}if (context->eepFMMU->FMMU2 !=0xff){context->slavelist[slave].FMMU2func = context->eepFMMU->FMMU2;}if (context->eepFMMU->FMMU3 !=0xff){context->slavelist[slave].FMMU3func = context->eepFMMU->FMMU3;}}}if (context->slavelist[slave].mbx_l > 0){if (context->slavelist[slave].SM[0].StartAddr == 0x0000) /* 这不应该发生 */{EC_PRINT("从站 %d 在配置中没有正确的邮箱,尝试默认值。\n", slave);context->slavelist[slave].SM[0].StartAddr = htoes(0x1000);context->slavelist[slave].SM[0].SMlength = htoes(0x0080);context->slavelist[slave].SM[0].SMflags = htoel(EC_DEFAULTMBXSM0);context->slavelist[slave].SMtype[0] = 1;}if (context->slavelist[slave].SM[1].StartAddr == 0x0000) /* 这不应该发生 */{EC_PRINT("从站 %d 在配置中没有正确的邮箱,尝试默认值。\n", slave);context->slavelist[slave].SM[1].StartAddr = htoes(0x1080);context->slavelist[slave].SM[1].SMlength = htoes(0x0080);context->slavelist[slave].SM[1].SMflags = htoel(EC_DEFAULTMBXSM1);context->slavelist[slave].SMtype[1] = 2;}/* 编程SM0邮箱接收和SM1邮箱发送 *//* 在一个数据报中同时写入两个SM可以解决旧NETX中的时序问题 */ecx_FPWR(context->port, configadr, ECT_REG_SM0, sizeof(ec_smt) * 2,&(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);}/* 一些从站需要在init->preop过渡中将eeprom可用于PDI */ecx_eeprom2pdi(context, slave);/* 用户可以覆盖自动状态更改 */if (context->manualstatechange == 0){/* 请求从站的pre_op状态 */ecx_FPWRw(context->port,configadr,ECT_REG_ALCTL,htoes(EC_STATE_PRE_OP | EC_STATE_ACK),EC_TIMEOUTRET3); /* 设置preop状态 */}}}return wkc;
}/* 如果从站具有SII映射并且以前执行相同从站ID,则使用先前的映射。* 这是安全的,因为对于相同的从站ID,SII映射是恒定的。*/
static int ecx_lookup_mapping(ecx_contextt *context, uint16 slave, uint32 *Osize, uint32 *Isize)
{int i, nSM;if ((slave > 1) && (*(context->slavecount) > 0)){i = 1;while(((context->slavelist[i].eep_man != context->slavelist[slave].eep_man) ||(context->slavelist[i].eep_id != context->slavelist[slave].eep_id ) ||(context->slavelist[i].eep_rev != context->slavelist[slave].eep_rev)) &&(i < slave)){i++;}if(i < slave){for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ){context->slavelist[slave].SM[nSM].SMlength = context->slavelist[i].SM[nSM].SMlength;context->slavelist[slave].SMtype[nSM] = context->slavelist[i].SMtype[nSM];}*Osize = context->slavelist[i].Obits;*Isize = context->slavelist[i].Ibits;context->slavelist[slave].Obits = (uint16)*Osize;context->slavelist[slave].Ibits = (uint16)*Isize;EC_PRINT("从站 %d 从 %d 复制映射。\n", slave, i);return 1;}}return 0;
}static int ecx_map_coe_soe(ecx_contextt *context, uint16 slave, int thread_n)
{uint32 Isize, Osize;int rval;ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* 检查状态是否变为Pre-op */EC_PRINT(" >从站 %d, 配置地址 %x, 状态 %2.2x\n",slave, context->slavelist[slave].configadr, context->slavelist[slave].state);/* 执行特殊的从站配置挂接 Pre-Op 到 Safe-OP */if(context->slavelist[slave].PO2SOconfig) /* 仅在已注册时执行 */{context->slavelist[slave].PO2SOconfig(slave);}if (context->slavelist[slave].PO2SOconfigx) /* 仅在已注册时执行 */{context->slavelist[slave].PO2SOconfigx(context, slave);}/* 如果在配置列表中未找到从站,则在从站本身中查找IO映射 */if (!context->slavelist[slave].configindex){Isize = 0;Osize = 0;if (context->slavelist[slave].mbx_proto & ECT_MBXPROT_COE) /* 具有CoE */{rval = 0;if (context->slavelist[slave].CoEdetails & ECT_COEDET_SDOCA) /* 具有Complete Access */{/* 通过CoE读取PDO映射并使用Complete Access */rval = ecx_readPDOmapCA(context, slave, thread_n, &Osize, &Isize);}if (!rval) /* CA不可用或未成功 */{/* 通过CoE读取PDO映射 */rval = ecx_readPDOmap(context, slave, &Osize, &Isize);}EC_PRINT(" CoE 输出大小:%u 输入大小:%u\n", Osize, Isize);}if ((!Isize && !Osize) && (context->slavelist[slave].mbx_proto & ECT_MBXPROT_SOE)) /* 具有SoE */{/* 通过SoE读取AT/MDT映射 */rval = ecx_readIDNmap(context, slave, &Osize, &Isize);context->slavelist[slave].SM[2].SMlength = htoes((uint16)((Osize + 7) / 8));context->slavelist[slave].SM[3].SMlength = htoes((uint16)((Isize + 7) / 8));EC_PRINT(" SoE 输出大小:%u 输入大小:%u\n", Osize, Isize);}context->slavelist[slave].Obits = (uint16)Osize;context->slavelist[slave].Ibits = (uint16)Isize;}return 1;
}static int ecx_map_sii(ecx_contextt *context, uint16 slave)
{uint32 Isize, Osize;int nSM;ec_eepromPDOt eepPDO;Osize = context->slavelist[slave].Obits;Isize = context->slavelist[slave].Ibits;if (!Isize && !Osize) /* 在具有相同ID的先前从站中查找PDO */{(void)ecx_lookup_mapping(context, slave, &Osize, &Isize);}if (!Isize && !Osize) /* 通过SII查找PDO映射 */{memset(&eepPDO, 0, sizeof(eepPDO));Isize = ecx_siiPDO(context, slave, &eepPDO, 0);EC_PRINT(" SII Isize:%u\n", Isize);for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ){if (eepPDO.SMbitsize[nSM] > 0){context->slavelist[slave].SM[nSM].SMlength = htoes((eepPDO.SMbitsize[nSM] + 7) / 8);context->slavelist[slave].SMtype[nSM] = 4;EC_PRINT(" SM%d 长度 %d\n", nSM, eepPDO.SMbitsize[nSM]);}}Osize = ecx_siiPDO(context, slave, &eepPDO, 1);EC_PRINT(" SII Osize:%u\n", Osize);for( nSM=0 ; nSM < EC_MAXSM ; nSM++ ){if (eepPDO.SMbitsize[nSM] > 0){context->slavelist[slave].SM[nSM].SMlength = htoes((eepPDO.SMbitsize[nSM] + 7) / 8);context->slavelist[slave].SMtype[nSM] = 3;EC_PRINT(" SM%d 长度 %d\n", nSM, eepPDO.SMbitsize[nSM]);}}}context->slavelist[slave].Obits = (uint16)Osize;context->slavelist[slave].Ibits = (uint16)Isize;EC_PRINT(" 输入大小:%d %d 输出大小:%d\n",context->slavelist[slave].Ibits, Isize, context->slavelist[slave].Obits);return 1;
}static int ecx_map_sm(ecx_contextt *context, uint16 slave)
{uint16 configadr;int nSM;configadr = context->slavelist[slave].configadr;EC_PRINT(" SM编程\n");if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[0].StartAddr){ecx_FPWR(context->port, configadr, ECT_REG_SM0,sizeof(ec_smt), &(context->slavelist[slave].SM[0]), EC_TIMEOUTRET3);EC_PRINT(" SM0 类型:%d 起始地址:%4.4x 标志:%8.8x\n",context->slavelist[slave].SMtype[0],etohs(context->slavelist[slave].SM[0].StartAddr),etohl(context->slavelist[slave].SM[0].SMflags));}if (!context->slavelist[slave].mbx_l && context->slavelist[slave].SM[1].StartAddr){ecx_FPWR(context->port, configadr, ECT_REG_SM1,sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET3);EC_PRINT(" SM1 类型:%d 起始地址:%4.4x 标志:%8.8x\n",context->slavelist[slave].SMtype[1],etohs(context->slavelist[slave].SM[1].StartAddr),etohl(context->slavelist[slave].SM[1].SMflags));}/* 编程SM2到SMx */for( nSM = 2 ; nSM < EC_MAXSM ; nSM++ ){if (context->slavelist[slave].SM[nSM].StartAddr){/* 检查SM长度是否为零 -> 清除启用标志 */if( context->slavelist[slave].SM[nSM].SMlength == 0){context->slavelist[slave].SM[nSM].SMflags =htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) & EC_SMENABLEMASK);}/* 如果SM长度为非零,始终设置启用标志 */else{context->slavelist[slave].SM[nSM].SMflags =htoel( etohl(context->slavelist[slave].SM[nSM].SMflags) | ~EC_SMENABLEMASK);}ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt)),sizeof(ec_smt), &context->slavelist[slave].SM[nSM], EC_TIMEOUTRET3);EC_PRINT(" SM%d 类型:%d 起始地址:%4.4x 标志:%8.8x\n", nSM,context->slavelist[slave].SMtype[nSM],etohs(context->slavelist[slave].SM[nSM].StartAddr),etohl(context->slavelist[slave].SM[nSM].SMflags));}}if (context->slavelist[slave].Ibits > 7){context->slavelist[slave].Ibytes = (context->slavelist[slave].Ibits + 7) / 8;}if (context->slavelist[slave].Obits > 7){context->slavelist[slave].Obytes = (context->slavelist[slave].Obits + 7) / 8;}return 1;
}#if EC_MAX_MAPT > 1
OSAL_THREAD_FUNC ecx_mapper_thread(void *param)
{ecx_mapt_t *maptp;maptp = param;ecx_map_coe_soe(maptp->context, maptp->slave, maptp->thread_n);maptp->running = 0;
}static int ecx_find_mapt(void)
{int p;p = 0;while((p < EC_MAX_MAPT) && ecx_mapt[p].running){p++;}if(p < EC_MAX_MAPT){return p;}else{return -1;}
}
#endifstatic int ecx_get_threadcount(void)
{int thrc, thrn;thrc = 0;for(thrn = 0 ; thrn < EC_MAX_MAPT ; thrn++){thrc += ecx_mapt[thrn].running;}return thrc;
}static void ecx_config_find_mappings(ecx_contextt *context, uint8 group)
{int thrn, thrc;uint16 slave;for (thrn = 0; thrn < EC_MAX_MAPT; thrn++){ecx_mapt[thrn].running = 0;}/* 查找多线程中的从站的CoE和SoE映射 */for (slave = 1; slave <= *(context->slavecount); slave++){if (!group || (group == context->slavelist[slave].group)){
#if EC_MAX_MAPT > 1/* 多线程版本 */while ((thrn = ecx_find_mapt()) < 0){osal_usleep(1000);}ecx_mapt[thrn].context = context;ecx_mapt[thrn].slave = slave;ecx_mapt[thrn].thread_n = thrn;ecx_mapt[thrn].running = 1;osal_thread_create(&(ecx_threadh[thrn]), 128000,&ecx_mapper_thread, &(ecx_mapt[thrn]));
#else/* 串行版本 */ecx_map_coe_soe(context, slave, 0);
#endif}}/* 等待所有线程完成 */do{thrc = ecx_get_threadcount();if (thrc){osal_usleep(1000);}} while (thrc);/* 查找从站的SII映射并编程SM */for (slave = 1; slave <= *(context->slavecount); slave++){if (!group || (group == context->slavelist[slave].group)){ecx_map_sii(context, slave);ecx_map_sm(context, slave);}}
}
static void ecx_config_create_input_mappings(ecx_contextt *context, void *pIOmap, uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
{int BitCount = 0;int FMMUdone = 0;int AddToInputsWKC = 0;uint16 ByteCount = 0;uint16 FMMUsize = 0;uint8 SMc = 0;uint16 EndAddr;uint16 SMlength;uint16 configadr;uint8 FMMUc;EC_PRINT(" =Slave %d, INPUT MAPPING\n", slave);configadr = context->slavelist[slave].configadr;FMMUc = context->slavelist[slave].FMMUunused;if (context->slavelist[slave].Obits) /* 查找空闲的FMMU */{while (context->slavelist[slave].FMMU[FMMUc].LogStart){FMMUc++;}}/* 搜索为输入映射做出贡献的SM */while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Ibits + 7) / 8))){EC_PRINT(" FMMU %d\n", FMMUc);while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4)){SMc++;}EC_PRINT(" SM%d\n", SMc);context->slavelist[slave].FMMU[FMMUc].PhysStart =context->slavelist[slave].SM[SMc].StartAddr;SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);ByteCount += SMlength;BitCount += SMlength * 8;EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;while ((BitCount < context->slavelist[slave].Ibits) && (SMc < (EC_MAXSM - 1))) /* 更多的SM用于输入 */{SMc++;while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 4)){SMc++;}/* 如果来自多个SM的地址连接在一起,使用一个FMMU,否则分成多个FMMU */if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr){break;}EC_PRINT(" SM%d\n", SMc);SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);ByteCount += SMlength;BitCount += SMlength * 8;EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;}/* 位定向从站 */if (!context->slavelist[slave].Ibytes){context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;*BitPos += context->slavelist[slave].Ibits - 1;if (*BitPos > 7){*LogAddr += 1;*BitPos -= 8;}FMMUsize = (uint16)(*LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1);context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;*BitPos += 1;if (*BitPos > 7){*LogAddr += 1;*BitPos -= 8;}}/* 字节定向从站 */else{if (*BitPos){*LogAddr += 1;*BitPos = 0;}context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;*BitPos = 7;FMMUsize = ByteCount;if ((FMMUsize + FMMUdone) > (int)context->slavelist[slave].Ibytes){FMMUsize = (uint16)(context->slavelist[slave].Ibytes - FMMUdone);}*LogAddr += FMMUsize;context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;*BitPos = 0;}FMMUdone += FMMUsize;if (context->slavelist[slave].FMMU[FMMUc].LogLength){context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;context->slavelist[slave].FMMU[FMMUc].FMMUtype = 1;context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;/* 为输入编程FMMU */ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);/* 设置标志以添加一个输入FMMU,单个ESC只能贡献一次 */AddToInputsWKC = 1;}if (!context->slavelist[slave].inputs){if (group){context->slavelist[slave].inputs =(uint8 *)(pIOmap) + etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) - context->grouplist[group].logstartaddr;}else{context->slavelist[slave].inputs =(uint8 *)(pIOmap) +etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);}context->slavelist[slave].Istartbit =context->slavelist[slave].FMMU[FMMUc].LogStartbit;EC_PRINT(" Inputs %p startbit %d\n",context->slavelist[slave].inputs,context->slavelist[slave].Istartbit);}FMMUc++;}context->slavelist[slave].FMMUunused = FMMUc;/* 如果标志为true,则添加一个WKC用于输入 */if (AddToInputsWKC)context->grouplist[group].inputsWKC++;
}static void ecx_config_create_output_mappings(ecx_contextt *context, void *pIOmap,uint8 group, int16 slave, uint32 * LogAddr, uint8 * BitPos)
{int BitCount = 0;int FMMUdone = 0;int AddToOutputsWKC = 0;uint16 ByteCount = 0;uint16 FMMUsize = 0;uint8 SMc = 0;uint16 EndAddr;uint16 SMlength;uint16 configadr;uint8 FMMUc;EC_PRINT(" 输出映射\n");FMMUc = context->slavelist[slave].FMMUunused;configadr = context->slavelist[slave].configadr;/* 搜索对输出映射有贡献的SM */while ((SMc < (EC_MAXSM - 1)) && (FMMUdone < ((context->slavelist[slave].Obits + 7) / 8))){EC_PRINT(" FMMU %d\n", FMMUc);while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3)){SMc++;}EC_PRINT(" SM%d\n", SMc);context->slavelist[slave].FMMU[FMMUc].PhysStart =context->slavelist[slave].SM[SMc].StartAddr;SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);ByteCount += SMlength;BitCount += SMlength * 8;EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;while ((BitCount < context->slavelist[slave].Obits) && (SMc < (EC_MAXSM - 1))) /* 更多的SM用于输出 */{SMc++;while ((SMc < (EC_MAXSM - 1)) && (context->slavelist[slave].SMtype[SMc] != 3)){SMc++;}/* 如果来自多个SM的地址连接在一起,使用一个FMMU,否则分成多个FMMU */if (etohs(context->slavelist[slave].SM[SMc].StartAddr) > EndAddr){break;}EC_PRINT(" SM%d\n", SMc);SMlength = etohs(context->slavelist[slave].SM[SMc].SMlength);ByteCount += SMlength;BitCount += SMlength * 8;EndAddr = etohs(context->slavelist[slave].SM[SMc].StartAddr) + SMlength;}/* 位定向从站 */if (!context->slavelist[slave].Obytes){context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;*BitPos += context->slavelist[slave].Obits - 1;if (*BitPos > 7){*LogAddr += 1;*BitPos -= 8;}FMMUsize = (uint16)(*LogAddr - etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) + 1);context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;*BitPos += 1;if (*BitPos > 7){*LogAddr += 1;*BitPos -= 8;}}/* 字节定向从站 */else{if (*BitPos){*LogAddr += 1;*BitPos = 0;}context->slavelist[slave].FMMU[FMMUc].LogStart = htoel(*LogAddr);context->slavelist[slave].FMMU[FMMUc].LogStartbit = *BitPos;*BitPos = 7;FMMUsize = ByteCount;if ((FMMUsize + FMMUdone)> (int)context->slavelist[slave].Obytes){FMMUsize = (uint16)(context->slavelist[slave].Obytes - FMMUdone);}*LogAddr += FMMUsize;context->slavelist[slave].FMMU[FMMUc].LogLength = htoes(FMMUsize);context->slavelist[slave].FMMU[FMMUc].LogEndbit = *BitPos;*BitPos = 0;}FMMUdone += FMMUsize;if (context->slavelist[slave].FMMU[FMMUc].LogLength){context->slavelist[slave].FMMU[FMMUc].PhysStartBit = 0;context->slavelist[slave].FMMU[FMMUc].FMMUtype = 2;context->slavelist[slave].FMMU[FMMUc].FMMUactive = 1;/* 为输出程序FMMU */ecx_FPWR(context->port, configadr, ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),sizeof(ec_fmmut), &(context->slavelist[slave].FMMU[FMMUc]), EC_TIMEOUTRET3);/* 设置标志以添加一个输出FMMU,一个单独的ESC只能贡献一次 */AddToOutputsWKC = 1;}if (!context->slavelist[slave].outputs){if (group){context->slavelist[slave].outputs =(uint8 *)(pIOmap) +etohl(context->slavelist[slave].FMMU[FMMUc].LogStart) -context->grouplist[group].logstartaddr;}else{context->slavelist[slave].outputs =(uint8 *)(pIOmap) +etohl(context->slavelist[slave].FMMU[FMMUc].LogStart);}context->slavelist[slave].Ostartbit =context->slavelist[slave].FMMU[FMMUc].LogStartbit;EC_PRINT(" 从站 %d 输出 %p 开始位 %d\n",slave,context->slavelist[slave].outputs,context->slavelist[slave].Ostartbit);}FMMUc++;}context->slavelist[slave].FMMUunused = FMMUc;/* 如果标志为真,则为输出添加一个WKC */if (AddToOutputsWKC)context->grouplist[group].outputsWKC++;
}/** 将一组从站中的所有PDO映射到IOmap,按顺序排列(传统SOEM方式)。** @param[in] context = 上下文结构* @param[out] pIOmap = 指向IOmap的指针* @param[in] group = 要映射的组,0表示所有组* @return IOmap大小*/
int ecx_config_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
{uint16 slave, configadr;uint8 BitPos;uint32 LogAddr = 0;uint32 oLogAddr = 0;uint32 diff;uint16 currentsegment = 0;uint32 segmentsize = 0;if ((*(context->slavecount) > 0) && (group < context->maxgroup)){EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);LogAddr = context->grouplist[group].logstartaddr;oLogAddr = LogAddr;BitPos = 0;context->grouplist[group].nsegments = 0;context->grouplist[group].outputsWKC = 0;context->grouplist[group].inputsWKC = 0;/* 查找映射并设置同步管理器 */ecx_config_find_mappings(context, group);/* 对从站进行输出映射并设置FMMUs */for (slave = 1; slave <= *(context->slavecount); slave++){configadr = context->slavelist[slave].configadr;if (!group || (group == context->slavelist[slave].group)){/* 创建输出映射 */if (context->slavelist[slave].Obits){ecx_config_create_output_mappings (context, pIOmap, group, slave, &LogAddr, &BitPos);diff = LogAddr - oLogAddr;oLogAddr = LogAddr;if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM)){context->grouplist[group].IOsegment[currentsegment] = segmentsize;if (currentsegment < (EC_MAXIOSEGMENTS - 1)){currentsegment++;segmentsize = diff;}}else{segmentsize += diff;}}}}if (BitPos){LogAddr++;oLogAddr = LogAddr;BitPos = 0;if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM)){context->grouplist[group].IOsegment[currentsegment] = segmentsize;if currentsegment < (EC_MAXIOSEGMENTS - 1)){currentsegment++;segmentsize = 1;}}else{segmentsize += 1;}}context->grouplist[group].outputs = pIOmap;context->grouplist[group].Obytes = LogAddr - context->grouplist[group].logstartaddr;context->grouplist[group].nsegments = currentsegment + 1;context->grouplist[group].Isegment = currentsegment;context->grouplist[group].Ioffset = (uint16)segmentsize;if (!group){context->slavelist[0].outputs = pIOmap;context->slavelist[0].Obytes = LogAddr - context->grouplist[group].logstartaddr; /* 在主记录中存储输出字节 */}/* 对从站进行输入映射并设置FMMUs */for (slave = 1; slave <= *(context->slavecount); slave++){configadr = context->slavelist[slave].configadr;if (!group || (group == context->slavelist[slave].group)){/* 创建输入映射 */if (context->slavelist[slave].Ibits){ecx_config_create_input_mappings(context, pIOmap, group, slave, &LogAddr, &BitPos);diff = LogAddr - oLogAddr;oLogAddr = LogAddr;if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM)){context->grouplist[group].IOsegment[currentsegment] = segmentsize;if (currentsegment < (EC_MAXIOSEGMENTS - 1)){currentsegment++;segmentsize = diff;}}else{segmentsize += diff;}}ecx_eeprom2pdi(context, slave); /* 将EEPROM控制设置为PDI *//* 用户可以覆盖自动状态更改 */if (context->manualstatechange == 0){/* 请求从站进入安全操作状态 */ecx_FPWRw(context->port,configadr,ECT_REG_ALCTL,htoes(EC_STATE_SAFE_OP),EC_TIMEOUTRET3); /* 设置安全操作状态 */}if (context->slavelist[slave].blockLRW){context->grouplist[group].blockLRW++;}context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;}}if (BitPos){LogAddr++;oLogAddr = LogAddr;BitPos = 0;if ((segmentsize + 1) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM)){context->grouplist[group].IOsegment[currentsegment] = segmentsize;if (currentsegment < (EC_MAXIOSEGMENTS - 1)){currentsegment++;segmentsize = 1;}}else{segmentsize += 1;}}context->grouplist[group].IOsegment[currentsegment] = segmentsize;context->grouplist[group].nsegments = currentsegment + 1;context->grouplist[group].inputs = (uint8 *)(pIOmap) + context->grouplist[group].Obytes;context->grouplist[group].Ibytes = LogAddr - context->grouplist[group].logstartaddr - context->grouplist[group].Obytes;if (!group){context->slavelist[0].inputs = (uint8 *)(pIOmap) + context->slavelist[0].Obytes;context->slavelist[0].Ibytes = LogAddr - context->grouplist[group].logstartaddr - context->slavelist[0].Obytes; /* 在主记录中存储输入字节 */}EC_PRINT("IOmapSize %d\n", LogAddr - context->grouplist[group].logstartaddr);return (LogAddr - context->grouplist[group].logstartaddr);}return 0;
}/** 将一组从站中的所有PDO映射到IOmap,包括输出和输入,具有重叠。注意:在使用LRW时必须使用这个功能。** @param[in] context = 上下文结构* @param[out] pIOmap = 指向IOmap的指针* @param[in] group = 要映射的组,0表示所有组* @return IOmap大小*/
int ecx_config_overlap_map_group(ecx_contextt *context, void *pIOmap, uint8 group)
{uint16 slave, configadr;uint8 BitPos;uint32 mLogAddr = 0;uint32 siLogAddr = 0;uint32 soLogAddr = 0;uint32 tempLogAddr;uint32 diff;uint16 currentsegment = 0;uint32 segmentsize = 0;if ((*(context->slavecount) > 0) && (group < context->maxgroup)){EC_PRINT("ec_config_map_group IOmap:%p group:%d\n", pIOmap, group);mLogAddr = context->grouplist[group].logstartaddr;siLogAddr = mLogAddr;soLogAddr = mLogAddr;BitPos = 0;context->grouplist[group].nsegments = 0;context->grouplist[group].outputsWKC = 0;context->grouplist[group].inputsWKC = 0;/* 查找映射并设置同步管理器 */ecx_config_find_mappings(context, group);/* 对从站进行IO映射并设置FMMUs */for (slave = 1; slave <= *(context->slavecount); slave++){configadr = context->slavelist[slave].configadr;siLogAddr = soLogAddr = mLogAddr;if (!group || (group == context->slavelist[slave].group)){/* 创建输出映射 */if (context->slavelist[slave].Obits){ecx_config_create_output_mappings(context, pIOmap, group, slave, &soLogAddr, &BitPos);if (BitPos){soLogAddr++;BitPos = 0;}}/* 创建输入映射 */if (context->slavelist[slave].Ibits){ecx_config_create_input_mappings(context, pIOmap, group, slave, &siLogAddr, &BitPos);if (BitPos){siLogAddr++;BitPos = 0;}}tempLogAddr = (siLogAddr > soLogAddr) ? siLogAddr : soLogAddr;diff = tempLogAddr - mLogAddr;mLogAddr = tempLogAddr;if ((segmentsize + diff) > (EC_MAXLRWDATA - EC_FIRSTDCDATAGRAM)){context->grouplist[group].IOsegment[currentsegment] = segmentsize;if (currentsegment < (EC_MAXIOSEGMENTS - 1)){currentsegment++;segmentsize = diff;}}else{segmentsize += diff;}ecx_eeprom2pdi(context, slave); /* 将EEPROM控制设置为PDI *//* 用户可以覆盖自动状态更改 */if (context->manualstatechange == 0){/* 请求从站进入安全操作状态 */ecx_FPWRw(context->port,configadr,ECT_REG_ALCTL,htoes(EC_STATE_SAFE_OP),EC_TIMEOUTRET3);}if (context->slavelist[slave].blockLRW){context->grouplist[group].blockLRW++;}context->grouplist[group].Ebuscurrent += context->slavelist[slave].Ebuscurrent;}}context->grouplist[group].IOsegment[currentsegment] = segmentsize;context->grouplist[group].nsegments = currentsegment + 1;context->grouplist[group].Isegment = 0;context->grouplist[group].Ioffset = 0;context->grouplist[group].Obytes = soLogAddr - context->grouplist[group].logstartaddr;context->grouplist[group].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;context->grouplist[group].outputs = pIOmap;context->grouplist[group].inputs = (uint8 *)pIOmap + context->grouplist[group].Obytes;/* 将计算的输入数据移动到带有OBytes偏移的位置 */for (slave = 1; slave <= *(context->slavecount); slave++){if (!group || (group == context->slavelist[slave].group)){if(context->slavelist[slave].Ibits > 0){context->slavelist[slave].inputs += context->grouplist[group].Obytes;}}}if (!group){/* 在主记录中存储输出字节 */context->slavelist[0].outputs = pIOmap;context->slavelist[0].Obytes = soLogAddr - context->grouplist[group].logstartaddr; context->slavelist[0].inputs = (uint8 *)pIOmap + context->slavelist[0].Obytes;context->slavelist[0].Ibytes = siLogAddr - context->grouplist[group].logstartaddr;}EC_PRINT("IOmapSize %d\n", context->grouplist[group].Obytes + context->grouplist[group].Ibytes);return (context->grouplist[group].Obytes + context->grouplist[group].Ibytes);}return 0;
}/** 恢复从站。** @param[in] context = 上下文结构* @param[in] slave = 要恢复的从站* @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3* @return >0 表示成功*/
int ecx_recover_slave(ecx_contextt *context, uint16 slave, int timeout)
{int rval;int wkc;uint16 ADPh, configadr, readadr;rval = 0;configadr = context->slavelist[slave].configadr;ADPh = (uint16)(1 - slave);/* 检查是否找到了与请求的从站不同的从站 */readadr = 0xfffe;wkc = ecx_APRD(context->port, ADPh, ECT_REG_STADR, sizeof(readadr), &readadr, timeout);/* 找到正确的从站,完成 */if(readadr == configadr){return 1;}/* 仅在没有配置地址时尝试 */if( (wkc > 0) && (readadr == 0)){/* 清除可能的从站地址EC_TEMPNODE */ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);/* 设置从站的临时节点地址 */if(ecx_APWRw(context->port, ADPh, ECT_REG_STADR, htoes(EC_TEMPNODE) , timeout) <= 0){ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , 0);return 0; /* 从站未响应 */}context->slavelist[slave].configadr = EC_TEMPNODE; /* 临时配置地址 */ecx_eeprom2master(context, slave); /* 设置EEPROM控制为主站 *//* 检查从站是否与之前配置的相同 */if ((ecx_FPRDw(context->port, EC_TEMPNODE, ECT_REG_ALIAS, timeout) ==htoes(context->slavelist[slave].aliasadr)) &&(ecx_readeeprom(context, slave, ECT_SII_ID, EC_TIMEOUTEEP) ==htoel(context->slavelist[slave].eep_id)) &&(ecx_readeeprom(context, slave, ECT_SII_MANUF, EC_TIMEOUTEEP) ==htoel(context->slavelist[slave].eep_man)) &&(ecx_readeeprom(context, slave, ECT_SII_REV, EC_TIMEOUTEEP) ==htoel(context->slavelist[slave].eep_rev))){rval = ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(configadr) , timeout);context->slavelist[slave].configadr = configadr;}else{/* 从站与预期的不同,移除配置地址 */ecx_FPWRw(context->port, EC_TEMPNODE, ECT_REG_STADR, htoes(0) , timeout);context->slavelist[slave].configadr = configadr;}}return rval;
}/** 重新配置从站。** @param[in] context = 上下文结构* @param[in] slave = 要重新配置的从站* @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3* @return 从站状态*/
int ecx_reconfig_slave(ecx_contextt *context, uint16 slave, int timeout)
{int state, nSM, FMMUc;uint16 configadr;configadr = context->slavelist[slave].configadr;if (ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_INIT) , timeout) <= 0){return 0;}state = 0;ecx_eeprom2pdi(context, slave); /* 设置EEPROM控制为PDI *//* 检查状态变化是否初始化 */state = ecx_statecheck(context, slave, EC_STATE_INIT, EC_TIMEOUTSTATE);if(state == EC_STATE_INIT){/* 程序所有已启用的SM */for( nSM = 0 ; nSM < EC_MAXSM ; nSM++ ){if (context->slavelist[slave].SM[nSM].StartAddr){ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_SM0 + (nSM * sizeof(ec_smt)),sizeof(ec_smt), &context->slavelist[slave].SM[nSM], timeout);}}ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_PRE_OP) , timeout);state = ecx_statecheck(context, slave, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); /* 检查状态变化到Pre-op */if( state == EC_STATE_PRE_OP){/* 执行特殊的从站配置钩子,从Pre-Op到Safe-Op */if(context->slavelist[slave].PO2SOconfig) /* 仅在注册时执行 */{context->slavelist[slave].PO2SOconfig(slave);}if (context->slavelist[slave].PO2SOconfigx) /* 仅在注册时执行 */{context->slavelist[slave].PO2SOconfigx(context, slave);} ecx_FPWRw(context->port, configadr, ECT_REG_ALCTL, htoes(EC_STATE_SAFE_OP) , timeout); /* 设置Safe-Op状态 */state = ecx_statecheck(context, slave, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE); /* 检查状态变化到Safe-Op *//* 程序已配置的FMMU */for( FMMUc = 0 ; FMMUc < context->slavelist[slave].FMMUunused ; FMMUc++ ){ecx_FPWR(context->port, configadr, (uint16)(ECT_REG_FMMU0 + (sizeof(ec_fmmut) * FMMUc),sizeof(ec_fmmut), &context->slavelist[slave].FMMU[FMMUc], timeout);}}}return state;
}#ifdef EC_VER1
/** 枚举并初始化所有从站。** @param[in] usetable = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表* @return 所发现的从站数据报的工作计数 = 找到的从站数量* @see ecx_config_init*/
int ec_config_init(uint8 usetable)
{return ecx_config_init(&ecx_context, usetable);
}/** 将一个组中的所有从站的所有PDOs按照顺序映射到IOmap中,遵循传统SOEM方式。** @param[out] pIOmap = 指向IOmap的指针* @param[in] group = 要映射的组,0表示所有组* @return IOmap的大小* @see ecx_config_map_group*/
int ec_config_map_group(void *pIOmap, uint8 group)
{return ecx_config_map_group(&ecx_context, pIOmap, group);
}/** 将一个组中的所有从站的所有PDOs映射到IOmap中,允许输出和输入重叠。注意:使用LRW时必须使用此方式,适用于TI ESC。** @param[out] pIOmap = 指向IOmap的指针* @param[in] group = 要映射的组,0表示所有组* @return IOmap的大小* @see ecx_config_overlap_map_group*/
int ec_config_overlap_map_group(void *pIOmap, uint8 group)
{return ecx_config_overlap_map_group(&ecx_context, pIOmap, group);
}/** 将所有从站的所有PDOs按照顺序映射到IOmap中,遵循传统SOEM方式。** @param[out] pIOmap = 指向IOmap的指针* @return IOmap的大小*/
int ec_config_map(void *pIOmap)
{return ec_config_map_group(pIOmap, 0);
}/** 将所有从站的所有PDOs映射到IOmap中,允许输出和输入重叠。注意:使用LRW时必须使用此方式,适用于TI ESC。** @param[out] pIOmap = 指向IOmap的指针* @return IOmap的大小*/
int ec_config_overlap_map(void *pIOmap)
{return ec_config_overlap_map_group(pIOmap, 0);
}/** 枚举、映射并初始化所有从站。** @param[in] usetable = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表* @param[out] pIOmap = 指向IOmap的指针* @return 所发现的从站数据报的工作计数 = 找到的从站数量*/
int ec_config(uint8 usetable, void *pIOmap)
{int wkc;wkc = ec_config_init(usetable);if (wkc){ec_config_map(pIOmap);}return wkc;
}/** 枚举、映射并初始化所有从站。** @param[in] usetable = TRUE表示使用配置表来初始化从站,FALSE表示不使用配置表* @param[out] pIOmap = 指向IOmap的指针* @return 所发现的从站数据报的工作计数 = 找到的从站数量*/
int ec_config_overlap(uint8 usetable, void *pIOmap)
{int wkc;wkc = ec_config_init(usetable);if (wkc){ec_config_overlap_map(pIOmap);}return wkc;
}/** 恢复从站。** @param[in] slave = 要恢复的从站* @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3* @return 如果成功则返回大于0* @see ecx_recover_slave*/
int ec_recover_slave(uint16 slave, int timeout)
{return ecx_recover_slave(&ecx_context, slave, timeout);
}/** 重新配置从站。** @param[in] slave = 要重新配置的从站* @param[in] timeout = 本地超时,例如EC_TIMEOUTRET3* @return 从站状态* @see ecx_reconfig_slave*/
int ec_reconfig_slave(uint16 slave, int timeout)
{return ecx_reconfig_slave(&ecx_context, slave, timeout);
}
#endif
该文档修改记录:
修改时间 | 修改说明 |
---|---|
2023年11月4日 | EtherCAT主站SOEM – 3 – SOEM之ethercatconfig.h/c文件解析 |
总结
以上就是EtherCAT主站SOEM – 3 --SOEM之ethercatconfig.h/c文件解析的内容。
有不明白的地方欢迎留言;有建议欢迎留言,我后面编写文档好改进。
创作不容,如果文档对您有帮助,记得给个赞。
相关文章:
EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析
EtherCAT主站SOEM -- 3 -- SOEM之ethercatconfig.h/c文件解析 一 ethercatconfig.h/c文件功能预览:二 ethercatconfig.h/c 中主要函数的作用:2.1.1 ec_config_init(uint8 usetable) 和 ecx_config_init(ecx_contextt *context, uint8 usetable)ÿ…...

洗地机哪个品牌好?家用洗地机选购攻略
随着家用洗地机的普及和市场的广泛认可,进入洗地机行业的制造商也越来越多。在面对众多洗地机品牌时,消费者常常感到困惑,不知道如何选择。面对众多选择,选择有良好保障的知名洗地机品牌是明智之举。知名品牌在质量、售后服务等方…...

Java数组的定义与常用使用方法
目录 一.什么是数组 二.数组的创建及初始化 数组的创建 数组的初始化 动态初始化: 静态初始化: 【注意】 三.数组的使用 数组中元素访问 遍历数组 四.数组作为方法的参数 参数传基本数据类型 参数传数组类型(引用数据类型) 作为方法的返回…...

[计算机网络]认识“协议”
认识“协议” 文章目录 认识“协议”序列化和反序列化网络计算器引入Sock类设计协议编写服务端类启动服务端编写客户端类启动客户端程序测试 序列化和反序列化 在网络体系结构中,应用层的应用程序会产生数据,这个数据往往不是简单的一段字符串数据&…...
“Notepad++“ 官网地址
notepad官网下载地址:https://notepad-plus-plus.org/downloads/ npp.8.5.8.Installer.x64 本下载地址- https://download.csdn.net/download/namekong8/88494023 1. Fix session file data loss issue. 2. Fix Explorer context menu "Edit with Notepad…...

基于单片机的自动感应门设计
博主主页:单片机辅导设计 博主简介:专注单片机技术领域和毕业设计项目。 主要内容:毕业设计、简历模板、学习资料、技术咨询。 文章目录 主要介绍一、自动感应门设计的功能概述二、系统总体方案2.1系统的总体计划2.2元器件的介绍2.2.1单片机的…...

【密评】商用密码应用安全性评估从业人员考核题库(二十-完结)
商用密码应用安全性评估从业人员考核题库(二十-完结) 国密局给的参考题库5000道只是基础题,后续更新完5000还会继续更其他高质量题库,持续学习,共同进步。 4640 单项选择题 在测评过程中遇到的PEM编码格式,…...

Tigger绕过激活锁/屏幕锁隐藏工具,支持登入iCloud有消息通知,支持iOS12.0-14.8.1。
绕过激活锁工具Tigger可以用来帮助因为忘记自己的ID或者密码而导致iPhone/iPad无法激活的工具来绕过自己的iPhone/iPad。工具支持Windows和Mac。 工具支持的功能: 1.Hello界面两网/三网/无基带/乱码绕过,可以完美重启,支持iCloud登录、有消…...

VueX mapState、mapGetters、mapActions和mapMutaions 的使用
一、mapState和mapGetters 如果我们想要读取VueX中的State数据的Getters数据时,需要使用$store.state.数据名 和 $store.getters.数据名。 当我们State和getters中的数据多了以后,书写会很麻烦: 如果我们想要使用方便可以配置计算属性来简化…...
GMP标准的制药级层流细胞实验室核心要点
随着生物医药技术的飞速发展,制药行业对细胞疗法和细胞药物的需求日益增长。这推动了制药级层流细胞实验室的发展,其作为生物医药研发的关键基础设施,为制药企业提供了进行细胞培养、基因编辑、疫苗研发等工作的高效平台。本文就围绕GMP标准的…...

[免费] 适用于 Windows的10 的十大数据恢复软件
Windows 10是微软开发的跨平台和设备应用程序的操作系统。它启动速度更快,具有熟悉且扩展的“开始”菜单,甚至可以在多种设备上以新的方式工作。所以,Windows 10非常流行,我们用它来保存我们的照片、音乐、文档和更多文件。但有时…...

【halcon踩坑】区域为空但个数是1
背景 我在做瑕疵检测的时候,通过计算瑕疵区域的个数(count_obj())是否为0,来判断是否有瑕疵,如果不为0,那边我就会在图片上标记这个瑕疵的位置! 但是有一次我发现明明没…...

第二十四章 BEV感知系列一(车道线感知)
前言 近期参与到了手写AI的车道线检测的学习中去,以此系列笔记记录学习与思考的全过程。车道线检测系列会持续更新,力求完整精炼,引人启示。所需前期知识,可以结合手写AI进行系统的学习。 BEV感知系列是对论文Delving into the De…...

C++入门讲解第一篇
大家好,我是Dark Fire,终于进入了C的学习,我知道面对我的将是什么,就算变成秃头佬,也要把C学好,今天是C入门第一篇,我会尽全力将知识以清晰易懂的方式表达出,希望我们一起加油&#…...

项目实战:分页功能实战
1、在index.html添加点击事件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"style/index.css"><script src"scr…...
AI人工智能大模型应用如何落地?
人工智能大模型是近年来人工智能领域的一项重要技术突破,其具备强大的计算能力和学习能力,能够处理大规模的数据和复杂的任务。 然而,要将人工智能大模型应用落地并实现实际价值,还需要克服一些挑战和问题。 首先,人…...

【优选算法系列】第一节.栈的简介(1047. 删除字符串中的所有相邻重复项和844. 比较含退格的字符串)
文章目录 前言一、删除字符串中的所有相邻重复项和 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写二、比较含退格的字符串 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写总结 前言 …...

PostgreSQL逻辑管理结构
1.数据库逻辑结构介绍 2.数据库基本操作 2.1 创建数据库 CREATE DATABASE name [ [ WITH ] [ OWNER [] user_name ] [ TEMPLATE [] template ] [ ENCODING [] encoding ] [ LC_COLLATE [] lc_collate ] [ LC_CTYPE [] lc_ctype ] [ TABLESPACE [] tablespace ] [ CONNECTION L…...

高匿IP有什么作用
在互联网的蓬勃发展中,IP地址作为网络通信的基础,一直扮演着举足轻重的角色。而在诸多IP地址中,高匿IP地址则是一种特殊类型,其作用和价值在某些特定场合下尤为突出。那么,高匿IP地址究竟有哪些用处呢? 首先…...
Ubuntu Linux 23.10安装manimgl
1. 简介:manimgl是使用Python语言开发数学动画的一个库。用来创建数学动画。版本有很多,今天介绍manimgl,他要依赖OpenGL库。 2. 打开Shell命令行,连接上互联网。先安装opengl。 Shell>>> sudo apt install l…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...

如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
Python爬虫实战:研究Restkit库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验
2024年初,人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目(一款融合大型语言模型能力的云端AI编程IDE)时,技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力,TRAE在WayToAGI等…...
6.9本日总结
一、英语 复习默写list11list18,订正07年第3篇阅读 二、数学 学习线代第一讲,写15讲课后题 三、408 学习计组第二章,写计组习题 四、总结 明天结束线代第一章和计组第二章 五、明日计划 英语:复习l默写sit12list17&#…...