STM32F407 + LAN8720A + LWIP 实现TCP服务器

STM32F407 + LAN8720A + LWIP 实现TCP服务器STM32F407+LAN8720A+LWIP实现TCP客户端环境说明:开发板:某宝买的,STM32F407IGSTM32CUBEMX5.6HALLibVersion1.25(一)配置时钟(二)配置调试串口(三)配置以太网ETH(1)基础配置顺序依次说明:LAN8720A使用的是RMII接口进行配置寄存器自动重连使能MAC地址LAN8720A的物理地址(类似IIC的从设备地址),可配置为0或者1,由LAN8720A的RXER/PHYAD0引脚控制

大家好,又见面了,我是你们的朋友全栈君。

STM32F407 + LAN8720A + LWIP 实现TCP客户端


环境说明:

  • 开发板:某宝买的,STM32F407IG
  • STM32CUBEMX5.6
  • HAL Lib Version 1.25

(一)配置时钟

在这里插入图片描述


(二)配置调试串口

在这里插入图片描述


(三)配置以太网ETH

(1)基础配置

在这里插入图片描述
顺序依次说明:

  • LAN8720A使用的是RMII接口进行配置寄存器
  • 自动重连使能
  • MAC地址
  • LAN8720A的物理地址(类似IIC的从设备地址),0或者1,LAN8720A上电后会读取RXER/PHYAD0引脚状态以此来确定设备地址,这里需要根据你自己实际的原理图进行配置,我的原理图是该引脚是悬空的,所以默认就是0。
    在这里插入图片描述
  • 接收模式:可选轮询和中断,我选择了轮询模式。(注:在STM32CUBEMX中如果开启了LWIP那么只能选择轮询模式,实际上是可以使用中断方式的,不过需要自己移植修改lwip协议栈,课参考正点原子)
  • 校验:可选软件和硬件,我选择了由硬件去校验

除此之外还有一个复位引脚ETH_RST,拉低是复位LAN8720A,根据你实际的原理图连线配置该IO为复用输出功能即可。

(2)高级配置

LAN8720A数据手册pdf下载:https://www.alldatasheet.com/datasheet-pdf/pdf/516623/SMSC/LAN8720A.html
在这里插入图片描述

         ~~~~~~~~         看下图,此处的配置就是根据实际的PHY芯片寄存器进行配置了,默认的是LAN8742A,而我们使用的是LAN8720A,所以需要更改为 user PHY,配置项默认即可。在这里插入图片描述
对于默认配置,我们将默认配置与LAN8720A的数据手册进行对比然后检查是否正确,以PHY Reset这一项为例,默认值是0x8000,去查看数据手册:
在这里插入图片描述
由上图的基本控制寄存器表可知该寄存器偏移地址为0,大小是16位,第15位是软件复位控制,=1是复位,默认为0,那么如果想要复位LAN8720A就需要将该寄存器的第15位置一,也就是0x8000,对比STM32CUBE的默认配置发现一致,其他配置项同理也是这么检查,检查完毕后发现默认配置是OK的。


配置LWIP协议栈

在这里插入图片描述
如上图,需要开启状态改变回调函数和连接状态改变回调函数,否则无法实现网线的热拔插。


工程代码修改

  • 文件ethernetif.c中找到函数low_level_init函数,添加复位LAN8720A代码:
    在这里插入图片描述
  • 在main函数的主循环中调用函数MX_LWIP_Process
  • 修改MX_LWIP_Process函数,在其中加入:
    在这里插入图片描述
    这个函数的作用是检测当前网线的连接状态,如果状态发生改变(例如网线被拔插了一下),那么就会调用回调函数ethernetif_update_config,看函数名就知道这是更新配置,而这个函数尾部又调用了函数ethernetif_notify_conn_changed,看函数名可知道函数作用是通知连接状态改变,所以我们就更改此函数来达到热拔插自动重连的目的。
    在这里插入图片描述

到此,连接上网线ping测试一下即可。


(四)TCP服务器代码

下面代码的流程是:接收来自客户端的数据->将数据从lwip中拷贝出来->发回去。

#if 1

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "lwip/tcp.h"
#include "lwip/err.h"
#include "lwip/memp.h"
#include "lwip/inet.h"


/ 回调函数控制宏 
#define USE_ERROR_CALLBACK 1
#define USE_SENT_CALLBACK 0
#define USE_POLL_CALLBACK 0



/ 调试信息输出 //
#define DEBUG

#ifdef DEBUG
#define debug(fmt, ...) do{printf(fmt, ##__VA_ARGS__);}while(0)
#else
#define debug(fmt, ...) do{;}while(0)
#endif



