STM32F7xx —— LAN8720(FreeRTOS+LWIP)

                                STM32F7xx —— LAN8720

 

 

    STM32F767自带以太网模块,需要外接PHY芯片,完成以太网通信(MII/RMII接口)。LAN8720详细资料看手册。LWIP:1.4.1 FreeRTOS V8.2.3。

#define ETH_CHANNEL               ETH
#define ETH_PREEMPT_PRIO          ETHERNET_PRIORITY
#define ETH_CLK_ENABLE()          __HAL_RCC_ETH_CLK_ENABLE()
#define ETH_IRQ                   ETH_IRQn
#define ETH_IRQ_FUNC              ETH_IRQHandler

#define ETH_RESET_PORT            GPIOH
#define ETH_RESET_PIN             GPIO_PIN_11
#define ETH_RESET_CONFIG()        GPIOConfig(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL);
#define ETH_RESET_HIGH()          HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_SET)
#define ETH_RESET_LOW()           HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_RESET)

#define ETH_MDIO_PORT             GPIOA
#define ETH_MDIO_PIN              GPIO_PIN_2
#define ETH_MDIO_AF               GPIO_AF11_ETH
#define ETH_MDIO_CONFIG()         GPIOConfigExt(ETH_MDC_PORT, ETH_MDC_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDC_AF);

#define ETH_MDC_PORT              GPIOC
#define ETH_MDC_PIN               GPIO_PIN_1
#define ETH_MDC_AF                GPIO_AF11_ETH
#define ETH_MDC_CONFIG()          GPIOConfigExt(ETH_MDIO_PORT, ETH_MDIO_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDIO_AF);

#define ETH_RMII_REF_CLK_PORT     GPIOA
#define ETH_RMII_REF_CLK_PIN      GPIO_PIN_1
#define ETH_RMII_REF_CLK_AF       GPIO_AF11_ETH
#define ETH_RMII_REF_CLK_CONFIG() GPIOConfigExt(ETH_RMII_REF_CLK_PORT, ETH_RMII_REF_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_REF_CLK_AF);

#define ETH_RMII_CRS_DV_PORT      GPIOA
#define ETH_RMII_CRS_DV_PIN       GPIO_PIN_7
#define ETH_RMII_CRS_DV_AF        GPIO_AF11_ETH
#define ETH_RMII_CRS_DV_CONFIG()  GPIOConfigExt(ETH_RMII_CRS_DV_PORT, ETH_RMII_CRS_DV_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_CRS_DV_AF);

#define ETH_RMII_RXD0_PORT        GPIOC
#define ETH_RMII_RXD0_PIN         GPIO_PIN_4
#define ETH_RMII_RXD0_AF          GPIO_AF11_ETH
#define ETH_RMII_RXD0_CONFIG()    GPIOConfigExt(ETH_RMII_RXD0_PORT, ETH_RMII_RXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD0_AF);

#define ETH_RMII_RXD1_PORT        GPIOC
#define ETH_RMII_RXD1_PIN         GPIO_PIN_5
#define ETH_RMII_RXD1_AF          GPIO_AF11_ETH
#define ETH_RMII_RXD1_CONFIG()    GPIOConfigExt(ETH_RMII_RXD1_PORT, ETH_RMII_RXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD1_AF);

#define ETH_RMII_TXEN_PORT        GPIOB
#define ETH_RMII_TXEN_PIN         GPIO_PIN_11
#define ETH_RMII_TXEN_AF          GPIO_AF11_ETH
#define ETH_RMII_TXEN_CONFIG()    GPIOConfigExt(ETH_RMII_TXEN_PORT, ETH_RMII_TXEN_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXEN_AF);

#define ETH_RMII_TXD0_PORT        GPIOG
#define ETH_RMII_TXD0_PIN         GPIO_PIN_13
#define ETH_RMII_TXD0_AF          GPIO_AF11_ETH
#define ETH_RMII_TXD0_CONFIG()    GPIOConfigExt(ETH_RMII_TXD0_PORT, ETH_RMII_TXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD0_AF);

#define ETH_RMII_TXD1_PORT        GPIOG
#define ETH_RMII_TXD1_PIN         GPIO_PIN_14
#define ETH_RMII_TXD1_AF          GPIO_AF11_ETH
#define ETH_RMII_TXD1_CONFIG()    GPIOConfigExt(ETH_RMII_TXD1_PORT, ETH_RMII_TXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD1_AF);
lan8720_dev_t lan8720_dev;

static void lan8720_var_init(void)
{
  lan8720_dev.dma_rx = MemAlloc(SRAM_TYPE_DTCM, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef)); //申请内存
  lan8720_dev.dma_tx = MemAlloc(SRAM_TYPE_DTCM, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef)); //申请内存
  lan8720_dev.rx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_RX_BUF_SIZE * ETH_RXBUFNB); //申请内存
  lan8720_dev.tx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_TX_BUF_SIZE * ETH_TXBUFNB); //申请内存
}

static void lan8720_gpio_init(void)
{
  ETH_CLK_ENABLE();

  ETH_RESET_CONFIG();
  ETH_MDIO_CONFIG();
  ETH_MDC_CONFIG();
  ETH_RMII_REF_CLK_CONFIG();
  ETH_RMII_CRS_DV_CONFIG();
  ETH_RMII_RXD0_CONFIG();
  ETH_RMII_RXD1_CONFIG();
  ETH_RMII_TXEN_CONFIG();
  ETH_RMII_TXD0_CONFIG();
  ETH_RMII_TXD1_CONFIG();
}

