环境说明:
开发板:某宝买的,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)高级配置
看下图,此处的配置就是根据实际的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
复制代码
|