SOEM_1(笔记,从别的博客文章学的笔记)

news/2024/4/29 10:41:31/文章来源:https://blog.csdn.net/a1347065101/article/details/131732267

目录介绍:

doc:帮助文档、
osal:主要是用于符合OSADL和实时进程创建。也就是说:发送EtherCAT数据包不能抖动太大,如果直接使用linux提供的原生线程,可能实时性无法满足。需要对Linux内核打上实时补丁,我们采用PREEMPT_RT
oshw:网卡的接口封装,不同操作系统对网卡操作不一样,linux下我们要求网卡支持混杂模式。
soem:EtherCAT主站的核心代码。包括COE,FOE等等。

main函数先开线程:

int main(int argc, char *argv[])
{printf("SOEM (Simple Open EtherCAT Master)\nSimple test\n");if (argc > 1){      /* create thread to handle slave error handling in OP */
//      pthread_create( &thread1, NULL, (void *) &ecatcheck, (void*) &ctime);   osal_thread_create(&thread1, 128000, &ecatcheck, (void*) &ctime);/* start cyclic part */simpletest(argv[1]);}else{printf("Usage: simple_test ifname1\nifname = eth0 for example\n");}   printf("End program\n");return (0);
}

 开了线程之后,调用simpletest函数,并把网卡名作为输入参数。

进入simpletest,第一步是调用ec_init函数:

int ec_init(const char * ifname)
{return ecx_init(&ecx_context, ifname);
}

注意一下ecx_context,这个结构体定义如下:

struct ecx_context
{/** port reference, may include red_port */ecx_portt      *port;/** slavelist reference */ec_slavet      *slavelist;/** number of slaves found in configuration */int            *slavecount;/** maximum number of slaves allowed in slavelist */int            maxslave;/** grouplist reference */ec_groupt      *grouplist;/** maximum number of groups allowed in grouplist */int            maxgroup;/** internal, reference to eeprom cache buffer */uint8          *esibuf;/** internal, reference to eeprom cache map */uint32         *esimap;/** internal, current slave for eeprom cache */uint16         esislave;/** internal, reference to error list */ec_eringt      *elist;/** internal, reference to processdata stack buffer info */ec_idxstackT   *idxstack;/** reference to ecaterror state */boolean        *ecaterror;/** internal, position of DC datagram in process data packet */uint16         DCtO;/** internal, length of DC datagram */uint16         DCl;/** reference to last DC time from slaves */int64          *DCtime;/** internal, SM buffer */ec_SMcommtypet *SMcommtype;/** internal, PDO assign list */ec_PDOassignt  *PDOassign;/** internal, PDO description list */ec_PDOdesct    *PDOdesc;/** internal, SM list from eeprom */ec_eepromSMt   *eepSM;/** internal, FMMU list from eeprom */ec_eepromFMMUt *eepFMMU;/** registered FoE hook */int            (*FOEhook)(uint16 slave, int packetnumber, int datasize);/** registered EoE hook */int            (*EOEhook)(ecx_contextt * context, uint16 slave, void * eoembx);/** flag to control legacy automatic state change or manual state change */int            manualstatechange;
};

于是,变量ecx_context又把总线运行的一系列信息放在了这里:

ecx_contextt  ecx_context = {&ecx_port,          // .port          =&ec_slave[0],       // .slavelist     =&ec_slavecount,     // .slavecount    =EC_MAXSLAVE,        // .maxslave      =&ec_group[0],       // .grouplist     =EC_MAXGROUP,        // .maxgroup      =&ec_esibuf[0],      // .esibuf        =&ec_esimap[0],      // .esimap        =0,                  // .esislave      =&ec_elist,          // .elist         =&ec_idxstack,       // .idxstack      =&EcatError,         // .ecaterror     =0,                  // .DCtO          =0,                  // .DCl           =&ec_DCtime,         // .DCtime        =&ec_SMcommtype[0],  // .SMcommtype    =&ec_PDOassign[0],   // .PDOassign     =&ec_PDOdesc[0],     // .PDOdesc       =&ec_SM,             // .eepSM         =&ec_FMMU,           // .eepFMMU       =NULL,               // .FOEhook()NULL,               // .EOEhook()0                   // .manualstatechange
};

再然后,ecx_init调用了ecx_setupnic:套娃套娃

int ecx_init(ecx_contextt *context, const char * ifname)
{return ecx_setupnic(context->port, ifname, FALSE);
}

最终是这个函数:

int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary) 
{int i;char ifn[IF_NAME_SIZE];int unit_no = -1;   ETHERCAT_PKT_DEV * pPktDev;/* Get systick info, sysClkRateGet return ticks per second */usec_per_tick =  USECS_PER_SEC / sysClkRateGet();/* Don't allow 0 since it is used in DIV */if(usec_per_tick == 0)usec_per_tick = 1;/* Make reference to packet device struct, keep track if the packet* device is the redundant or not.*/if (secondary){pPktDev = &(port->redport->pktDev);pPktDev->redundant = 1;}else{pPktDev = &(port->pktDev);pPktDev->redundant = 0;}/* Clear frame counters*/pPktDev->tx_count = 0;pPktDev->rx_count = 0;pPktDev->overrun_count = 0;/* Create multi-thread support semaphores */port->sem_get_index = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);/* Get the dev name and unit from ifname * We assume form gei1, fei0... */memset(ifn,0x0,sizeof(ifn));for(i=0; i < strlen(ifname);i++){if(isdigit(ifname[i])){strncpy(ifn, ifname, i);unit_no = atoi(&ifname[i]);break;}}/* Detach IP stack *///ipDetach(pktDev.unit,pktDev.name);pPktDev->port = port;/* Bind to mux driver for given interface, include ethercat driver pointer * as user reference *//* Bind to mux */pPktDev->pCookie = muxBind(ifn,unit_no, mux_rx_callback, NULL, NULL, NULL, MUX_PROTO_SNARF, "ECAT SNARF", pPktDev);if (pPktDev->pCookie == NULL){/* fail */NIC_LOGMSG("ecx_setupnic: muxBind init for gei: %d failed\n", unit_no, 2, 3, 4, 5, 6);goto exit;}/* Get reference tp END obje */pPktDev->endObj = endFindByName(ifn, unit_no);if (port->pktDev.endObj == NULL){/* fail */NIC_LOGMSG("error_hook:  endFindByName failed, device gei: %d not found\n",unit_no, 2, 3, 4, 5, 6);goto exit;}if (secondary){/* secondary port struct available? */if (port->redport){port->redstate                   = ECT_RED_DOUBLE;port->redport->stack.txbuf       = &(port->txbuf);port->redport->stack.txbuflength = &(port->txbuflength);port->redport->stack.rxbuf       = &(port->redport->rxbuf);port->redport->stack.rxbufstat   = &(port->redport->rxbufstat);port->redport->stack.rxsa        = &(port->redport->rxsa);/* Create mailboxes for each potential EtherCAT frame index */for (i = 0; i < EC_MAXBUF; i++){port->redport->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);if (port->redport->msgQId[i] == MSG_Q_ID_NULL){NIC_LOGMSG("ecx_setupnic: Failed to create redundant MsgQ[%d]",i, 2, 3, 4, 5, 6);goto exit;}}ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));}else{/* fail */NIC_LOGMSG("ecx_setupnic: Redundant port not allocated",unit_no, 2, 3, 4, 5, 6);goto exit;}}else{port->lastidx           = 0;port->redstate          = ECT_RED_NONE;port->stack.txbuf       = &(port->txbuf);port->stack.txbuflength = &(port->txbuflength);port->stack.rxbuf       = &(port->rxbuf);port->stack.rxbufstat   = &(port->rxbufstat);port->stack.rxsa        = &(port->rxsa);/* Create mailboxes for each potential EtherCAT frame index */for (i = 0; i < EC_MAXBUF; i++){port->msgQId[i] = msgQCreate(1, sizeof(M_BLK_ID), MSG_Q_FIFO);if (port->msgQId[i] == MSG_Q_ID_NULL){NIC_LOGMSG("ecx_setupnic: Failed to create MsgQ[%d]",i, 2, 3, 4, 5, 6);goto exit;}}ecx_clear_rxbufstat(&(port->rxbufstat[0]));}/* setup ethernet headers in tx buffers so we don't have to repeat it */for (i = 0; i < EC_MAXBUF; i++) {ec_setupheader(&(port->txbuf[i]));port->rxbufstat[i] = EC_BUF_EMPTY;}ec_setupheader(&(port->txbuf2));return 1;exit:return 0;}