static void lan8720_mode_init(void)
{
  uint8_t mac[6] = {0};

  ETH_RESET_CONFIG();
  ETH_RESET_HIGH();
  delay_ms(100);
  ETH_RESET_LOW();
  delay_ms(100);

  mac[0] = lwip_dev.mac[0];
  mac[1] = lwip_dev.mac[1];
  mac[2] = lwip_dev.mac[2];
  mac[3] = lwip_dev.mac[3];
  mac[4] = lwip_dev.mac[4];
  mac[5] = lwip_dev.mac[5];

  lan8720_dev.handle.Instance = ETH;
  lan8720_dev.handle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; // 使能自协商模式
  lan8720_dev.handle.Init.Speed = ETH_SPEED_100M;                       // 速度100M,如果开启了自协商模式,此配置就无效
  lan8720_dev.handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;             // 全双工模式,如果开启了自协商模式,此配置就无效
  lan8720_dev.handle.Init.PhyAddress = LAN8720_PHY_ADDRESS;             // LAN8720地址
  lan8720_dev.handle.Init.MACAddr = mac;                                // MAC地址
  lan8720_dev.handle.Init.RxMode = ETH_RXINTERRUPT_MODE;                // 中断接收模式
  lan8720_dev.handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;      // 硬件帧校验
  lan8720_dev.handle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;    // RMII接口
  HAL_ETH_Init(&lan8720_dev.handle);
}

static void lan8720_nvic_init(void)
{
  HAL_NVIC_SetPriority(ETH_IRQ, 3, 0); // 优先级必须小于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
  HAL_NVIC_EnableIRQ(ETH_IRQ);
}

uint32_t LAN8720ReadPHY(uint16_t reg)
{
  uint32_t regval;

  HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &regval);

  return regval;
}

void LAN8720WritePHY(uint16_t reg, uint16_t value)
{
  uint32_t temp = value;

  HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &temp);
}

static uint32_t lan8720_rx_size(ETH_DMADescTypeDef *dma_rx)
{
  uint32_t length = 0;

  if(((dma_rx->Status & ETH_DMARXDESC_OWN) == (uint32_t)RESET) &&
      ((dma_rx->Status & ETH_DMARXDESC_ES) == (uint32_t)RESET) &&
      ((dma_rx->Status & ETH_DMARXDESC_LS) != (uint32_t)RESET))
  {
    length = ((dma_rx->Status & ETH_DMARXDESC_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT);
  }

  return length;
}

void LAN8720Init(void)
{
  lan8720_var_init();
  lan8720_gpio_init();
  lan8720_mode_init();
  lan8720_nvic_init();
}

void ETH_IRQ_FUNC(void)
{
  while(lan8720_rx_size(lan8720_dev.handle.RxDesc))
  {
    LWIPCommProcess(); // 处理以太网数据,即将数据提交给LWIP
  }
  //清除中断标志位
  __HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_R);
  __HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_NIS);
}

sys_arch文件:

#ifndef _ARCH_SYS_ARCH_H_
#define _ARCH_SYS_ARCH_H_

#include "arch/cc.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"

#define MAX_QUEUE_ENTRIES     20  // 每个消息邮箱的大小

// LWIP消息邮箱结构体
typedef struct
{
  QueueHandle_t xQueue;
} lwip_mbox;

typedef SemaphoreHandle_t sys_sem_t;   // LWIP使用的信号量
typedef lwip_mbox   sys_mbox_t;       // LWIP使用的消息邮箱,其实就是UCOS中的消息队列
typedef unsigned char  sys_thread_t;  // 线程ID,也就是任务优先级

#endif /* _ARCH_SYS_ARCH_H_ */
// 参照lwip_sys.h

static const uint32_t console_null;

// 创建一个消息邮箱
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
  if(size > MAX_QUEUE_ENTRIES)
  {
    size = MAX_QUEUE_ENTRIES;
  }
  mbox->xQueue = xQueueCreate(size, sizeof(void *));

  if(mbox->xQueue != NULL)
  {
    return ERR_OK;
  }
  else
  {
    return ERR_MEM;
  }
}

// 释放并删除一个消息邮箱
void sys_mbox_free(sys_mbox_t *mbox)
{
  vQueueDelete(mbox->xQueue);

  mbox->xQueue = NULL;
}

// 向消息邮箱中发送一条消息(必须发送成功)
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  if(msg == NULL)
  {
    msg = (void*)&console_null;  //当msg为空时 msg等于pvNullPointer指向的值
  }
  if((SCB_ICSR_REG & 0xFF) == 0) //线程执行
  {
    while(xQueueSendToBack(mbox->xQueue, &msg, portMAX_DELAY) != pdPASS);//portMAX_DELAY,死等直到发送成功
  }
  else
  {
    while(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  }
}

// 尝试向一个消息邮箱发送消息
// 此函数相对于sys_mbox_post函数只发送一次消息,发送失败后不会尝试第二次发送
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  if(msg == NULL)
  {
    msg = (void*)&console_null;  //当消息为空,则用常量NullMessage的地址替换
  }
  if((SCB_ICSR_REG & 0xFF) == 0)
  {
    if(xQueueSendToBack(mbox->xQueue, &msg, 0) != pdPASS)
    {
      return ERR_MEM;
    }
  }
  else
  {
    if(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS)
    {
      return ERR_MEM;
    }
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  }
  return ERR_OK;
}

