1、配置ETH
在这里插入图片描述
选择RMII接口,MAC地址默认,数据接收模式选择轮询模式
另外还需配置一个复位引脚ETH_RST,拉低是复位LAN8720A
2、配置LWIP
在这里插入图片描述
使能LWIP,这里使用静态IP,故关闭DHCP,手动填写IP信息,使能TCP模式,可以设置TCP最大连接数,关闭UDP模式
在这里插入图片描述
Platform Setting这里选择LAN8742,CUBEMX没有LAN8720可选,选择LAN8742一样可行
3、生成代码
点击生成代码
修改lwip.c中MX_LWIP_Init函数,可再次修改IP信息,复位PHY的代码也可放在此处

void MX_LWIP_Init(void)
{
  /* IP addresses initialization */
  IP_ADDRESS[0] = 192;
  IP_ADDRESS[1] = 168;
  IP_ADDRESS[2] = 114;
  IP_ADDRESS[3] = 10;
  NETMASK_ADDRESS[0] = 255;
  NETMASK_ADDRESS[1] = 255;
  NETMASK_ADDRESS[2] = 255;
  NETMASK_ADDRESS[3] = 0;
  GATEWAY_ADDRESS[0] = 192;
  GATEWAY_ADDRESS[1] = 168;
  GATEWAY_ADDRESS[2] = 114;
  GATEWAY_ADDRESS[3] = 1;

/* USER CODE BEGIN IP_ADDRESSES */
	//重新设定IP信息
	IP_ADDRESS[0] = tcp_addr[0];
	IP_ADDRESS[1] = tcp_addr[1];
	IP_ADDRESS[2] = tcp_addr[2];
	IP_ADDRESS[3] = tcp_addr[3];
	NETMASK_ADDRESS[0] = tcp_addr[8];
	NETMASK_ADDRESS[1] = tcp_addr[9];
	NETMASK_ADDRESS[2] = tcp_addr[10];
	NETMASK_ADDRESS[3] = tcp_addr[11];
	GATEWAY_ADDRESS[0] = tcp_addr[4];
	GATEWAY_ADDRESS[1] = tcp_addr[5];
	GATEWAY_ADDRESS[2] = tcp_addr[6];
	GATEWAY_ADDRESS[3] = tcp_addr[7];
	//复位
	HAL_GPIO_WritePin(ETH_RST_GPIO_Port,ETH_RST_Pin,GPIO_PIN_RESET);
	HAL_Delay(60);
	HAL_GPIO_WritePin(ETH_RST_GPIO_Port,ETH_RST_Pin,GPIO_PIN_SET);
	HAL_Delay(60);
/* USER CODE END IP_ADDRESSES */

  /* Initilialize the LwIP stack without RTOS */
  lwip_init();

  /* IP addresses initialization without DHCP (IPv4) */
  IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
  IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
  IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);

  /* add the network interface (IPv4/IPv6) without RTOS */
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

  /* Registers the default network interface */
  netif_set_default(&gnetif);

  /* We must always bring the network interface up connection or not... */
  netif_set_up(&gnetif);

  /* Set the link callback function, this function is called on change of link status*/
  netif_set_link_callback(&gnetif, ethernet_link_status_updated);

  /* Create the Ethernet link handler thread */

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */
}

如需修改MAC地址,可打开etnernetif.c,修改函数low_level_init

static void low_level_init(struct netif *netif)
{
  HAL_StatusTypeDef hal_eth_init_status = HAL_OK;
  /* Start ETH HAL Init */

   uint8_t MACAddr[6] ;
  heth.Instance = ETH;
  MACAddr[0] = 0x00;
  MACAddr[1] = 0x80;
  MACAddr[2] = 0xE1;
  MACAddr[3] = 0x00;
  MACAddr[4] = 0x00;
  MACAddr[5] = 0x00;
  heth.Init.MACAddr = &MACAddr[0];
  heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
  heth.Init.TxDesc = DMATxDscrTab;
  heth.Init.RxDesc = DMARxDscrTab;
  heth.Init.RxBuffLen = 1536;

  /* USER CODE BEGIN MACADDRESS */
  //重新设定MAC地址
  MACAddr[0] = mac_addr[0];
  MACAddr[1] = mac_addr[1];
  MACAddr[2] = mac_addr[2];
  MACAddr[3] = mac_addr[3];
  MACAddr[4] = mac_addr[4];
  MACAddr[5] = mac_addr[5];
  heth.Init.MACAddr = &MACAddr[0];
  /* USER CODE END MACADDRESS */

  hal_eth_init_status = HAL_ETH_Init(&heth);

  memset(&TxConfig, 0 , sizeof(ETH_TxPacketConfig));
  TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
  TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
  TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;

  /* End ETH HAL Init */

  /* Initialize the RX POOL */
  LWIP_MEMPOOL_INIT(RX_POOL);

#if LWIP_ARP || LWIP_ETHERNET

  /* set MAC hardware address length */
  netif->hwaddr_len = ETH_HWADDR_LEN;

  /* set MAC hardware address */
  netif->hwaddr[0] =  heth.Init.MACAddr[0];
  netif->hwaddr[1] =  heth.Init.MACAddr[1];
  netif->hwaddr[2] =  heth.Init.MACAddr[2];
  netif->hwaddr[3] =  heth.Init.MACAddr[3];
  netif->hwaddr[4] =  heth.Init.MACAddr[4];
  netif->hwaddr[5] =  heth.Init.MACAddr[5];

  /* maximum transfer unit */
  netif->mtu = ETH_MAX_PAYLOAD;

  /* Accept broadcast address and ARP traffic */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  #if LWIP_ARP
    netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
  #else
    netif->flags |= NETIF_FLAG_BROADCAST;
  #endif /* LWIP_ARP */

/* USER CODE BEGIN PHY_PRE_CONFIG */

/* USER CODE END PHY_PRE_CONFIG */
  /* Set PHY IO functions */
  LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx);

  /* Initialize the LAN8742 ETH PHY */
  LAN8742_Init(&LAN8742);

  if (hal_eth_init_status == HAL_OK)
  {
  /* Get link state */
  ethernet_link_check_state(netif);
  }
  else
  {
    Error_Handler();
  }