简单来说,就是分配收发缓冲区地址,打开硬件,再把数据包头写到每一个发送缓冲区首部,免得后续每次都写。另外初始化了一些保护关键代码段的互斥锁。如果是裸跑的话,在保护关键代码的时候可能要考虑用开关中断来实现了。再一个,可以看到,这里实际上是可以打开第二个网口的。两个网口,一个作为输出,一个作为输入。这个按实际情况来做吧,目前见到的应用,多数是只用一个网口的。

这里的重点在于这个port。可以看到,实际上这里是用了在ethercatmain.c文件中定义的全局变量:

ecx_portt               ecx_port;

还记得之前说的那个ecx_context吗?对,这个ecx_port就是ecx_context里面的。

在nicdrv.h文件中,这个exc_portt定义如下:

typedef struct ecx_port
{/** Stack reference */   ec_stackT stack;/** Packet device instance */ETHERCAT_PKT_DEV pktDev;/** rx buffers */ec_bufT rxbuf[EC_MAXBUF];/** rx buffer status */int rxbufstat[EC_MAXBUF];/** rx MAC source address */int rxsa[EC_MAXBUF];/** transmit buffers */ec_bufT txbuf[EC_MAXBUF];/** transmit buffer lengths */int txbuflength[EC_MAXBUF];/** temporary tx buffer */ec_bufT txbuf2;/** temporary tx buffer length */int txbuflength2;/** last used frame index */int lastidx;/** current redundancy state */int redstate;/** pointer to redundancy port and buffers */ecx_redportt *redport;   /** Semaphore to protect single resources */SEM_ID  sem_get_index;/** MSG Q for receive callbacks to post into */MSG_Q_ID  msgQId[EC_MAXBUF];
} ecx_portt;