// 等待邮箱中的消息
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
#if 0
	u32_t rtos_timeout;
  BaseType_t temp;
	
	if(timeout == 0)
	{
		rtos_timeout = 2;
	}
	else
	{
		rtos_timeout = timeout;
		temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout);
	}
	
	return rtos_timeout;
	
#else
  u32_t rtos_timeout, timeout_new;
  BaseType_t temp;

  temp = xQueueReceive(mbox->xQueue, msg, 0);
  if((temp == pdPASS) && (*msg != NULL))
  {
    if(*msg == (void*)&console_null)
    {
      *msg = NULL;
    }
    return 0;
  }

  if(timeout != 0)
  {
    rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 转换为节拍数,因为freertos延时使用的是节拍数,而LWIP是用ms
    if(rtos_timeout < 1)
    {
      rtos_timeout = 1; // 至少1个节拍
    }
    else if(rtos_timeout >= portMAX_DELAY)
    {
      rtos_timeout = portMAX_DELAY - 1;
    }
  }
  else
  {
    rtos_timeout = 0;
  }
  timeout = HAL_GetTick(); //获取系统时间
  if(rtos_timeout != 0)
  {
    temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout);  // 请求消息队列,等待时限为rtos_timeout
  }
  else
  {
    temp = xQueueReceive(mbox->xQueue, msg, portMAX_DELAY);  // 为0则无限等
  }

  if(temp == errQUEUE_EMPTY)
  {
    timeout = SYS_ARCH_TIMEOUT; //请求超时
    *msg = NULL;
  }
  else
  {
    if(*msg != NULL)
    {
      if(*msg == (void*)&console_null)
      {
        *msg = NULL;
      }
    }
    timeout_new = HAL_GetTick();
    if (timeout_new > timeout)
    {
      timeout_new = timeout_new - timeout;  //算出请求消息或使用的时间
    }
    else
    {
      timeout_new = 0xffffffff - timeout + timeout_new;
    }
    timeout = timeout_new * 1000 / configTICK_RATE_HZ + 1;
  }
  return timeout;
#endif
}

// 尝试获取消息
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
  BaseType_t temp;

  temp = xQueueReceive(mbox->xQueue, msg, 0);
  if((temp == pdPASS) && (*msg != NULL))
  {
    if(*msg == (void*)&console_null)
    {
      *msg = NULL;
    }

    return 0;
  }
  else
  {
    return SYS_MBOX_EMPTY;
  }
}

// 检查一个消息邮箱是否有效
// 返回值:1,有效.  0,无效
int sys_mbox_valid(sys_mbox_t *mbox)
{
  if(mbox->xQueue != NULL)
  {
    return 1;
  }

  return 0;
}

// 设置一个消息邮箱为无效
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{
  mbox->xQueue = NULL;
}


// 创建一个信号量
err_t sys_sem_new(sys_sem_t* sem, uint8_t count)
{
  *sem = xSemaphoreCreateCounting(0xFF, count);
  if(*sem == NULL)
  {
    return ERR_MEM;
  }

  return ERR_OK;
}

// 等待一个信号量
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
  u32_t rtos_timeout, timeout_new;
  BaseType_t temp;

  if(xSemaphoreTake(*sem, 0) == pdPASS)
  {
    return 0;
  }

  if( timeout != 0)
  {
    rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用ms
    if(rtos_timeout < 1)
    {
      rtos_timeout = 1;
    }
  }
  else
  {
    rtos_timeout = 0;
  }

  timeout = HAL_GetTick();

  if(rtos_timeout != 0)
  {
    temp = xSemaphoreTake(*sem, rtos_timeout);
  }
  else
  {
    temp = xSemaphoreTake(*sem, portMAX_DELAY);
  }

  if(temp != pdPASS)
  {
    timeout = SYS_ARCH_TIMEOUT; // 请求超时
  }
  else
  {
    timeout_new = HAL_GetTick();
    if (timeout_new >= timeout)
    {
      timeout_new = timeout_new - timeout;
    }
    else
    {
      timeout_new = 0xffffffff - timeout + timeout_new;
    }
    timeout = (timeout_new * 1000 / configTICK_RATE_HZ + 1); // 算出请求消息或使用的时间(ms)
  }

  return timeout;
}

// 发送一个信号量
void sys_sem_signal(sys_sem_t *sem)
{
  while(xSemaphoreGive(*sem) != pdTRUE);
}

// 释放并删除一个信号量
void sys_sem_free(sys_sem_t *sem)
{
  vSemaphoreDelete(*sem);
  *sem = NULL;
}

// 查询一个信号量的状态,无效或有效
int sys_sem_valid(sys_sem_t *sem)
{
  if(*sem != NULL)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

// 设置一个信号量无效
void sys_sem_set_invalid(sys_sem_t *sem)
{
  *sem = NULL;
}

// arch初始化
void sys_init(void)
{
  // 不做任何事情
}

TaskHandle_t os_lwip_handle;

sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
  xTaskCreate((TaskFunction_t)thread,
              (const char*  )name,
              (uint16_t     )stacksize,
              (void*        )NULL,
              (UBaseType_t  )prio,
              (TaskHandle_t*)&os_lwip_handle);//创建TCP IP内核任务
  return 0;
}

// lwip延时函数
void sys_msleep(u32_t ms)
{
  delay_ms(ms);
}

