LWIP个人移植心得,平台STM32F4,HAL库,CMSIS_OS系统封装

本文分享了在STM32F4平台上移植LWIP的详细步骤,包括源码下载、文件配置、系统适配、网卡驱动编写等关键环节,为嵌入式开发人员提供实用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LWIP个人移植心得,平台STM32F4,HAL库,CMSIS_OS系统封装

1、先在官网下载LWIP源码及例程文件,以1.4.1版本为例分别是lwip-1.4.1和contrib-1.4.1,将lwip-1.4.1的src文件夹全都拷贝到自己工程中,同时先在这个src下新建一个空文件夹命名arch。
2、然后将contrib-1.4.1\ports\win32\include\arch下的bpstruct.h、cc.h、epstruct.h、perf.h拷贝到上面自己新建的arch目录。
3、在自建的arch目录下新建lwipopts.h、sys_arch.c、sys_arch.h文件,src\include\lwip下的sys.h中说明了所有要在sys_arch.c中实现的函数,同时该目录下的opt.h说明了所有对LIWP进行裁剪和盘配置的参数,不要在opt.h中直接修改,因为opt是条件编译的,万一有在别的地方定义过,则你在opt.h中的配置就无效了,可以根据需要另外重新在lwipopts.h进行定义,sys_arch.h主要定义三个操作系统数据类型:
信号量sys_sem_t、消息队列sys_mbox_t、任务sys_thread_t,如果没有定义LWIP_COMPAT_MUTEX为1(LWIP_COMPAT_MUTEX表示是否用信号量来
替代互斥信号量,它被用在sys.h作互斥量的编译条件),则还需要定义互斥量类型sys_mutex_t。

以下是我修改编写的这三个文件

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

#define LWIP_COMPAT_MUTEX 1    //use sem in place of mutex

#define NO_SYS                     0
#define LWIP_SOCKET               (NO_SYS==0)
#define LWIP_NETCONN              (NO_SYS==0)

#define LWIP_IGMP                  1
#define LWIP_ICMP                  1
#define LWIP_SNMP                  0

#define LWIP_DNS                   1

#define LWIP_HAVE_LOOPIF           1
#define LWIP_NETIF_LOOPBACK        1
#define LWIP_LOOPBACK_MAX_PBUFS    10

#define TCP_LISTEN_BACKLOG         0

#define LWIP_COMPAT_SOCKETS        1
#define LWIP_SO_RCVTIMEO           1
#define LWIP_SO_RCVBUF             1

#define LWIP_TCPIP_CORE_LOCKING    0

#define LWIP_NETIF_LINK_CALLBACK   1
#define LWIP_NETIF_STATUS_CALLBACK 1

#ifdef LWIP_DEBUG

#define LWIP_DBG_MIN_LEVEL         0
#define PPP_DEBUG                  LWIP_DBG_OFF
#define MEM_DEBUG                  LWIP_DBG_OFF
#define MEMP_DEBUG                 LWIP_DBG_OFF
#define PBUF_DEBUG                 LWIP_DBG_OFF
#define API_LIB_DEBUG              LWIP_DBG_OFF
#define API_MSG_DEBUG              LWIP_DBG_OFF
#define TCPIP_DEBUG                LWIP_DBG_OFF
#define NETIF_DEBUG                LWIP_DBG_OFF
#define SOCKETS_DEBUG              LWIP_DBG_OFF
#define DNS_DEBUG                  LWIP_DBG_OFF
#define AUTOIP_DEBUG               LWIP_DBG_OFF
#define DHCP_DEBUG                 LWIP_DBG_OFF
#define IP_DEBUG                   LWIP_DBG_OFF
#define IP_REASS_DEBUG             LWIP_DBG_OFF
#define ICMP_DEBUG                 LWIP_DBG_OFF
#define IGMP_DEBUG                 LWIP_DBG_OFF
#define UDP_DEBUG                  LWIP_DBG_OFF
#define TCP_DEBUG                  LWIP_DBG_OFF
#define TCP_INPUT_DEBUG            LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG           LWIP_DBG_OFF
#define TCP_RTO_DEBUG              LWIP_DBG_OFF
#define TCP_CWND_DEBUG             LWIP_DBG_OFF
#define TCP_WND_DEBUG              LWIP_DBG_OFF
#define TCP_FR_DEBUG               LWIP_DBG_OFF
#define TCP_QLEN_DEBUG             LWIP_DBG_OFF
#define TCP_RST_DEBUG              LWIP_DBG_OFF
#endif

