SOEM代码Slaveinfo Demo分析

分析Windows版本的Demo,以便之后移植到STM32F746上。

SOEM代码Slaveinfo Demo分析_第1张图片

1. slaveinfo.exe入口函数为main,需要一个以太网PHY的网卡名,网卡名在Windows下是\Device\NPF_{XXXXXXX...XXXXXXXXX},在STM32F746下如何表示?

2. 首先需要调用ec_init,初始化网卡,绑定socket

/* initialise SOEM, bind socket to ifname */
   if (ec_init(ifname))
   {
      printf("ec_init on %s succeeded.\n",ifname);

ec_init位于ethercatmain.c中,ec_init里边是ecx_init,再往里边是ecx_setupnic,exc_setupnic位于nicdrv.c中,这个nicdrv.c相当于Windows下的网卡驱动。值得注意的是ecx_contextt结构体,这个结构体定义在ethercatmain.h中

/** Context structure , referenced by all ecx functions*/
typedef struct ecx_context ecx_contextt;
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);
};

在exc_setupnic中,入参是ecx_context.port,网卡名,false(单网卡模式)。该函数作用是绑定socket

/** Basic setup to connect NIC to socket.
 * @param[in] port        = port context struct
 * @param[in] ifname       = Name of NIC device, f.e. "eth0"
 * @param[in] secondary      = if >0 then use secondary stack instead of primary
 * @return >0 if succeeded
 */
int ecx_setupnic(ecx_portt *port, const char *ifname, int secondary)

由于secondary是false(单网卡模式),因此直接进入else

else
   {
      InitializeCriticalSection(&(port->getindex_mutex));
      InitializeCriticalSection(&(port->tx_mutex));
      InitializeCriticalSection(&(port->rx_mutex));
      port->sockhandle        = NULL;
      port->lastidx           = 0;
      port->redstate          = ECT_RED_NONE;
      port->stack.sock        = &(port->sockhandle);
      port->stack.txbuf       = &(port->txbuf);
      port->stack.txbuflength = &(port->txbuflength);
      port->stack.tempbuf     = &(port->tempinbuf);
      port->stack.rxbuf       = &(port->rxbuf);
      port->stack.rxbufstat   = &(port->rxbufstat);
      port->stack.rxsa        = &(port->rxsa);
      ecx_clear_rxbufstat(&(port->rxbufstat[0]));
      psock = &(port->sockhandle);
   }

InitializeCriticalSection作用是初始化一个临界资源对象。详情见百度。“临界区”CCriticalSection是临界资源对象指针,该函数无返回值。单进程的各个线程可以使用临界资源对象来解决同步互斥问题,该对象不能保证哪个线程能够获得到临界资源对象,该系统能公平的对待每一个线程。

之后就是配置port

再之后就是用Winpcap,Winpcap与socket区别见 https://blog.csdn.net/code_while/article/details/80234336

我们使用Winpcap发送RAW数据包  https://blog.csdn.net/ykiwmy/article/details/86560781

SOEM代码Slaveinfo Demo分析_第2张图片

上图是来自Winpcap官方文档的一个架构图,Winpcap最底层是一个NPF(Netgroup Packet Filter,网络组包过滤)驱动,这是一个NDIS中间件驱动,所有经过网卡的数据包都会“途经”该驱动,在收到路过的数据包时,NPF可以选择仅仅统计一下包的情况或写入文件(做流量监控),或者写到一个环形缓冲区中,然后用户态程序调用wpcap.dll中一些函数,使用WinAPI和驱动程序交互,获取驱动缓冲区中的数据,则达到了监控底层数据包的目的。至于发送数据包,用户态程序调用Winpcap的SDK函数,这些函数调用dll中的方法,dll再用API和NPF通信,NPF是很最低层的驱动了,但它并不负责直接收发数据,NPF再调用更底层的网卡驱动实现数据包的发送。

pcap_open见 https://blog.csdn.net/swartz_lubel/article/details/75670474

pcap_open主要目的是使用winpcap打开网卡接口\Device\NPF_{XXXXXXX...XXXX}

然而这个winpcap在STM32F746上并没有,是否应该参考linux下的demo?

继续看ec_setupheader,作用是组以太网帧,采用广播模式发送,

/** Fill buffer with ethernet header structure.
 * Destination MAC is always broadcast.
 * Ethertype is always ETH_P_ECAT.
 * @param[out] p = buffer
 */
void ec_setupheader(void *p)
{
   ec_etherheadert *bp;
   bp = p;
   bp->da0 = htons(0xffff);
   bp->da1 = htons(0xffff);
   bp->da2 = htons(0xffff);
   bp->sa0 = htons(priMAC[0]);
   bp->sa1 = htons(priMAC[1]);
   bp->sa2 = htons(priMAC[2]);
   bp->etype = htons(ETH_P_ECAT);
}

至此,ec_init结束,作用是使用pcap_open打开网卡接口,并组以太网帧头(类型是ETH_P_ETH)。ec_init成功会返回1。

3. ec_config

int ec_config 	( 	uint8  	usetable,
		void *  	pIOmap 
	) 		

Enumerate / map and init all slaves.

Parameters:
    [in]	usetable	= TRUE when using configtable to init slaves, FALSE otherwise
    [out]	pIOmap	= pointer to IOmap

Returns:
    Workcounter of slave discover datagram = number of slaves found 

返回值是number of slaves found,之后ec_configdc配置分布式时钟

之后就是不断地打印出ec_slave结构体数组里的slave信息。

 

你可能感兴趣的:(EtherCAT)