// 获取系统时间,LWIP1.4.1增加的函数
// 返回值:当前系统时间(单位:毫秒)
u32_t sys_now(void)
{
  return (HAL_GetTick() * 1000 / configTICK_RATE_HZ + 1); // 将节拍数转换为LWIP的时间MS
}

// 用在cc.h的SYS_ARCH_PROTECT(lev)
uint32_t SysCriticalEnter(void)
{
  if(SCB_ICSR_REG & 0xFF) //在中断里
  {
    return taskENTER_CRITICAL_FROM_ISR();
  }
  else  // 在任务
  {
    taskENTER_CRITICAL();
  }

  return 0;
}

// 用在cc.YS_ARCH_UNPROTECT(lev)
void SysCriticalExit(uint32_t lev)
{
  if(SCB_ICSR_REG & 0xFF) // 在中断里
  {
    taskEXIT_CRITICAL_FROM_ISR(lev);
  }
  else  // 在任务
  {
    taskEXIT_CRITICAL();
  }
}

cpu.h:#define BYTE_ORDER LITTLE_ENDIAN  // 小端模式

cc.h:


#include "cpu.h"
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"

// LWIP需要的类型重命名
typedef unsigned   char    u8_t;    /* Unsigned 8 bit quantity         */
typedef signed     char    s8_t;    /* Signed    8 bit quantity        */
typedef unsigned   short   u16_t;   /* Unsigned 16 bit quantity        */
typedef signed     short   s16_t;   /* Signed   16 bit quantity        */
typedef unsigned   long    u32_t;   /* Unsigned 32 bit quantity        */
typedef signed     long    s32_t;   /* Signed   32 bit quantity        */
typedef u32_t mem_ptr_t;           /* Unsigned 32 bit quantity        */
typedef int sys_prot_t;

// LWIP需要的临界段
#define OS_CRITICAL_METHOD

#ifdef OS_CRITICAL_METHOD

#define SCB_ICSR_REG    (*((volatile uint32_t * ) 0xe000ed04))

extern uint32_t SysCriticalEnter(void);
extern void SysCriticalExit(uint32_t lev);


#define SYS_ARCH_DECL_PROTECT(lev)   u32_t lev

#define SYS_ARCH_PROTECT(lev)        lev = SysCriticalEnter()

#define SYS_ARCH_UNPROTECT(lev)      SysCriticalExit(lev)

#endif


// 对齐方案
#if defined (__ICCARM__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES

#elif defined (__CC_ARM)

#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#elif defined (__GNUC__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#elif defined (__TASKING__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#endif


#define U16_F "4d"
#define S16_F "4d"
#define X16_F "4x"
#define U32_F "8ld"
#define S32_F "8ld"
#define X32_F "8lx"

/*--------------macros--------------------------------------------------------*/
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) \
    do \
    {   printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \
    } while(0)
#endif

#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif

lwipopts.h

#ifndef _LWIPOPTS_H_
#define _LWIPOPTS_H_

// 与FreeRTOS配置类似(FreeRTOSConfig.h)

// 线程优先级
#ifndef TCPIP_THREAD_PRIO
#define TCPIP_THREAD_PRIO     (configMAX_PRIORITIES - 1) // 定义内核任务的优先级为最高
#endif
#undef  DEFAULT_THREAD_PRIO
#define DEFAULT_THREAD_PRIO   8


#define SYS_LIGHTWEIGHT_PROT    1     // 为1时使用实时操作系统的轻量级保护,保护关键代码不被中断打断
#define NO_SYS                  0     // 使用UCOS操作系统
#define MEM_ALIGNMENT           4     // 使用4字节对齐模式
#define MEM_SIZE                16000 // 内存堆heap大小
#define MEMP_NUM_PBUF           20    // MEMP_NUM_PBUF:memp结构的pbuf数量,如果应用从ROM或者静态存储区发送大量数据时,这个值应该设置大一点
#define MEMP_NUM_UDP_PCB        6     // MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB.
#define MEMP_NUM_TCP_PCB        10    // MEMP_NUM_TCP_PCB:同时建立激活的TCP数量
#define MEMP_NUM_TCP_PCB_LISTEN 6     // MEMP_NUM_TCP_PCB_LISTEN:能够监听的TCP连接数量
#define MEMP_NUM_TCP_SEG        15    // MEMP_NUM_TCP_SEG:最多同时在队列中的TCP段数量
#define MEMP_NUM_SYS_TIMEOUT    8     // MEMP_NUM_SYS_TIMEOUT:能够同时激活的timeout个数

// pbuf内存池
#define PBUF_POOL_SIZE          20    // PBUF_POOL_SIZE:pbuf内存池个数
#define PBUF_POOL_BUFSIZE       512   // PBUF_POOL_BUFSIZE:每个pbuf内存池大小

#define LWIP_TCP                1     // 使用TCP
#define TCP_TTL                 255   // 生存时间

#undef TCP_QUEUE_OOSEQ
#define TCP_QUEUE_OOSEQ         0     // 当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0

#undef TCPIP_MBOX_SIZE
#define TCPIP_MBOX_SIZE         MAX_QUEUE_ENTRIES // tcpip创建主线程时的消息邮箱大小

#undef DEFAULT_TCP_RECVMBOX_SIZE
#define DEFAULT_TCP_RECVMBOX_SIZE       MAX_QUEUE_ENTRIES

