STM32H7+FreeRTOS+LwIP移植EtherCAT開源主站SOEM

代碼下載什么的就不多說了,直接看需要移植修改的代碼。

1、osal.c修改

/*******************************************************************************                *          ***                    ****              ***          ***                    **** ***  ****  **********     ***        *****       ***  ****          ****** *********  **********     ***      *********     ************     ********** ****         ***          ***              ***   ***       ****   **** ***          ***  ******  ***      ***********   ***        ****   ****** ***          ***  ******  ***    *************   ***        ****      ****** ***          ****         ****   ***       ***   ***       ****          **** ***           *******      ***** **************  *************    ********** ***             *****        ***   *******   **  **  ******         ******                           t h e  r e a l t i m e  t a r g e t  e x p e r t s** http://www.rt-labs.com* Copyright (C) 2009. rt-labs AB, Sweden. All rights reserved.*------------------------------------------------------------------------------* $Id: osal.c 452 2013-02-26 21:02:58Z smf.arthur $*------------------------------------------------------------------------------*/#include "FreeRTOS.h"
#include "task.h"
#include "FreeRTOSConfig.h"
#include "osal.h"
#include <stdint.h>
#include <stdbool.h>/* --- 常量定義 --- */
#define USECS_PER_SEC   1000000UL
#define USECS_PER_MSEC  1000UL
#define SECS_PER_DAY    86400UL/* --- 靜態函數聲明 --- */
static void udelay(uint32_t us);
static inline uint32_t get_usec_per_tick(void);/* * 獲取每個tick的微秒數(運行時計算避免除法)*/
static inline uint32_t get_usec_per_tick(void) {static const uint32_t usec_per_tick = USECS_PER_SEC / configTICK_RATE_HZ;return usec_per_tick;
}/*** @brief 獲取高精度時間戳(微秒)* @return 從系統啟動開始的微秒數*/
uint64_t get_highres_time_us(void) {static uint32_t last_cnt = 0;static uint32_t overflow_count = 0;uint32_t current_cnt = TIM17->CNT;// 檢測計數器溢出if (current_cnt < last_cnt) {overflow_count++;}last_cnt = current_cnt;// 計算總微秒數return (uint64_t)overflow_count * 65536 + current_cnt;
}/*** @brief 高精度微秒級延時(基于 TIM17)* @param us 延時的微秒數*/
void udelay(uint32_t us) {if (us == 0) return;// 獲取 TIM17 當前計數值uint32_t start = TIM17->CNT;// 計算目標計數值(處理計數器溢出)uint32_t target = start + us;// 處理 16 位計數器溢出if (target > 0xFFFF) {// 等待計數器溢出while (TIM17->CNT >= start) {__NOP();}// 重新計算目標值target = us - (0xFFFF - start + 1);start = 0;}// 等待計數器達到目標值while (TIM17->CNT < target) {__NOP();}
}/** 獲取當前時間(高精度實現)*/
int gettimeofday(struct timeval *tp, void *tzp) {if (!tp) return -1;const TickType_t total_ticks = xTaskGetTickCount();const uint32_t ticks_per_sec = configTICK_RATE_HZ;tp->tv_sec = total_ticks / ticks_per_sec;tp->tv_usec = (uint32_t)((total_ticks % ticks_per_sec) * (uint64_t)USECS_PER_SEC / ticks_per_sec);(void)tzp;return 0;
}/** 微秒級睡眠*/
int osal_usleep(uint32_t usec) {udelay(usec);return 0;
}/** 獲取當前時間(標準函數封裝)*/
int osal_gettimeofday(struct timeval *tv, struct timezone *tz) {(void)tz; // 時區通常不使用return gettimeofday(tv, NULL);
}/** 獲取當前時間(ECAT格式)*/
ec_timet osal_current_time(void) {struct timeval tv;gettimeofday(&tv, NULL);return (ec_timet){tv.tv_sec, tv.tv_usec};
}/** 啟動定時器*/
void osal_timer_start(osal_timert *self, uint32_t timeout_us) {if (!self) return;self->start_tick = xTaskGetTickCount();const uint32_t usec_per_tick = get_usec_per_tick();// 四舍五入計算tick數self->timeout_ticks = (timeout_us + usec_per_tick / 2) / usec_per_tick;// 確保至少1個tick的延時if (self->timeout_ticks == 0 && timeout_us > 0) {self->timeout_ticks = 1;}
}/** 檢查定時器是否過期*/
bool osal_timer_is_expired(const osal_timert *self) {if (!self) return true;const TickType_t current = xTaskGetTickCount();const TickType_t elapsed = current - self->start_tick;// 處理tick計數器溢出情況if (elapsed > current) {// 發生溢出時,elapsed值將大于當前值return true;}return (elapsed >= self->timeout_ticks);
}

