【FreeRTOS的SOEM-master(1.4.1)移植】

1. 本次移植系统及前提

工作需要移植soem做EtherCAT主站,就跟大家分享如何移植soem,板块网口一个,实现soem与lwip能共用,下面是移植时系统配置:

  • stm32f07做裸机开发
  • FreeRTOS做为实时系统
  • 移植lwip做tcp通讯

2. 移植三步骤(osal、oshw、soem)

移植时候参考了,下面几个资料,都讲的非常好:

  1. NUC980 DIY项目大挑战 - EtherCAT实现:https://club.rt-thread.org/ask/article/160414965e80294e.html : link
  2. 在RT-Thread上移植EtherCAT开源主站SOEM1.4.0:https://blog.youkuaiyun.com/lg28870983/article/details/124647952 link
  3. 基于STM32构建EtherCAT主站:https://blog.youkuaiyun.com/cln512/article/details/122093655: link
  4. EtherCATマスターSOEMをマイコンに移植する https://lipoyang.hatenablog.com/entry/2019/12/08/101951: link
  5. 知网搜:基于嵌入式平台的EtherCAT主站实现研究

2.1 官方soem移植下载

soem移植源码兼容很多版本:erika、intime、linux、macosx、rtems、rtk、vxworks、win32

官方soem下载源文件你可以在github下载(1.4.0)版本:https://github.com/OpenEtherCATsociety/SOEM: link

但为了移植统一,建议你下载上面几篇文章移植版本再根据自己工程修改:.

2.2 FreeRTOS版本SOEM移植

Some移植主要是三个文件 osal.c,oshw.c和nicdrv.c。

  • osal.c 主要是微秒及的延时和定时函数;
  • oshw.c 主要是网络端和本机端数据的大小端转换;
  • nicdrv.c 主要是网络数据收发

Some已经给出了很多操作系统移植,我的移植是基于rtk,这个是嵌入式系统实现,和我们的FreeRTOS开发环境最接近。
可以将下面三个文件夹中其他操作系统移植文件删掉,留下rtk:
在这里插入图片描述
最终成这样:
在这里插入图片描述
少的:netdev_hook.c与netdev_hook.h是后面添加的

2.2.1 osal.c修改

主要内容是实现osal_usleep和osal_gettimeofday两个函数。

由于我的系统FreeRTOS移植系统时钟采用SysTick定时器,TIM10定时器作为HAL基础时钟时,所有将TIM10时钟与SOEM移植时钟共享,就不会浪费多一个定时器去做SOEM的系统时钟。

查看HAL的系统时钟是哪一个定时器:
在这里插入图片描述
可知道:hal定时器是每隔1ms产生中断,定时器__HAL_TIM_GET_COUNTER(&htim10)就是us数;
则osal_usleep与osal_gettimeofday可以修改成这样:

/*
 * Licensed under the GNU General Public License version 2 with exceptions. See
 * LICENSE file in the project root for full license information
 */

#include "osal.h"
#include <time.h>
#include <string.h>
#include <sys/time.h>
#include "stdlib.h"
#include "FreeRTOSConfig.h"

//此处由于与lwip有重定义所以除掉,没有的话可以开启
//#define  timercmp(a, b, CMP)                                \
//  (((a)->tv_sec == (b)->tv_sec) ?                           \
//   ((a)->tv_usec CMP (b)->tv_usec) :                        \
//   ((a)->tv_sec CMP (b)->tv_sec))
//#define  timeradd(a, b, result)                             \
//  do {                                                      \
//    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec;           \
//    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;        \
//    if ((result)->tv_usec >= 1000000)                       \
//    {                                                       \
//       ++(result)->tv_sec;                                  \
//       (result)->tv_usec -= 1000000;                        \
//    }                                                       \
//  } while (0)
//#define  timersub(a, b, result)                             \
//  do {                                                      \
//    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;           \
//    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;        \
//    if ((result)->tv_usec < 0) {                            \
//      --(result)->tv_sec;                                   \
//      (result)->tv_usec += 1000000;                         \
//    }                                                       \
//  } while (0)

#define USECS_PER_SEC   1000000
#define USECS_PER_TICK  (USECS_PER_SEC / CFG_TICKS_PER_SECOND)


/* Workaround for rt-labs defect 776.
 * Default implementation of udelay() didn't work correctly when tick was
 * shorter than one millisecond.
 */
#include "stm32f4xx_hal.h"
extern TIM_HandleTypeDef   htim10;
#define false 0

//延迟ms
void MY_MsDelay(uint32_t Delay)
{
	uint16_t start_ms,start_us;
	if(Delay==0)
	{
		return;
	}
	//不超过1ms
	start_ms=HAL_GetTick();
	start_us =__HAL_TIM_GET_COUNTER(&htim10);
	while((HAL_GetTick() - start_ms) < Delay)
	{
	}
	while(__HAL_TIM_GET_COUNTER(&htim10) <start_us)
	{
	}
}

//延迟us
void MY_UsDelay(uint32_t Delay)
{
	uint16_t wait_ms,wait_us,start_us,realse_us;
	if(Delay==0)
	{
		return;
	}
	wait_ms=Delay/1000;
	wait_us=Delay%1000;
	realse_us =__HAL_TIM_GET_COUNTER(&htim10);
	if(wait_ms!=0)
	{
		MY_MsDelay(wait_ms);
	}
	if(wait_us!=0)
	{
		start_us =__HAL_TIM_GET_COUNTER(&htim10);
		if((start_us+wait_us)>999)
		{
			wait_us=start_us+wait_us-1000;
			while(__HAL_TIM_GET_COUNTER(&htim10)>=start_us || __HAL_TIM_GET_COUNTER(&htim10)<wait_us)
			{
			}
		}else
		{
			while(__HAL_TIM_GET_COUNTER(&htim10)-start_us <wait_us)
			{
			}
		}
	}
}


void udelay (uint32_t us)
{
	MY_UsDelay(us);
}


uint16 test_mss;

int gettimeofday(struct timeval *tp, void *tzp)
{
//   tick_t tick = tick_get();
//   tick_t ticks_left;
//
//   ASSERT (tp != NULL);
//
//   tp->tv_sec = tick / CFG_TICKS_PER_SECOND;
//
//   ticks_left = tick % CFG_TICKS_PER_SECOND;
//   tp->tv_usec = ticks_left * USECS_PER_TICK;
//   ASSERT (tp->tv_usec < USECS_PER_SEC);
//	configASSERT(tp != NULL);
	tp->tv_sec=HAL_GetTick()/1000;
	tp->tv_usec =(HAL_GetTick()%1000)*1000+__HAL_TIM_GET_COUNTER(&htim10);
	test_mss=__HAL_TIM_GET_COUNTER(&htim10);

	return 0;
}

int osal_usleep (uint32 usec)
{
   udelay(usec);
   return 0;
}

int osal_gettimeofday(struct timeval *tv, struct timezone *tz)
{
   return gettimeofday(tv, tz);
}

ec_timet osal_current_time (void)
{
   struct timeval current_time;
   ec_timet return_value;

   gettimeofday (&current_time, 0);
   return_value.sec = current_time.tv_sec;
   return_value.usec = current_time.tv_usec;
   return return_value;
}

void osal_timer_start (osal_timert * self, uint32 timeout_usec)
{
   struct timeval start_time;
   struct timeval timeout;
   struct timeval stop_time;

   gettimeofday (&start_time, 0);
   timeout.tv_sec = timeout_usec / USECS_PER_SEC;
   timeout.tv_usec = timeout_usec % USECS_PER_SEC;
   timeradd (&start_time, &timeout, &stop_time);

   self->stop_time.sec = stop_time.tv_sec;
   self->stop_time.usec = stop_time.tv_usec;
}

boolean osal_timer_is_expired (osal_timert * self)
{
   struct timeval current_time;
   struct timeval stop_time;
   int is_not_yet_expired;

   gettimeofday (&current_time, 0);
   stop_time.tv_sec = self->stop_time.sec;
   stop_time.tv_usec = self->stop_time.usec;
   is_not_yet_expired = timercmp (&current_time, &stop_time, <);

   return is_not_yet_expired == false;
}

void *osal_malloc(size_t size)
{
   return malloc(size);
}

void osal_free(void *ptr)
{
   free(ptr);
}

int osal_thread_create(void *thandle, int stacksize, void *func, void *param)
{
//   thandle = task_spawn ("worker", func, 6,stacksize, param);
//   if(!thandle)
//   {
//      return 0;
//   }
   return 1;
}

int osal_thread_create_rt(void *thandle, int stacksize, void *func, void *param)
{
//   thandle = task_spawn ("worker_rt", func, 15 ,stacksize, param);
//   if(!thandle)
//   {
//      return 0;
//   }
   return 1;
}

2.2.2 oshw.c修改

不需要做什么

2.2.3 nicdrv.c修改及增加netdev_hook.c

主要修改就是调用自己的网络发送和接收函数,我把它们命名为net_send和net_recv。这两个函数最好的实现是直接操作网卡(或者叫emac),我现在的实现参考了tcpdump的方法,在协议栈中加钩子(hook)实现,这样对原来系统影响最小,网口除了EtherCAT,还可以当正常的网口用。
ecx_setupnic函数中创建mutex(这个按照FreeRTOS格式改一下即可),安装网络钩子
ecx_closenic函数中删除mutex,卸载网络钩子

其原理就是soem在ecx_setupnic网络初始化时候,找到lwip网络端口结构体:now_netif = netif_find(ifname); 在now_netif结构体中有:netif->linkoutput与netif->input为发送与接收处理钩子函数,将其保存并指引到新的发送与接收钩子函数处理,绕过原来lwip协议。 在ecx_closenic网络注销中,将钩子函数退出来写入lwip原来钩子处理函数。

感兴趣可以学习lwip的网络收发过程。

nicdrv.c文件:

/*
 * Licensed under the GNU General Public License version 2 with exceptions. See
 * LICENSE file in the project root for full license information
 */

/** \file
 * \brief
 * EtherCAT RAW socket driver.
 *
 * Low level interface functions to send and receive EtherCAT packets.
 * EtherCAT has the property that packets are only send by the master,
 * and the send packets always return in the receive buffer.
 * There can be multiple packets "on the wire" before they return.
 * To combine the received packets with the original send packets a buffer
 * system is installed. The identifier is put in the index item of the
 * EtherCAT header. The index is stored and compared when a frame is received.
 * If there is a match the packet can be combined with the transmit packet
 * and returned to the higher level function.
 *
 * The socket layer can exhibit a reversal in the packet order (rare).
 * If the Tx order is A-B-C the return order could be A-C-B. The indexed buffer
 * will reorder the packets automatically.
 *
 * The "redundant" option will configure two sockets and two NIC interfaces.
 * Slaves are connected to both interfaces, one on the IN port and one on the
 * OUT port. Packets are send via both interfaces. Any one of the connections
 * (also an interconnect) can be removed and the slaves are still serviced with
 * packets. The software layer will detect the possible failure modes and
 * compensate. If needed the packets from interface A are resent through interface B.
 * This layer is fully transparent for the higher layers.
 */

//#include <kern.h>
//#include <ioctl.h>
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "netdev_hook.h"
//#include "lw_mac/lw_emac.h"

#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

/** Redundancy modes */
enum
{
   /** No redundancy, single NIC mode */
   ECT_RED_NONE,
   /** Double redundant NIC connection */
   ECT_RED_DOUBLE
};

/** Primary source MAC address used for EtherCAT.
 * This address is not the MAC address used from the NIC.
 * EtherCAT does not care about MAC addressing, but it is used here to
 * differentiate the route the packet traverses through the EtherCAT
 * segment. This is needed to find out the packet flow in redundant
 * configurations. */
const uint16 priMAC[3] = { 0x0101, 0x0101, 0x0101 };
/** Secondary source MAC address used for EtherCAT. */
const uint16 secMAC[3] = { 0x0404, 0x0404, 0x0404 };

/** second MAC word is used for identification */
#define RX_PRIM priMAC[1]
/** second MAC word is used for identification */
#define RX_SEC secMAC[1]

static void ecx_clear_rxbufstat(int *rxbufstat)
{
   int i;
   for(i = 0; i < EC_MAXBUF; i++)
   {
      rxbufstat[i] = EC_BUF_EMPTY;
   }
}

/** 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)
{
   int i;
   int rVal;
   int *psock;

//   port->getindex_mutex = mtx_create();
//   port->tx_mutex = mtx_create();
//   port->rx_mutex = mtx_create();

//   rVal = bfin_EMAC_init((uint8_t *)priMAC);
//   if (rVal != 0)
//      return 0;

   if (secondary)
   {
      /* secondary port struct available? */
      if (port->redport)
      {
         /* when using secondary socket it is automatically a redundant setup */
         psock = &(port->redport->sockhandle);
         *psock = -1;
         port->redstate                   = ECT_RED_DOUBLE;
         port->redport->stack.sock        = &(port->redport->sockhandle);
         port->redport->stack.txbuf       = &(port->txbuf);
         port->redport->stack.txbuflength = &(port->txbuflength);
         port->redport->stack.tempbuf     = &(port->redport->tempinbuf);
         port->redport->stack.rxbuf       = &(port->redport->rxbuf);
         port->redport->stack.rxbufstat   = &(port->redport->rxbufstat);
         port->redport->stack.rxsa        = &(port->redport->rxsa);
         ecx_clear_rxbufstat(&(port->redport->rxbufstat[0]));
      }
      else
      {
         /* fail */
         return 0;
      }
   }
   else
   {
//      port->getindex_mutex = mtx_create();
//      port->tx_mutex = mtx_create();
//      port->rx_mutex = mtx_create();
//	  sys_mutex_new(port->getindex_mutex);
//	  sys_mutex_new(port->tx_mutex);
//	  sys_mutex_new(port->rx_mutex);
	  port->getindex_mutex=xSemaphoreCreateMutex();
	  port->tx_mutex=xSemaphoreCreateMutex();
	  port->rx_mutex=xSemaphoreCreateMutex();

      port->sockhandle        = -1;
      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);
   }

	if(install_hook(port, ifname)==0){
	  printf("ecx_setupnic fail\n");
	  return 0; //fail
	}


   /* 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;
}

/** Close sockets used
 * @param[in] port        = port context struct
 * @return 0
 */