#undef DEFAULT_ACCEPTMBOX_SIZE
#define DEFAULT_ACCEPTMBOX_SIZE         MAX_QUEUE_ENTRIES


#define TCP_MSS                 (1500 - 40) // 最大TCP分段,TCP_MSS = (MTU - IP报头大小 - TCP报头大小
#define TCP_SND_BUF             (4*TCP_MSS) // TCP发送缓冲区大小(bytes).
#define TCP_SND_QUEUELEN        (2* TCP_SND_BUF/TCP_MSS)  //TCP_SND_QUEUELEN: TCP发送缓冲区大小(pbuf).这个值最小为(2 * TCP_SND_BUF/TCP_MSS)
#define TCP_WND                 (2*TCP_MSS)   // TCP发送窗口
#define LWIP_ICMP               1   // 使用ICMP协议
#define LWIP_DHCP               0   // 使用DHCP
#define LWIP_UDP                1   // 使用UDP服务
#define UDP_TTL                 255 // UDP数据包生存时间
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1

// 帧校验和选项,STM32F7xx允许通过硬件识别和计算IP,UDP和ICMP的帧校验和
#define CHECKSUM_BY_HARDWARE // 定义CHECKSUM_BY_HARDWARE,使用硬件帧校验
#ifdef CHECKSUM_BY_HARDWARE
//CHECKSUM_GEN_IP==0: 硬件生成IP数据包的帧校验和
#define CHECKSUM_GEN_IP                 0
//CHECKSUM_GEN_UDP==0: 硬件生成UDP数据包的帧校验和
#define CHECKSUM_GEN_UDP                0
//CHECKSUM_GEN_TCP==0: 硬件生成TCP数据包的帧校验和
#define CHECKSUM_GEN_TCP                0
//CHECKSUM_CHECK_IP==0: 硬件检查输入的IP数据包帧校验和
#define CHECKSUM_CHECK_IP               0
//CHECKSUM_CHECK_UDP==0: 硬件检查输入的UDP数据包帧校验和
#define CHECKSUM_CHECK_UDP              0
//CHECKSUM_CHECK_TCP==0: 硬件检查输入的TCP数据包帧校验和
#define CHECKSUM_CHECK_TCP              0
//CHECKSUM_CHECK_ICMP==1:硬件检查输入的ICMP数据包帧校验和
#define CHECKSUM_GEN_ICMP               0
#else
//CHECKSUM_GEN_IP==1: 软件生成IP数据包帧校验和
#define CHECKSUM_GEN_IP                 1
// CHECKSUM_GEN_UDP==1: 软件生成UDOP数据包帧校验和
#define CHECKSUM_GEN_UDP                1
//CHECKSUM_GEN_TCP==1: 软件生成TCP数据包帧校验和
#define CHECKSUM_GEN_TCP                1
// CHECKSUM_CHECK_IP==1: 软件检查输入的IP数据包帧校验和
#define CHECKSUM_CHECK_IP               1
// CHECKSUM_CHECK_UDP==1: 软件检查输入的UDP数据包帧校验和
#define CHECKSUM_CHECK_UDP              1
//CHECKSUM_CHECK_TCP==1: 软件检查输入的TCP数据包帧校验和
#define CHECKSUM_CHECK_TCP              1
//CHECKSUM_CHECK_ICMP==1:软件检查输入的ICMP数据包帧校验和
#define CHECKSUM_GEN_ICMP               1
#endif

#define LWIP_NETCONN                    1 // LWIP_NETCONN==1:使能NETCON函数(要求使用api_lib.c)
#define LWIP_SOCKET                     1 // LWIP_SOCKET==1:使能Socket API(要求使用sockets.c)
#define LWIP_COMPAT_MUTEX               1
#define LWIP_SO_RCVTIMEO                1 // 通过定义LWIP_SO_RCVTIMEO使能netconn结构体中recv_timeout,使用recv_timeout可以避免阻塞线程

// 有关系统的选项
#define TCPIP_THREAD_STACKSIZE          1024  // 内核任务堆栈大小
#define DEFAULT_UDP_RECVMBOX_SIZE       2000
#define DEFAULT_THREAD_STACKSIZE        512

//LWIP调试选项
#define LWIP_DEBUG                      0 // 关闭DEBUG选项
#define ICMP_DEBUG                      LWIP_DBG_OFF // 开启/关闭ICMPdebug


#endif /* __LWIPOPTS_H__ */

主函数需调用的初始化接口:

lwip_dev_t lwip_dev;            // lwip控制结构体
static struct netif lwip_netif; // 网络接口

extern uint32_t memp_get_memorysize(void);  // 在memp.c里面定义
extern uint8_t *memp_memory;                        // 在memp.c里面定义.
extern uint8_t *ram_heap;                           // 在mem.c里面定义.

// 释放内存
static void lwip_comm_mem_free(void)
{
  MemFree(SRAM_TYPE_IN, memp_memory);
  MemFree(SRAM_TYPE_IN, ram_heap);
}

// 分配内存
static uint8_t lwip_comm_mem_malloc(void)
{
  uint32_t mempsize;
  uint32_t ramheapsize;

  mempsize = memp_get_memorysize(); // 得到memp_memory数组大小
  memp_memory = MemAlloc(SRAM_TYPE_IN, mempsize); // 为memp_memory申请内存

  ramheapsize = LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + 2 * LWIP_MEM_ALIGN_SIZE(4 * 3) + MEM_ALIGNMENT; // 得到ram heap大小
  ram_heap = MemAlloc(SRAM_TYPE_IN, ramheapsize); // 为ram_heap申请内存

  if(!memp_memory || !ram_heap)
  {
    lwip_comm_mem_free();
    return 1;
  }
  return 0;
}