#endif /* LWIP_ARP || LWIP_ETHERNET */

/* USER CODE BEGIN LOW_LEVEL_INIT */

/* USER CODE END LOW_LEVEL_INIT */
}

主函数中添加MX_LWIP_Process();

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_LWIP_Init();
  MX_USART1_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  //网络数据处理
	  MX_LWIP_Process();
  }
  /* USER CODE END 3 */
}

4、单片机作为服务器
新建tcpserver.c和tcpserver.h
tcpserver.c

#include "tcpserver.h"

struct tcp_pcb *server_pcb_modbus = NULL;
struct tcp_pcb *server_pcb_hart = NULL;

uint8_t modbus_rx_data[TCP_DATA_LEN] = {0},hart_rx_data[TCP_DATA_LEN] = {0}; // 接受数据缓存区
uint8_t modbus_tx_data[TCP_DATA_LEN] = {0},hart_tx_data[TCP_DATA_LEN] = {0}; // 发送数据缓存区
uint16_t modbus_rx_len = 0,hart_rx_len = 0;
uint16_t modbus_tx_len = 0,hart_tx_len = 0;

//接收回调函数
static err_t tcp_recv_modbus(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p != NULL)
    {
        /* 更新窗口*/
        tcp_recved(tpcb, p->tot_len); // 读取数据的控制块   得到所有数据的长度
        server_pcb_modbus = tpcb;    // 直接赋值
        memcpy(modbus_rx_data, (int *)p->payload, p->tot_len);//数据复制
        modbus_rx_len = p->tot_len;
		
        modbus_process_tcp();//数据处理程序
		
        pbuf_free(p);
    }
    else if (err == ERR_OK) // 检测到对方主动关闭连接时,也会调用recv函数,此时p为空
    {
        return tcp_close(tpcb);
    }

    return ERR_OK;
}

static err_t tcp_recv_hart(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    if (p != NULL)
    {
        /* 更新窗口*/
        tcp_recved(tpcb, p->tot_len); // 读取数据的控制块   得到所有数据的长度
        server_pcb_hart = tpcb;    // 直接赋值
        memcpy(hart_rx_data, (int *)p->payload, p->tot_len);//数据复制
        hart_rx_len = p->tot_len;
		
		//数据处理程序
		HAL_GPIO_WritePin(HART_RTS_GPIO_Port,HART_RTS_Pin,GPIO_PIN_RESET);
		uart_send(&huart3,hart_rx_data,hart_rx_len);
		
        pbuf_free(p);
    }
    else if (p == NULL) // 检测到对方主动关闭连接时,也会调用recv函数,此时p为空
    {
        return tcp_close(tpcb);
    }
	else if(err != ERR_OK)
	{
		pbuf_free(p);
	}

    return ERR_OK;
}

static err_t tcp_accept_modbus(void *arg, struct tcp_pcb *newpcb, err_t err) // 由于这个函数是*tcp_accept_fn类型的
{
//	/* 确认监听与连接 */
//    tcp_arg(newpcb, mem_calloc(sizeof(struct name), 1));

//确保只有一个客户端连接至服务端,有新的客户端建立连接时,主动断开之前的客户端连接
//避免客户端异常断开导致的客户端连接数超限问题
	if((server_pcb_modbus->local_port == TCP_PORT_MODBUS) &&(server_pcb_modbus->remote_port != newpcb->remote_port))
	{
		tcp_abort(server_pcb_modbus);
	}
	server_pcb_modbus = newpcb;    // 直接赋值
	
	// 当收到数据时,回调用户自己写的tcpecho_recv
    tcp_recv(newpcb, tcp_recv_modbus);  
    
    return ERR_OK;
}