int ecx_closenic(ecx_portt *port)
{
	vSemaphoreDelete(port->getindex_mutex);
	vSemaphoreDelete(port->tx_mutex);
	vSemaphoreDelete(port->rx_mutex);
//	sys_mutex_free(port->getindex_mutex);
//	sys_mutex_free(port->tx_mutex);
//	sys_mutex_free(port->rx_mutex);
	uninstall_hook(port);
    return 0;
}

/** 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 = oshw_htons(0xffff);
   bp->da1 = oshw_htons(0xffff);
   bp->da2 = oshw_htons(0xffff);
   bp->sa0 = oshw_htons(priMAC[0]);
   bp->sa1 = oshw_htons(priMAC[1]);
   bp->sa2 = oshw_htons(priMAC[2]);
   bp->etype = oshw_htons(ETH_P_ECAT);
}

/** Get new frame identifier index and allocate corresponding rx buffer.
 * @param[in] port        = port context struct
 * @return new index.
 */
uint8 ecx_getindex(ecx_portt *port)
{
   uint8 idx;
   uint8 cnt;

//   mtx_lock (port->getindex_mutex);
//   sys_mutex_lock(port->getindex_mutex);
   xSemaphoreTake(port->getindex_mutex, portMAX_DELAY);

   idx = port->lastidx + 1;
   /* index can't be larger than buffer array */
   if (idx >= EC_MAXBUF)
   {
      idx = 0;
   }
   cnt = 0;
   /* try to find unused index */
   while ((port->rxbufstat[idx] != EC_BUF_EMPTY) && (cnt < EC_MAXBUF))
   {
      idx++;
      cnt++;
      if (idx >= EC_MAXBUF)
      {
         idx = 0;
      }
   }
   port->rxbufstat[idx] = EC_BUF_ALLOC;
   if (port->redstate != ECT_RED_NONE)
   {
      port->redport->rxbufstat[idx] = EC_BUF_ALLOC;
   }
   port->lastidx = idx;

//   mtx_unlock (port->getindex_mutex);
//   sys_mutex_unlock(port->getindex_mutex);
   xSemaphoreGive(port->getindex_mutex);

   return idx;
}

/** Set rx buffer status.
 * @param[in] port     = port context struct
 * @param[in] idx      = index in buffer array
 * @param[in] bufstat  = status to set
 */
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat)
{
   port->rxbufstat[idx] = bufstat;
   if (port->redstate != ECT_RED_NONE)
   {
      port->redport->rxbufstat[idx] = bufstat;
   }
}

/** Transmit buffer over socket (non blocking).
 * @param[in] port        = port context struct
 * @param[in] idx         = index in tx buffer array
 * @param[in] stacknumber  = 0=Primary 1=Secondary stack
 * @return socket send result
 */
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber)
{
   int lp, rval;
   ec_stackT *stack;

   if (!stacknumber)
   {
      stack = &(port->stack);
   }
   else
   {
      stack = &(port->redport->stack);
   }
   lp = (*stack->txbuflength)[idx];
   (*stack->rxbufstat)[idx] = EC_BUF_TX;

   rval = net_send((*stack->txbuf)[idx], lp);

   return rval;
}

/** Transmit buffer over socket (non blocking).
 * @param[in] port        = port context struct
 * @param[in] idx = index in tx buffer array
 * @return socket send result
 */
int ecx_outframe_red(ecx_portt *port, uint8 idx)
{
   ec_comt *datagramP;
   ec_etherheadert *ehp;
   int rval;

   ehp = (ec_etherheadert *)&(port->txbuf[idx]);
   /* rewrite MAC source address 1 to primary */
   ehp->sa1 = oshw_htons(priMAC[1]);
   /* transmit over primary socket*/
   rval = ecx_outframe(port, idx, 0);
   if (port->redstate != ECT_RED_NONE)
   {
      //mtx_lock (port->tx_mutex);
//	   sys_mutex_lock(port->tx_mutex);
	   xSemaphoreTake(port->tx_mutex, portMAX_DELAY);


      ehp = (ec_etherheadert *)&(port->txbuf2);
      /* use dummy frame for secondary socket transmit (BRD) */
      datagramP = (ec_comt*)&(port->txbuf2[ETH_HEADERSIZE]);
      /* write index to frame */
      datagramP->index = idx;
      /* rewrite MAC source address 1 to secondary */
      ehp->sa1 = oshw_htons(secMAC[1]);
      /* transmit over secondary socket */
      //send(sockhandle2, &ec_txbuf2, ec_txbuflength2 , 0);
      // OBS! redundant not ACTIVE for BFIN, just added to compile
	  if(net_send(&(port->txbuf2), port->txbuflength2) == -1)
      {
         port->redport->rxbufstat[idx] = EC_BUF_EMPTY;
      }
	  port->redport->rxbufstat[idx] = EC_BUF_TX;
      //mtx_unlock (port->tx_mutex);
//      sys_mutex_unlock(port->tx_mutex);
      xSemaphoreGive(port->tx_mutex);
   }

   return rval;
}

/** Non blocking read of socket. Put frame in temporary buffer.
 * @param[in] port        = port context struct
 * @param[in] stacknumber = 0=primary 1=secondary stack
 * @return >0 if frame is available and read
 */
static int ecx_recvpkt(ecx_portt *port, int stacknumber)
{
   int lp, bytesrx;
   ec_stackT *stack;

   if (!stacknumber)
   {
      stack = &(port->stack);
   }
   else
   {
      stack = &(port->redport->stack);
   }
   lp = sizeof(port->tempinbuf);

   bytesrx = net_recv((*stack->tempbuf), lp);

   port->tempinbufs = bytesrx;

   return (bytesrx > 0);
}

/** Non blocking receive frame function. Uses RX buffer and index to combine
 * read frame with transmitted frame. To compensate for received frames that
 * are out-of-order all frames are stored in their respective indexed buffer.
 * If a frame was placed in the buffer previously, the function retrieves it
 * from that buffer index without calling ec_recvpkt. If the requested index
 * is not already in the buffer it calls ec_recvpkt to fetch it. There are
 * three options now, 1 no frame read, so exit. 2 frame read but other
 * than requested index, store in buffer and exit. 3 frame read with matching
 * index, store in buffer, set completed flag in buffer status and exit.
 *
 * @param[in] port        = port context struct
 * @param[in] idx         = requested index of frame
 * @param[in] stacknumber = 0=primary 1=secondary stack
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME or EC_OTHERFRAME.
 */
int ecx_inframe(ecx_portt *port, uint8 idx, int stacknumber)
{
   uint16  l;
   int     rval;
   uint8   idxf;
   ec_etherheadert *ehp;
   ec_comt *ecp;
   ec_stackT *stack;
   ec_bufT *rxbuf;

   if (!stacknumber)
   {
      stack = &(port->stack);
   }
   else
   {
      stack = &(port->redport->stack);
   }
   rval = EC_NOFRAME;
   rxbuf = &(*stack->rxbuf)[idx];
   /* check if requested index is already in buffer ? */
   if ((idx < EC_MAXBUF) && (   (*stack->rxbufstat)[idx] == EC_BUF_RCVD))
   {
      l = (*rxbuf)[0] + ((uint16)((*rxbuf)[1] & 0x0f) << 8);
      /* return WKC */
      rval = ((*rxbuf)[l] + ((uint16)(*rxbuf)[l + 1] << 8));
      /* mark as completed */
      (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
   }
   else
   {
//      mtx_lock (port->rx_mutex);
//	   sys_mutex_lock(port->rx_mutex);
	   xSemaphoreTake(port->rx_mutex, portMAX_DELAY);
      /* non blocking call to retrieve frame from socket */

      if (ecx_recvpkt(port, stacknumber))
      {
         rval = EC_OTHERFRAME;
         ehp =(ec_etherheadert*)(stack->tempbuf);
         /* check if it is an EtherCAT frame */
         if (ehp->etype == oshw_htons(ETH_P_ECAT))
         {
            ecp =(ec_comt*)(&(*stack->tempbuf)[ETH_HEADERSIZE]);
            l = etohs(ecp->elength) & 0x0fff;
            idxf = ecp->index;
            /* found index equals requested index ? */
            if (idxf == idx)
            {
               /* yes, put it in the buffer array (strip ethernet header) */
               memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idx] - ETH_HEADERSIZE);
               /* return WKC */
               rval = ((*rxbuf)[l] + ((uint16)((*rxbuf)[l + 1]) << 8));
               /* mark as completed */
               (*stack->rxbufstat)[idx] = EC_BUF_COMPLETE;
               /* store MAC source word 1 for redundant routing info */
               (*stack->rxsa)[idx] = oshw_ntohs(ehp->sa1);
            }
            else
            {
               /* check if index exist and someone is waiting for it */
               if (idxf < EC_MAXBUF && (*stack->rxbufstat)[idxf] == EC_BUF_TX)
               {
                  rxbuf = &(*stack->rxbuf)[idxf];
                  /* put it in the buffer array (strip ethernet header) */
                  memcpy(rxbuf, &(*stack->tempbuf)[ETH_HEADERSIZE], (*stack->txbuflength)[idxf] - ETH_HEADERSIZE);
                  /* mark as received */
                  (*stack->rxbufstat)[idxf] = EC_BUF_RCVD;
                  (*stack->rxsa)[idxf] = oshw_ntohs(ehp->sa1);
               }
               else
               {
                  /* strange things happened */
               }
            }
         }
      }
//      mtx_unlock (port->rx_mutex);
//      sys_mutex_unlock(port->rx_mutex);
      xSemaphoreGive(port->rx_mutex);

   }

   /* WKC if matching frame found */
   return rval;
}

/** Blocking redundant receive frame function. If redundant mode is not active then
 * it skips the secondary stack and redundancy functions. In redundant mode it waits
 * for both (primary and secondary) frames to come in. The result goes in an decision
 * tree that decides, depending on the route of the packet and its possible missing arrival,
 * how to reroute the original packet to get the data in an other try.
 *
 * @param[in] port        = port context struct
 * @param[in] idx = requested index of frame
 * @param[in] timer = absolute timeout time
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME.
 */