2、oshw.c 增加大小端轉換宏定義

#ifndef htons
#define htons(x) ((((x)&0xff)<<8)|(((x)&0xff00)>>8))
#endif#ifndef ntohs
#define ntohs(x) htons(x)
#endif#ifndef htonl
#define htonl(x) ((((x)&0xff)<<24)| \(((x)&0xff00)<<8) | \(((x)&0xff0000)>>8) | \(((x)&0xff000000)>>24))
#endif#ifndef ntohl
#define ntohl(x) htonl(x)
#endif

3、nicdrv.c

在nicdrv.h增加FreeRTOS的互斥量操作宏定義

#include "FreeRTOS.h"
#include "semphr.h"// 定義信號量類型
typedef SemaphoreHandle_t mtx_t;// 創建互斥鎖
#define mtx_create() xSemaphoreCreateMutex()// 鎖定互斥鎖(帶超時)
#define mtx_lock(mutex) (xSemaphoreTake((mutex), portMAX_DELAY) == pdPASS)// 解鎖互斥鎖
#define mtx_unlock(mutex) (xSemaphoreGive(mutex) == pdPASS)// 銷毀互斥鎖
#define mtx_destroy(mutex) vSemaphoreDelete(mutex)
/** Simple Open EtherCAT Master Library ** File    : nicdrv.c* Version : 1.3.0* Date    : 24-02-2013* Copyright (C) 2005-2013 Speciaal Machinefabriek Ketels v.o.f.* Copyright (C) 2005-2013 Arthur Ketels* Copyright (C) 2008-2009 TU/e Technische Universiteit Eindhoven ** SOEM is free software; you can redistribute it and/or modify it under* the terms of the GNU General Public License version 2 as published by the Free* Software Foundation.** SOEM is distributed in the hope that it will be useful, but WITHOUT ANY* WARRANTY; without even the implied warranty of MERCHANTABILITY or* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License* for more details.** As a special exception, if other files instantiate templates or use macros* or inline functions from this file, or you compile this file and link it* with other works to produce a work based on this file, this file does not* by itself cause the resulting work to be covered by the GNU General Public* License. However the source code for this file must still be made available* in accordance with section (3) of the GNU General Public License.** This exception does not invalidate any other reasons why a work based on* this file might be covered by the GNU General Public License.** The EtherCAT Technology, the trade name and logo "EtherCAT" are the intellectual* property of, and protected by Beckhoff Automation GmbH. You can use SOEM for* the sole purpose of creating, using and/or selling or otherwise distributing* an EtherCAT network master provided that an EtherCAT Master License is obtained* from Beckhoff Automation GmbH.** In case you did not receive a copy of the EtherCAT Master License along with* SOEM write to Beckhoff Automation GmbH, Eiserstra?e 5, D-33415 Verl, Germany* (www.beckhoff.com).*//** \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 allways 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 recieved.* 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 resend through interface B.* This layer is fully transparent for the higher layers.*/
//主要是網絡數據收發
#include <stdio.h>
#include <string.h>
#include "osal.h"
#include "oshw.h"
#include "soem_hook.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 connecetion */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]/** 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 stuct 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);}else{/* fail */return 0;}}else{port->getindex_mutex = mtx_create();port->tx_mutex = mtx_create();port->rx_mutex = mtx_create();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);psock = &(port->sockhandle);}  if(install_hook(port, ifname)==0){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) 
{mtx_destroy(port->getindex_mutex);mtx_destroy(port->tx_mutex);mtx_destroy(port->rx_mutex);uninstall_hook(port);return 0;
}/** Fill buffer with ethernet header structure.* Destination MAC is allways broadcast.* Ethertype is allways 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.*/
int ecx_getindex(ecx_portt *port)
{int idx;int cnt;mtx_lock (port->getindex_mutex);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);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, int 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, int idx, int stacknumber)
{int lp, rval;ec_stackT *stack;if (!stacknumber){stack = &(port->stack);}else{stack = &(port->redport->stack);}lp = (*stack->txbuflength)[idx];rval = net_send((*stack->txbuf)[idx], lp);(*stack->rxbufstat)[idx] = EC_BUF_TX;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, int 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);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
//      ASSERT (0);net_send(port->txbuf2, port->txbuflength2);mtx_unlock (port->tx_mutex);port->redport->rxbufstat[idx] = EC_BUF_TX;}   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 retreives 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, int 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);/* 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 reqested 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? */if (idxf < EC_MAXBUF) {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 happend */}}}}mtx_unlock (port->rx_mutex);}/* WKC if mathing 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, int idx, const 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 reveived MAC source on primary socket */primrx = 0;if (wkc > EC_NOFRAME) {  primrx = port->rxsa[idx];}/* secrx if the reveived 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, int idx, int timeout)
{int wkc;osal_timert timer;osal_timer_start (&timer, timeout);wkc = ecx_waitinframe_red(port, idx, timer);/* if nothing received, clear buffer index status so it can be used again */if (wkc <= EC_NOFRAME) {ecx_setbufstat(port, idx, EC_BUF_EMPTY);}return wkc;
}/** Blocking send and recieve 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, int 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));/* if nothing received, clear buffer index status so it can be used again */if (wkc <= EC_NOFRAME) {ecx_setbufstat(port, idx, EC_BUF_EMPTY);}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);
}int ec_getindex(void)
{return ecx_getindex(&ecx_port);
}void ec_setbufstat(int idx, int bufstat)
{ecx_setbufstat(&ecx_port, idx, bufstat);
}int ec_outframe(int idx, int stacknumber)
{return ecx_outframe(&ecx_port, idx, stacknumber);
}int ec_outframe_red(int idx)
{return ecx_outframe_red(&ecx_port, idx);
}int ec_inframe(int idx, int stacknumber)
{return ecx_inframe(&ecx_port, idx, stacknumber);
}int ec_waitinframe(int idx, int timeout)
{return ecx_waitinframe(&ecx_port, idx, timeout);
}int ec_srconfirm(int idx, int timeout)
{return ecx_srconfirm(&ecx_port, idx, timeout);
}
#endif