#define LWIP_DBG_TYPES_ON         (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT)


/* ---------- Memory options ---------- */
/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which
   lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
   byte alignment -> define MEM_ALIGNMENT to 2. */
/* MSVC port: intel processors don't need 4-byte alignment,
   but are faster that way! */
#define MEM_ALIGNMENT           4

/* MEM_SIZE: the size of the heap memory. If the application will send
a lot of data that needs to be copied, this should be set high. */
#define MEM_SIZE               10240

/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
   sends a lot of data out of ROM (or other static memory), this
   should be set high. */
#define MEMP_NUM_PBUF           16
/* MEMP_NUM_RAW_PCB: the number of UDP protocol control blocks. One
   per active RAW "connection". */
#define MEMP_NUM_RAW_PCB        3
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
   per active UDP "connection". */
#define MEMP_NUM_UDP_PCB        4
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
   connections. */
#define MEMP_NUM_TCP_PCB        5
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
   connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 8
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
   segments. */
#define MEMP_NUM_TCP_SEG        16
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
   timeouts. */
#define MEMP_NUM_SYS_TIMEOUT    15

/* The following four are used only with the sequential API and can be
   set to 0 if the application only will use the raw API. */
/* MEMP_NUM_NETBUF: the number of struct netbufs. */
#define MEMP_NUM_NETBUF         2
/* MEMP_NUM_NETCONN: the number of struct netconns. */
#define MEMP_NUM_NETCONN        10
/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used
   for sequential API communication and incoming packets. Used in
   src/api/tcpip.c. */
#define MEMP_NUM_TCPIP_MSG_API   16
#define MEMP_NUM_TCPIP_MSG_INPKT 16


/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */
#define PBUF_POOL_SIZE          120

/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */
#define PBUF_POOL_BUFSIZE       128

/* PBUF_LINK_HLEN: the number of bytes that should be allocated for a
   link level header. */
#define PBUF_LINK_HLEN          16

/** SYS_LIGHTWEIGHT_PROT
 * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
 * for certain critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT    (NO_SYS==0)


/* ---------- TCP options ---------- */
#define LWIP_TCP                1
#define TCP_TTL                 255

/* Controls if TCP should queue segments that arrive out of
   order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ         1

/* TCP Maximum segment size. */
#define TCP_MSS                 1024

/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF             2048

/* TCP sender buffer space (pbufs). This must be at least = 2 *
   TCP_SND_BUF/TCP_MSS for things to work. */
#define TCP_SND_QUEUELEN       (4 * TCP_SND_BUF/TCP_MSS)

/* TCP writable space (bytes). This must be less than or equal
   to TCP_SND_BUF. It is the amount of space which must be
   available in the tcp snd_buf for select to return writable */
#define TCP_SNDLOWAT           (TCP_SND_BUF/2)

/* TCP receive window. */
#define TCP_WND                 8096

/* Maximum number of retransmissions of data segments. */
#define TCP_MAXRTX              8

/* Maximum number of retransmissions of SYN segments. */
#define TCP_SYNMAXRTX           4


/* ---------- ARP options ---------- */
#define LWIP_ARP                1
#define ARP_TABLE_SIZE          10
#define ARP_QUEUEING            1


/* ---------- IP options ---------- */
/* Define IP_FORWARD to 1 if you wish to have the ability to forward
   IP packets across network interfaces. If you are going to run lwIP
   on a device with only one network interface, define this to 0. */
#define IP_FORWARD              1

/* IP reassembly and segmentation.These are orthogonal even
 * if they both deal with IP fragments */
#define IP_REASSEMBLY           1
#define IP_REASS_MAX_PBUFS      10
#define MEMP_NUM_REASSDATA      10
#define IP_FRAG                 1


/* ---------- ICMP options ---------- */
#define ICMP_TTL                255


/* ---------- DHCP options ---------- */
/* Define LWIP_DHCP to 1 if you want DHCP configuration of
   interfaces. */