static err_t tcpecho_accept_hart(void *arg, struct tcp_pcb *newpcb, err_t err) // 由于这个函数是*tcp_accept_fn类型的
{
//	/* 确认监听与连接 */
//    tcp_arg(newpcb, mem_calloc(sizeof(struct name), 1));

//确保只有一个客户端连接至服务端,有新的客户端建立连接时,主动断开之前的客户端连接
//避免客户端异常断开导致的客户端连接数超限问题
	if((server_pcb_hart->local_port == TCP_PORT_HART) &&(server_pcb_hart->remote_port != newpcb->remote_port))
	{
		tcp_abort(server_pcb_hart);
	}
	server_pcb_hart = newpcb;    // 直接赋值
		
	// 当收到数据时,回调用户自己写的tcpecho_recv
    tcp_recv(newpcb, tcp_recv_hart);  
    
    return ERR_OK;
}

void tcp_server_init(void)
{
    struct tcp_pcb *server_modbus = NULL;
    struct tcp_pcb *server_hart = NULL;

    /* 创建一路MODBUS  */
    server_modbus = tcp_new();

    /* 绑定TCP控制块 */
    tcp_bind(server_modbus, IP_ADDR_ANY, TCP_PORT_MODBUS);

    /* 进入监听状态 */
    server_modbus = tcp_listen(server_modbus);

    /* 处理连接 注册函数,侦听到连接时被注册的函数被回调 */
    tcp_accept(server_modbus, tcp_accept_modbus); // 侦听到连接后,回调用户编写的tcpecho_accept

    /* 创建一路HART  */
    server_hart = tcp_new();

    /* 绑定TCP控制块 */
    tcp_bind(server_hart, IP_ADDR_ANY, TCP_PORT_HART);

    /* 进入监听状态 */
    server_hart = tcp_listen(server_hart);

    /* 处理连接 注册函数,侦听到连接时被注册的函数被回调 */
    tcp_accept(server_hart, tcpecho_accept_hart); // 侦听到连接后,回调用户编写的tcpecho_accept

}

tcpserver.h

#ifndef _TCPSERVER_H_
#define _TCPSERVER_H_

#include "main.h"
#include "lwip.h"
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>
#include <string.h>
#include "modbus.h"

#define TCP_PORT_MODBUS 5010
#define TCP_PORT_HART 	5020

#define TCP_DATA_LEN  512

extern struct netif gnetif;

extern struct tcp_pcb *server_pcb_modbus;
extern struct tcp_pcb *server_pcb_hart;
extern uint8_t tcp_rx_data[1024]; // 接受数据缓存区
extern uint8_t tcp_tx_data[1024]; // 发送数据缓存区
extern uint8_t modbus_rx_data[TCP_DATA_LEN],hart1_rx_data[TCP_DATA_LEN]; // 接受数据缓存区
extern uint8_t modbus_tx_data[TCP_DATA_LEN],hart1_tx_data[TCP_DATA_LEN]; // 发送数据缓存区
extern uint16_t modbus_rx_len,hart_rx_len;
extern uint16_t modbus_tx_len ,hart_tx_len;

void tcp_server_init(void);

#endif




主函数调用*tcp_server_init()*初始化
5、TCP发送数据
tcp_write() 是 lwIP 协议栈中用于将数据写入 TCP 发送缓冲区的函数,但不会立即触发数据发送。实际发送需后续调用 tcp_output()。

err_t err;
u16_t available = tcp_sndbuf(server_pcb_modbus);

if (available >= len) 
{
    err = tcp_write(server_pcb_modbus, data, len, TCP_WRITE_FLAG_COPY); // 安全拷贝数据
    if (err == ERR_OK) 
    {
        tcp_output(server_pcb_modbus); // 立即触发数据发送
    } else 
    {
        // 处理错误(窗口关闭/内存不足)
    }
} else 
{
    // 等待缓冲区可用或分段发送
}

这部分参考

https://blog.csdn.net/m0_56800366/article/details/143193781

6、IP重设
程序运行过程中可能需重新设定IP信息,可使用以下函数

void lwip_reset_ipaddr(void)
{
	IP4_ADDR(&ipaddr, tcp_addr[0], tcp_addr[1], tcp_addr[2], tcp_addr[3]);
	IP4_ADDR(&netmask, tcp_addr[8], tcp_addr[9] , tcp_addr[10], tcp_addr[11]);
	IP4_ADDR(&gw, tcp_addr[4], tcp_addr[5], tcp_addr[6], tcp_addr[7]);

	netif_set_down(&gnetif);
	
	netif_set_gw(&gnetif, &gw);
	netif_set_netmask(&gnetif, &netmask);
	netif_set_ipaddr(&gnetif, &ipaddr);
	
	netif_set_up(&gnetif);
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