// 网络默认配置
static void lwip_comm_default_ip_set(lwip_dev_t *dev)
{
  uint32_t sn0;

  SocIDGet(&sn0, CONFIG_SYSTEM_HARDWARE_TYPE);

  // MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)
  dev->mac[0] = 2;
  dev->mac[1] = 0;
  dev->mac[2] = 0;
  dev->mac[3] = (sn0 >> 16) & 0xFF;
  dev->mac[4] = (sn0 >> 8) & 0xFF;
  dev->mac[5] = sn0 & 0xFF;

  // 默认远程IP
  dev->remoteip[0] = CONFIG_ETHERNET_REMOTE_IP0;
  dev->remoteip[1] = CONFIG_ETHERNET_REMOTE_IP1;
  dev->remoteip[2] = CONFIG_ETHERNET_REMOTE_IP2;
  dev->remoteip[3] = CONFIG_ETHERNET_REMOTE_IP3;

  // 本地IP
  dev->ip[0] = CONFIG_ETHERNET_LOCAL_IP0;
  dev->ip[1] = CONFIG_ETHERNET_LOCAL_IP1;
  dev->ip[2] = CONFIG_ETHERNET_LOCAL_IP2;
  dev->ip[3] = CONFIG_ETHERNET_LOCAL_IP3;

  // 子网掩码
  dev->netmask[0] = CONFIG_ETHERNET_NETMASK0;
  dev->netmask[1] = CONFIG_ETHERNET_NETMASK1;
  dev->netmask[2] = CONFIG_ETHERNET_NETMASK2;
  dev->netmask[3] = CONFIG_ETHERNET_NETMASK3;

  // 网关
  dev->gateway[0] = CONFIG_ETHERNET_GATEWAY0;
  dev->gateway[1] = CONFIG_ETHERNET_GATEWAY1;
  dev->gateway[2] = CONFIG_ETHERNET_GATEWAY2;
  dev->gateway[3] = CONFIG_ETHERNET_GATEWAY3;

  dev->dhcpstatus = 0; // 没有DHCP
}

// LWIP初始化(LWIP启动的时候使用)
uint8_t LWIPCommInit(void)
{
  struct netif *netif_init;           // 调用netif_add()函数时的返回值,用于判断网络初始化是否成功
  struct ip_addr ipaddr, netmask, gw; //ip地址  子网掩码  网关

  if(lwip_comm_mem_malloc())
  {
    return LWIP_COMM_STATUS_RAM_ERROR;  //内存申请失败
  }

  lwip_comm_default_ip_set(&lwip_dev);  //设置默认IP等信息

  LAN8720Init(); // 硬件网口初始化

  tcpip_init(NULL, NULL); // 初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务

  IP4_ADDR(&ipaddr, lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);
  IP4_ADDR(&netmask, lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);
  IP4_ADDR(&gw, lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);

  printf("网卡en的MAC地址%d.%d.%d.%d.%d.%d\r\n", lwip_dev.mac[0], lwip_dev.mac[1], lwip_dev.mac[2], lwip_dev.mac[3], lwip_dev.mac[4], lwip_dev.mac[5]);
  printf("静态IP地址.....%d.%d.%d.%d\r\n", lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);
  printf("子网掩码.......%d.%d.%d.%d\r\n", lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);
  printf("默认网关.......%d.%d.%d.%d\r\n", lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);

  netif_init = netif_add(&lwip_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input); // 向网卡列表中添加一个网口
  if(netif_init == NULL)
  {
    return LWIP_COMM_STATUS_NET_ADD_FAIL;  // 网卡添加失败
  }
  else // 网口添加成功后,设置netif为默认值,并且打开netif网口
  {
    netif_set_default(&lwip_netif); // 设置netif为默认网口
    netif_set_up(&lwip_netif);      // 打开netif网口
  }

  return LWIP_COMM_STATUS_OK; // OK.
}

// 用于以太网中断调用
void LWIPCommProcess(void)
{
  ethernetif_input(&lwip_netif);
}

ethernetif.c:

// 由ethernetif_init()调用用于初始化硬件
// netif:网卡结构体指针
// 返回值:ERR_OK,正常
//       其他,失败
static err_t low_level_init(struct netif *netif)
{
  netif->hwaddr_len = ETHARP_HWADDR_LEN; // 设置MAC地址长度,为6个字节
  // 初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复
  netif->hwaddr[0] = lwip_dev.mac[0];
  netif->hwaddr[1] = lwip_dev.mac[1];
  netif->hwaddr[2] = lwip_dev.mac[2];
  netif->hwaddr[3] = lwip_dev.mac[3];
  netif->hwaddr[4] = lwip_dev.mac[4];
  netif->hwaddr[5] = lwip_dev.mac[5];
  netif->mtu = 1500; //最大允许传输单元,允许该网卡广播和ARP功能

  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

  HAL_ETH_DMATxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_tx, lan8720_dev.tx_buffer, ETH_TXBUFNB); // 初始化发送描述符
  HAL_ETH_DMARxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_rx, lan8720_dev.rx_buffer, ETH_RXBUFNB); // 初始化接收描述符
  HAL_ETH_Start(&lan8720_dev.handle); // 开启MAC和DMA

  return ERR_OK;
}