static int ecx_waitinframe_red(ecx_portt *port, uint8 idx, osal_timert timer)
{

   int wkc  = EC_NOFRAME;
   int wkc2 = EC_NOFRAME;
   int primrx, secrx;

   /* if not in redundant mode then always assume secondary is OK */
   if (port->redstate == ECT_RED_NONE)
   {
      wkc2 = 0;
   }
   do
   {
      /* only read frame if not already in */
      if (wkc <= EC_NOFRAME)
      {
         wkc  = ecx_inframe(port, idx, 0);
      }
      /* only try secondary if in redundant mode */
      if (port->redstate != ECT_RED_NONE)
      {
         /* only read frame if not already in */
         if (wkc2 <= EC_NOFRAME)
            wkc2 = ecx_inframe(port, idx, 1);
      }
   /* wait for both frames to arrive or timeout */
   } while (((wkc <= EC_NOFRAME) || (wkc2 <= EC_NOFRAME)) && (osal_timer_is_expired(&timer) == FALSE));
   /* only do redundant functions when in redundant mode */
   if (port->redstate != ECT_RED_NONE)
   {
      /* primrx if the received MAC source on primary socket */
      primrx = 0;
      if (wkc > EC_NOFRAME)
      {
         primrx = port->rxsa[idx];
      }
      /* secrx if the received MAC source on psecondary socket */
      secrx = 0;
      if (wkc2 > EC_NOFRAME)
      {
         secrx = port->redport->rxsa[idx];
      }
      /* primary socket got secondary frame and secondary socket got primary frame */
      /* normal situation in redundant mode */
      if ( ((primrx == RX_SEC) && (secrx == RX_PRIM)) )
      {
         /* copy secondary buffer to primary */
         memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
         wkc = wkc2;
      }
      /* primary socket got nothing or primary frame, and secondary socket got secondary frame */
      /* we need to resend TX packet */
      if ( ((primrx == 0) && (secrx == RX_SEC)) ||
           ((primrx == RX_PRIM) && (secrx == RX_SEC)) )
      {
         osal_timert read_timer;

         /* If both primary and secondary have partial connection retransmit the primary received
          * frame over the secondary socket. The result from the secondary received frame is a combined
          * frame that traversed all slaves in standard order. */
         if ( (primrx == RX_PRIM) && (secrx == RX_SEC) )
         {
            /* copy primary rx to tx buffer */
            memcpy(&(port->txbuf[idx][ETH_HEADERSIZE]), &(port->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
         }
         osal_timer_start(&read_timer, EC_TIMEOUTRET);
         /* resend secondary tx */
         ecx_outframe(port, idx, 1);
         do
         {
            /* retrieve frame */
            wkc2 = ecx_inframe(port, idx, 1);
         } while ((wkc2 <= EC_NOFRAME) && (osal_timer_is_expired(&read_timer) == FALSE));
         if (wkc2 > EC_NOFRAME)
         {
            /* copy secondary result to primary rx buffer */
            memcpy(&(port->rxbuf[idx]), &(port->redport->rxbuf[idx]), port->txbuflength[idx] - ETH_HEADERSIZE);
            wkc = wkc2;
         }
      }
   }

   /* return WKC or EC_NOFRAME */
   return wkc;
}

/** Blocking receive frame function. Calls ec_waitinframe_red().
 * @param[in] port        = port context struct
 * @param[in] idx       = requested index of frame
 * @param[in] timeout   = timeout in us
 * @return Workcounter if a frame is found with corresponding index, otherwise
 * EC_NOFRAME.
 */
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout)
{
   int wkc;
   osal_timert timer;

   osal_timer_start (&timer, timeout);
   wkc = ecx_waitinframe_red(port, idx, timer);

   return wkc;
}

/** Blocking send and receive frame function. Used for non processdata frames.
 * A datagram is build into a frame and transmitted via this function. It waits
 * for an answer and returns the workcounter. The function retries if time is
 * left and the result is WKC=0 or no frame received.
 *
 * The function calls ec_outframe_red() and ec_waitinframe_red().
 *
 * @param[in] port        = port context struct
 * @param[in] idx      = index of frame
 * @param[in] timeout  = timeout in us
 * @return Workcounter or EC_NOFRAME
 */
int ecx_srconfirm(ecx_portt *port, uint8 idx, int timeout)
{
   int wkc = EC_NOFRAME;
   osal_timert timer;

   osal_timer_start(&timer, timeout);
   do
   {
      osal_timert read_timer;

      /* tx frame on primary and if in redundant mode a dummy on secondary */
      ecx_outframe_red(port, idx);

      osal_timer_start(&read_timer, MIN(timeout, EC_TIMEOUTRET));
      /* get frame from primary or if in redundant mode possibly from secondary */
      wkc = ecx_waitinframe_red(port, idx, read_timer);

   /* wait for answer with WKC>0 or otherwise retry until timeout */
   } while ((wkc <= EC_NOFRAME) && (osal_timer_is_expired(&timer) == FALSE));

   return wkc;
}


#ifdef EC_VER1
int ec_setupnic(const char *ifname, int secondary)
{
   return ecx_setupnic(&ecx_port, ifname, secondary);
}

int ec_closenic(void)
{
   return ecx_closenic(&ecx_port);
}

uint8 ec_getindex(void)
{
   return ecx_getindex(&ecx_port);
}

void ec_setbufstat(uint8 idx, int bufstat)
{
   ecx_setbufstat(&ecx_port, idx, bufstat);
}

int ec_outframe(uint8 idx, int stacknumber)
{
   return ecx_outframe(&ecx_port, idx, stacknumber);
}

int ec_outframe_red(uint8 idx)
{
   return ecx_outframe_red(&ecx_port, idx);
}

int ec_inframe(uint8 idx, int stacknumber)
{
   return ecx_inframe(&ecx_port, idx, stacknumber);
}

int ec_waitinframe(uint8 idx, int timeout)
{
   return ecx_waitinframe(&ecx_port, idx, timeout);
}

int ec_srconfirm(uint8 idx, int timeout)
{
   return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif
nicdrv.h文件:

```c
/*
 * Licensed under the GNU General Public License version 2 with exceptions. See
 * LICENSE file in the project root for full license information
 */

/** \file
 * \brief
 * Headerfile for nicdrv.c
 */

#ifndef _nicdrvh_
#define _nicdrvh_

#include "ethercattype.h"

/** pointer structure to Tx and Rx stacks */
typedef struct
{
   /** socket connection used */
   int         *sock;
   /** tx buffer */
   ec_bufT     (*txbuf)[EC_MAXBUF];
   /** tx buffer lengths */
   int         (*txbuflength)[EC_MAXBUF];
   /** temporary receive buffer */
   ec_bufT     *tempbuf;
   /** rx buffers */
   ec_bufT     (*rxbuf)[EC_MAXBUF];
   /** rx buffer status fields */
   int         (*rxbufstat)[EC_MAXBUF];
   /** received MAC source address (middle word) */
   int         (*rxsa)[EC_MAXBUF];
} ec_stackT;

/** pointer structure to buffers for redundant port */
typedef struct
{
   ec_stackT   stack;
   int         sockhandle;
   /** rx buffers */
   ec_bufT rxbuf[EC_MAXBUF];
   /** rx buffer status */
   int rxbufstat[EC_MAXBUF];
   /** rx MAC source address */
   int rxsa[EC_MAXBUF];
   /** temporary rx buffer */
   ec_bufT tempinbuf;
} ecx_redportt;

/** pointer structure to buffers, vars and mutexes for port instantiation */
#include "sys.h"
#include "sys_arch.h"
typedef struct
{
   ec_stackT   stack;
   int         sockhandle;
   /** rx buffers */
   ec_bufT rxbuf[EC_MAXBUF];
   /** rx buffer status */
   int rxbufstat[EC_MAXBUF];
   /** rx MAC source address */
   int rxsa[EC_MAXBUF];
   /** temporary rx buffer */
   ec_bufT tempinbuf;
   /** temporary rx buffer status */
   int tempinbufs;
   /** 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 */
   uint8 lastidx;
   /** current redundancy state */
   int redstate;
   /** pointer to redundancy port and buffers */
   ecx_redportt *redport;
//   mtx_t * getindex_mutex;
//   mtx_t * tx_mutex;
//   mtx_t * rx_mutex;
//   sys_mutex_t * getindex_mutex;
//   sys_mutex_t * tx_mutex;
//   sys_mutex_t * rx_mutex;
   SemaphoreHandle_t getindex_mutex;
   SemaphoreHandle_t tx_mutex;
   SemaphoreHandle_t rx_mutex;
//   uart3DMATxCpltMutex = xSemaphoreCreateMutex()
//   xSemaphoreTake(TCP_TxCpltMutex, portMAX_DELAY);
//   xSemaphoreGive(TCP_TxCpltMutex);

} ecx_portt;

extern const uint16 priMAC[3];
extern const uint16 secMAC[3];

#ifdef EC_VER1
extern ecx_portt     ecx_port;
extern ecx_redportt  ecx_redport;

int ec_setupnic(const char * ifname, int secondary);
int ec_closenic(void);
void ec_setbufstat(uint8 idx, int bufstat);
uint8 ec_getindex(void);
int ec_outframe(uint8 idx, int stacknumber);
int ec_outframe_red(uint8 idx);
int ec_waitinframe(uint8 idx, int timeout);
int ec_srconfirm(uint8 idx,int timeout);
#endif

void ec_setupheader(void *p);
int ecx_setupnic(ecx_portt *port, const char * ifname, int secondary);
int ecx_closenic(ecx_portt *port);
void ecx_setbufstat(ecx_portt *port, uint8 idx, int bufstat);
uint8 ecx_getindex(ecx_portt *port);
int ecx_outframe(ecx_portt *port, uint8 idx, int stacknumber);
int ecx_outframe_red(ecx_portt *port, uint8 idx);
int ecx_waitinframe(ecx_portt *port, uint8 idx, int timeout);
int ecx_srconfirm(ecx_portt *port, uint8 idx,int timeout);

#endif

netdev_hook.c文件:



#include "netif.h"
#include "nicdrv.h"
#include "FreeRTOS.h"
#include "FreeRTOSConfig.h"
#include "stdint.h"
#include "string.h"
/******************************************************************************
* receive fifo buf
*/
#define HOOK_RX_BUFSIZE 10

static uint8_t netfrmbuf[HOOK_RX_BUFSIZE][1540];
static int netfrmbuf_cnt[HOOK_RX_BUFSIZE];
static int netfrm_head = 0;
static int netfrm_tail = 0;
static int netfrm_full = 0;

int hook_rx_dump = 0;
int hook_tx_dump = 0;



/******************************************************************************
* store netif and old function addr
*/
static struct netif *netif = NULL;
static netif_linkoutput_fn link_output;
static netif_input_fn input;

/******************************************************************************
* hex dump 
*/
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void hex_dump(const uint8_t *ptr, size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;

    configASSERT(ptr != NULL);

    for (i = 0; i < buflen; i += 16)
    {
        printf("%08X: ", i);

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%02X ", buf[i + j]);
            else
                printf("   ");
        printf(" ");

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
        printf("\n");
    }
}

/******************************************************************************
* rx/tx hook function 
*/
/* get tx data */
static err_t _netif_linkoutput(struct netif *netif, struct pbuf *p)
{
	return link_output(netif, p);
}

/* get rx data */
static err_t _netif_input(struct pbuf *p, struct netif *inp)
{

	if(p->tot_len>=14)
	{
		char *data = p->payload;
		if(data[12]=='\x88' && data[13]=='\xa4') //filter for ethercat frame
		{
			if(netfrm_full == 0){
				pbuf_copy_partial(p, netfrmbuf[netfrm_tail], p->tot_len, 0);
				netfrmbuf_cnt[netfrm_tail] = p->tot_len;
				netfrm_tail = (netfrm_tail+1) % HOOK_RX_BUFSIZE;
				if(netfrm_tail==netfrm_head)
					netfrm_full = 1;
			}
			//rt_kprintf("tail = %d, full = %d\n", netfrm_tail, netfrm_full);
		}
	}
    return input(p, inp);
}

/******************************************************************************
* hook install
*/
int install_hook(ecx_portt *port, const char *ifname)
{
	struct netif *now_netif;
	
	netfrm_head = 0;
	netfrm_tail = 0;
	netfrm_full = 0;


	if(netif == NULL){
		now_netif = netif_find(ifname);
		if (now_netif == NULL){
			printf("hook install error 'device == NULL'\n");
			return 0;
		}
		if ((now_netif == NULL) || (now_netif->linkoutput == NULL)){
			printf("hook install error '(netif == NULL) || (netif->linkoutput == NULL)'\n");
			return 0;
		}
	}else{
		printf("device %s hook already installed, must be uninstall it before intall new on\n", ifname);
	}
    netif = now_netif;

    //install netdev hook
	taskENTER_CRITICAL();	//进入临界段
    link_output = netif->linkoutput;
    netif->linkoutput = _netif_linkoutput;

    input = netif->input;
    netif->input = _netif_input;
	taskEXIT_CRITICAL();	//退出临界段
	printf("hook installed on %s\n", ifname);

	return 1;
}

/******************************************************************************
* hook uninstall
*/
int uninstall_hook(ecx_portt *port)
{
	//uninstall netdev hook
	if(netif != NULL){
		taskENTER_CRITICAL();	//进入临界段
		netif->input = input;
		netif->linkoutput = link_output;
		taskEXIT_CRITICAL();	//退出临界段
		netif = NULL;
	}
	printf("hook uninstalled\n");
	return 1;
}

/******************************************************************************
* netdev send/recv api
*/
int net_send(unsigned char *data, int len)
{
	int ret = -1;
	struct pbuf *p;
	
    p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL);
    
    if (p != NULL)
    {
		pbuf_take(p, data, len);
		
		if(hook_tx_dump){
			printf("send --- len=%d>>>\n",len);
			hex_dump(p->payload, p->tot_len);
		}
		_netif_linkoutput(netif,p);
        pbuf_free(p);
		ret = len;
	}
	else{
		printf("net_send alloc buffer error\n");
	}
	return ret;
}

int net_recv(unsigned char *data, int len)
{

	if(netfrm_full == 0 && netfrm_tail==netfrm_head){
		return 0;
	}
	
	int total = netfrmbuf_cnt[netfrm_head];
	if(total > len) total = len;
	
	memcpy(data, netfrmbuf[netfrm_head], total);
	netfrm_head = (netfrm_head+1) % HOOK_RX_BUFSIZE;
	if(netfrm_tail==netfrm_head)
		netfrm_full = 0;

	if(hook_rx_dump){
		printf("recv <<<---\n");
		hex_dump(data, total);
		printf("head = %d, tail = %d, full = %d\n", netfrm_head, netfrm_tail, netfrm_full);
	}
	return total;
}

//extern osSemaphoreId s_xSemaphore;
extern ETH_HandleTypeDef heth;

void net_hook_test(void)
{
	unsigned char frame[] = "\xff\xff\xff\xff\xff\xff\x01\x01\x01\x01\x01\x01\x88\xa4\x0d\x10\
\x08\x01\x00\x00\x03\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

//	unsigned char frame[] = "\xff\xff\xff\xff\xff\xff\xfc\x34\x97\x4a\xa6\xd2\x88\xa4\x0d\x10\
//\x08\x01\x00\x00\x03\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\
//\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
//\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

	if(install_hook(NULL, "st0")==1)
	{

		uint32_t data = heth.Instance->MACFFR;
		heth.Instance->MACFFR = data | 0x80000000;

		net_send(frame,60);
		osal_usleep(500);
//		osSemaphoreRelease(s_xSemaphore);
		vTaskDelay(1000);
//		net_recv();
//		etharp_output
		uninstall_hook(NULL);
	}
}

netdev_hook.h文件:

#ifndef _netdev_hook_h_
#define _netdev_hook_h_

#ifdef __cplusplus
extern "C"
{
#endif

#include "osal.h"
#include "oshw.h"

int install_hook(ecx_portt *port, const char *ifname);

int uninstall_hook(ecx_portt *port);

int net_send(unsigned char *data, int len);

int net_recv(unsigned char *data, int len);

void net_hook_test(void);

#ifdef __cplusplus
}
#endif

#endif

2.2.4 soem的剪切

stm32f407的flash有限,所以必须对文件定义堆栈跟空间进行缩减:
在这里插入图片描述
找的这几个定义修改成这样:或者更具自工程自己修改
在这里插入图片描述

2.2.5 确保lwip开启mac混杂模式

在这里插入图片描述
确保有这段代码:

macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE;
macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_ENABLE;

3. 测试代码

好了整体移植成功后,新建任务测试一下:

	/* 创建伺服电机控制任务 */
	xReturn = xTaskCreate((TaskFunction_t)soem_motor_Task,		//伺服电机
						  (const char*   )"soem_motor_Task",
						  (uint16_t      )1024*2,
						  (void*         )NULL,
						  (UBaseType_t   )6,
						  (TaskHandle_t* )&soem_motor_Task_Handle);
	if(pdPASS == xReturn)
		printf("创建PHY_Link_Check任务成功!\r\n");

soem_motor.c测试代码:

/*
 * PVD.c
 *
 *  Created on: 2022年9月22日
 *      Author: shengmidao
 */

#include "soem_motor.h"


#include <string.h>
#include <stdio.h>

#include "nicdrv.h"

#include <lwip/pbuf.h>
#include <lwip/inet.h>

#include "ethercat.h"
#include "FreeRTOSConfig.h"
#include "osal.h"
#include "oshw.h"
#include "netif.h"



static void test_oshw_htons (void)
{
	   uint16 network;
	   uint16 host;

	   host = 0x1234;
	   network = oshw_htons (host);
	   configASSERT(network == htons (host));
}

static void test_oshw_ntohs (void)
{
	   uint16 host;
	   uint16 network;

	   network = 0x1234;
	   host = oshw_ntohs (network);
	   configASSERT(host == ntohs (network));
}

//static uint8_t frame_data[] =
//{
//        /*0x50,0xFA,0x84,0x15,0x3C,0x3C,*/                            /* ??MAC */
//        //0xff,0xFf,0xff,0xff,0xff,0xff,
//		0x00,0xE0,0x4C,0x36,0x10,0x00,
//		0x0,0x80,0xE1,0x0,0x0,0x0,                                /* ??MAC */
//        0x8,0x0,                                                  /* ip?? */
//        //0x7,0x10,                                                  /* ip?? */
//        0x45,0x0,0x0,0x26/*l*/,0x0,0x0,0x0,0x0,0xFF,0x11,0x0,0x0, /* UDP?? */
//        0xC0,0xA8,0x1f,0xef,                                       /* ??IP */
//        0xC0,0xA8,0x1f,0x63,                                        /* ??IP */
//        0x22,0xB0,                                                /* ???? */
//        0x22,0xB1,                                                /* ???? */
//        0x0,0x12,                                                 /* UDP??*/
//        0x0,0x0,                                                  /* UDP?? */
//        0x68,0x65,0x6C,0x6C,0x6F,0x20,0x7A,0x6F,0x72,0x62         /* ?? */
//};

//static void echo_time()
//{
//	struct timeval tp;
//	osal_gettimeofday(&tp, 0);
//	printf("cur time = %d,%03d,%03d(us)\n", tp.tv_sec,tp.tv_usec/1000,tp.tv_usec%1000);
//}
//static void test_osal_timer_timeout_us (const uint32 timeout_us)
//{
//   osal_timert timer;

//   RT_ASSERT (timeout_us > 4000);

//   osal_timer_start (&timer, timeout_us);
//   RT_ASSERT (osal_timer_is_expired (&timer) == FALSE);
//   osal_usleep (timeout_us - 2000);
//   RT_ASSERT (osal_timer_is_expired (&timer) == FALSE);
//   osal_usleep (4000);
//   RT_ASSERT (osal_timer_is_expired (&timer));
//}

//static void test_osal_timer_timeout_us2 (const uint32 timeout_us)
//{
//	osal_timert timer;
//	struct timeval tp,tp1;
//
//	RT_ASSERT (timeout_us > 4000);

//	osal_gettimeofday(&tp1, 0);
//
//	osal_timer_start (&timer, timeout_us);
//	if(osal_timer_is_expired (&timer)){
//		osal_gettimeofday(&tp, 0);
//		//rt_kprintf("\ttime1 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		rt_kprintf("\tFail-1 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		goto fail;
//	}
//	osal_usleep (timeout_us - 2000);
//	if(osal_timer_is_expired (&timer)){
//		osal_gettimeofday(&tp, 0);
//		//rt_kprintf("\ttime1 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		rt_kprintf("\tFail-2 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		goto fail;
//	}
//	osal_usleep (4000);
//	if(osal_timer_is_expired (&timer)==FALSE){
//		osal_gettimeofday(&tp, 0);
//		//rt_kprintf("\ttime1 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		rt_kprintf("\tFail-3 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//		goto fail;
//	}
//	osal_gettimeofday(&tp, 0);
//	rt_kprintf("\ttime1 = %d,%03d,%03d(us)\n", tp1.tv_sec,tp1.tv_usec/1000,tp1.tv_usec%1000);
//	rt_kprintf("\ttime2 = %d,%03d,%03d(us)\n", tp.tv_sec,tp.tv_usec/1000,tp.tv_usec%1000);

//	rt_kprintf("%d us test pass\n", timeout_us);

//	return;

//fail:
//	rt_kprintf("%d us test fail\n", timeout_us);
//}

//static void test_osal_timer (void)
//{
//   test_osal_timer_timeout_us (10*1000);     /* 10ms */
//   test_osal_timer_timeout_us (100*1000);    /* 100ms */
//   test_osal_timer_timeout_us (1000*1000);   /* 1s */
//   test_osal_timer_timeout_us (2000*1000);   /* 2s */
//}

#define USECS_PER_SEC   1000000
#define USECS_PER_TICK  1000
#ifndef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#endif

static int32 time_difference_us (const ec_timet stop, const ec_timet start)
{
	   int32 difference_us;

	   configASSERT (stop.sec >= start.sec);
	   if (stop.sec == start.sec)
	   {
		   configASSERT (stop.usec >= start.usec);
	   }

	   difference_us = (stop.sec - start.sec) * USECS_PER_SEC;
	   difference_us += ((int32)stop.usec - (int32)start.usec);

	   configASSERT (difference_us >= 0);
	   return difference_us;
}

/**
 * Test osal_current_time() by using it for measuring how long an osal_usleep()
 * takes, in specified number of microseconds.
 */
ec_timet start_test;
ec_timet stop_test;
int32 sleep_test;
static void test_osal_current_time_for_delay_us (const int32 sleep_time_us)
{
	ec_timet start;
	ec_timet stop;
	int32 measurement_us;
	int32 deviation_us;
	const int32 usleep_accuracy_us = USECS_PER_TICK;
	boolean is_deviation_within_tolerance;

	printf("sleep_us(%ld)...\n", sleep_time_us);

	sleep_test=sleep_time_us;
	start = osal_current_time ();
	start_test=start;
	osal_usleep (sleep_time_us);
	stop = osal_current_time ();
	stop_test=stop;

	printf("\tstart time = %ld,%03ld,%03ld(us)\n", start.sec,start.usec/1000,start.usec%1000);
	printf("\tstop  time = %ld,%03ld,%03ld(us)\n", stop.sec,stop.usec/1000,stop.usec%1000);

	measurement_us = time_difference_us (stop, start);
	deviation_us = ABS (measurement_us - sleep_time_us);
	is_deviation_within_tolerance = deviation_us <= usleep_accuracy_us;
	if(is_deviation_within_tolerance){
		printf("\terr=%ld pass\n", measurement_us - sleep_time_us);
	}else{
		printf("\terr=%ld fail\n", measurement_us - sleep_time_us);
	}

}

static void test_osal_current_time (void)
{

   test_osal_current_time_for_delay_us (1000);

   test_osal_current_time_for_delay_us (2000);

   test_osal_current_time_for_delay_us (3000);

   test_osal_current_time_for_delay_us (0);

   test_osal_current_time_for_delay_us (1);

   test_osal_current_time_for_delay_us (500);

   test_osal_current_time_for_delay_us (USECS_PER_TICK);
   test_osal_current_time_for_delay_us (USECS_PER_TICK-1);
   test_osal_current_time_for_delay_us (USECS_PER_TICK+1);
   test_osal_current_time_for_delay_us (2 * 1000 * 1000);  /* 2s */
   test_osal_current_time_for_delay_us (400);
   test_osal_current_time_for_delay_us (600);
   test_osal_current_time_for_delay_us (800);
}




char IOmap[4096];
ec_ODlistt ODlist;
ec_OElistt OElist;
boolean printSDO = TRUE;
boolean printMAP = TRUE;
char usdo[128];


#define OTYPE_VAR               0x0007
#define OTYPE_ARRAY             0x0008
#define OTYPE_RECORD            0x0009

#define ATYPE_Rpre              0x01
#define ATYPE_Rsafe             0x02
#define ATYPE_Rop               0x04
#define ATYPE_Wpre              0x08
#define ATYPE_Wsafe             0x10
#define ATYPE_Wop               0x20


char* dtype2string(uint16 dtype, uint16 bitlen)
{
    static char str[32] = { 0 };

    switch(dtype)
    {
        case ECT_BOOLEAN:
            sprintf(str, "BOOLEAN");
            break;
        case ECT_INTEGER8:
            sprintf(str, "INTEGER8");
            break;
        case ECT_INTEGER16:
            sprintf(str, "INTEGER16");
            break;
        case ECT_INTEGER32:
            sprintf(str, "INTEGER32");
            break;
        case ECT_INTEGER24:
            sprintf(str, "INTEGER24");
            break;
        case ECT_INTEGER64:
            sprintf(str, "INTEGER64");
            break;
        case ECT_UNSIGNED8:
            sprintf(str, "UNSIGNED8");
            break;
        case ECT_UNSIGNED16:
            sprintf(str, "UNSIGNED16");
            break;
        case ECT_UNSIGNED32:
            sprintf(str, "UNSIGNED32");
            break;
        case ECT_UNSIGNED24:
            sprintf(str, "UNSIGNED24");
            break;
        case ECT_UNSIGNED64:
            sprintf(str, "UNSIGNED64");
            break;
        case ECT_REAL32:
            sprintf(str, "REAL32");
            break;
        case ECT_REAL64:
            sprintf(str, "REAL64");
            break;
        case ECT_BIT1:
            sprintf(str, "BIT1");
            break;
        case ECT_BIT2:
            sprintf(str, "BIT2");
            break;
        case ECT_BIT3:
            sprintf(str, "BIT3");
            break;
        case ECT_BIT4:
            sprintf(str, "BIT4");
            break;
        case ECT_BIT5:
            sprintf(str, "BIT5");
            break;
        case ECT_BIT6:
            sprintf(str, "BIT6");
            break;
        case ECT_BIT7:
            sprintf(str, "BIT7");
            break;
        case ECT_BIT8:
            sprintf(str, "BIT8");
            break;
        case ECT_VISIBLE_STRING:
            sprintf(str, "VISIBLE_STR(%d)", bitlen);
            break;
        case ECT_OCTET_STRING:
            sprintf(str, "OCTET_STR(%d)", bitlen);
            break;
        default:
            sprintf(str, "dt:0x%4.4X (%d)", dtype, bitlen);
    }
    return str;
}

char* otype2string(uint16 otype)
{
    static char str[32] = { 0 };

    switch(otype)
    {
        case OTYPE_VAR:
            sprintf(str, "VAR");
            break;
        case OTYPE_ARRAY:
            sprintf(str, "ARRAY");
            break;
        case OTYPE_RECORD:
            sprintf(str, "RECORD");
            break;
        default:
            sprintf(str, "ot:0x%4.4X", otype);
    }
    return str;
}

char* access2string(uint16 access)
{
    static char str[32] = { 0 };

    sprintf(str, "%s%s%s%s%s%s",
            ((access & ATYPE_Rpre) != 0 ? "R" : "_"),
            ((access & ATYPE_Wpre) != 0 ? "W" : "_"),
            ((access & ATYPE_Rsafe) != 0 ? "R" : "_"),
            ((access & ATYPE_Wsafe) != 0 ? "W" : "_"),
            ((access & ATYPE_Rop) != 0 ? "R" : "_"),
            ((access & ATYPE_Wop) != 0 ? "W" : "_"));
    return str;
}

char* SDO2string(uint16 slave, uint16 index, uint8 subidx, uint16 dtype)
{
   int l = sizeof(usdo) - 1, i;
   uint8 *u8;
   int8 *i8;
   uint16 *u16;
   int16 *i16;
   uint32 *u32;
   int32 *i32;
   uint64 *u64;
   int64 *i64;
   float *sr;
   double *dr;
   char es[32];

   memset(&usdo, 0, 128);
   ec_SDOread(slave, index, subidx, FALSE, &l, &usdo, EC_TIMEOUTRXM);
   if (EcatError)
   {
      return ec_elist2string();
   }
   else
   {
      static char str[64] = { 0 };
      switch(dtype)
      {
         case ECT_BOOLEAN:
            u8 = (uint8*) &usdo[0];
            if (*u8) sprintf(str, "TRUE");
            else sprintf(str, "FALSE");
            break;
         case ECT_INTEGER8:
            i8 = (int8*) &usdo[0];
            sprintf(str, "0x%2.2x / %d", *i8, *i8);
            break;
         case ECT_INTEGER16:
            i16 = (int16*) &usdo[0];
            sprintf(str, "0x%4.4x / %d", *i16, *i16);
            break;
         case ECT_INTEGER32:
         case ECT_INTEGER24:
            i32 = (int32*) &usdo[0];
            sprintf(str, "0x%8.8lx / %ld", *i32, *i32);
            break;
         case ECT_INTEGER64:
            i64 = (int64*) &usdo[0];
            sprintf(str, "0x%16.16"PRIx64" / %"PRId64, *i64, *i64);
            break;
         case ECT_UNSIGNED8:
            u8 = (uint8*) &usdo[0];
            sprintf(str, "0x%2.2x / %u", *u8, *u8);
            break;
         case ECT_UNSIGNED16:
            u16 = (uint16*) &usdo[0];
            sprintf(str, "0x%4.4x / %u", *u16, *u16);
            break;
         case ECT_UNSIGNED32:
         case ECT_UNSIGNED24:
            u32 = (uint32*) &usdo[0];
            sprintf(str, "0x%8.8lx / %lu", *u32, *u32);
            break;
         case ECT_UNSIGNED64:
            u64 = (uint64*) &usdo[0];
            sprintf(str, "0x%16.16"PRIx64" / %"PRIu64, *u64, *u64);
            break;
         case ECT_REAL32:
            sr = (float*) &usdo[0];
            sprintf(str, "%f", *sr);
            sprintf(str, "%f", *sr);
            break;
         case ECT_REAL64:
            dr = (double*) &usdo[0];
            sprintf(str, "%f", *dr);
            break;
         case ECT_BIT1:
         case ECT_BIT2:
         case ECT_BIT3:
         case ECT_BIT4:
         case ECT_BIT5:
         case ECT_BIT6:
         case ECT_BIT7:
         case ECT_BIT8:
            u8 = (uint8*) &usdo[0];
            sprintf(str, "0x%x / %u", *u8, *u8);
            break;
         case ECT_VISIBLE_STRING:
            strcpy(str, "\"");
            strcat(str, usdo);
            strcat(str, "\"");
            break;
         case ECT_OCTET_STRING:
            str[0] = 0x00;
            for (i = 0 ; i < l ; i++)
            {
               sprintf(es, "0x%2.2x ", usdo[i]);
               strcat( str, es);
            }
            break;
         default:
            sprintf(str, "Unknown type");
      }
      return str;
   }
}

/** Read PDO assign structure */
int si_PDOassign(uint16 slave, uint16 PDOassign, int mapoffset, int bitoffset)
{
    uint16 idxloop, nidx, subidxloop, rdat, idx, subidx;
    uint8 subcnt;
    int wkc, bsize = 0, rdl;
    int32 rdat2;
    uint8 bitlen, obj_subidx;
    uint16 obj_idx;
    int abs_offset, abs_bit;

    rdl = sizeof(rdat); rdat = 0;
    /* read PDO assign subindex 0 ( = number of PDO's) */
    wkc = ec_SDOread(slave, PDOassign, 0x00, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
    rdat = etohs(rdat);
    /* positive result from slave ? */
    if ((wkc > 0) && (rdat > 0))
    {
        /* number of available sub indexes */
        nidx = rdat;
        bsize = 0;
        /* read all PDO's */
        for (idxloop = 1; idxloop <= nidx; idxloop++)
        {
            rdl = sizeof(rdat); rdat = 0;
            /* read PDO assign */
            wkc = ec_SDOread(slave, PDOassign, (uint8)idxloop, FALSE, &rdl, &rdat, EC_TIMEOUTRXM);
            /* result is index of PDO */
            idx = etohs(rdat);
            if (idx > 0)
            {
                rdl = sizeof(subcnt); subcnt = 0;
                /* read number of subindexes of PDO */
                wkc = ec_SDOread(slave,idx, 0x00, FALSE, &rdl, &subcnt, EC_TIMEOUTRXM);
                subidx = subcnt;
                /* for each subindex */
                for (subidxloop = 1; subidxloop <= subidx; subidxloop++)
                {
                    rdl = sizeof(rdat2); rdat2 = 0;
                    /* read SDO that is mapped in PDO */
                    wkc = ec_SDOread(slave, idx, (uint8)subidxloop, FALSE, &rdl, &rdat2, EC_TIMEOUTRXM);
                    rdat2 = etohl(rdat2);
                    /* extract bitlength of SDO */
                    bitlen = LO_BYTE(rdat2);
                    bsize += bitlen;
                    obj_idx = (uint16)(rdat2 >> 16);
                    obj_subidx = (uint8)((rdat2 >> 8) & 0x000000ff);
                    abs_offset = mapoffset + (bitoffset / 8);
                    abs_bit = bitoffset % 8;
                    ODlist.Slave = slave;
                    ODlist.Index[0] = obj_idx;
                    OElist.Entries = 0;
                    wkc = 0;
                    /* read object entry from dictionary if not a filler (0x0000:0x00) */
                    if(obj_idx || obj_subidx)
                        wkc = ec_readOEsingle(0, obj_subidx, &ODlist, &OElist);
                    printf("  [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
                    if((wkc > 0) && OElist.Entries)
                    {
                        printf(" %-12s %s\n", dtype2string(OElist.DataType[obj_subidx], bitlen), OElist.Name[obj_subidx]);
                    }
                    else
                        printf("\n");
                    bitoffset += bitlen;
                };
            };
        };
    };
    /* return total found bitlength (PDO) */
    return bsize;
}

int si_map_sdo(int slave)
{
    int wkc, rdl;
    int retVal = 0;
    uint8 nSM, iSM, tSM;
    int Tsize, outputs_bo, inputs_bo;
    uint8 SMt_bug_add;

    printf("PDO mapping according to CoE :\n");
    SMt_bug_add = 0;
    outputs_bo = 0;
    inputs_bo = 0;
    rdl = sizeof(nSM); nSM = 0;
    /* read SyncManager Communication Type object count */
    wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, 0x00, FALSE, &rdl, &nSM, EC_TIMEOUTRXM);
    /* positive result from slave ? */
    if ((wkc > 0) && (nSM > 2))
    {
        /* make nSM equal to number of defined SM */
        nSM--;
        /* limit to maximum number of SM defined, if true the slave can't be configured */
        if (nSM > EC_MAXSM)
            nSM = EC_MAXSM;
        /* iterate for every SM type defined */
        for (iSM = 2 ; iSM <= nSM ; iSM++)
        {
            rdl = sizeof(tSM); tSM = 0;
            /* read SyncManager Communication Type */
            wkc = ec_SDOread(slave, ECT_SDO_SMCOMMTYPE, iSM + 1, FALSE, &rdl, &tSM, EC_TIMEOUTRXM);
            if (wkc > 0)
            {
                if((iSM == 2) && (tSM == 2)) // SM2 has type 2 == mailbox out, this is a bug in the slave!
                {
                    SMt_bug_add = 1; // try to correct, this works if the types are 0 1 2 3 and should be 1 2 3 4
                    printf("Activated SM type workaround, possible incorrect mapping.\n");
                }
                if(tSM)
                    tSM += SMt_bug_add; // only add if SMt > 0

                if (tSM == 3) // outputs
                {
                    /* read the assign RXPDO */
                    printf("  SM%1d outputs\n     addr b   index: sub bitl data_type    name\n", iSM);
                    Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].outputs - (uint8 *)&IOmap[0]), outputs_bo );
                    outputs_bo += Tsize;
                }
                if (tSM == 4) // inputs
                {
                    /* read the assign TXPDO */
                    printf("  SM%1d inputs\n     addr b   index: sub bitl data_type    name\n", iSM);
                    Tsize = si_PDOassign(slave, ECT_SDO_PDOASSIGN + iSM, (int)(ec_slave[slave].inputs - (uint8 *)&IOmap[0]), inputs_bo );
                    inputs_bo += Tsize;
                }
            }
        }
    }

    /* found some I/O bits ? */
    if ((outputs_bo > 0) || (inputs_bo > 0))
        retVal = 1;
    return retVal;
}

int si_siiPDO(uint16 slave, uint8 t, int mapoffset, int bitoffset)
{
    uint16 a , w, c, e, er, Size;
    uint8 eectl;
    uint16 obj_idx;
    uint8 obj_subidx;
    uint8 obj_name;
    uint8 obj_datatype;
    uint8 bitlen;
    int totalsize;
    ec_eepromPDOt eepPDO;
    ec_eepromPDOt *PDO;
    int abs_offset, abs_bit;
    char str_name[EC_MAXNAME + 1];

    eectl = ec_slave[slave].eep_pdi;
    Size = 0;
    totalsize = 0;
    PDO = &eepPDO;
    PDO->nPDO = 0;
    PDO->Length = 0;
    PDO->Index[1] = 0;
    for (c = 0 ; c < EC_MAXSM ; c++) PDO->SMbitsize[c] = 0;
    if (t > 1)
        t = 1;
    PDO->Startpos = ec_siifind(slave, ECT_SII_PDO + t);
    if (PDO->Startpos > 0)
    {
        a = PDO->Startpos;
        w = ec_siigetbyte(slave, a++);
        w += (ec_siigetbyte(slave, a++) << 8);
        PDO->Length = w;
        c = 1;
        /* traverse through all PDOs */
        do
        {
            PDO->nPDO++;
            PDO->Index[PDO->nPDO] = ec_siigetbyte(slave, a++);
            PDO->Index[PDO->nPDO] += (ec_siigetbyte(slave, a++) << 8);
            PDO->BitSize[PDO->nPDO] = 0;
            c++;
            /* number of entries in PDO */
            e = ec_siigetbyte(slave, a++);
            PDO->SyncM[PDO->nPDO] = ec_siigetbyte(slave, a++);
            a++;
            obj_name = ec_siigetbyte(slave, a++);
            a += 2;
            c += 2;
            if (PDO->SyncM[PDO->nPDO] < EC_MAXSM) /* active and in range SM? */
            {
                str_name[0] = 0;
                if(obj_name)
                  ec_siistring(str_name, slave, obj_name);
                if (t)
                  printf("  SM%1d RXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
                else
                  printf("  SM%1d TXPDO 0x%4.4X %s\n", PDO->SyncM[PDO->nPDO], PDO->Index[PDO->nPDO], str_name);
                printf("     addr b   index: sub bitl data_type    name\n");
                /* read all entries defined in PDO */
                for (er = 1; er <= e; er++)
                {
                    c += 4;
                    obj_idx = ec_siigetbyte(slave, a++);
                    obj_idx += (ec_siigetbyte(slave, a++) << 8);
                    obj_subidx = ec_siigetbyte(slave, a++);
                    obj_name = ec_siigetbyte(slave, a++);
                    obj_datatype = ec_siigetbyte(slave, a++);
                    bitlen = ec_siigetbyte(slave, a++);
                    abs_offset = mapoffset + (bitoffset / 8);
                    abs_bit = bitoffset % 8;

                    PDO->BitSize[PDO->nPDO] += bitlen;
                    a += 2;

                    /* skip entry if filler (0x0000:0x00) */
                    if(obj_idx || obj_subidx)
                    {
                       str_name[0] = 0;
                       if(obj_name)
                          ec_siistring(str_name, slave, obj_name);

                       printf("  [0x%4.4X.%1d] 0x%4.4X:0x%2.2X 0x%2.2X", abs_offset, abs_bit, obj_idx, obj_subidx, bitlen);
                       printf(" %-12s %s\n", dtype2string(obj_datatype, bitlen), str_name);
                    }
                    bitoffset += bitlen;
                    totalsize += bitlen;
                }
                PDO->SMbitsize[ PDO->SyncM[PDO->nPDO] ] += PDO->BitSize[PDO->nPDO];
                Size += PDO->BitSize[PDO->nPDO];
                c++;
            }
            else /* PDO deactivated because SM is 0xff or > EC_MAXSM */
            {
                c += 4 * e;
                a += 8 * e;
                c++;
            }
            if (PDO->nPDO >= (EC_MAXEEPDO - 1)) c = PDO->Length; /* limit number of PDO entries in buffer */
        }
        while (c < PDO->Length);
    }
    if (eectl) ec_eeprom2pdi(slave); /* if eeprom control was previously pdi then restore */
    return totalsize;
}


int si_map_sii(int slave)
{
    int retVal = 0;
    int Tsize, outputs_bo, inputs_bo;

    printf("PDO mapping according to SII :\n");

    outputs_bo = 0;
    inputs_bo = 0;
    /* read the assign RXPDOs */
    Tsize = si_siiPDO(slave, 1, (int)(ec_slave[slave].outputs - (uint8*)&IOmap), outputs_bo );
    outputs_bo += Tsize;
    /* read the assign TXPDOs */
    Tsize = si_siiPDO(slave, 0, (int)(ec_slave[slave].inputs - (uint8*)&IOmap), inputs_bo );
    inputs_bo += Tsize;
    /* found some I/O bits ? */
    if ((outputs_bo > 0) || (inputs_bo > 0))
        retVal = 1;
    return retVal;
}

void si_sdo(int cnt)
{
    int i, j;

    ODlist.Entries = 0;
    memset(&ODlist, 0, sizeof(ODlist));
    if( ec_readODlist(cnt, &ODlist))
    {
        printf(" CoE Object Description found, %d entries.\n",ODlist.Entries);
        for( i = 0 ; i < ODlist.Entries ; i++)
        {
            uint8_t max_sub;
            char name[128] = { 0 };

            ec_readODdescription(i, &ODlist);
            while(EcatError) printf(" - %s\n", ec_elist2string());
            snprintf(name, sizeof(name) - 1, "\"%s\"", ODlist.Name[i]);
            if (ODlist.ObjectCode[i] == OTYPE_VAR)
            {
                printf("0x%04x      %-40s      [%s]\n", ODlist.Index[i], name,
                       otype2string(ODlist.ObjectCode[i]));
            }
            else
            {
                printf("0x%04x      %-40s      [%s  maxsub(0x%02x / %d)]\n",
                       ODlist.Index[i], name, otype2string(ODlist.ObjectCode[i]),
                       ODlist.MaxSub[i], ODlist.MaxSub[i]);
            }
            memset(&OElist, 0, sizeof(OElist));
            ec_readOE(i, &ODlist, &OElist);
            while(EcatError) printf("- %s\n", ec_elist2string());

            if(ODlist.ObjectCode[i] != OTYPE_VAR)
            {
                int l = sizeof(max_sub);
                ec_SDOread(cnt, ODlist.Index[i], 0, FALSE, &l, &max_sub, EC_TIMEOUTRXM);
            }
            else {
                max_sub = ODlist.MaxSub[i];
            }

            for( j = 0 ; j < max_sub+1 ; j++)
            {
                if ((OElist.DataType[j] > 0) && (OElist.BitLength[j] > 0))
                {
                    snprintf(name, sizeof(name) - 1, "\"%s\"", OElist.Name[j]);
                    printf("    0x%02x      %-40s      [%-16s %6s]      ", j, name,
                           dtype2string(OElist.DataType[j], OElist.BitLength[j]),
                           access2string(OElist.ObjAccess[j]));
                    if ((OElist.ObjAccess[j] & 0x0007))
                    {
                        printf("%s", SDO2string(cnt, ODlist.Index[i], j, OElist.DataType[j]));
                    }
                    printf("\n");
                }
            }
        }
    }
    else
    {
        while(EcatError) printf("%s", ec_elist2string());
    }
}

void soem_run(const char *param)
{
	int cnt, i, j, nSM;
    uint16 ssigen;
    int expectedWKC;

	printf("Starting slaveinfo\n");
	if (ec_init(param))
	{

	  printf("ec_init on %s succeeded.\n",param);
         ec_configdc();

	  if ( ec_config(FALSE, &IOmap) > 0 )
      {
         while(EcatError) printf("%s", ec_elist2string());
         printf("%d slaves found and configured.\n",ec_slavecount);
         expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
         printf("Calculated workcounter %d\n", expectedWKC);
         /* wait for all slaves to reach SAFE_OP state */
         ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE * 3);
         if (ec_slave[0].state != EC_STATE_SAFE_OP )
         {
            printf("Not all slaves reached safe operational state.\n");
            ec_readstate();
            for(i = 1; i<=ec_slavecount ; i++)
            {
               if(ec_slave[i].state != EC_STATE_SAFE_OP)
               {
                  printf("Slave %d State=%2x StatusCode=%4x : %s\n",
                     i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
               }
            }
         }

         ec_readstate();
         for( cnt = 1 ; cnt <= ec_slavecount ; cnt++)
         {
            printf("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %ld[ns]\n Has DC: %d\n",
                  cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
                  ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
            if (ec_slave[cnt].hasdc) printf(" DCParentport:%d\n", ec_slave[cnt].parentport);
            printf(" Activeports:%d.%d.%d.%d\n", (ec_slave[cnt].activeports & 0x01) > 0 ,
                                         (ec_slave[cnt].activeports & 0x02) > 0 ,
                                         (ec_slave[cnt].activeports & 0x04) > 0 ,
                                         (ec_slave[cnt].activeports & 0x08) > 0 );
            printf(" Configured address: %4.4x\n", ec_slave[cnt].configadr);
            printf(" Man: %8.8x ID: %8.8x Rev: %8.8x\n", (int)ec_slave[cnt].eep_man, (int)ec_slave[cnt].eep_id, (int)ec_slave[cnt].eep_rev);
            for(nSM = 0 ; nSM < EC_MAXSM ; nSM++)
            {
               if(ec_slave[cnt].SM[nSM].StartAddr > 0)
                  printf(" SM%1d A:%4.4x L:%4d F:%8.8lx Type:%d\n",nSM, etohs(ec_slave[cnt].SM[nSM].StartAddr), etohs(ec_slave[cnt].SM[nSM].SMlength),
                         etohl(ec_slave[cnt].SM[nSM].SMflags), ec_slave[cnt].SMtype[nSM]);
            }
            for(j = 0 ; j < ec_slave[cnt].FMMUunused ; j++)
            {
               printf(" FMMU%1d Ls:%8.8lx Ll:%4d Lsb:%d Leb:%d Ps:%4.4x Psb:%d Ty:%2.2x Act:%2.2x\n", j,
                       etohl(ec_slave[cnt].FMMU[j].LogStart), etohs(ec_slave[cnt].FMMU[j].LogLength), ec_slave[cnt].FMMU[j].LogStartbit,
                       ec_slave[cnt].FMMU[j].LogEndbit, etohs(ec_slave[cnt].FMMU[j].PhysStart), ec_slave[cnt].FMMU[j].PhysStartBit,
                       ec_slave[cnt].FMMU[j].FMMUtype, ec_slave[cnt].FMMU[j].FMMUactive);
            }
            printf(" FMMUfunc 0:%d 1:%d 2:%d 3:%d\n",
                     ec_slave[cnt].FMMU0func, ec_slave[cnt].FMMU1func, ec_slave[cnt].FMMU2func, ec_slave[cnt].FMMU3func);
            printf(" MBX length wr: %d rd: %d MBX protocols : %2.2x\n", ec_slave[cnt].mbx_l, ec_slave[cnt].mbx_rl, ec_slave[cnt].mbx_proto);
            ssigen = ec_siifind(cnt, ECT_SII_GENERAL);
            /* SII general section */
            if (ssigen)
            {
               ec_slave[cnt].CoEdetails = ec_siigetbyte(cnt, ssigen + 0x07);
               ec_slave[cnt].FoEdetails = ec_siigetbyte(cnt, ssigen + 0x08);
               ec_slave[cnt].EoEdetails = ec_siigetbyte(cnt, ssigen + 0x09);
               ec_slave[cnt].SoEdetails = ec_siigetbyte(cnt, ssigen + 0x0a);
               if((ec_siigetbyte(cnt, ssigen + 0x0d) & 0x02) > 0)
               {
                  ec_slave[cnt].blockLRW = 1;
                  ec_slave[0].blockLRW++;
               }
               ec_slave[cnt].Ebuscurrent = ec_siigetbyte(cnt, ssigen + 0x0e);
               ec_slave[cnt].Ebuscurrent += ec_siigetbyte(cnt, ssigen + 0x0f) << 8;
               ec_slave[0].Ebuscurrent += ec_slave[cnt].Ebuscurrent;
            }
            printf(" CoE details: %2.2x FoE details: %2.2x EoE details: %2.2x SoE details: %2.2x\n",
                    ec_slave[cnt].CoEdetails, ec_slave[cnt].FoEdetails, ec_slave[cnt].EoEdetails, ec_slave[cnt].SoEdetails);
            printf(" Ebus current: %d[mA]\n only LRD/LWR:%d\n",
                    ec_slave[cnt].Ebuscurrent, ec_slave[cnt].blockLRW);
            if ((ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE) && printSDO)
                    si_sdo(cnt);
            if(printMAP)
            {
                    if (ec_slave[cnt].mbx_proto & ECT_MBXPROT_COE)
                        si_map_sdo(cnt);
                    else
                        si_map_sii(cnt);
            }
         }
      }
      else
      {
         printf("No slaves found!\n");
      }
      printf("End slaveinfo, close socket\n");
      /* stop SOEM, close socket */
      ec_close();

	}else{
		printf("ec_init on %s failed.\n",param);
	}

}

#include "lwip.h"
extern struct netif gnetif;

void soem_motor_Task(void* parameter)
{
	while(1)
	{
		printf("SOEM (Simple Open EtherCAT Master)\n");

		//
		printf("test_oshw_htons...");
		test_oshw_htons ();
		printf("Ok\n");

		//
		printf("test_oshw_htons...");
		test_oshw_ntohs ();
		printf("Ok\n");

		printf("test_osal_current_time...\n");
//		test_osal_current_time ();

		printf("Test finished\n");
//		ReceiveAll


//		ETH_MACDMAConfig
//		EthHandle.Instance->MACFFR
//		ethernetif_input
//		ethernet_input
//		HAL_ETH_GetReceivedFrame_IT
//		tcpip_input
//		HAL_ETH_RxCpltCallback
//		ETH_InitStructure
//		ETH_DeInit
//		IP_SOF_BROADCAST
//		net_hook_test();
//		HAL_ETH_Start
//		ethernetif_input
//		tcpip_thread
		//slaveinfo test
//		gnetif.name
		soem_run("st0");
//		EC_MAXODLIST
//		EC_MAXSLAVE
//		EC_MAXBUF
		vTaskDelay(5000);
	}
}

soem_motor.h测试代码:

#ifndef _SOEM_MOTOR_H
#define _SOEM_MOTOR_H


void soem_motor_Task(void* parameter);

#endif

4. 周期同步位置模式CSP控制代码

下面是测试csp周期位置模式测试代码:
周期发送位置就动起来了

/*
 * PVD.c
 *
 *  Created on: 2022年9月22日
 *      Author: shengmidao
 */

#include "soem_motor.h"


#include <string.h>
#include <stdio.h>

#include "nicdrv.h"

#include <lwip/pbuf.h>
#include <lwip/inet.h>

#include "ethercat.h"
#include "FreeRTOSConfig.h"
#include "osal.h"
#include "oshw.h"
#include "netif.h"
#include "netdev_hook.h"

//csp代码
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void hex_dump(const uint8_t *ptr, size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;

    configASSERT(ptr != NULL);

    for (i = 0; i < buflen; i += 16)
    {
        printf("%08X: ", i);

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%02X ", buf[i + j]);
            else
                printf("   ");
        printf(" ");

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
        printf("\n");
    }
}


static char IOmap[4096];
typedef struct  __attribute__((__packed__))
{
	unsigned char  mode_byte;
	unsigned short control_word;
	long  dest_pos;
	unsigned short error_word;
	unsigned short status_word;
	long  cur_pos;
}SERVO_DATA_T;

typedef struct
{
	SERVO_DATA_T servo_data[3];
}SERVOS_T;

SERVOS_T *servos = (SERVOS_T *)IOmap;

void view_slave_data()
{
	hex_dump(IOmap,32);
}

static void echo_time()
{
	struct timeval tp;
	osal_gettimeofday(&tp, 0);
//	printf("****cur time = %d,%03d,%03d(us)\n", tp.tv_sec,tp.tv_usec/1000,tp.tv_usec%1000);
}

int safe_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex, int size, void *data)
{
	int wkc, cnt=0;
	do{
		wkc = ec_SDOwrite(Slave, Index, SubIndex, FALSE, size, data, EC_TIMEOUTRXM);
		cnt++;
	}while(wkc<=0 && cnt<10);
	return wkc;
}
int safe_SDCwrite_b(uint16 Slave, uint16 Index, uint8 SubIndex, uint8 b)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 1, &b);
}
int safe_SDCwrite_w(uint16 Slave, uint16 Index, uint8 SubIndex, uint16 w)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 2, &w);
}
int safe_SDCwrite_dw(uint16 Slave, uint16 Index, uint8 SubIndex, uint32 dw)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 4, &dw);
}

int safe_SDOread(uint16 Slave, uint16 Index, uint8 SubIndex, int size, void *data)
{
	int wkc, cnt=0;
	do{
		wkc = ec_SDOread(Slave, Index, SubIndex, FALSE, &size, data, EC_TIMEOUTRXM);
	}while(wkc<=0 && cnt<10);
	return wkc;
}
int safe_SDOread_b(uint16 Slave, uint16 Index, uint8 SubIndex, uint8 b)
{
	return safe_SDOread(Slave, Index, SubIndex, 1, &b);
}

int safe_SDOread_w(uint16 Slave, uint16 Index, uint8 SubIndex, uint16 w)
{
	return safe_SDOread(Slave, Index, SubIndex, 2, &w);
}

int safe_SDOread_dw(uint16 Slave, uint16 Index, uint8 SubIndex, uint32 dw)
{
	return safe_SDOread(Slave, Index, SubIndex, 4, &dw);
}


void viewSDO(uint16_t slave, uint16_t index, uint16_t subindex, int bytes)
{
	uint32_t dw = 0;
	int wkc;
	safe_SDOread(slave, index, subindex, bytes, &dw);
	printf("SDO read=%s, SDO[0x%04x.%02x] = 0x%08x\n", wkc?"success":"fail",index, subindex, dw);
}

void process_data_config()
{
	u8_t     ind;

	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		//rpdo------------
		//1c12.0
		safe_SDCwrite_b(slave, 0x1c12, 0, 0);
		safe_SDCwrite_w(slave, 0x1c12, 1, htoes(0x1600));

		//1600
		ind = 0;
		safe_SDCwrite_b(slave, 0x1600, 0, 0);
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60600008));//6060h(控制模式)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60400010));//6040h(控制字)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x607a0020));//607Ah(目标位置)
		safe_SDCwrite_b(slave, 0x1600, 0, ind);

		//1c12.0
		safe_SDCwrite_b(slave, 0x1c12, 0, 1);

		//tpdo-------------
		//1c13.0
		safe_SDCwrite_b(slave, 0x1c13, 0x00, 0);
		safe_SDCwrite_w(slave, 0x1c13, 0x01, htoes(0x1a00));

		//1a00
		ind = 0;
		safe_SDCwrite_b(slave, 0x1a00, 0, 0);
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x603F0010));//603Fh(错误码)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x60410010));//6041h(状态字)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x60640020));//6064h(位置反馈)
		safe_SDCwrite_b(slave, 0x1a00, 0, ind);

		//1c13.0
		safe_SDCwrite_b(slave, 0x1c13, 0, 1);

		safe_SDCwrite_b(slave, 0x6060, 0, 1);		//pp模式

	}
}


void servo_switch_op()
{
	int sta;
	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		int idx = slave - 1;
		sta = servos->servo_data[idx].status_word & 0x3ff;			//5687->0x1637:		5744->0x1670:0x270
		//printf("servo_switch_op: slave %d [6041]=%04x\n",slave,servos->servo_data[idx].status_word );
		if(servos->servo_data[idx].status_word & 0x8) //故障处理
		{  //0x21f  //0x218
			if(sta==0x0218)
			{
				servos->servo_data[idx].control_word = 0x80;
	//			printf("***slave %d control=%04x\n",slave,servos->servo_data[idx].control_word );
			}
			continue;
		}
		//printf("servo_switch_op: slave %d sta=%04x\n", slave, sta );
		//base on cia402       				control_word              	status_word bit0~9
		/* 上电初始化														0x0000
		 * 初*-伺服无故障													0x0250
		 * 伺*-伺服准备好						0x0006						0x0231
		 * 伺*-等待使能伺服						0x0007						0x0233
		 * 等*-伺服运行							0x000F						0x0237
		 *
		 * 伺*-等待打开伺服使能					0x0007						0x0233
		 * 等*-伺服准备好						0x0006						0x0231
		 * 伺*-伺服无故障						0x0000						0x0250
		 *
		 * 伺服运行-伺服准备好					0x0006						0x0231
		 * 伺服运行-伺服无故障					0x0000						0x0250
		 * 等待打开伺服使能-伺服无故障			0x0000						0x0250
		 * 伺服运行-快速停机						0x0002						0x0217
		 *
		 * 快速停机-伺服无故障												0x0250
		 * -故障停机															0x021F
		 * 故障停机-故障														0x0218
		 * 故障-伺服无故障						0x80						0x0250
		 * 快速停机-伺服运行						0x0F						0x0237
		 *
		 */

		switch(sta)
		{
			case 0x250:
			case 0x270:
				servos->servo_data[idx].control_word = 0x6;
				break;
			case 0x231:
				servos->servo_data[idx].control_word = 0x7;
				break;
			case 0x233:
				servos->servo_data[idx].control_word = 0xf;
				break;
			case 0x217:
				servos->servo_data[idx].control_word = 0xf;
				break;
			default:
				//servos->servo_data[idx].control_word = 0x6;
				break;
		}
		//printf("slave %d control=%04x\n",slave,servos->servo_data[idx].control_word );
	}

}
void servo_switch_idle()
{
	int sta;
	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		servos->servo_data[slave-1].control_word = 0x0;
	}
}

#define EC_TIMEOUTMON 500
int expectedWKC;
boolean needlf;
volatile int wkc;
boolean inOP;
uint8 currentgroup = 0;
long test_poit;
uint16 test_error;
void sv660n_config(char *ifname)
{
	needlf = FALSE;
    inOP = FALSE;

	ecx_context.manualstatechange = 1;

//	printf("========================\n");
//	printf("sv660 config\n");
	echo_time();

	if (ec_init(ifname))
	{
//		printf("ec_init on %s succeeded.\n",ifname);

		//init status
//		printf("\nRequest init state for all slaves\n");
		ec_slave[0].state = EC_STATE_INIT;
		//request INIT state for all slaves
		ec_writestate(0);
		ec_readstate();

		//显示1状态
		/* wait for all slaves to reach SAFE_OP state */
		ec_statecheck(0, EC_STATE_INIT,  EC_TIMEOUTSTATE * 3);
		if (ec_slave[0].state != EC_STATE_INIT ){
//			printf("Not all slaves reached init state.\n");
			ec_readstate();
			for(int i = 1; i<=ec_slavecount ; i++){
				if(ec_slave[i].state != EC_STATE_INIT){
//					printf("Slave %d State=0x%2x StatusCode=0x%04x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
				}
			}
		}
		echo_time();
		//if ( ec_config(FALSE, &IOmap) > 0 )

		wkc = ec_config_init(0/*usetable*/);

		if (wkc > 0)
		{

			ec_configdc();
//			ec_dcsync0(1, TRUE, 2000000, 50); // SYNC0 on slave 1
			while(EcatError) printf("%s", ec_elist2string());
//			printf("%d slaves found and configured.\n",ec_slavecount);


			/* request pre_op for slave */
//			printf("\nRequest pre_op state for all slaves\n");
			ec_slave[0].state = EC_STATE_PRE_OP | EC_STATE_ACK;
			ec_writestate(0);
			ec_readstate();

//			//故障复位
//			safe_SDOread(1,0x603f,0,2,&test_error);		//30081
//			if(test_error==30081)
//			{
//				safe_SDCwrite_w(1,0x6040, 0, 0x80);
//			}
//			ec_readstate();

			//现在应该在pre_op状态
			//显示2状态
			process_data_config(); //config tpdo/rpdo

			//config fmmu
			ec_config_map(IOmap);


			/* request safe_op for slave */
			ec_slave[0].state = EC_STATE_SAFE_OP;
			ec_writestate(0);
			ec_readstate();

			//safe-op
			expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
//			printf("Calculated workcounter %d\n", expectedWKC);

			/* wait for all slaves to reach SAFE_OP state */
			ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE * 3);
			if (ec_slave[0].state != EC_STATE_SAFE_OP ){
//				printf("Not all slaves reached safe operational state.\n");
				ec_readstate();
				for(int i = 1; i<=ec_slavecount ; i++){
					if(ec_slave[i].state != EC_STATE_SAFE_OP){
//						printf("Slave %d State=0x%2x StatusCode=0x%04x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
					}
				}
			}else{
				//显示4状态
				//启动伺服
				servos->servo_data[0].mode_byte = 8; //csp mode

				//op status
//				printf("Request operational state for all slaves\n");
				expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
//				printf("Calculated workcounter %d\n", expectedWKC);

				// send one valid process data to make outputs in slaves happy
				ec_slave[0].state = EC_STATE_OPERATIONAL;
				ec_send_processdata();
				ec_receive_processdata(EC_TIMEOUTRET*3);
				ec_slave[0].state = EC_STATE_OPERATIONAL;
				ec_writestate(0);
				ec_readstate();

				int chk = 200;
				// wait for all slaves to reach OP state
				do
				{
					ec_slave[0].state = EC_STATE_OPERATIONAL;
					ec_send_processdata();
					ec_receive_processdata(EC_TIMEOUTRET);
//					printf("--->workcounter %d\n", wkc);
					ec_statecheck(0, EC_STATE_OPERATIONAL, 50000);
				}
				while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL));

				if (ec_slave[0].state == EC_STATE_OPERATIONAL )
				{
//					printf("<<<Operational>>> state reached for all slaves.\n");
					inOP = TRUE;

					osal_timert t;
					osal_timer_start(&t, 1000);

						// cyclic loop
						for(int i = 1; i <= 20000; i++)
						{
							servo_switch_op();
							if(servos->servo_data[0].control_word==7){
								servos->servo_data[0].dest_pos = servos->servo_data[0].cur_pos;
								test_poit=servos->servo_data[0].dest_pos;
								//printf("cur pos = %ld\n", servos->servo_data[0].cur_pos);
							}
							if(servos->servo_data[0].control_word==0xf){
								if(i<=300)
								{
									servos->servo_data[0].dest_pos += (i*10);
								}else
								{
									servos->servo_data[0].dest_pos += 3000;
								}
							}
							while(osal_timer_is_expired(&t)==FALSE);
							osal_timer_start(&t, 1000);

							ec_send_processdata();
							wkc = ec_receive_processdata(EC_TIMEOUTRET);

							if(wkc >= expectedWKC){
								//printf("~~~~WKC %d \n", wkc);
							}
							if(wkc <=0 ){
	//							printf("Error.\n");
								break;
							}
						}
						osal_timer_start(&t, 1000);
						while(osal_timer_is_expired(&t)==FALSE);
						servos->servo_data[0].control_word = 0x0002;
						ec_send_processdata();
						ec_receive_processdata(EC_TIMEOUTRET);

//						servos->servo_data[0].control_word = 0x0;
//						ec_send_processdata();
//						ec_receive_processdata(EC_TIMEOUTRET);
//
//						servos->servo_data[0].control_word = 0x0;
//						ec_send_processdata();
//						ec_receive_processdata(EC_TIMEOUTRET);
						inOP = FALSE;

				}
				else
				{
//					printf("Not all slaves reached operational state.\n");
					ec_readstate();
					for(int i = 1; i<=ec_slavecount ; i++)
					{
						if(ec_slave[i].state != EC_STATE_OPERATIONAL)
						{
//							printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n",i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
						}
					}
				}

				//init status
//				printf("\nRequest init state for all slaves\n");
				ec_slave[0].state = EC_STATE_INIT;
				//request INIT state for all slaves
				ec_writestate(0);
			}
		} else {
//			printf("No slaves found!\n");
		}

		echo_time();
//		printf("End soem, close socket\n");

		// stop SOEM, close socket
		ec_close();
	}else{
//		printf("ec_init on %s failed.\n",ifname);
	}
//	printf("IOMAP addr = 0x%08x\n", (uint32_t)IOmap);

//	printf("========================\n");
	view_slave_data();
}



//伺服控制线程
#include "lwip.h"
extern struct netif gnetif;

void soem_motor_Task(void* parameter)
{

	while(1)
	{
//测试代码程序
//		printf("SOEM (Simple Open EtherCAT Master)\n");
//
//		//
//		printf("test_oshw_htons...");
//		test_oshw_htons ();
//		printf("Ok\n");
//
//		//
//		printf("test_oshw_htons...");
//		test_oshw_ntohs ();
//		printf("Ok\n");
//
//		printf("test_osal_current_time...\n");
//		test_osal_current_time ();
//
//		printf("Test finished\n");
//
		sv660n_config("st0");
		vTaskDelay(10000);
	}
}

5. 周期同步扭矩模式CST控制代码

下面是测试cst周期扭矩模式测试代码:
周期发送扭矩就动起来了

/*
 * PVD.c
 *
 *  Created on: 2022年9月22日
 *      Author: shengmidao
 */

#include "soem_motor.h"


#include <string.h>
#include <stdio.h>

#include "nicdrv.h"

#include <lwip/pbuf.h>
#include <lwip/inet.h>

#include "ethercat.h"
#include "FreeRTOSConfig.h"
#include "osal.h"
#include "oshw.h"
#include "netif.h"
#include "netdev_hook.h"


//csp代码
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void hex_dump(const uint8_t *ptr, size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;

    configASSERT(ptr != NULL);

    for (i = 0; i < buflen; i += 16)
    {
        printf("%08X: ", i);

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%02X ", buf[i + j]);
            else
                printf("   ");
        printf(" ");

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
        printf("\n");
    }
}


static char IOmap[4096];
typedef struct  __attribute__((__packed__))
{
	unsigned char  mode_byte;
	unsigned short control_word;
	long  dest_pos;
	long  dest_V;
	unsigned short dest_T;
	unsigned short error_word;
	unsigned short status_word;
	long  cur_pos;
	long  cur_V;
	unsigned short cur_T;
}SERVO_DATA_T;

typedef struct
{
	SERVO_DATA_T servo_data[3];
}SERVOS_T;

SERVOS_T *servos = (SERVOS_T *)IOmap;

void view_slave_data()
{
	hex_dump(IOmap,32);
}

static void echo_time()
{
	struct timeval tp;
	osal_gettimeofday(&tp, 0);
//	printf("****cur time = %d,%03d,%03d(us)\n", tp.tv_sec,tp.tv_usec/1000,tp.tv_usec%1000);
}

int safe_SDOwrite(uint16 Slave, uint16 Index, uint8 SubIndex, int size, void *data)
{
	int wkc, cnt=0;
	do{
		wkc = ec_SDOwrite(Slave, Index, SubIndex, FALSE, size, data, EC_TIMEOUTRXM);
		cnt++;
	}while(wkc<=0 && cnt<10);
	return wkc;
}
int safe_SDCwrite_b(uint16 Slave, uint16 Index, uint8 SubIndex, uint8 b)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 1, &b);
}
int safe_SDCwrite_w(uint16 Slave, uint16 Index, uint8 SubIndex, uint16 w)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 2, &w);
}
int safe_SDCwrite_dw(uint16 Slave, uint16 Index, uint8 SubIndex, uint32 dw)
{
	return safe_SDOwrite(Slave, Index, SubIndex, 4, &dw);
}

int safe_SDOread(uint16 Slave, uint16 Index, uint8 SubIndex, int size, void *data)
{
	int wkc, cnt=0;
	do{
		wkc = ec_SDOread(Slave, Index, SubIndex, FALSE, &size, data, EC_TIMEOUTRXM);
	}while(wkc<=0 && cnt<10);
	return wkc;
}
int safe_SDOread_b(uint16 Slave, uint16 Index, uint8 SubIndex, uint8 b)
{
	return safe_SDOread(Slave, Index, SubIndex, 1, &b);
}

int safe_SDOread_w(uint16 Slave, uint16 Index, uint8 SubIndex, uint16 w)
{
	return safe_SDOread(Slave, Index, SubIndex, 2, &w);
}

int safe_SDOread_dw(uint16 Slave, uint16 Index, uint8 SubIndex, uint32 dw)
{
	return safe_SDOread(Slave, Index, SubIndex, 4, &dw);
}


void viewSDO(uint16_t slave, uint16_t index, uint16_t subindex, int bytes)
{
	uint32_t dw = 0;
	int wkc;
	safe_SDOread(slave, index, subindex, bytes, &dw);
	printf("SDO read=%s, SDO[0x%04x.%02x] = 0x%08x\n", wkc?"success":"fail",index, subindex, dw);
}

void process_data_config()
{
	u8_t     ind;

	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		//rpdo------------
		//1c12.0
		safe_SDCwrite_b(slave, 0x1c12, 0, 0);
		safe_SDCwrite_w(slave, 0x1c12, 1, htoes(0x1600));

		//1600
		ind = 0;
		safe_SDCwrite_b(slave, 0x1600, 0, 0);
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60600008));//6060h(控制模式)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60400010));//6040h(控制字)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x607a0020));//607Ah(目标位置)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60ff0020));//60ffh(目标速度)
		safe_SDCwrite_dw(slave, 0x1600, ++ind, htoel(0x60710010));//6071h(目标扭矩)
		safe_SDCwrite_b(slave, 0x1600, 0, ind);

		//1c12.0
		safe_SDCwrite_b(slave, 0x1c12, 0, 1);

		//tpdo-------------
		//1c13.0
		safe_SDCwrite_b(slave, 0x1c13, 0x00, 0);
		safe_SDCwrite_w(slave, 0x1c13, 0x01, htoes(0x1a00));

		//1a00
		ind = 0;
		safe_SDCwrite_b(slave, 0x1a00, 0, 0);
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x603F0010));//603Fh(错误码)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x60410010));//6041h(状态字)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x60640020));//6064h(位置反馈)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x606c0020));//6064h(速度反馈)
		safe_SDCwrite_dw(slave, 0x1a00, ++ind, htoel(0x60770010));//6064h(扭矩反馈)
		safe_SDCwrite_b(slave, 0x1a00, 0, ind);

		//1c13.0
		safe_SDCwrite_b(slave, 0x1c13, 0, 1);

		safe_SDCwrite_b(slave, 0x6060, 0, 1);		//pp模式

	}
}


void servo_switch_op()
{
	int sta;
	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		int idx = slave - 1;
		sta = servos->servo_data[idx].status_word & 0x3ff;			//5687->0x1637:		5744->0x1670:0x270
		//printf("servo_switch_op: slave %d [6041]=%04x\n",slave,servos->servo_data[idx].status_word );
		if(servos->servo_data[idx].status_word & 0x8) //故障处理
		{  //0x21f  //0x218
			if(sta==0x0218)
			{
				servos->servo_data[idx].control_word = 0x80;
	//			printf("***slave %d control=%04x\n",slave,servos->servo_data[idx].control_word );
			}
			continue;
		}
		//printf("servo_switch_op: slave %d sta=%04x\n", slave, sta );
		//base on cia402       				control_word              	status_word bit0~9
		/* 上电初始化														0x0000
		 * 初*-伺服无故障													0x0250
		 * 伺*-伺服准备好						0x0006						0x0231
		 * 伺*-等待使能伺服						0x0007						0x0233
		 * 等*-伺服运行						0x000F						0x0237
		 *
		 * 伺*-等待打开伺服使能					0x0007						0x0233
		 * 等*-伺服准备好						0x0006						0x0231
		 * 伺*-伺服无故障						0x0000						0x0250
		 *
		 * 伺服运行-伺服准备好					0x0006						0x0231
		 * 伺服运行-伺服无故障					0x0000						0x0250
		 * 等待打开伺服使能-伺服无故障				0x0000						0x0250
		 * 伺服运行-快速停机						0x0002						0x0217
		 *
		 * 快速停机-伺服无故障												0x0250
		 * -故障停机														0x021F
		 * 故障停机-故障													0x0218
		 * 故障-伺服无故障						0x80						0x0250
		 * 快速停机-伺服运行						0x0F						0x0237
		 *
		 */

		switch(sta)
		{
			case 0x250:
			case 0x270:
				servos->servo_data[idx].control_word = 0x6;
				break;
			case 0x231:
				servos->servo_data[idx].control_word = 0x7;
				break;
			case 0x233:
				servos->servo_data[idx].control_word = 0xf;
				break;
			case 0x217:
				servos->servo_data[idx].control_word = 0xf;
				break;
			default:
				//servos->servo_data[idx].control_word = 0x6;
				break;
		}
		//printf("slave %d control=%04x\n",slave,servos->servo_data[idx].control_word );
	}

}
void servo_switch_idle()
{
	int sta;
	for(int slave = 1; slave <= *ecx_context.slavecount; slave++)
	{
		servos->servo_data[slave-1].control_word = 0x0;
	}
}

#define EC_TIMEOUTMON 500
int expectedWKC;
boolean needlf;
volatile int wkc;
boolean inOP;
uint8 currentgroup = 0;
uint16 test_error;
void sv660n_config(char *ifname,char *mode)
{
	needlf = FALSE;
    inOP = FALSE;

	ecx_context.manualstatechange = 1;

//	printf("========================\n");
//	printf("sv660 config\n");
	echo_time();

	if (ec_init(ifname))
	{
//		printf("ec_init on %s succeeded.\n",ifname);

		//init status
//		printf("\nRequest init state for all slaves\n");
		ec_slave[0].state = EC_STATE_INIT;
		//request INIT state for all slaves
		ec_writestate(0);
		ec_readstate();

		//显示1状态
		/* wait for all slaves to reach SAFE_OP state */
		ec_statecheck(0, EC_STATE_INIT,  EC_TIMEOUTSTATE * 3);
		if (ec_slave[0].state != EC_STATE_INIT ){
//			printf("Not all slaves reached init state.\n");
			ec_readstate();
			for(int i = 1; i<=ec_slavecount ; i++){
				if(ec_slave[i].state != EC_STATE_INIT){
//					printf("Slave %d State=0x%2x StatusCode=0x%04x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
				}
			}
		}
		echo_time();
		//if ( ec_config(FALSE, &IOmap) > 0 )

		wkc = ec_config_init(0/*usetable*/);

		if (wkc > 0)
		{

			ec_configdc();
//			ec_dcsync0(1, TRUE, 2000000, 50); // SYNC0 on slave 1
			while(EcatError) printf("%s", ec_elist2string());
//			printf("%d slaves found and configured.\n",ec_slavecount);


			/* request pre_op for slave */
//			printf("\nRequest pre_op state for all slaves\n");
			ec_slave[0].state = EC_STATE_PRE_OP | EC_STATE_ACK;
			ec_writestate(0);
			ec_readstate();

//			//故障复位
//			safe_SDOread(1,0x603f,0,2,&test_error);		//30081
//			if(test_error==30081)
//			{
//				safe_SDCwrite_w(1,0x6040, 0, 0x80);
//			}
//			ec_readstate();

			//现在应该在pre_op状态
			//显示2状态
			process_data_config(); //config tpdo/rpdo

			//config fmmu
			ec_config_map(IOmap);


			/* request safe_op for slave */
			ec_slave[0].state = EC_STATE_SAFE_OP;
			ec_writestate(0);
			ec_readstate();

			//safe-op
			expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
//			printf("Calculated workcounter %d\n", expectedWKC);

			/* wait for all slaves to reach SAFE_OP state */
			ec_statecheck(0, EC_STATE_SAFE_OP,  EC_TIMEOUTSTATE * 3);
			if (ec_slave[0].state != EC_STATE_SAFE_OP ){
//				printf("Not all slaves reached safe operational state.\n");
				ec_readstate();
				for(int i = 1; i<=ec_slavecount ; i++){
					if(ec_slave[i].state != EC_STATE_SAFE_OP){
//						printf("Slave %d State=0x%2x StatusCode=0x%04x : %s\n", i, ec_slave[i].state, ec_slave[i].ALstatuscode, ec_ALstatuscode2string(ec_slave[i].ALstatuscode));
					}
				}
			}else{
				//显示4状态
				//启动伺服
				servos->servo_data[0].mode_byte = 10; //cst mode

				//op status
//				printf("Request operational state for all slaves\n");
				expectedWKC = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
//				printf("Calculated workcounter %d\n", expectedWKC);

				// send one valid process data to make outputs in slaves happy
				ec_slave[0].state = EC_STATE_OPERATIONAL;
				ec_send_processdata();
				ec_receive_processdata(EC_TIMEOUTRET*3);
				ec_slave[0].state = EC_STATE_OPERATIONAL;
				ec_writestate(0);
				ec_readstate();

				int chk = 200;
				// wait for all slaves to reach OP state
				do
				{
					ec_slave[0].state = EC_STATE_OPERATIONAL;
					ec_send_processdata();
					ec_receive_processdata(EC_TIMEOUTRET);
//					printf("--->workcounter %d\n", wkc);
					ec_statecheck(0, EC_STATE_OPERATIONAL, 50000);
				}
				while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL));

				if (ec_slave[0].state == EC_STATE_OPERATIONAL )
				{
//					printf("<<<Operational>>> state reached for all slaves.\n");
					inOP = TRUE;

					osal_timert t;
					osal_timer_start(&t, 1000);

						// cyclic loop
						for(int i = 1; i <= 20000; i++)
						{
							servo_switch_op();
							if(servos->servo_data[0].control_word==7){
								servos->servo_data[0].dest_T = 0;
								//printf("cur pos = %ld\n", servos->servo_data[0].cur_pos);
							}
							if(servos->servo_data[0].control_word==0xf){
								if(i<=100)
								{
									servos->servo_data[0].dest_T = (i);
								}else
								{
									servos->servo_data[0].dest_T = 100;
								}
							}

							while(osal_timer_is_expired(&t)==FALSE);
							osal_timer_start(&t, 400);

							ec_send_processdata();
							wkc = ec_receive_processdata(EC_TIMEOUTRET);

							if(wkc >= expectedWKC){
								//printf("~~~~WKC %d \n", wkc);
							}
							if(wkc <=0 ){
	//							printf("Error.\n");
								break;
							}
						}
						osal_timer_start(&t, 1000);
						while(osal_timer_is_expired(&t)==FALSE);
						servos->servo_data[0].control_word = 0x0002;
						ec_send_processdata();
						ec_receive_processdata(EC_TIMEOUTRET);
						inOP = FALSE;
				}
				else
				{
					ec_readstate();
					for(int i = 1; i<=ec_slavecount ; i++)
					{
						if(ec_slave[i].state != EC_STATE_OPERATIONAL)
						{
						}
					}
				}
				ec_slave[0].state = EC_STATE_INIT;
				ec_writestate(0);
			}
		} else {
		}
		echo_time();
		ec_close();
	}else{

	}
	view_slave_data();
}



//伺服控制线程
#include "lwip.h"
extern struct netif gnetif;

void soem_motor_Task(void* parameter)
{

	while(1)
	{
//测试代码程序
//		printf("SOEM (Simple Open EtherCAT Master)\n");
//
//		//
//		printf("test_oshw_htons...");
//		test_oshw_htons ();
//		printf("Ok\n");
//
//		//
//		printf("test_oshw_htons...");
//		test_oshw_ntohs ();
//		printf("Ok\n");
//
//		printf("test_osal_current_time...\n");
//		test_osal_current_time ();
//
//		printf("Test finished\n");
//
//		soem_run("st0");
		sv660n_config("st0","CSP");	//CSP CSV CST
		vTaskDelay(10000);
	}
}



6. 汇川使用注意点

一定要开启DC同步时钟,将ec_dcsync0(1, TRUE, 2000000, 3000)开启
在这里插入图片描述
在这里插入图片描述
如果汇川驱动器显示无法从safe_op进入op状态,无法进入OP的原因就是同步失败导致的了。官方手册给出的错误代码原因是网络抖动造成的从站同步信号丢失,说明设置的DC周期太短了,在当前主站性能和网络条件下无法实现,所以适当增加DC周期即可解决。如:从默认的2000us改为10000us,如:ec_dcsync0(1, TRUE, 10000000, 3000)

7. 相关资料

ethercat协议+ CIA402协议+FreeRTOS例子:资料: link
链接稍后再挂。

都看到这里,来都来了,不点赞关注下,有什么问题可以在评论区提问

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值