/ tcp接收发送缓存 ///
#define TCP_RX_LEN 8192
uint8_t TCP_RX_BUF[TCP_RX_LEN];
volatile uint16_t TCP_RX_STA = 0;  /* bit15:有无数据标志位 bit14-0:数据量计数 */



/ TCP结构句柄 ///
struct tcp_pcb* tcppcb = NULL;



/ 本地函数定义 ///
static void tcp_server_disconnect(struct tcp_pcb *tpcb);
static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len);



/ 私有函数实现 ///

#if (USE_ERROR_CALLBACK == 1)
static void error_callback(void *arg, err_t err)
{ 
   
	debug("\r\n error_callback:%d.", err);
}
#endif

#if (USE_ERROR_CALLBACK == 1)
static void error_callback(void *arg, err_t err)
{ 
   
	debug("\r\n error_callback:%d.", err);
	switch(err)
	{ 
   
	/* PC上位机如果正常运行中闪退或者不良退出会出现这个错误,此时服务器需要释放掉连接 */
	case ERR_RST:
	tcp_server_disconnect(tcppcb);
	break;
	default:break;
	}
}

#endif

static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{ 
   
	if (p == NULL) /* 接收到空包表示对方断开连接 */
	{ 
   
		tcp_server_disconnect(tpcb);
		err = ERR_CLSD;
	}
	else if (err != ERR_OK) /* 收到非空包但是出现错误 */
	{ 
   
		pbuf_free(p);
	}
	else /* 接收数据正常,遍历pbuf拷贝出接收到的数据 */
	{ 
   
		struct pbuf* it = p;

		if ((TCP_RX_STA & 0x8000) == 0) /* 当前缓存为空 */
		{ 
   
			for (it = p; it != NULL; it = it->next)
			{ 
   
				if (TCP_RX_STA + it->len > TCP_RX_LEN) /* 缓存满了 */
					break;
				memcpy(TCP_RX_BUF + TCP_RX_STA, it->payload, it->len);  /* 将接收到的数据拷贝到自己的缓存中 */
				TCP_RX_STA += it->len;
			}
			TCP_RX_STA |= 0x8000;    /* 标记有数据收到 */
		}
		tcp_recved(tpcb, p->tot_len); /* 滑动TCP窗口 */
		pbuf_free(p); /* 释放pbuf */
	}
	return err;
}


static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{ 
   
	if (tcppcb == NULL)
	{ 
   
		if (err == ERR_OK)
		{ 
   
			tcppcb = newpcb;
			tcp_arg(newpcb, NULL);
			tcp_recv(newpcb, recv_callback);

#if (USE_ERROR_CALLBACK == 1)
			tcp_err(newpcb, error_callback);
#endif

#if (USE_SENT_CALLBACK == 1)
			tcp_sent(newpcb, sent_callback);
#endif

#if (USE_POLL_CALLBACK == 1)
			tcp_poll(newpcb, poll_callback, 1);
#endif
			debug("\r\n %s:%d connect.", inet_ntoa(newpcb->remote_ip), newpcb->remote_port);
		}
		else
		{ 
   
			tcp_server_disconnect(newpcb);
		}
	}
	else
	{ 
   
		tcp_abort(newpcb);
		debug("\r\n already connected. ");
	}
	return err;
}

static void tcp_server_disconnect(struct tcp_pcb *tpcb)
{ 
   
	tcp_arg(tpcb, NULL);
	tcp_recv(tpcb, NULL);

#if (USE_SENT_CALLBACK == 1)
	tcp_sent(tpcb, NULL);
#endif

#if (USE_POLL_CALLBACK == 1)
	tcp_poll(tpcb, NULL, 0);
#endif

#if (USE_ERROR_CALLBACK == 1)
	tcp_err(tpcb, NULL);
#endif

	tcp_abort(tpcb); /* 关闭连接并释放tpcb控制块 */
	tcppcb = NULL;
	debug("\r\n disconnected.");
}

static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len)
{ 
   
	uint32_t nwrite = 0, total = 0;
	const uint8_t* p = (const uint8_t *) buf;
	err_t err = ERR_OK;
	if (!tpcb)
		return 0;
	while ((err == ERR_OK) && (len != 0) && (tcp_sndbuf(tpcb) > 0))
	{ 
   
		nwrite = tcp_sndbuf(tpcb) >= len ? len : tcp_sndbuf(tpcb);
		err = tcp_write(tpcb, p, nwrite, 1);
		if (err == ERR_OK)
		{ 
   
			len -= nwrite;
			total += nwrite;
			p += nwrite;
		}
		tcp_output(tpcb);
	}
	return total;
}

