STM32 + LAN8720A + LWIP无操作系统实现TCP服务器
·
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, ðernetif_init, ðernet_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);
}
更多推荐

所有评论(0)