这里EC_MAXBUF是ethercattype.h文件当中的宏定义:

#define EC_MAXBUF          16

该文件在SOEM文件夹当中。还有ec_bufT的定义

#define EC_MAXECATFRAME    1518

#define EC_BUFSIZE         EC_MAXECATFRAME

typedef uint8 ec_bufT[EC_BUFSIZE];

这个1518数字看着眼熟。看看EtherCAT数据帧格式就一目了然了:

顺便也说一下这个ecx_redportt结构体:

/** pointer structure to buffers for redundant port */
typedef struct ecx_redport
{/** Stack reference */   ec_stackT stack;/** Packet device instance */ETHERCAT_PKT_DEV pktDev;/** rx buffers */ec_bufT rxbuf[EC_MAXBUF];/** rx buffer status */int rxbufstat[EC_MAXBUF];/** rx MAC source address */int rxsa[EC_MAXBUF];/** MSG Q for receive callbacks to post into */MSG_Q_ID  msgQId[EC_MAXBUF];
} ecx_redportt;

    依然是在nicdrv.h当中定义。这就是一个只有接收功能的ecx_portt的阉割版本,没有发送缓冲区及其状态标志,因为收发过程中的关键代码保护互斥量已经有了,所以这里连互斥量都省了。细心的你可能在ecx_portt当中发现一个问题:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_331165.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELK-日志服务【kafka-配置使用】

