ESP32使用LAN8720模块进行数据传输
开发板
基于 ESP32-WROOM-32 模组
重要参数
串口芯片:CH340G
ROM:4M
RAM:520 KB
带有专用 DMA 的以太网 MAC 接口,支持 IEEE 1588
引脚说明
| 端口 | 输入 | 输出 | 备注信息 |
|---|---|---|---|
| 3.3V | 3.3V电源输出 | ||
| 5V | 5V电源输出 | ||
| GND | 接地 | ||
| 0 | pulled up | 是 | |
| 1 | TX Pin | 是 | |
| 2 | 是 | 是 | |
| 3 | 是 | RX Pin | |
| 4-5 | 是 | 是 | |
| 6-11 | 否 | 否 | 模组集成SPI flash |
| 12-33 | 是 | 是 | |
| 34-35 | 是 | 否 | 输入引脚 |
| 36(VP) | 是 | 否 | 输入引脚 |
| 39(VN) | 是 | 否 | 输入引脚 |
通信模块
LAN8720 介绍:
LAN8720A 是低功耗的 10/100M 以太网 PHY 层芯片,I/O 引脚电压符合 IEEE802.3 - 2005 标准。它需配合集成 MAC 控制器的 MCU,通过 RMII 接口与以太网 MAC 层通信。芯片内置 10 - BASE - T/100BASE - TX 全双工传输模块,支持 10Mbps 和 100Mbps 速率。能通过自协商与目的主机确定最佳连接方式(速度和双工模式),还具备 HP Auto - MDIX 自动翻转功能,可免换网线实现直连与交叉连接的切换。
引脚使用说明:
PHY 芯片地址设置
- 操作原理:MAC 层通过 SMI 总线对 PHY 内部寄存器读写,SMI 可控制 32 个 PHY 芯片,通过不同地址区分操作对象。
- LAN8720A 配置:通过 PHYAD0 引脚(与 RXER 引脚复用)配置,芯片内部自带下拉电阻,默认值为 0。硬复位结束后,芯片读取该引脚电平作为 SMI 地址,接下拉电阻(或浮空)时 SMI 地址为 0,外接上拉电阻时为 1。
nINT/REFCLKO 引脚功能配置
- 功能选择:该引脚可作中断输出或参考时钟输出,由 LED2(nINTSEL)引脚决定。
- 配置方式:芯片复位后读取 LED2 引脚值,接上拉电阻(或浮空,内置上拉电阻)时,nINT/REFCLKO 作为中断输出引脚(REF_CLK IN 模式);接下拉电阻时,作为参考时钟输出引脚(REF_CLK OUT 模式)。
- 时钟要求:REF_CLK IN 模式下,外部需为 LAN8720A 的 XTAL1(CLKIN)引脚提供 50MHz 参考时钟;REF_CLK OUT 模式下,LAN8720A 可外接 25MHz 石英晶振,内部倍频到 50MHz 后通过 REFCLKO 引脚输出给 MAC 控制器。
RMII接口
- 作用是达成以太网数据在 MCU 与 PHY 芯片之间的双向传输,为数据在两者间的交互提供通道,保障数据能顺利在 MCU 和 PHY 芯片之间流动。
SMI站点管理接口
- 其功能是让 MCU 对 PHY 芯片内部的寄存器进行读(R)和写(W)操作,通过该接口,MCU 可对 PHY 芯片内部的各种参数和状态进行配置与查询,实现对 PHY 芯片的管理与控制。
nINT/REFCLKO引脚有两种功能模式,其模式选择由LED2(nINTSEL)引脚决定:
- 中断输出模式(REF_CLK IN模式) :如果
LED2引脚接上拉电阻(或者浮空,因为芯片内置上拉电阻),在芯片正常工作后,nINT/REFCLKO引脚将作为中断输出引脚。由于此时处于REF_CLK IN模式,外部需要提供50MHz参考时钟给LAN8720A的XTAL1(CLKIN)引脚。而模块上自带的50M晶振正好可以满足这个需求,为LAN8720提供稳定的时钟源。 - 参考时钟输出模式(REF_CLK OUT模式) :当
LED2引脚接下拉电阻时,正常工作后,nINT/REFCLKO引脚将作为参考时钟输出。不过在这种模式下,通常是外接25MHz石英晶振,通过内部倍频到50MHz,然后通过REFCLKO引脚输出50MHz参考时钟给MAC控制器。虽然模块自带50M晶振,但在该模式下可能不会使用这个晶振的输出作为REFCLKO的输出源(一般还是按设计用内部倍频方式),而是将其闲置,除非有特殊的电路设计和配置
- 外部直接提供50M时钟到REF_CLK,比如有源晶振,同时驱动MCU和PHY芯片。
- PHY芯片外接25M晶振,芯片内部倍频到50M,然后通过REF_CLK输出到MCU,(最常用的方案)。
- 由MCU配置引脚输出50M的时钟,然后将这个引脚接到REF_CLK ,同时驱动MCU和PHY芯片。
方案说明
方式一:MCU 集成 MAC 控制器,外接 PHY 芯片(本次方案)
- 原理:MCU 内部集成 MAC 控制器(ETH),通过介质独立接口(MII)或简化介质独立接口(RMII)外挂一颗 PHY 芯片,利用站点管理接口(SMI)与外部 PHY 通讯,实现网口输出。
- 适用场景:适用于内部有 MAC 控制器的 MCU,可借助外部 PHY 芯片完成物理层功能。
- 示例芯片:LAN8720
- 协议实现:MAC + PHY 完成 TCP/IP 五层模型的物理层和数据链路层,网络层、传输层、应用层需在 MCU 中使用开源 TCP/IP 协议栈(如 uIP、LwIP)实现。
方式二:MCU 无 MAC 控制器,外接 MAC 和 PHY 芯片
- 原理:对于内部没有 MAC 控制器的 MCU,需外接 MAC 芯片和 PHY 芯片,这两颗芯片可以分立,也可集成在一颗芯片中。
- 适用场景:针对没有集成 MAC 控制器的 MCU,补充其网络通信相关功能。
- 示例芯片:ENC28J60
- 协议实现:同方式一,MAC + PHY 负责物理层和数据链路层,其他高层协议在 MCU 用开源协议栈实现。
方式三:MCU 通过特定总线外挂集成 TCP/IP 协议的芯片
- 原理:MCU 通过 SPI、8080 并行总线、串口等接口外挂一颗已经集成了 TCP/IP 协议的芯片,实现网口输出。
- 适用场景:希望简化 MCU 开发,降低协议实现复杂度的场景。
- 示例芯片:W5500
- 协议实现:使用硬件 TCP/IP 协议栈,MCU 无需实现 TCP/IP 协议,仅需实现应用层代码,但存在 socket 个数有限、通信数据量不宜太大的缺点。
方式四:MCU 内部集成 PHY
- 原理:部分 MCU 内部集成了 PHY,无需外挂额外的网络芯片即可实现网口输出。
- 适用场景:对硬件体积、成本有严格要求,且对网络性能要求不是特别高的场景。
- 示例芯片:沁恒 CH32V307(自带 10 兆以太网 PHY)
其他:高性能芯片集成 PHY 或通过 SDIO 外挂通信模组
- 原理:高级嵌入式芯片内部集成 PHY 以实现网络功能,或者通过 SDIO 接口外挂通信模组来完成网口输出。
- 适用场景:适用于对网络性能、功能扩展性有较高要求的复杂应用场景。
传感器
DS18B20
电气参数
| 参数类别 | 参数详情 |
|---|---|
| 工作电压 | 3V - 5.5V |
| 测温范围 | -55°C - +125°C(-67°F - +257°F) |
| 精度 | -55°C - +125°C 范围内:±2°C -10°C - +85°C 范围内:±0.5°C |
| 模块尺寸 | 21mm × 10mm |
开发环境
Arduino IED 下载地址
https://www.arduino.cc/en/software/
开发板管理器地址
https://dl.espressif.com/dl/package_esp32_index.json
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
串口驱动官方资料地址
https://www.wch.cn/downloads/CH341SER_EXE.html
传感器资料地址
https://www.yourcee.com/productinfo/884740.html
接线说明
RMII 接口引脚连接
ESP32与LAN8720间RMII PHY的连接定义,用于以太网通信 ,需按照定义连接
| GPIO | RMII Signal | ESP32 EMAC Function | 说明 |
|---|---|---|---|
| GPIO21 | TX_EN | EMAC_TX_EN | 用于控制数据发送使能 |
| GPIO19 | TX0 | EMAC_TXD0 | 发送数据位 0 |
| GPIO22 | TX1 | EMAC_TXD1 | 发送数据位 1 |
| GPIO25 | RX0 | EMAC_RXD0 | 接收数据位 0 |
| GPIO26 | RX1 | EMAC_RXD1 | 接收数据位 1 |
| GPIO27 | CRS_DV | EMAC_RX_DRV | 载波侦听/接收数据有效信号 |
SMI 接口引脚连接
SMI 接口用于 ESP32 读写 LAN8720 的寄存器,相关引脚理论上可连接到 ESP32 任何支持对应功能的引脚,默认连接如下:
| GPIO | RMII Signal | 说明1 | 说明2 |
|---|---|---|---|
| GPIO23 | MDC | 输出至 LAN8720 | 也可使用 ESP32 其他输出脚 |
| GPIO18 | MDIO | 双向通讯 | 也可使用 ESP32 其他双向脚 |
Arduino 程序
参考资料使用Arduino开发ESP32(06):Ethernet的使用(基于LAN8720)_arduino lan8720-CSDN博客
/**
******************************************************************************
* @file eth_temp_server.ino
* @author mlm
* @version V1.0
* @date 2025-08-12
* @brief ESP32以太网温度监测
* @details 基于LAN8720和DS18B20的UDP温度服务器
* 功能特性:
* - 静态IP网络配置(192.168.4.1)
* - 12位高精度温度采集
* - UDP协议通信(端口2333)
* - 自动检测传感器断线
******************************************************************************
* @attention
*
* 硬件配置:
* - 开发板:ESP32-WROOM-32(ESP32-devkitc-32E)
* - PHY芯片:LAN8720
* - 温度传感器:DS18B20
*
* 引脚连接:
* | ESP32引脚 | 外设引脚 | 功能说明 |
* |----------|----------|-----------------|
* | GPIO4 | DS18B20 | 单总线数据线 |
* | GPIO23 | LAN8720 | MDC时钟线 |
* | GPIO18 | LAN8720 | MDIO数据线 |
* | GPIO17 | LAN8720 | 50MHz时钟输出 |
*
* 通信协议:
* - 请求命令:"gettemp"(不含引号)
* - 响应格式:"Temperature: xx.xx ℃"
*
* 更新日志:
* - V1.0 2025-08-12 初始版本
* • 实现基础温度采集功能
* • 完成UDP通信框架
*
******************************************************************************
*/
#include <ETH.h>
#include <WiFiUdp.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// 以太网配置
#define ETH_PHY_ADDR 1
#define ETH_POWER_PIN -1
#define ETH_MDC_PIN 23
#define ETH_MDIO_PIN 18
#define ETH_PHY_TYPE ETH_PHY_LAN8720
#define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT
// DS18B20 配置
#define DSB_PIN 4
OneWire oneWire(DSB_PIN);
DallasTemperature sensors(&oneWire);
// UDP 配置
WiFiUDP Udp;
unsigned int localUdpPort = 2333;
// 静态IP
IPAddress localIP(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress gateway(192, 168, 4, 1);
// 读取温度
float readTemperature() {
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
return (tempC != DEVICE_DISCONNECTED_C) ? tempC : -999.9;
}
// 发送UDP回复(带调试信息)
void sendUdpResponse(float tempC, IPAddress remoteIP, unsigned int remotePort) {
String reply = "Temperature: " + String(tempC) + " ℃";
Serial.print("[UDP] Sending: ");
Serial.println(reply);
if (!Udp.beginPacket(remoteIP, remotePort)) {
Serial.println("[UDP] Error: beginPacket failed");
return;
}
Udp.print(reply);
if (!Udp.endPacket()) {
Serial.println("[UDP] Error: endPacket failed");
} else {
Serial.println("[UDP] Reply sent successfully");
}
}
void setup() {
Serial.begin(115200);
Serial.println("\nInitializing...");
// 初始化以太网
ETH.begin(ETH_PHY_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_PHY_TYPE, ETH_CLK_MODE);
ETH.config(localIP, gateway, subnet);
while (!ETH.localIP()) {
delay(500);
Serial.print(".");
}
Serial.println("\nEthernet Connected");
Serial.print("IP: ");
Serial.println(ETH.localIP());
// 初始化温度传感器
sensors.begin();
sensors.setResolution(12);
Serial.println("DS18B20 Ready (12-bit resolution)");
// 启动UDP
Udp.begin(localUdpPort);
Serial.println("UDP Server Started");
}
void loop() {
float tempC = readTemperature();
Serial.print(tempC);
// 检查UDP数据
int packetSize = Udp.parsePacket();
if (packetSize > 0) {
char buf[packetSize + 1];
Udp.read(buf, packetSize);
buf[packetSize] = '\0';
// 打印接收到的原始数据(调试用)
Serial.print("\n[UDP] Received: ");
Serial.write(buf, packetSize);
Serial.println();
// 去除首尾空白字符后比较
String received = String(buf);
received.trim();
if (received == "gettemp") {
Serial.println("[UDP] Valid 'gettemp' request");
sendUdpResponse(tempC, Udp.remoteIP(), Udp.remotePort());
}
}
delay(1000);
}
2025-10-05 补充一下:

过程解释:根据上方代码,发送 gettemp ESP32会返回温度数据,建立的是UDP连接,存在丢包的情况。
[
{
"id": "6cbafc4f6f60f953",
"type": "udp in",
"z": "udp-flow",
"name": "",
"iface": "",
"port": "2335",
"ipv": "udp4",
"multicast": "false",
"group": "",
"datatype": "utf8",
"x": 260,
"y": 200,
"wires": [
[
"c2cb7f5b86c2e2ce",
"7cd4ebefeb1cf754"
]
]
},
{
"id": "c2cb7f5b86c2e2ce",
"type": "debug",
"z": "udp-flow",
"name": "debug 3",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 480,
"y": 200,
"wires": []
},
{
"id": "6e8aeb1b539e8770",
"type": "udp out",
"z": "udp-flow",
"name": "",
"addr": "192.168.4.1",
"iface": "",
"port": "2332",
"ipv": "udp4",
"outport": "2335",
"base64": false,
"multicast": "false",
"x": 560,
"y": 120,
"wires": []
},
{
"id": "7a02b102c7c5b8df",
"type": "inject",
"z": "udp-flow",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "10",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "gettemp",
"payloadType": "str",
"x": 280,
"y": 120,
"wires": [
[
"6e8aeb1b539e8770",
"789410be31ca6ca8"
]
]
},
{
"id": "789410be31ca6ca8",
"type": "debug",
"z": "udp-flow",
"name": "debug 4",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 540,
"y": 60,
"wires": []
},
{
"id": "7cd4ebefeb1cf754",
"type": "function",
"z": "udp-flow",
"name": "function 2",
"func": "// 提取温度值(示例数据: \"Temperature: 28.25 ℃\")\nconst match = msg.payload.match(/Temperature: (\\d+\\.\\d+) ℃/);\nif (match) {\n msg.payload = `解析成功: ${match[1]} ℃`;\n} else {\n msg.payload = \"解析失败: 数据格式不符\";\n}\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 500,
"y": 260,
"wires": [
[
"d385b5de0cbc0eec"
]
]
},
{
"id": "d385b5de0cbc0eec",
"type": "debug",
"z": "udp-flow",
"name": "debug 5",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 660,
"y": 260,
"wires": []
}
]

浙公网安备 33010602011771号