#define LWIP_DHCP               1

/* 1 if you want to do an ARP check on the offered address
   (recommended). */
#define DHCP_DOES_ARP_CHECK    (LWIP_DHCP)


/* ---------- AUTOIP options ------- */
#define LWIP_AUTOIP             0
#define LWIP_DHCP_AUTOIP_COOP  (LWIP_DHCP && LWIP_AUTOIP)


/* ---------- UDP options ---------- */
#define LWIP_UDP                1
#define LWIP_UDPLITE            1
#define UDP_TTL                 255


/* ---------- Statistics options ---------- */

#define LWIP_STATS              1
#define LWIP_STATS_DISPLAY      1

#if LWIP_STATS
#define LINK_STATS              1
#define IP_STATS                1
#define ICMP_STATS              1
#define IGMP_STATS              1
#define IPFRAG_STATS            1
#define UDP_STATS               1
#define TCP_STATS               1
#define MEM_STATS               1
#define MEMP_STATS              1
#define PBUF_STATS              1
#define SYS_STATS               1
#endif /* LWIP_STATS */


/* ---------- PPP options ---------- */

#define PPP_SUPPORT             0      /* Set > 0 for PPP */

#if PPP_SUPPORT

#define NUM_PPP                 1      /* Max PPP sessions. */


/* Select modules to enable.  Ideally these would be set in the makefile but
 * we're limited by the command line length so you need to modify the settings
 * in this file.
 */
#define PPPOE_SUPPORT           1
#define PPPOS_SUPPORT           1

#define PAP_SUPPORT             1      /* Set > 0 for PAP. */
#define CHAP_SUPPORT            1      /* Set > 0 for CHAP. */
#define MSCHAP_SUPPORT          0      /* Set > 0 for MSCHAP (NOT FUNCTIONAL!) */
#define CBCP_SUPPORT            0      /* Set > 0 for CBCP (NOT FUNCTIONAL!) */
#define CCP_SUPPORT             0      /* Set > 0 for CCP (NOT FUNCTIONAL!) */
#define VJ_SUPPORT              1      /* Set > 0 for VJ header compression. */
#define MD5_SUPPORT             1      /* Set > 0 for MD5 (see also CHAP) */

#endif /* PPP_SUPPORT */

#endif /* __LWIPOPTS_H__ */
/*
*********************************************************************************************************
*                                              lwIP TCP/IP Stack
*                                    	 port for CMSIS_OS RTOS Interface
*
* File : sys_arch.c
* By   : XiaoMing
*********************************************************************************************************
*/
#ifndef __SYS_ARCH_H__
#define __SYS_ARCH_H__
#include "cmsis_os.h"
#include "arch/cc.h"

#define SYS_MBOX_NULL   (void*)0
#define SYS_SEM_NULL    (void*)0

#define MAX_QUEUES        20
#define MAX_QUEUE_ENTRIES 20
 
typedef osSemaphoreId sys_sem_t;
typedef osMessageQId  sys_mbox_t;//the structure defined above
typedef osThreadId  sys_thread_t;

#endif

/*
*********************************************************************************************************
*                                              lwIP TCP/IP Stack
*                                    	 port for CMSIS_OS RTOS Interface
*
* File : sys_arch.c
* By   : XiaoMing
*********************************************************************************************************
*/


#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"

#include "arch/sys_arch.h" 

const void * const pvNullPointer;

static char pcQueueMemoryPool[MAX_QUEUES * 4 ];
osPoolDef_t osPoolDef_t0={MAX_QUEUES,4,pcQueueMemoryPool};


/*-----------------------------------------------------------------------------------*/
/* This func should be called first in lwip task!
 * -------------------------------------------------		*/
sys_mutex_t lwip_prot_mutex;
// Initialize sys arch
void sys_init(void)
{
  if(sys_mutex_new(&lwip_prot_mutex)!=ERR_OK){
		printf("error at %s line %d",__FILE__,__LINE__);
		while(1){}
	}
}


/*-----------------------------------------------------------------------------------*/
err_t sys_sem_new(sys_sem_t *sem, u8_t count){
    osSemaphoreDef(pSem);
		*sem=osSemaphoreCreate (osSemaphore(pSem),count);
		return *sem!=NULL?ERR_OK:ERR_MEM;
}
/*-----------------------------------------------------------------------------------*/
void sys_sem_free(sys_sem_t *sem)
{
	osSemaphoreDelete(*sem);
}