kafka-01 10.0.0.21 kafka-02 10.0.0.22 kafka-03 10.0.0.23 【1】安装zk集群、配置 [rootes-01 ~]# yum -y install java maven [rootes-01 ~]# tar xf apache-zookeeper-3.5.9-bin.tar.gz -C /opt/[rootes-01 ~]# cd /opt/apache-zookeeper-3.5.9-bin/conf/ [rootes-…

复习第五课 C语言-初识数组

目录 【1】初识数组 【2】一维数组 【3】清零函数 【4】字符数组 【5】计算字符串实际长度 练习&#xff1a; 【1】初识数组 1. 概念&#xff1a;具有一定顺序的若干变量的集合 2. 定义格式&#xff1a; 数组名 &#xff1a;代表数组的首地址&#xff0c;地址常量&…

字符函数和内存函数(二)

目录 一、strtok函数 二、strerror函数 三、memcpy函数 3.1memcpy函数的认识 3.2memcpy函数的模拟实现 四、memmove函数 4.1memmove函数的认识 4.2memmove函数的模拟实现 五、memcmp函数 5.1memcmp函数的认识 5.2memcmp函数的模拟实现 六、memset函数 七、字符分类函…

PyCharm 自动添加作者信息、创建时间等信息

PyCharm 自动添加作者信息、创建时间等信息‘ 第一步 找到settings 第二步&#xff0c;找到下图所示位置输入下面代码&#xff0c;作者改成你自己的缩写&#xff0c;你也可以添加其他的 Project &#xff1a;${PROJECT_NAME} File &#xff1a;${NAME}.py IDE &…

【技能实训】DMS数据挖掘项目-Day09

文章目录 任务9【任务9.1.1】升级DataBase类为可序列化的类&#xff0c;以便在文件保存或网络中传递【任务9.1.2】升级LogRec类为可序列化的类&#xff0c;以便在文件保存或网络中传递【任务9.1.3】升级MatchedLogRec类为可序列化的类&#xff0c;以便在文件保存或网络中传递【…

StringBuffer类 StringBuilder 类

StringBuffer类 介绍 StringBuffer是一个容器&#xff0c;代表可变的字符序列&#xff0c;可以对字符串内容进行增删。 StringBuffer是可变长度的。 实现了序列化接口&#xff0c;可实现串行化&#xff08;可以将内容保存至文件或者网络传输&#xff09;&#xff1a; Serial…

关于Java的网络编程

网络的一些了解 网络通信协议 链路层&#xff1a;链路层是用于定义物理传输通道&#xff0c;通常是对某些网络连接设备的驱动协议&#xff0c;例如针对光纤、网线提供的驱动。网络层&#xff1a;网络层是整个TCP/IP协议的核心&#xff0c;它主要用于将传输的数据进行分组&…

华为申请注册盘古大模型商标;京东推出言犀大模型,率先布局产业应用

7月14日科技新闻早知道&#xff0c;一分钟速览。 1.华为申请注册盘古大模型商标&#xff1a; 据天眼查 App 显示&#xff0c;7 月 7 日&#xff0c;华为技术有限公司申请注册“华为云盘古”、“Huawei Cloud Pangu Models”文字及图形商标&#xff0c;国际分类为网站服务、社…

文心一言 VS 讯飞星火 VS chatgpt (59)-- 算法导论6.4 3题

文心一言 VS 讯飞星火 VS chatgpt &#xff08;59&#xff09;-- 算法导论6.4 3题 三、对于一个按升序排列的包含 n 个元素的有序数组 A 来说&#xff0c;HEAPSORT 的时间复杂度是多少?如果 A 是降序呢? 文心一言&#xff1a; 对于一个按升序排列的包含 n 个元素的有序数组…

C++图形开发(14):游戏完善——无限空中起跳解决

文章目录 1.问题描述2.如何解决&#xff1f;3.整段代码 1.问题描述 在游玩过程中&#xff0c;我们肯定发现了之前所给出的游戏源码中的一个小bug&#xff1a; 小球可以空中无限起跳&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&…