// 用于发送数据包的最底层函数(lwip通过netif->linkoutput指向该函数)
// netif:网卡结构体指针
// p:pbuf数据结构体指针
// 返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  err_t errval;
  struct pbuf *q;
  uint8_t *buffer = (uint8_t *)(lan8720_dev.handle.TxDesc->Buffer1Addr);
  __IO ETH_DMADescTypeDef *DmaTxDesc;
  uint32_t framelength = 0;
  uint32_t bufferoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t payloadoffset = 0;

  DmaTxDesc = lan8720_dev.handle.TxDesc;
  bufferoffset = 0;

  // 从pbuf中拷贝要发送的数据
  for(q = p; q != NULL; q = q->next)
  {
    // 判断此发送描述符是否有效,即判断此发送描述符是否归以太网DMA所有
    if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
    {
      errval = ERR_USE;
      goto error;              // 发送描述符无效,不可用
    }
    byteslefttocopy = q->len;   // 要发送的数据长度
    payloadoffset = 0;

    // 将pbuf中要发送的数据写入到以太网发送描述符中,有时候我们要发送的数据可能大于一个以太网
    // 描述符的Tx Buffer,因此我们需要分多次将数据拷贝到多个发送描述符中
    while((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE )
    {
      // 将数据拷贝到以太网发送描述符的Tx Buffer中
      memcpy((uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));
      // DmaTxDsc指向下一个发送描述符
      DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
      // 检查新的发送描述符是否有效
      if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
      {
        errval = ERR_USE;
        goto error;     // 发送描述符无效,不可用
      }
      buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); // 更新buffer地址,指向新的发送描述符的Tx Buffer
      byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
      payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
      framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
      bufferoffset = 0;
    }
    // 拷贝剩余的数据
    memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
    bufferoffset = bufferoffset + byteslefttocopy;
    framelength = framelength + byteslefttocopy;
  }
  // 当所有要发送的数据都放进发送描述符的Tx Buffer以后就可发送此帧了
  HAL_ETH_TransmitFrame(&lan8720_dev.handle, framelength);
  errval = ERR_OK;
error:
  // 发送缓冲区发生下溢,一旦发送缓冲区发生下溢TxDMA会进入挂起状态
  if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
  {
    // 清除下溢标志
    lan8720_dev.handle.Instance->DMASR = ETH_DMASR_TUS;
    // 当发送帧中出现下溢错误的时候TxDMA会挂起,这时候需要向DMATPDR寄存器
    // 随便写入一个值来将其唤醒,此处我们写0
    lan8720_dev.handle.Instance->DMATPDR = 0;
  }
  return errval;
}

// 用于接收数据包的最底层函数
// neitif:网卡结构体指针
// 返回值:pbuf数据结构体指针
static struct pbuf * low_level_input(struct netif *netif)
{
  struct pbuf *p = NULL;
  struct pbuf *q;
  uint16_t len;
  uint8_t *buffer;
  __IO ETH_DMADescTypeDef *dmarxdesc;
  uint32_t bufferoffset = 0;
  uint32_t payloadoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t i = 0;

  if(HAL_ETH_GetReceivedFrame(&lan8720_dev.handle) != HAL_OK) // 判断是否接收到数据
  {
    return NULL;
  }

  len = lan8720_dev.handle.RxFrameInfos.length;               // 获取接收到的以太网帧长度
  buffer = (uint8_t *)lan8720_dev.handle.RxFrameInfos.buffer; // 获取接收到的以太网帧的数据buffer

  if(len > 0)
  {
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);  // 申请pbuf
  }
  if(p != NULL)                                // pbuf申请成功
  {
    dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc;  // 获取接收描述符链表中的第一个描述符
    bufferoffset = 0;
    for(q = p; q != NULL; q = q->next)
    {
      byteslefttocopy = q->len;
      payloadoffset = 0;
      // 将接收描述符中Rx Buffer的数据拷贝到pbuf中
      while((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE )
      {
        // 将数据拷贝到pbuf中
        memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
        // dmarxdesc向下一个接收描述符
        dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
        // 更新buffer地址,指向新的接收描述符的Rx Buffer
        buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);

        byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
        payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
        bufferoffset = 0;
      }
      // 拷贝剩余的数据
      memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);
      bufferoffset = bufferoffset + byteslefttocopy;
    }
  }
  // 释放DMA描述符
  dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc;
  for(i = 0; i < lan8720_dev.handle.RxFrameInfos.SegCount; i++)
  {
    dmarxdesc->Status |= ETH_DMARXDESC_OWN;     //标记描述符归DMA所有
    dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
  }
  lan8720_dev.handle.RxFrameInfos.SegCount = 0;          //清除段计数器
  if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) //接收缓冲区不可用
  {
    // 清除接收缓冲区不可用标志
    lan8720_dev.handle.Instance->DMASR = ETH_DMASR_RBUS;
    // 当接收缓冲区不可用的时候RxDMA会进去挂起状态,通过向DMARPDR写入任意一个值来唤醒Rx DMA
    lan8720_dev.handle.Instance->DMARPDR = 0;
  }
  return p;
}