/*-----------------------------------------------------------------------------------*/

void sys_sem_signal(sys_sem_t*sem)
{
	osSemaphoreRelease(*sem);
}
/*-----------------------------------------------------------------------------------*/

u32_t sys_arch_sem_wait(sys_sem_t*sem, u32_t timeout)
{
	u32_t t=osKernelSysTick();
	osSemaphoreWait(*sem,timeout);
	t=osKernelSysTick()-t;
	if(t>timeout)
		t=SYS_ARCH_TIMEOUT;
	return t;
}


/*-----------------------------------------------------------------------------------*/
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
	osMessageQDef(Q,size,void*);
	*mbox=osMessageCreate (osMessageQ(Q),NULL);
	return *mbox!=NULL?ERR_OK:ERR_MEM;
}
/*-----------------------------------------------------------------------------------*/
void sys_mbox_free(sys_mbox_t *mbox){
	osMessageDelete(*mbox);
}

/*-----------------------------------------------------------------------------------*/
void sys_mbox_post(sys_mbox_t *mbox, void *msg){
	osMessagePut (*mbox,(uint32_t)msg,osWaitForever);
}
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg){
	return osMessagePut (*mbox,(uint32_t)msg,0);
}
/*-----------------------------------------------------------------------------------*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout){
	u32_t t=osKernelSysTick();
	osEvent e= osMessageGet(*mbox,timeout);
	t=osKernelSysTick()-t;
	if(e.status==osEventMessage){
		*msg=e.value.p;
		return t;
	}
	return SYS_ARCH_TIMEOUT;
}

u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg){
	osEvent e= osMessageGet(*mbox,0);
	if(e.status==osEventMessage){
		*msg=e.value.p;
		return 0;
	}
	else return SYS_MBOX_EMPTY;
}
/*----------------------------------------------------------------------*/


/*------------------------------------------------------------------------*/
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio){
	const osThreadDef_t os_thread_def = 
	{  (char*)name,(void(*)(const void*arg))(thread), (osPriority)(prio), (0), (stacksize)};
	return osThreadCreate (&os_thread_def,arg);
}
int sys_mbox_valid(sys_mbox_t *mbox){
  if (*mbox == SYS_MBOX_NULL) 
    return 0;
  else
    return 1;
}

void sys_mbox_set_invalid(sys_mbox_t *mbox){
	*mbox = SYS_MBOX_NULL;   
}
int sys_sem_valid(sys_sem_t *sem){
	
  if (*sem == SYS_SEM_NULL)
    return 0;
  else
    return 1;  
}
void sys_sem_set_invalid(sys_sem_t *sem){
	*sem = SYS_SEM_NULL; 
}

sys_prot_t sys_arch_protect(void)
{
  sys_mutex_lock(lwip_prot_mutex);
  return (sys_prot_t)1;
}

void sys_arch_unprotect(sys_prot_t pval)
{
  ( void ) pval;
  sys_mutex_unlock(lwip_prot_mutex);
}



4、给自己工程添加LIWP源码,src\api下所有c代码,src\arch下的sys_arch.c,src\core下所有c代码及src\core\ipv4下所有c代码,src\netif下
的etharp.c和ethernetif.c。其他文件一般都用不到。
5、给自己工程添加头文件目录:…\src\include;…\src\include\ipv4;…\src\include\netif;…\src\include\lwip;…\src;…\src\arch。
到这工程已经可以编译通过了,但还不能正常使用,因为还没实现网卡的驱动。
6、根据自己的硬件编写ethernetif.c中的网卡驱动(最容易出问题的部分),需要修改的函数有

low_level_init(struct netif *netif);
static err_t low_level_output(struct netif *netif, struct pbuf *p);
static void  ethernetif_input(struct netif *netif);

low_level_init用于完成完成底层硬件的初始化,一般包括数据链路层和物理层的初始化,
ethernetif_input负责调用底层实际输入接收函数,
low_level_output负责调用底层实际输出发送函数。

以下是我针对STM32F4的板子修改的网卡驱动