【Linux操作系统】多线程抢票逻辑——学习互斥量(锁)函数接口

文章目录 1.进程线程间的互斥相关背景概念2.联系代码学习同步互斥问题3.互斥量&#xff08;锁&#xff09;的函数接口3.1初始化互斥量3.2销毁互斥量3.3互斥量加锁和解锁3.4改进多线程抢票代码 1.进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫…

在UI中使用SpriteMask裁减任意shader的粒子效果

前言 由于我们需要在Mask中对粒子效果进行裁减。但是我们的的特效同事不愿意每个shader都去添加Stencil。所以使用SpriteMask方式进行裁减。 使用步骤 1. 添加SpriteMask Component 更具你需要的Mask形状设置精灵图片。又因为实际是精灵&#xff0c;并不属于UI系统&#xff…

静态库 的制作与使用

文章目录 重要命令程序组成静态库制作流程静态库的使用 gcc main.c -o APP -I(i) 头文件路径 -l(L) 库名字&#xff08;xxx) -L 库路径 重要命令 gcc -o -I(大写i) -l(小写L) -L cp&#xff1a;复制文件 -r 递归&#xff0c;用于复制文件夹所有文件 mv&#xff1a;移动文件 程…

【ArcGIS Pro二次开发】(46):要素类从上到下、从左到右排序

要素类经过编辑之后&#xff0c;【OBJECTID】字段会变得不规律。应部分网友要求&#xff0c;做了这个从上到下、从左到右排序的工具。 不过后来在ArcGIS Pro中发现了一个【排序】工具&#xff0c;已经可以完美实现这个功能需求&#xff0c;发现自己做了个白工。 不过做了不能白…

【统计函数3】——excel常见函数

相关数据资料来源于网易 函数一览&#xff1a; rank、rand、randbetween、floor、int rank函数: 求某单元格在某区域内的排名 RANK(数值,引用区域,降序0/升序1)范围多指定&#xff1a; 分开的范围之间可用逗号隔开&#xff0c;最后再用一个小括号括起来。F4可以快速锁定行和列。…

20230714----重返学习-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性

day-112-one-hundred-and-twelve-20230714-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性 DOM-diff算法 vue2中diff算法 同级比对&#xff0c;跨级比对性能差。而且采用的方式是递归比对&#xff0c;更差一点。根节点只能有一个&#xff0c;比对的时候会从根节…

MySql5.6版本开启慢SQL功能

文章目录 1引言1.1目的1.2注意点说明1.3 操作步骤1.3.1 临时生效操作步骤1.3.2 永久生效操作步骤1.3.3 按日期生成日志文件1.3.4 执行成功后验证功能是否开启 1.4 慢SQL日志记录内容介绍1.5 Shell脚本 1引言 1.1目的 开启 MySQL 的慢查询日志&#xff08;Slow Query Log&…

【Distributed】分布式Ceph存储系统

文章目录 一、存储基础1. 单机存储设备1.1 DAS1.2 NAS1.3 SAN1.4 单机存储的问题1.5 商业存储解决方案 2. 分布式存储&#xff08;软件定义的存储 SDS &#xff09;分布式存储的类型 3. Ceph 简介4. Ceph 优势5. Ceph 架构6. Ceph 核心组件7. OSD 存储后端7.1 Filestore7.2 Blu…

微服务架构+创建微服务工程(商品/订单)

目录 1.微服务架构 1.1.单体应用架构 1.2.微服务应用 1.3 微服务架构的优势 1.4.微服务架构的缺点(挑战) 1.5. SpringCloud与微服务关系 1.6.SpringBoot和SpringCloud关系 2. 创建微服务工程 2.1.数据库 2.2.搭建父工程 2.2 创建公共模块 2.3.商品系统 2.4.订单微…

在Linux中使用curl访问EasyCVR页面,返回报错Unauthorized是什么原因?

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&#xff0c;比如&#xff1a;视…