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

news2024/12/26 12:59:58

目录介绍:

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.coloradmin.cn/o/754535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高等数学❤️第一章~第二节~极限❤️极限的概念与性质~数列极限详解

【精讲】高等数学中的数列极限解析 博主&#xff1a;命运之光的主页 专栏&#xff1a;高等数学 目录 导言 一、数列极限的定义 二、数列极限的判定方法 三、数列极限的性质 必需记忆知识点 例题&#xff08;用于熟悉数列极限&#xff09; 例题1 例题2 例题3 结论 导言…

【QQ好友列表展示-设置HeaderView Objective-C语言】

一、好,看一下,刚才我们这个btnGroupTitle的frame为什么是0 现在我们首先知道,当你程序一运行起来以后,你这里的self,就是 当你程序运行起来以后,这个headerView 的frame,默认应该是0的 就是我们看到的效果,它应该都是0 的 一开始都是0 的 那么其实也就是说,是这样…

Pruning 系列 (十)Pruning VGG 实战

环境 python 3.9numpy 1.24.1pytorch 2.0.0+cu117流程 一、 VGG 模型代码 # -*- coding: utf-8 -*- import torch import torch.nn as nndefault_cfg = {11: [64, M, 128, M, 256, 256, M, 512, 512, M, 512, 512],13: [64, 64, M, 128, 128, M, 256, 256, M, 512, 512, M, …

认识企业级定时任务Quartz

文章目录 前言一、实现一个Quartz的小案例1.创建一个maven项目2.添加Quartz依赖3.创建一个配置文件配置Quartz信息4.创建一个Job类继承Job接口5.编写主方法逻辑进行测试6.测试运行结果 二、Job和JobDetail总结 前言 目前仍有大部分企业仍在使用Quartz这种定时任务框架&#xf…

【分布式应用】ceph分布式存储

目录 一、存储基础1.1单机存储设备1.2单机存储的问题1.3分布式存储的类型 二、Ceph简介2.1Ceph 优势2.2Ceph 架构2.3Ceph核心组件OSD&#xff08;Object Storage Daemon&#xff0c;守护进程 ceph-osd&#xff09;PG&#xff08;Placement Group 归置组&#xff09;PoolMonitor…

【运维】第03讲(上):Nginx 负载均衡常见架构及问题解析

实际上 Nginx 除了承担代理网关角色外还会应用于 7 层应用上的负载均衡&#xff0c;本课时重点讲解 Nginx 的负载均衡应用架构&#xff0c;及最常见的问题。 学前提示 Nginx 作为负载均衡是基于代理模式的基础之上&#xff0c;所以在学习本课时前&#xff0c;你需要对 Nginx …

Codeforces Round 884 (Div. 1 + Div. 2)(视频讲解A--D)