/*
 * This file is a skeleton for developing Ethernet network interface
 * drivers for lwIP. Add code to the low_level functions and do a
 * search-and-replace for the word "ethernetif" to replace it with
 * something that better describes your network interface.
 */

#include "lwip/opt.h"

#if 1 /* don't build, this is only a skeleton, see previous comment */

#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include <lwip/stats.h>
#include <lwip/snmp.h>
#include "netif/etharp.h"
#include "netif/ppp_oe.h"
#include "eth.h"
#include "sys.h"
#include "string.h"

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

/**
 * Helper struct to hold private data used to operate your ethernet interface.
 * Keeping the ethernet address of the MAC in this struct is not necessary
 * as it is already kept in the struct netif.
 * But this is only an example, anyway...
 */
struct ethernetif {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

/* Forward declarations. */
static void  ethernetif_input(struct netif *netif);

sys_sem_t sem;
void eth_input_task(void*argument){
	while(1){
		sys_arch_sem_wait(&sem,osWaitForever);
		ethernetif_input((struct netif *)argument);
	}
}
/**
 * In this function, the hardware should be initialized.
 * Called from ethernetif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
__ALIGN_BEGIN ETH_DMADescTypeDef  DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;
__ALIGN_BEGIN ETH_DMADescTypeDef  DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;
__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END;
__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END;

static void
low_level_init(struct netif *netif)
{
  struct ethernetif *ethernetif = netif->state;
  
  /* set MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
  netif->hwaddr[0] = 0x00;
  netif->hwaddr[1] = 0x80;
  netif->hwaddr[2] = 0xE1;
  netif->hwaddr[3] = 0x00;
  netif->hwaddr[4] = 0x00;
  netif->hwaddr[5] = 0x00;

  /* maximum transfer unit */
  netif->mtu = 1500;
  
  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
 
  /* Do whatever else is needed to initialize interface. */ 
  /* Initialize Tx Descriptors list: Chain Mode */
  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
     
  /* Initialize Rx Descriptors list: Chain Mode  */
  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); 
	
	sys_sem_new(&sem,ETH_RXBUFNB);
	for(int i=0;i<ETH_RXBUFNB;i++)
		sys_arch_sem_wait(&sem,osWaitForever);
	sys_thread_new("eth_input_thread",eth_input_task,netif,512, 0);
	
	HAL_ETH_Start(&heth);
	
  uint32_t regvalue = 0;
  HAL_ETH_ReadPHYRegister(&heth, PHY_ISFR, &regvalue);
  regvalue |= (PHY_ISFR_INT4);

  /* Enable Interrupt on change of link status */ 
  HAL_ETH_WritePHYRegister(&heth, PHY_ISFR , regvalue );
  
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_ISFR , &regvalue);
}
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
  sys_sem_signal(&sem);
}
/**
 * This function should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
 * @return ERR_OK if the packet could be sent
 *         an err_t value if the packet couldn't be sent
 *
 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
 *       strange results. You might consider waiting for space in the DMA queue
 *       to become availale since the stack doesn't retry to send a packet
 *       dropped because of memory failure (except for the TCP timers).
 */

static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
  err_t errval;
  struct pbuf *q;
  uint8_t *buffer = (uint8_t *)(heth.TxDesc->Buffer1Addr);
  uint32_t bufferoffset = 0;
  bufferoffset = 0;
  
  /* copy frame from pbufs to driver buffers */
  for(q = p; q != NULL; q = q->next)
	{
		/* Copy the remaining bytes */
		memcpy( (uint8_t*)buffer + bufferoffset,(uint8_t*)q->payload, q->len );
		bufferoffset = bufferoffset + q->len;
	}
  
  /* Prepare transmit descriptors to give to DMA */ 
  HAL_ETH_TransmitFrame(&heth, bufferoffset);
  
  errval = ERR_OK;
 
  return errval;
}

/**
 * Should allocate a pbuf and transfer the bytes of the incoming
 * packet from the interface into the pbuf.
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return a pbuf filled with the received packet (including MAC header)
 *         NULL on memory error
 */