4、創建一個hook源文件,使用LwIP協議棧底層hook函數來處理PHY芯片的收發。

#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"#include "osal.h"#define HOOK_RX_BUFSIZE 10static 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 bool netfrm_full = false;static struct netif *target_netif = NULL;
static netif_input_fn orig_input = NULL;
static netif_linkoutput_fn orig_linkoutput = NULL;  // 正確類型聲明
static SemaphoreHandle_t buf_mutex = NULL;/******************************************************************************
* Hook Functions
******************************************************************************/
// 修正為正確的參數和返回類型
static err_t _netif_linkoutput(struct netif *netif, struct pbuf *p) {return orig_linkoutput(netif, p);
}static err_t _netif_input(struct pbuf *p, struct netif *inp) {if (p->tot_len >= 14) {uint8_t *data = (uint8_t*)p->payload;if (data[12] == 0x88 && data[13] == 0xa4) { // EtherCAT framexSemaphoreTake(buf_mutex, portMAX_DELAY);if (!netfrm_full) {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;netfrm_full = (netfrm_tail == netfrm_head);}xSemaphoreGive(buf_mutex);}}return orig_input(p, inp);
}/******************************************************************************
* Hook Management
******************************************************************************/
int install_hook(const char *ifname) {if (buf_mutex == NULL) {buf_mutex = xSemaphoreCreateMutex();if (!buf_mutex) return 0;}target_netif = netif_find(ifname);if (!target_netif) {EC_PRINT("[HOOK] Netif %s not found\n", ifname);return 0;}taskENTER_CRITICAL();// 確保使用正確的類型匹配orig_linkoutput = target_netif->linkoutput;   // 2個參數的linkoutputorig_input = target_netif->input;target_netif->linkoutput = _netif_linkoutput;  // 2個參數target_netif->input = _netif_input;taskEXIT_CRITICAL();EC_PRINT("[HOOK] Installed on %s (0x%p)\n", ifname, (void*)target_netif);return 1;
}int uninstall_hook(void) {if (!target_netif) return 0;taskENTER_CRITICAL();target_netif->input = orig_input;target_netif->linkoutput = orig_linkoutput;target_netif = NULL;taskEXIT_CRITICAL();EC_PRINT("[HOOK] Uninstalled\n");return 1;
}/******************************************************************************
* Network Operations
******************************************************************************/
int net_send(uint8_t *data, int len) {if (len <= 0 || !target_netif) return -1;struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);if (!p) {EC_PRINT("[SEND] PBuf alloc failed\n");return -1;}pbuf_take(p, data, len);err_t err = _netif_linkoutput(target_netif, p);pbuf_free(p);return (err == ERR_OK) ? len : -1;
}int net_recv(uint8_t *data, int max_len) {if (!data || max_len <= 0) return -1;xSemaphoreTake(buf_mutex, portMAX_DELAY);if (netfrm_head == netfrm_tail && !netfrm_full) {xSemaphoreGive(buf_mutex);return 0;}int total = netfrmbuf_cnt[netfrm_head];if (total > max_len) total = max_len;memcpy(data, netfrmbuf[netfrm_head], total);netfrm_head = (netfrm_head + 1) % HOOK_RX_BUFSIZE;netfrm_full = false;xSemaphoreGive(buf_mutex);return total;
}

5 、hook測試函數

lwip 默認stm32的網卡名為“st”

void test_hook(void) {#define TEST_FRAME_LEN 64 const char *ifname = "st"; uint8_t send_buf[TEST_FRAME_LEN];uint8_t recv_buf[TEST_FRAME_LEN];int recv_len;// 初始化一個測試幀,設置協議類型為0x88a4(EtherCAT)memset(send_buf, 0, TEST_FRAME_LEN);send_buf[12] = 0x88;send_buf[13] = 0xa4;const char payload[8] = {0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc};memcpy(send_buf + 14, payload, sizeof(payload));// 安裝鉤子if (!install_hook(ifname)) {EC_PRINT("Hook installation failed.\n");return;}// 發送測試幀if (net_send(send_buf, TEST_FRAME_LEN) < 0) {EC_PRINT("Send test frame failed.\n");goto uninstall;}// 由于我們的鉤子函數在發送時也會被調用(實際上是通過linkoutput鉤子發送的,但注意我們的接收鉤子是在輸入路徑上)// 但我們發送的幀不會直接進入接收鉤子,除非我們將其注入到接收路徑(比如在回環接口上發送)。// 因此,這里需要說明:在真實硬件上,發送的幀不會被自己接收,除非網絡設備支持回環或者我們使用回環接口。// 所以我們需要調整測試方法:在同一個網絡接口上,我們不能直接捕獲自己發送的幀(除非是回環)。// 我們可能需要兩個設備或者使用一個支持回環的接口。// 由于測試環境限制,我們可能無法捕獲自己發送的幀。因此,這個測試用例可能需要修改:// 方案1:如果我們的網絡接口支持回環(例如通過配置),則可以通過回環接收到發送的幀。// 方案2:在模擬的TAP設備上,可以自己發送自己接收。// 方案3:修改鉤子函數,使其同時捕獲發送和接收的幀。但原設計是只捕獲接收路徑上的EtherCAT幀。// 重新考慮:我們設計的鉤子函數是掛接在netif的input函數上,即當有數據包從網絡設備接收時會被調用。發送的數據包只會經過linkoutput。// 因此,我們之前的接收鉤子不會捕獲發送的包。// 所以我們需要改變測試思路:我們測試接收鉤子的正確性需要另一個設備發送EtherCAT幀,或者我們通過其他方式注入一個接收幀(比如直接調用netif->input函數)。// 對于單元測試,我們可以模擬一個接收幀直接調用netif->input函數,但這樣就不經過硬件,而我們的鉤子函數就是掛在這個input上的。// 但是我們安裝鉤子后,原來的input函數已經被替換,所以我們可以直接調用鉤子函數來模擬接收。// 由于測試的復雜性,這里我們改變策略,分為兩個測試:// 測試1:測試發送功能。直接調用net_send,然后在外部驗證(如使用抓包工具)是否發送成功。// 測試2:測試接收功能。我們可以模擬一個接收事件:通過直接調用安裝鉤子后的input函數(即_netif_input)來模擬接收。// 測試1:發送// 已經通過net_send發送了一個幀,在外部抓包驗證。EC_PRINT("Test frame sent. Please use external tool to verify.\n");// 測試2:模擬接收// 構建一個模擬接收的幀uint8_t fake_recv_frame[TEST_FRAME_LEN];memset(fake_recv_frame, 0, TEST_FRAME_LEN);fake_recv_frame[12] = 0x88;fake_recv_frame[13] = 0xa4;const char *fake_payload = "Fake received EtherCAT frame";memcpy(fake_recv_frame+14, fake_payload, strlen(fake_payload)+1);// 構建一個pbuf來模擬接收struct pbuf *p = pbuf_alloc(PBUF_RAW, TEST_FRAME_LEN, PBUF_POOL);if (p == NULL) {EC_PRINT("pbuf_alloc failed for simulated receive.\n");goto uninstall;}pbuf_take(p, fake_recv_frame, TEST_FRAME_LEN);// 調用鉤子函數(即我們掛接的_netif_input)來處理這個模擬的接收幀_netif_input(p, NULL);   // 第二個參數是netif,可以傳NULL因為我們內部不用(注意:內部函數使用了target_netif?實際我們函數里沒用到inp參數)pbuf_free(p);// 現在我們應該可以通過net_recv接收到這個幀recv_len = net_recv(recv_buf, TEST_FRAME_LEN);if (recv_len <= 0) {EC_PRINT("Didn't receive the simulated frame.\n");} else {EC_PRINT("Received frame, len=%d\n", recv_len);// 打印接收到的內容,對比if (memcmp(fake_recv_frame, recv_buf, recv_len) == 0) {EC_PRINT("Simulated receive frame matches the sent one.\n");} else {EC_PRINT("Simulated receive frame does NOT match.\n");}}uninstall:// 卸載鉤子uninstall_hook();EC_PRINT("Test finished.\n");
}

6、測試實例

在任務中循環調用test_hook函數

在這里插入圖片描述

使用wireshark抓包觀察

在這里插入圖片描述,數據收發ok,可以識別ethercat frame
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917860.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917860.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917860.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VijosOJ:中文信息學競賽的二十年開源之路

VijosOJ&#xff1a;中文信息學競賽領域的老牌開源在線判題系統 在中文編程教育與信息學競賽的發展歷程中&#xff0c;在線判題系統&#xff08;OJ&#xff09;扮演了至關重要的角色。它們不僅是選手訓練的 “戰場”&#xff0c;更是知識傳遞與社區交流的樞紐。VijosOJ&#x…

QPainter::CompositionMode解析

基本概念目標(Destination)&#xff1a;已經存在的像素。源(Source)&#xff1a;要繪制的新像素。組合模式&#xff1a;決定源和目標如何混合。總結SourceOver&#xff1a;源繪制在目標之上。DestinationOver&#xff1a;目標繪制在源之上。Clear&#xff1a;二者重疊區域被清空…

對接釘釘審批過程記錄(C#版本)

釘釘開放平臺&#xff1a;API總覽 - 釘釘開放平臺 按照開放平臺操作指引&#xff0c;進入到釘釘開發者后臺&#xff1a;開發者后臺統一登錄 - 釘釘統一身份認證&#xff0c;進行應用創建。 按照開放平臺指引下載釘釘SDK&#xff08;新版&#xff09;。 在vs引入釘釘dll文件。 獲…

AFSIM入門教程03.03:更新所有依賴庫版本

系列索引&#xff1a;AFSIM入門教程索引 上一篇中更新了tiff庫版本&#xff0c;本文將更新所有使用到的依賴庫版本。 失敗了 依賴庫 首先獲取哪些庫被使用了。打開源碼目錄&#xff0c;搜索# Configure the 3rd_party&#xff0c;可以看到調用第三方庫的代碼。 官方提供的…

完美解決hive external表中csv字段內容含“,“逗號的問題

為解決hive表中csv字段內容含","逗號的問題&#xff0c;網上幾乎都是說要用org.apache.hadoop.hive.serde2.OpenCSVSerde。 使用方法為&#xff1a; 1、mysql導出時&#xff0c;加一個ENCLOSED BY ‘"’&#xff0c; 示例&#xff1a; mysql -h 10.16.0.10 -P …

【Git】修改本地和遠程的分支名稱

其原理是&#xff1a; 對于本地&#xff1a;可直接修改分支名稱&#xff1b;對于遠程&#xff1a;不可直接重命名分支&#xff0c;所以應該將修改好名稱的分支以新分支的形式推送上遠程倉庫&#xff0c;之后將新分支與遠程新分支關聯&#xff0c;之后可選擇刪除舊分支# 例子&am…

ubuntu24.04安裝selenium、chrome、chromedriver

實驗環境&#xff1a;kaggle notebook、colab notebook1、安裝chrome!wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb!sudo dpkg -i google-chrome-stable_current_amd64.deb!sudo apt-get install -f!export QT_QPA_PLATFORMoffscreen!sudo…

西門子PLC基礎指令6:讀取時鐘指令、設置時鐘指令、使能含義與注意

讀/寫指令 1. 讀取時鐘 指令 READ_RTCREAD_RTC &#xff08;RTC 全稱是 Real - Time Clock&#xff0c;即實時時鐘 &#xff09;指令的主要作用是將 CPU 內部實時時鐘&#xff08;RTC&#xff09;的當前日期和時間信息讀取出來&#xff0c;并存儲到以指定字節地址&#xff08;圖…

GeoTools 結合 OpenLayers 實現緩沖區分析

前言? 緩沖區分析是地理信息系統&#xff08;GIS&#xff09;空間分析的核心功能之一。它通過圍繞點、線或面等地理實體&#xff0c;自動生成指定距離&#xff08;或寬度&#xff09;的等距區域&#xff08;緩沖區&#xff09;。該功能為量化空間鄰近度、評估影響范圍、識別潛…

SpringBoot 接入SSE實現消息實時推送的優點,原理以及實現

SpringBoot 接入SSE實現消息實時推送的優點,原理以及實現 前言 上一篇文章 我寫的關于SpringBoot整合t-io是websocket實時通信的文章中我們可以了解到 websocket是雙向通信的,而且需要TCP連接的支持,今天在這里我要說的SSE(Server-Sent Events) 是一個單項通信的消息實時推…

創建型設計模式:對象誕生的藝術與智慧

&#x1f3ad; 創建型設計模式&#xff1a;對象誕生的藝術與智慧 &#x1f4a1; 溫馨提示&#xff1a;本文將以輕松有趣的方式帶你探索設計模式的世界&#xff0c;就像在聽一個關于"如何優雅地生孩子"的故事一樣&#xff01; &#x1f6aa; 傳送門&#xff1a;在開始…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘gensim’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘gensim’問題 摘要 在使用 PyCharm 2025 進行 Python 開發時&#xff0c;常常需要通過 pip install 安裝第三方包以豐富項目功能。但在安裝 gensim 等包時&…

【嵌入式電機控制#26】BLDC:三相模擬采集

之前有些網友試著用有刷的平均電流法采集三相&#xff0c;還搞了個閉環控制。求&#xff01;結果直接把驅動板給干沒了......做過仿真的朋友們都知道&#xff0c;無刷電機的相電流波形是介于方波和正弦波的。如果拿平均電流去測量&#xff0c;很不靠譜。這節內容為大家分享采集…

ref存儲對象和reactive深度響應式遞歸地對對象的嵌套屬性進行響應式處理

ref 不會遞歸地對 對象 或 數組 中的每個屬性或元素進行深度響應式處理。如果你需要遞歸處理嵌套屬性&#xff0c;reactive 是更適合的選擇。讓我通過具體的例子來展示這一點。 例子&#xff1a;ref 存儲對象和嵌套對象 1. 使用 ref 存儲嵌套對象&#xff1a; import { createA…

小鵬汽車前端面經

前端基礎與瀏覽器機制 (Front-End Fundamentals & Browser Mechanics) 這些問題涵蓋了Web工作的基本原理&#xff0c;包括網絡、渲染和瀏覽器特性。 1. 瀏覽器渲染與性能 (Browser Rendering & Performance) URL輸入發生什么&#xff1f;(What happens when you type a…

利用DeepSeek編寫go語言按行排序程序

聽說google出品的go語言也是系統開發中常用的&#xff0c;性能到底怎么樣&#xff0c;還是老辦法&#xff0c;讓DeepSeek寫個程序跑一下&#xff0c;基于以往的經驗&#xff0c;直接讓它同時編寫有緩沖和無緩沖版本。 package mainimport ("bufio""fmt"&qu…

《解構Angular組件變化檢測:從自動到手 動的效能突破》

為何有時數據更新后視圖卻無動于衷?為何看似簡單的操作會引發連鎖式的性能損耗?要解開這些疑問,需要穿透表層的API調用,深入到框架設計的底層邏輯中去。變化檢測的核心使命,是確保視圖層能夠準確反映數據層的當前狀態。這種"數據-視圖"的同步關系,是所有前端框…

書單 | AI編程+Python+Go三大核心領域書單

這份書單聚焦AI編程、Python開發、Go語言三大核心領域&#xff0c;精選6本本月 最具前瞻性的技術圖書&#xff0c;為你構建"工具鏈業務層系統層"的全棧能力。 1、人人皆可Vibe編程&#xff1a;玩轉氛圍編程 作者&#xff1a;池志煒,薛志榮 本書圍繞Vibe編程這一AI驅…

Kali Linux 2025.2基于MITRE ATTCK框架

從徹底革新的菜單結構到新增的13款尖端工具&#xff0c;再到首次支持智能手表Wi-Fi注入&#xff0c;Kali Linux 2025.2為紅隊、藍隊以及安全研究人員提供了更高效、更直觀的工具生態。菜單結構大變革&#xff1a;基于MITRE ATT&CK框架Kali Linux 2025.2最引人注目的變化之一…

javacc實現簡單SQL解析器

文章目錄前言本章節源碼需求1&#xff1a;實現一個最簡單的select sql要求實現jj文件編譯測試需求2&#xff1a;理解Token及其他屬性說明javajj文件需求3&#xff1a;實現解析得到SQL語法樹 & 精確點位資料獲取前言 博主介紹&#xff1a;?目前全網粉絲4W&#xff0c;csdn…