// 网卡接收数据(lwip直接调用)
// netif:网卡结构体指针
// 返回值:ERR_OK,发送正常
//       ERR_MEM,发送失败
err_t ethernetif_input(struct netif *netif)
{
  err_t err;
  struct pbuf *p;
  p = low_level_input(netif); //调用low_level_input函数接收数据
  if(p == NULL)
  {
    return ERR_MEM;
  }
  err = netif->input(p, netif); //调用netif结构体中的input字段(一个函数)来处理数据包
  if(err != ERR_OK)
  {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
    pbuf_free(p);
    p = NULL;
  }
  return err;
}

// 使用low_level_init()函数来初始化网络
// netif:网卡结构体指针
// 返回值:ERR_OK,正常
//       其他,失败
err_t ethernetif_init(struct netif *netif)
{
  LWIP_ASSERT("netif!=NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME     // LWIP_NETIF_HOSTNAME 
  netif->hostname = "lwip"; // 初始化名称
#endif
  netif->name[0] = IFNAME0; // 初始化变量netif的name字段
  netif->name[1] = IFNAME1; // 在文件外定义这里不用关心具体值
  netif->output = etharp_output; // IP层发送数据包函数
  netif->linkoutput = low_level_output; // ARP模块发送数据包函数
  low_level_init(netif);    // 底层硬件初始化函数
  return ERR_OK;
}

TCP客户端实现:

#ifndef _LWIP_TCP_CLIENT_H_
#define _LWIP_TCP_CLIENT_H_


void TCPClientProcess(void);

void TCPClientSend(uint8_t *data, uint16_t length);

#endif /* _LWIP_TCP_CLIENT_H_ */

TaskHandle_t os_tcp_handle;

#define TCP_CLIENT_RX_BUFSIZE 1500  // 接收缓冲区长度

typedef struct
{
  struct netconn *sockfd; // TCP CLIENT网络连接结构体
  err_t connect_err;
  err_t recv_err;
  err_t send_err;

  // 服务器IP和端口
  ip_addr_t server_ipaddr;
  uint16_t server_port;

  // 客户端IP和端口
  ip_addr_t loca_ipaddr;
  uint16_t loca_port;

  uint8_t state; // 网络连接的状态

	uint8_t recv_buf[TCP_CLIENT_RX_BUFSIZE];
	uint16_t recv_len;
} tcp_client_t;

static tcp_client_t tcp_client;

static void tcp_client_close(void)
{
  netconn_close(tcp_client.sockfd);
  netconn_delete(tcp_client.sockfd);
  printf("服务器	%d.%d.%d.%d断开连接 \r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]);
}

static void tcp_client_recv(void)
{
  struct netbuf *recvbuf;
  struct pbuf *q;

  while(1)
  {
    tcp_client.recv_err = netconn_recv(tcp_client.sockfd, &recvbuf);

    if(tcp_client.recv_err == ERR_OK) //接收到数据
    {
      taskENTER_CRITICAL();

			tcp_client.recv_len = 0;

      for(q = recvbuf->p; q != NULL; q = q->next) // 遍历完整个pbuf链表
      {
        printf(" %d ...  \r\n", q->len);
        memcpy(tcp_client.recv_buf + tcp_client.recv_len, q->payload, q->len);

        tcp_client.recv_len += q->len;
      }
      taskEXIT_CRITICAL();

			LWIPParse(tcp_client.recv_buf, tcp_client.recv_len);
			
      netbuf_delete(recvbuf);
    }
    else if(tcp_client.recv_err == ERR_CLSD)   //关闭连接
    {
      tcp_client_close();
      break;
    }

    // 网线断掉 删除连接
    tcp_client.state = LAN8720ReadPHY(PHY_BSR);
    if((tcp_client.state & 0x04) == 0)
    {
      printf("close\r\n");
      tcp_client_close();
      break;
    }
  }
}

static void task_tcp_client(void *arg)
{
  tcp_client.server_port = CONFIG_ETHERNET_REMOTE_PORT;
  IP4_ADDR(&tcp_client.server_ipaddr, lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]);

  while (1)
  {
	tcp_client.sockfd = netconn_new(NETCONN_TCP); // 创建一个TCP链接
		
	tcp_client.connect_err = netconn_connect(tcp_client.sockfd, &tcp_client.server_ipaddr, tcp_client.server_port); //连接服务器
		
	if(tcp_client.connect_err != ERR_OK)
	{
	  netconn_delete(tcp_client.sockfd); // 删除tcp_clientconn连接
	}
	else
	{
	  tcp_client.sockfd->recv_timeout = 5;
	  netconn_getaddr(tcp_client.sockfd, &tcp_client.loca_ipaddr, &tcp_client.loca_port, 1); // 获取本地IP主机IP地址和端口号
	  printf("连接上服务器: %d.%d.%d.%d, 本机端口:%d\r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3], tcp_client.loca_port);
		
	  tcp_client_recv();
	}
  }
}

void TCPClientProcess(void)
{
  LWIPCommInit();

  xTaskCreate((TaskFunction_t)task_tcp_client,
              (const char*  )"tcp_client_task",
              (uint16_t     )OS_TCP_STK_SIZE,
              (void*        )NULL,
              (UBaseType_t  )OS_PRIO_TCP,
              (TaskHandle_t*)&os_tcp_handle);
}

void TCPClientSend(uint8_t *data, uint16_t length)
{
  tcp_client.send_err = netconn_write(tcp_client.sockfd, data, length, NETCONN_COPY); //发送数据
  if(tcp_client.send_err != ERR_OK)
  {
    printf("发送失败\r\n");
    tcp_client_close();
  }
}

 

 

 

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值