static struct pbuf *
low_level_input(struct netif *netif)
{
  struct pbuf *p = NULL;
  struct pbuf *q = NULL;
  uint16_t len = 0;
  uint8_t *buffer;
  __IO ETH_DMADescTypeDef *dmarxdesc;
  uint32_t bufferoffset = 0;
  
  /* get received frame */
  if (HAL_ETH_GetReceivedFrame_IT(&heth) != HAL_OK)
    return NULL;
  
  /* Obtain the size of the packet and put it into the "len" variable. */
  len = heth.RxFrameInfos.length;
  buffer = (uint8_t *)heth.RxFrameInfos.buffer;
  
  if (len > 0)
  {
    /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  }
  
  if (p != NULL)
  {
    bufferoffset = 0;
    for(q = p; q != NULL; q = q->next)
    {
      /* Copy remaining data in pbuf */
      memcpy( (uint8_t*)q->payload, (uint8_t*)buffer + bufferoffset, q->len);
      bufferoffset = bufferoffset + q->len;
    }
  }  
  
	/* Release descriptors to DMA */
	dmarxdesc = heth.RxFrameInfos.FSRxDesc;

	/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
	dmarxdesc->Status |= ETH_DMARXDESC_OWN;

	/* Clear Segment_Count */
	heth.RxFrameInfos.SegCount =0;  
  
  /* When Rx Buffer unavailable flag is set: clear it and resume reception */
  if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)  
  {
    /* Clear RBUS ETHERNET DMA flag */
    heth.Instance->DMASR = ETH_DMASR_RBUS;
    /* Resume DMA reception */
    heth.Instance->DMARPDR = 0;
  }
  return p;
}

/**
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface. Then the type of the received packet is determined and
 * the appropriate input function is called.
 *
 * @param netif the lwip network interface structure for this ethernetif
 */
static void
ethernetif_input(struct netif *netif)
{
  struct ethernetif *ethernetif;
  struct eth_hdr *ethhdr;
  struct pbuf *p;

  ethernetif = netif->state;

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* no packet could be read, silently ignore this */
  if (p == NULL) return;
  /* points to packet payload, which starts with an Ethernet header */
  ethhdr = p->payload;

  switch (htons(ethhdr->type)) {
  /* IP or ARP packet? */
  case ETHTYPE_IP:
  case ETHTYPE_ARP:
#if PPPOE_SUPPORT
  /* PPPoE packet? */
  case ETHTYPE_PPPOEDISC:
  case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
    /* full packet send to tcpip_thread to process */
    if (netif->input(p, netif)!=ERR_OK)
     { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
       pbuf_free(p);
       p = NULL;
     }
    break;

  default:
    pbuf_free(p);
    p = NULL;
    break;
  }
}

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 * This function should be passed as a parameter to netif_add().
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return ERR_OK if the loopif is initialized
 *         ERR_MEM if private data couldn't be allocated
 *         any other err_t on error
 */
err_t
ethernetif_init(struct netif *netif)
{
  struct ethernetif *ethernetif;

  LWIP_ASSERT("netif != NULL", (netif != NULL));
    
  ethernetif = mem_malloc(sizeof(struct ethernetif));
  if (ethernetif == NULL) {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
    return ERR_MEM;
  }

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);

  netif->state = ethernetif;
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
  netif->output = etharp_output;
  netif->linkoutput = low_level_output;
  
  ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
  
  /* initialize the hardware */
  low_level_init(netif);

  return ERR_OK;
}

#endif /* 0 */

7、总之、需要自己编写修改的文件只有四个lwipopts.h、sys_arch.c、sys_arch.h、ethernetif.c。
完整工程下载