/ 导出以下函数供外部调用 ///

//extern int tcp_server_start(uint16_t port);
//extern int user_senddata(const void* buf,uint32_t len);
//extern int transfer_data();


/** * 启动TCP服务器 * @param port 本地端口号 * @return 成功返回0 */
int tcp_server_start(uint16_t port)
{ 
   
	int ret = 0;
	struct tcp_pcb* pcb = NULL;
	err_t err = ERR_OK;

	/* create new TCP PCB structure */
	pcb = tcp_new();
	if (!pcb)
	{ 
   
		debug("Error creating PCB. Out of Memory\n\r");
		ret = -1;
		goto __exit;
	}

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ADDR_ANY, port);
	if (err != ERR_OK)
	{ 
   
		debug("Unable to bind to port %d: err = %d\n\r", port, err);
		ret = -2;
		goto __exit;
	}

	/* listen for connections */
	pcb = tcp_listen(pcb);
	if (!pcb)
	{ 
   
		debug("Out of memory while tcp_listen\n\r");
		ret = -3;
	}

	/* specify callback to use for incoming connections */
	tcp_accept(pcb, accept_callback);

	/* create success */
	debug("TCP echo server started @ port %d\n\r", port);
	return ret;

__exit:
	if (pcb)
		memp_free(MEMP_TCP_PCB, pcb);
	return ret;
}

/** * TCP发送数据 * @param buf 待发送的数据 * @param len 数据长度 * @return 返回实际发送的字节数 */
int user_senddata(const void* buf, uint32_t len)
{ 
   
	return tcp_server_send(tcppcb, buf, len);
}

/** * 轮询函数,放置于main函数的while死循环中 * @return 无 */
int transfer_data()
{ 
   
	uint32_t nsend = 0;
	if (tcppcb != NULL && tcppcb->state == ESTABLISHED) /* 连接有效 */
	{ 
   
		if (TCP_RX_STA & 0x8000)   /* 有数据收到 */
		{ 
   
			nsend = user_senddata(TCP_RX_BUF, TCP_RX_STA & 0x7FFF);    /* 将接收到的数据发回去 */
			TCP_RX_STA = 0;
			debug("\r\n send %d bytes success.", nsend);
		}
	}
	return 0;
}

#endif

以上代码已测试!


ends。。。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/150155.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 经纬度与距离的换算关系图_经纬度对应距离

    经纬度与距离的换算关系图_经纬度对应距离一、经纬度距离换算a)在纬度相等的情况下:经度每隔0.00001度,距离相差约1米;每隔0.0001度,距离相差约10米;每隔0.001度,距离相差约100米;每隔0.01度,距离相差约1000米;每隔0.1度,距离相差约10000米。b)在经度相等的情况下:纬度每隔0.00001度,距离相差约1.1米;每隔0.0001度,距离相差约11米;…

    2022年9月2日
    2
  • dede从www跟目录迁移,网站空间

    dede从www跟目录迁移,网站空间

    2021年9月20日
    47
  • idea202112激活码永久【永久激活】

    (idea202112激活码永久)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月21日
    96
  • MRT(MODIS Reprojection Tool) 下载及安装教程[通俗易懂]

    MRT(MODIS Reprojection Tool) 下载及安装教程[通俗易懂]大家下载MODIS数据的时候,大多是hdf的格式数据。HDF数据包括11个波段的数据(如下图),假如想要其中一个波段数据,我们需要批量提取,这时就要用到NASA提供的MODISReprojectionTool,此工具虽不能实现全自动的批量提取,但是可以实现按月进行数据的提取及拼接,官网提供了Linux、macos、windows的版本,本文以windows环境为例,说明MRT软件的安装。…

    2022年5月29日
    44
  • 缺陷管理工具-QC篇「建议收藏」

    缺陷管理工具-QC篇「建议收藏」缺陷管理工具-QC篇一、QC介绍:QC的全称Qualitycenter,质量中心的意思,它是一款缺陷管理工具,可以组织和管理一个项目所有的测试阶段,如:从需求-用例编写-用例执行-提交缺陷-回归测试等。QC的背景:原属于Mercury公司,这是一家软件测试工具开发商,除了研发了QC之外,比较出名的工具有:LoadRunner—-性能测试工具,QTP—-自动化测试工具。后来M…

    2025年7月23日
    1
  • 安装OpenStack Identity Service

    安装OpenStack Identity Service

    2021年8月28日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号