[TOC](Codeforces Round 884 (Div. 1 Div. 2)&#xff08;视频讲解A–D&#xff09;) 视频链接&#xff1a;Codeforces Round 884 (Div. 1 Div. 2)&#xff08;视频讲解A–D&#xff09; A Subtraction Game 1、 板书&#xff1a; 2、代码 #include<bits/stdc.h> #…

网络协议与攻击模拟-19-FTP协议

FTP 协议 1、了解 FTP 协议 2、使用在 Windows 操作系统上使用 serv - U 软件搭建 FTP 服务器 3、分析 FTP 流量 一、 FTP 协议 1、概念 FTP &#xff08;文件传输协议&#xff09;&#xff0c;由两部分组成&#xff1a;客户端&#xff0f;服务器 C / S 架构&#xff0c;应…

Uniapp 版本更新

文章目录 前言Uniapp更新确定接口是否能够使用基本代码封装更新软件区别 前言 软件发布之后更新是经常出现的需求。我们希望软件能够自动连网更新软件&#xff0c;而不是重新去手动安装一个apk安装包。不需要更新的软件只有两个&#xff0c;一个是微信小程序&#xff0c;另一个…

C++浅谈const

在学C的时候&#xff0c;必不可少的是const&#xff0c;他或许在C语言中是可有可无的&#xff0c;但是在C中绝对不是&#xff0c;我们在学习C语言的时候&#xff0c;我们知道的是&#xff0c;经常有人说加const是为了保证“安全”&#xff0c;那么&#xff0c;所谓的“安全”是…

MFC第十六天 CFileDialog、CEdit简介、(线程)进程的启动,以及Notepad的开发(托盘技术-->菜单功能)

文章目录 CCommonDialogCFileDialogCEdit托盘技术进程的启动附录1:启动线程方式附录2:MFC对话框的退出过程 CCommonDialog 通用对话框 CCommonDialog 这些对话框类封装 Windows 公共对话框。 它们提供了易于使用的复杂对话框实现。 CFileDialog 提供用于打开或保存文件的标准对…

使用Git上传大文件模型(超过100MB)教程

目录 modelscope官方教程 服务器拒绝的解决方案 检查.gitattributes文件 modelscope官方教程 我们

Amazon 上的数字孪生:使用 L3 预测性数字孪生来预测“行为”

在上一篇博文中&#xff0c;我们讨论了数字孪生的定义和框架&#xff0c;这与我们的客户在其应用中使用数字孪生的方式一致。我们将数字孪生定义为“单个物理系统的动态数字表示&#xff0c;它通过数据进行动态更新以模仿物理系统的真实结构、状态和行为&#xff0c;从而加快获…

Vue3+ts;枚举(enum);Partial全部可选/Pick选一部分/配置 svg 图标/unplugin-vue-components组件自动按需加载

项目的创建 使用 create-vue 脚手架创建项目。 1.执行创建命令 pnpm create vue # or npm init vuelatest # or yarn create vue2.选择项目依赖内容。 ✔ Project name: … //项目名 ✔ Add TypeScript? … No / Yes ✔ Add JSX Support? … No / Yes ✔ Add Vue Router …

macOS 开发 - 纯代码生成 Window

文章目录 1、创建项目删除项目自带 window创建 BaseWindowController 继承自 NSWindowController子 WC 继承 BaseWC个人更喜欢纯代码:控制力、方便复制,不用卡卡的打开 xib 这里不使用各种项目自带的 storyboard/xib,使用纯代码创建 window(controller) 本示例代码将创建如下…

记录一下Mybatis中的if标签使用时遇到的问题

记录一下Mybatis中的if标签使用时遇到的问题 前言一、if标签遇到的问题二、if标签中""和的问题字符串等于条件的两种写法&#xff1a; 三 、总结 前言 今天在项目中进行查询时使用了if标签&#xff0c;遇到了问题&#xff1a; 开始时拉过代码来的时候是这样的 <…

Java关键字interface(接口)

文章目录 接口的理解接口的声明接口的成员说明接口的使用规则类实现接口&#xff08;implements&#xff09; 接口的多实现接口的多继承(extends)接口与实现类对象构成多态引用使用接口的静态成员使用接口的非静态方法JDK8中相关冲突问题总结小测试接口与抽象类之间的对比练习 …

不允许你不知道的 MySQL 优化实战(二)

文章目录 11、使用联合索引时&#xff0c;注意索引列的顺序&#xff0c;一般遵循最左匹配原则。12、对查询进行优化&#xff0c;应考虑在where及order by涉及的列上建立索引&#xff0c;尽量避免全表扫描。13、如果插入数据过多&#xff0c;考虑批量插入。14、在适当的时候&…

[QT编程系列-9]:C++图形用户界面编程,QT框架快速入门培训 - 3- QT窗体设计 - 自动布局

目录 3. QT窗体设计 3.7 自动布局 3.7.1 自动布局 3.7.2 在主窗口中自动布局 3.7.3 在自动布局容器中自动布局 3.7.4 在widget中自动布局 3.7.5 自动布局工件 3. QT窗体设计 3.7 自动布局 3.7.1 自动布局 在QT中&#xff0c;自动布局是一种灵活而强大的方式来管理和排…

【ArcGIS Pro二次开发】(47):要素类追加至空库(批量)

本工具主要是针对国空数据入库而做的。 如果你手头已经整理了一部分要素类数据&#xff0c;但是数据格式&#xff0c;字段值可能并没有完全按照规范设置好&#xff0c;需要将这些数据按规范批量和库&#xff0c;就可以尝试用这个工具。 准备数据&#xff1a;标准空库、你已做…