<think>首先,用户的问题是关于STM32F4xx中使用LWIP协议栈时,如何处理HAL_ETH_RxLinkCallback函数中的原始数据。他们提到一个具体函数:HAL_ETH_RxLinkCallback,这是在HAL中用于以太网接收的回调函数。用户需要方法或代码示例来在这个回调函数中处理原始数据。回想用户提供的引用:-[^1]提到STM32硬件配置和CUBEMX设置。-[^2]是关于ETH接口和LWIP配置。-[^3]涉及LWIP集成和MQTT协议。系统指令要求:-使用$...$和$$...$$格式处理数学表达式(虽然在本问题中不明显)。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在引用的段落末尾添加引用标识,如[^1]。用户的问题焦点:HAL_ETH_RxLinkCallback的处理方法。这是在LWIP的上下文中。从STM32HAL文档中:HAL_ETH_RxLinkCallback()是当以太网接收缓冲链表状态变化时调用的回调函数。开发者可以重写这个函数来处理原始接收到的数据包。在LWIP协议栈集成中,通常使用这个回调来驱动LWIP的数据处理。标准做法是在回调中将接收到的数据包传递给LWIP的输入处理函数。可能的代码结构:-在HAL_ETH_RxLinkCallback中,使用ETH_HANDLE_TYPE定义的宏或函数。-需要调用LWIPAPI函数如low_level_input()(通常定义在底层驱动中),来将数据推送到LWIP。用户请求代码示例,所以需要提供一个简单的代码片段,展示如何处理原始数据。参考用户提供的引用:-[^2]在描述ETH配置时提到cubemx配置和硬件接口。-[^3]提到添加LWIP和集成驱动。因此,回答应结合LWIP集成。步骤构造回答:1.**解释函数作用**:简述HAL_ETH_RxLinkCallback的功能。2.**提供代码示例**:给出一个示例,如何使用这个回调处理数据。-在STM32CubeMX生成的代码中,通常会有一个stm32f4xx_hal_eth.c文件,其中定义了这个回调。-开发者需要重写这个函数。-示例代码:在这个函数中,检查接收描述符状态,提取数据缓冲区,调用LWIP输入函数。3.**关键点**:强调使用LWIP的函数如etharp_input()或tcpip_input()(需要包含lwip源文件)。4.**完整性和安全性**:确保代码正确设置描述符和缓冲。5.**生成相关问题**:按照指令,在结束时添加相关问题。相关问题列表(根据用户问题类型):1.如何配置LWIP接收处理?2.LWIPSTM32F4上优化数据接收的方法?3.如何处理以太网接收错误?用中文回答。结构化回答:-开头:直接回答查询。-主体:解释方法,提供代码示例。-结束:添加相关问题。代码示例基于标准HAL实现:假设在stm32f4xx_hal_eth.c中定义。标准回调可能为空,开发者重写它。示例代码:```cvoidHAL_ETH_RxLinkCallback(ETH_HandleTypeDef*heth){structpbuf*p=low_level_input(heth->Instance);//假设有low_level_input函数if(p!=NULL){/*传递数据到lwip栈*/if(etharp_input(p,&heth->gnetif)!=ERR_OK){pbuf_free(p);//释放缓冲区}}}```在LWIP集成中,有ethernetif.c文件定义了底层函数。最好参考那个。在STM32CubeMX生成的工程中,有ethernetif.c,其中实现eth_handler()等。HAL_ETH_RxLinkCallback可以直接调用自定义处理函数。为简单起见,展示如何在这个回调中提取数据。</think> 以下是针对您查询关于STM32F4xx系列MCU在LWIP协议栈中使用`HAL_ETH_RxLinkCallback`函数处理原始数据的详细解答。`HAL_ETH_RxLinkCallback`是STM32 HAL中的一个回调函数,当以太网接收缓冲链表(Rx Descriptor Chain)状态更新(如接收到新数据包)时自动触发。在LWIP协议栈中,开发者需要在这个回调函数中处理原始以太网帧数据,并将其传递给LWIP输入队列以实现网络通信。下面我将逐步解释处理方法并提供代码示例。 ### 1. **函数作用和关键概念** - **`HAL_ETH_RxLinkCallback` 的功能**: - 当以太网控制器(ETH)接收缓冲链表有新数据包到达时自动调用。 - 开发者需要在此函数中提取原始数据(Raw Data),并通过LWIP API将数据推送至协议栈处理[^2]。 - 关键操作包括:检查接收描述符状态、提取数据缓冲区地址、构造LWIP包结构(`struct pbuf`),并调用输入处理函数。 - **处理步骤概述**: 1. 检查接收描述符(Rx Descriptor)的 `OWN` 位(表示数据所有权:0表示DMA已完成传输,数据可用)。 2. 获取数据缓冲区的物理地址和长度。 3. 使用LWIP的 `pbuf` 结构封装原始数据。 4. 调用LWIP输入函数(如`ethernet_input()`)将数据传入协议栈。 5. 更新描述符状态以允许DMA接收新数据。 ### 2. **代码示例:`HAL_ETH_RxLinkCallback` 中的数据处理** 下面的代码示例基于STM32CubeMX生成的LWIP项目(结合用户引用[^2][^3]),展示如何在`HAL_ETH_RxLinkCallback`中实现原始数据处理。代码假设已配置好ETH外设和LWIP协议栈(通过STM32CubeMX配置)。示例使用STM32F407 MCU的HALLWIP版本2.x。 ```c /* 文件: ethernetif.c (自定义ETH驱动文件,由用户实现) */ #include "lwip/etharp.h" #include "lwip/pbuf.h" extern ETH_HandleTypeDef heth; // 声明HAL ETH句柄(在main.c中定义) void HAL_ETH_RxLinkCallback(ETH_HandleTypeDef *heth) { /* 1. 检查描述符状态: 遍历Rx描述符链表 */ for (int i = 0; i < ETH_RXBUFNB; i++) { ETH_DMADescTypeDef *dma_rx_desc = heth->RxDescList[i]; /* 检查OWN位: 若为0,数据包有效 */ if (!(dma_rx_desc->Status & ETH_DMARXDESC_OWN)) { uint32_t frame_length = dma_rx_desc->Status & ETH_DMARXDESC_FL; uint8_t *buffer = (uint8_t *)(dma_rx_desc->Buffer1Addr); /* 2. 创建LWIP pbuf包 */ struct pbuf *p = pbuf_alloc(PBUF_RAW, frame_length, PBUF_POOL); if (p != NULL) { /* 3. 复制原始数据到pbuf */ if (dma_rx_desc->Status & ETH_DMARXDESC_FS) { // 确认是第一段数据 memcpy(p->payload, buffer, frame_length); /* 4. 传递数据到LWIP协议栈 */ if (ethernet_input(p, &gnetif) != ERR_OK) { // gnetif: 全局网络接口结构 LWIP_DEBUGF(NETIF_DEBUG, ("HAL_ETH_RxLinkCallback: ethernet_input failed\n")); pbuf_free(p); // 释放pbuf } } } /* 5. 重置描述符,允许DMA再次使用 */ dma_rx_desc->Status = ETH_DMARXDESC_OWN; heth->RxDescList[i] = dma_rx_desc; // 更新链表 } } /* 可选:触发Rx继续接收 */ __HAL_ETH_DMARX_DESC_RESTART(heth); } ``` ### 3. **关键注意点** - **硬件配置依赖**:此代码依赖于STM32CubeMX生成的ETH初始化代码(如`heth`句柄定义)。确保在CUBEMX中正确配置ETH外设引脚和时钟(参考用户引用[^1][^2])。 - **LWIP集成**:需在项目中包含LWIP源码并配置`lwipopts.h`(启用`LWIP_NETIF_LINK_CALLBACK=1`,参考用户引用[^3])。 - **性能优化**: - 避免在回调中长时间处理数据;使用DMA描述符链表管理高效数据传输。 - 可使用零拷贝机制(直接操作描述符地址)减少内存复制开销,但需确保缓存一致性。 - **调试提示**:在LWIP配置中启用调试输出(如`LWIP_DEBUG`)监控输入错误。 ### 4. **常见问题处理** - **数据包丢失**:检查描述符OWN位更新逻辑是否错误;确保调用`__HAL_ETH_DMARX_DESC_RESTART()`重启接收。 - **LWIP输入失败**:确认网络接口初始化正确(`netif_add()`在`main()`中调用);检查MAC地址和IP配置。 - **以太网驱动初始化**:参考STM32CubeMX自动生成的`ethernetif_init()`函数。 ### 相关问题 1. **如何在STM32F4xx中优化LWIP的以太网接收性能?** (如使用DMA双缓冲减少延迟) 2. **在LWIP中如何处理以太网接收错误(如CRC错误)?** (检查ETH状态寄存器的RX错误位) 3. **如何将`HAL_ETH_RxLinkCallback`与FreeRTOS任务集成?** (通过信号量通知LWIP处理线程) 4. **LWIPSTM32F4上支持哪些高级协议?** (如MQTT客户端实现)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值