文章目录
-
- 一、开发环境与硬件准备
-
- [1.1 开发环境搭建](#1.1 开发环境搭建)
- [1.2 硬件资源说明](#1.2 硬件资源说明)
- [1.3 硬件接线表(RMII接口)](#1.3 硬件接线表(RMII接口))
- 二、开发流程总览
- 三、STM32CubeMX配置步骤
-
- [3.1 新建工程](#3.1 新建工程)
- [3.2 时钟配置](#3.2 时钟配置)
- [3.3 引脚配置](#3.3 引脚配置)
- [3.4 外设配置](#3.4 外设配置)
- [3.5 LwIP协议栈配置](#3.5 LwIP协议栈配置)
- [3.6 生成代码](#3.6 生成代码)
- 四、底层驱动编写
-
- [4.1 LAN8720A复位驱动(文件名:lan8720a_reset.c)](#4.1 LAN8720A复位驱动(文件名:lan8720a_reset.c))
- [4.2 LAN8720A复位驱动头文件(文件名:lan8720a_reset.h)](#4.2 LAN8720A复位驱动头文件(文件名:lan8720a_reset.h))
- [4.3 ETH底层驱动扩展(文件名:eth_driver_ext.c)](#4.3 ETH底层驱动扩展(文件名:eth_driver_ext.c))
- [4.4 ETH底层驱动扩展头文件(文件名:eth_driver_ext.h)](#4.4 ETH底层驱动扩展头文件(文件名:eth_driver_ext.h))
- 五、LwIP协议栈移植与配置
-
- [5.1 LwIP核心配置(文件名:lwipopts.h)](#5.1 LwIP核心配置(文件名:lwipopts.h))
- [5.2 LwIP初始化(文件名:lwip_init.c)](#5.2 LwIP初始化(文件名:lwip_init.c))
- [5.3 LwIP初始化头文件(文件名:lwip_init.h)](#5.3 LwIP初始化头文件(文件名:lwip_init.h))
- 六、TCP应用层开发
-
- [6.1 TCP服务器端(文件名:tcp_server.c)](#6.1 TCP服务器端(文件名:tcp_server.c))
- [6.2 TCP服务器端头文件(文件名:tcp_server.h)](#6.2 TCP服务器端头文件(文件名:tcp_server.h))
- 七、主函数整合(文件名:main.c)
- 八、编译下载与测试
-
- [8.1 编译代码](#8.1 编译代码)
- [8.2 下载代码](#8.2 下载代码)
- [8.3 网络测试](#8.3 网络测试)
- 九、常见问题排查
-
- [9.1 硬件问题](#9.1 硬件问题)
- [9.2 软件问题](#9.2 软件问题)
- 总结
一、开发环境与硬件准备
1.1 开发环境搭建
在开始实战前,需完成以下环境配置,确保代码能正常编译、下载和调试:
- 编译器:MDK-ARM (Keil uVision5) V5.38 及以上版本
- 固件库:STM32F4xx Standard Peripheral Library (V1.8.0) 或 STM32CubeF4 (V1.27.0)
- 调试器:J-Link / ST-Link V2
- LwIP版本:LwIP 2.1.2(STM32CubeMX集成版本)
- 驱动库:LAN8720A 底层驱动(无需单独下载,集成在LwIP适配层)
1.2 硬件资源说明
| 硬件模块 | 核心参数 | 连接关系 |
|---|---|---|
| STM32F407ZGT6 | 主频168MHz,带ETH MAC控制器 | 主控芯片,连接LAN8720A和调试器 |
| LAN8720A | 10/100M自适应以太网物理层芯片 | 通过RMII接口与STM32F407连接 |
| 外部晶振 | 25MHz(ETH时钟源) | 为LAN8720A提供时钟 |
| 电源 | 3.3V(LAN8720A)、5V(外设供电) | 稳定供电保证通信可靠性 |
1.3 硬件接线表(RMII接口)
| STM32F407引脚 | LAN8720A引脚 | 功能说明 |
|---|---|---|
| PA1 | RMII_RXDV | 接收数据有效信号 |
| PA2 | RMII_TXD0 | 发送数据0 |
| PA3 | RMII_TXD1 | 发送数据1 |
| PA7 | RMII_CRS_DV | 载波侦听/接收有效 |
| PB11 | RMII_TX_EN | 发送使能 |
| PC4 | RMII_RXD0 | 接收数据0 |
| PC5 | RMII_RXD1 | 接收数据1 |
| PG11 | RMII_MDC | 管理数据时钟 |
| PG13 | RMII_MDIO | 管理数据输入/输出 |
| PG0 | LAN8720A_RST | LAN8720A复位引脚 |
| 25MHz晶振 | XTAL_IN/OUT | LAN8720A时钟输入 |
二、开发流程总览
以下流程图清晰展示从环境配置到最终通信测试的完整流程,深色底+白色字体保证可读性:
否
是
环境搭建
STM32CubeMX配置
LAN8720A底层驱动编写
LwIP协议栈移植
TCP/IP应用层开发
代码编译下载
网络调试工具测试
通信成功?
排查硬件/代码问题
功能扩展/优化
三、STM32CubeMX配置步骤
3.1 新建工程
- 打开STM32CubeMX,选择
File -> New Project,搜索STM32F407ZGT6并选中 - 选择
Start Project进入配置界面
3.2 时钟配置
- 进入
Clock Configuration,设置外部高速时钟(HSE)为25MHz - 配置系统时钟(SYSCLK)为168MHz,HCLK=168MHz,PCLK2=84MHz(ETH时钟依赖PCLK2)
- ETH MAC时钟配置:
ETH_CLK= PCLK2 (84MHz) / 2 = 42MHz(满足RMII接口时钟要求)
3.3 引脚配置
- 进入
Pinout & Configuration,依次配置1.3节中所有RMII接口引脚,模式均为Alternate Function Mode - 配置LAN8720A复位引脚(PG0)为
GPIO_Output模式 - 为所有ETH相关引脚分配复用功能(AF11):
- PA1/PA2/PA3/PA7: AF11
- PB11: AF11
- PC4/PC5: AF11
- PG11/PG13: AF11
3.4 外设配置
- 启用ETH外设:
Connectivity -> ETH,选择RMII模式 - 关闭
ETH_PTP(无需精确时间协议) - 配置NVIC:开启ETH全局中断,优先级设置为中等(如抢占优先级2,子优先级0)
3.5 LwIP协议栈配置
- 进入
Middleware -> LwIP,启用LwIP协议栈 - 选择
TCP和UDP协议(按需选择,本文以TCP为例) - IP地址配置:
- 静态IP:192.168.1.100
- 子网掩码:255.255.255.0
- 网关:192.168.1.1
- 关闭DHCP(新手先使用静态IP,降低复杂度)
- 配置LwIP内存池:MEM_SIZE=16384,MEMP_NUM_TCP_PCB=5
3.6 生成代码
- 进入
Project Manager,设置工程名(如STM32F407_LAN8720A_LwIP),选择保存路径 - 工具链选择
MDK-ARM v5 - 点击
GENERATE CODE生成工程,完成后用Keil打开
四、底层驱动编写
4.1 LAN8720A复位驱动(文件名:lan8720a_reset.c)
c
#include "lan8720a_reset.h"
#include "gpio.h"
#include "delay.h" // 需自行实现简单的延时函数
// LAN8720A复位引脚定义(PG0)
#define LAN8720A_RST_PIN GPIO_PIN_0
#define LAN8720A_RST_PORT GPIOG
/**
* @brief LAN8720A硬件复位
* @note 复位脉冲宽度需满足芯片手册要求(至少100us)
* @param 无
* @retval 无
*/
void LAN8720A_Reset(void)
{
// 拉低复位引脚
HAL_GPIO_WritePin(LAN8720A_RST_PORT, LAN8720A_RST_PIN, GPIO_PIN_RESET);
// 延时1ms,确保复位完成
HAL_Delay(1);
// 释放复位
HAL_GPIO_WritePin(LAN8720A_RST_PORT, LAN8720A_RST_PIN, GPIO_PIN_SET);
// 等待芯片初始化(至少10ms)
HAL_Delay(10);
}
/**
* @brief LAN8720A初始化
* @note 复位后检测芯片是否正常
* @param 无
* @retval 0:成功 其他:失败
*/
uint8_t LAN8720A_Init(void)
{
// 复位LAN8720A
LAN8720A_Reset();
// 简单检测:读取LAN8720A的PHY ID(可选,新手可跳过)
uint16_t phy_id = ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_ID1);
if((phy_id & 0xFFF0) != 0x0070) // LAN8720A的PHY ID高位为0x0070
{
return 1; // 检测失败
}
return 0; // 初始化成功
}
4.2 LAN8720A复位驱动头文件(文件名:lan8720a_reset.h)
c
#ifndef __LAN8720A_RESET_H
#define __LAN8720A_RESET_H
#include "stm32f4xx_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
// LAN8720A PHY地址(默认0x00)
#define ETH_PHY_ADDRESS 0x00
// PHY寄存器地址定义
#define PHY_ID1 0x02
// 函数声明
void LAN8720A_Reset(void);
uint8_t LAN8720A_Init(void);
#ifdef __cplusplus
}
#endif
#endif /* __LAN8720A_RESET_H */
4.3 ETH底层驱动扩展(文件名:eth_driver_ext.c)
c
#include "eth_driver_ext.h"
#include "lan8720a_reset.h"
/**
* @brief 读取PHY寄存器
* @note 底层ETH驱动扩展函数,用于访问LAN8720A寄存器
* @param phy_addr: PHY地址
* @param reg_addr: 寄存器地址
* @retval 寄存器值
*/
uint16_t ETH_ReadPHYRegister(uint8_t phy_addr, uint8_t reg_addr)
{
uint16_t reg_value = 0;
HAL_ETH_ReadPHYRegister(&heth, phy_addr, reg_addr, ®_value);
return reg_value;
}
/**
* @brief 写入PHY寄存器
* @param phy_addr: PHY地址
* @param reg_addr: 寄存器地址
* @param reg_value: 要写入的值
* @retval 无
*/
void ETH_WritePHYRegister(uint8_t phy_addr, uint8_t reg_addr, uint16_t reg_value)
{
HAL_ETH_WritePHYRegister(&heth, phy_addr, reg_addr, reg_value);
}
/**
* @brief ETH外设初始化扩展
* @note 包含LAN8720A初始化和ETH参数配置
* @param 无
* @retval HAL状态
*/
HAL_StatusTypeDef ETH_Init_Ext(void)
{
// 初始化LAN8720A
if(LAN8720A_Init() != 0)
{
return HAL_ERROR;
}
// 配置ETH工作模式(100M全双工)
ETH_WritePHYRegister(ETH_PHY_ADDRESS, 0x00, 0x2100);
return HAL_OK;
}
4.4 ETH底层驱动扩展头文件(文件名:eth_driver_ext.h)
c
#ifndef __ETH_DRIVER_EXT_H
#define __ETH_DRIVER_EXT_H
#include "stm32f4xx_hal.h"
#include "eth.h"
#ifdef __cplusplus
extern "C" {
#endif
// 函数声明
uint16_t ETH_ReadPHYRegister(uint8_t phy_addr, uint8_t reg_addr);
void ETH_WritePHYRegister(uint8_t phy_addr, uint8_t reg_addr, uint16_t reg_value);
HAL_StatusTypeDef ETH_Init_Ext(void);
#ifdef __cplusplus
}
#endif
#endif /* __ETH_DRIVER_EXT_H */
五、LwIP协议栈移植与配置
5.1 LwIP核心配置(文件名:lwipopts.h)
c
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__
// 1. 基础配置
#define NO_SYS 0 // 使用操作系统(本文用裸机,0为不使用OS)
#define LWIP_SOCKET 1 // 启用socket API
#define LWIP_NETCONN 1 // 启用netconn API
#define LWIP_TCP 1 // 启用TCP协议
#define LWIP_UDP 1 // 启用UDP协议
#define LWIP_IPV4 1 // 启用IPv4
#define LWIP_DHCP 0 // 关闭DHCP,使用静态IP
#define LWIP_ICMP 1 // 启用ICMP(ping功能)
#define LWIP_RAW 1 // 启用RAW API
// 2. 内存配置
#define MEM_ALIGNMENT 4 // 内存对齐方式
#define MEM_SIZE 16384 // 内存池大小(16KB)
#define MEMP_NUM_TCP_PCB 5 // TCP连接控制块数量
#define MEMP_NUM_UDP_PCB 5 // UDP控制块数量
#define MEMP_NUM_NETBUF 10 // 网络缓冲区数量
#define MEMP_NUM_NETCONN 10 // 网络连接数量
// 3. TCP配置
#define TCP_MSS 1460 // TCP最大分段大小
#define TCP_WND 2048 // TCP窗口大小
#define TCP_SND_BUF 2048 // TCP发送缓冲区大小
#define TCP_SND_QUEUELEN 5 // TCP发送队列长度
#define TCP_LISTEN_BACKLOG 5 // TCP监听队列长度
// 4. 网络接口配置
#define LWIP_NETIF_LINK_CALLBACK 1 // 启用链路状态回调
#define LWIP_NETIF_HOSTNAME 1 // 启用主机名
#define NETIF_HOSTNAME "STM32F407_LAN8720A" // 主机名
// 5. 时钟配置(STM32F407主频168MHz)
#define SYSTICK_PERIOD_MS 1 // 系统滴答定时器周期(1ms)
#define LWIP_TIMERS 1 // 启用LwIP定时器
// 6. 调试配置(新手可关闭)
#define LWIP_DEBUG 0
#define TCP_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */
5.2 LwIP初始化(文件名:lwip_init.c)
c
#include "lwip_init.h"
#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/api.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "netif/etharp.h"
#include "eth_driver_ext.h"
#include "stm32f4xx_hal.h"
// 网络接口结构体
struct netif gnetif;
// IP地址配置
#define IP_ADDR0 192
#define IP_ADDR1 168
#define IP_ADDR2 1
#define IP_ADDR3 100
#define NETMASK0 255
#define NETMASK1 255
#define NETMASK2 255
#define NETMASK3 0
#define GW_ADDR0 192
#define GW_ADDR1 168
#define GW_ADDR2 1
#define GW_ADDR3 1
/**
* @brief LwIP时钟处理函数
* @note 需在SysTick中断中调用,周期1ms
* @param 无
* @retval 无
*/
void LwIP_Time_Handler(void)
{
sys_check_timeouts();
}
/**
* @brief 网络接口状态回调函数
* @note 打印链路状态(可选)
* @param netif: 网络接口指针
* @retval 无
*/
void netif_link_callback(struct netif *netif)
{
if(netif_is_link_up(netif))
{
// 链路已连接(可添加LED指示或串口打印)
}
else
{
// 链路断开(可添加LED指示或串口打印)
}
}
/**
* @brief LwIP初始化函数
* @note 完成协议栈和网络接口初始化
* @param 无
* @retval 0:成功 其他:失败
*/
uint8_t LwIP_Init(void)
{
ip4_addr_t ipaddr, netmask, gw;
// 1. 初始化TCP/IP内核
tcpip_init(NULL, NULL);
// 2. 配置IP地址、子网掩码、网关
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK0, NETMASK1, NETMASK2, NETMASK3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
// 3. 添加网络接口
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
// 4. 设置默认网络接口
netif_set_default(&gnetif);
// 5. 启用链路状态回调
netif_set_link_callback(&gnetif, netif_link_callback);
// 6. 初始化ETH外设
if(ETH_Init_Ext() != HAL_OK)
{
return 1;
}
// 7. 标记网络接口为UP状态
netif_set_up(&gnetif);
// 8. 标记链路为UP状态
netif_set_link_up(&gnetif);
return 0;
}
5.3 LwIP初始化头文件(文件名:lwip_init.h)
c
#ifndef __LWIP_INIT_H
#define __LWIP_INIT_H
#include "lwip/netif.h"
#ifdef __cplusplus
extern "C" {
#endif
// 外部声明网络接口结构体
extern struct netif gnetif;
// 函数声明
void LwIP_Time_Handler(void);
uint8_t LwIP_Init(void);
#ifdef __cplusplus
}
#endif
#endif /* __LWIP_INIT_H */
六、TCP应用层开发
6.1 TCP服务器端(文件名:tcp_server.c)
c
#include "tcp_server.h"
#include "lwip/api.h"
#include "lwip/sys.h"
#include "stm32f4xx_hal.h"
#define TCP_SERVER_PORT 8080 // TCP服务器端口
#define TCP_BUF_SIZE 1024 // 数据缓冲区大小
// TCP服务器线程函数(裸机下用轮询方式)
static void tcp_server_thread(void)
{
struct netconn *conn, *newconn;
err_t err, recv_err;
struct netbuf *buf;
void *data;
u16_t len;
ip_addr_t client_ip;
u16_t client_port;
// 1. 创建TCP连接控制块
conn = netconn_new(NETCONN_TCP);
if(conn == NULL)
{
return; // 创建失败
}
// 2. 绑定端口
err = netconn_bind(conn, IP_ADDR_ANY, TCP_SERVER_PORT);
if(err != ERR_OK)
{
netconn_delete(conn);
return; // 绑定失败
}
// 3. 开始监听
err = netconn_listen(conn);
if(err != ERR_OK)
{
netconn_delete(conn);
return; // 监听失败
}
// 4. 轮询等待客户端连接
while(1)
{
// 等待新连接(阻塞式)
err = netconn_accept(conn, &newconn);
if(err == ERR_OK)
{
// 获取客户端IP和端口
client_ip = newconn->pcb.tcp->remote_ip;
client_port = newconn->pcb.tcp->remote_port;
// 向客户端发送欢迎信息
char welcome_msg[64];
sprintf(welcome_msg, "Welcome! Client IP: %d.%d.%d.%d, Port: %d\r\n",
ip4_addr1(&client_ip), ip4_addr2(&client_ip),
ip4_addr3(&client_ip), ip4_addr4(&client_ip),
client_port);
netconn_write(newconn, welcome_msg, strlen(welcome_msg), NETCONN_COPY);
// 循环接收客户端数据
while(1)
{
// 接收数据
recv_err = netconn_recv(newconn, &buf);
if(recv_err == ERR_OK)
{
// 提取数据和长度
netbuf_data(buf, &data, &len);
// 回显数据给客户端(echo功能)
netconn_write(newconn, data, len, NETCONN_COPY);
// 释放缓冲区
netbuf_delete(buf);
}
else
{
// 连接断开或出错
break;
}
}
// 关闭连接
netconn_close(newconn);
netconn_delete(newconn);
}
}
}
/**
* @brief TCP服务器初始化
* @note 启动TCP服务器(裸机下直接调用线程函数)
* @param 无
* @retval 无
*/
void tcp_server_init(void)
{
// 裸机环境下直接调用轮询函数
tcp_server_thread();
}
6.2 TCP服务器端头文件(文件名:tcp_server.h)
c
#ifndef __TCP_SERVER_H
#define __TCP_SERVER_H
#ifdef __cplusplus
extern "C" {
#endif
// 函数声明
void tcp_server_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __TCP_SERVER_H */
七、主函数整合(文件名:main.c)
c
#include "main.h"
#include "gpio.h"
#include "eth.h"
#include "lwip_init.h"
#include "tcp_server.h"
#include "delay.h"
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ETH_Init(void);
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
// 1. 复位所有外设,初始化Flash接口和SysTick
HAL_Init();
// 2. 配置系统时钟(168MHz)
SystemClock_Config();
// 3. 初始化所有配置的外设
MX_GPIO_Init();
MX_ETH_Init();
// 4. 初始化LwIP协议栈
if(LwIP_Init() != 0)
{
// 初始化失败,可点亮错误LED
while(1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // PA5为LED引脚示例
HAL_Delay(500);
}
}
// 5. 启动TCP服务器
tcp_server_init();
// 6. 主循环(LwIP时钟处理)
while (1)
{
// 1ms调用一次LwIP时钟处理函数(需确保SysTick中断周期为1ms)
LwIP_Time_Handler();
HAL_Delay(1);
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置外部高速振荡器(HSE)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置系统时钟总线
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 错误处理函数
* @retval None
*/
void Error_Handler(void)
{
// 用户可添加自定义错误处理逻辑
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief 断言失败处理函数
* @param file: 断言失败的文件名称
* @param line: 断言失败的行号
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
// 用户可添加自定义打印信息
}
#endif /* USE_FULL_ASSERT */
八、编译下载与测试
8.1 编译代码
- 在Keil中打开生成的工程,添加上述所有代码文件到对应目录
- 点击
Build或Rebuild编译工程,确保无错误和警告- 常见错误:缺少
delay.c/delay.h,需自行实现简单的延时函数 - 常见警告:未使用的变量,可忽略或注释掉
- 常见错误:缺少
8.2 下载代码
- 连接J-Link/ST-Link到STM32F407开发板
- 在Keil中配置调试器:
Options for Target -> Debug,选择对应调试器 - 点击
Download下载代码到开发板
8.3 网络测试
- 硬件连接:用网线将开发板连接到路由器/电脑网口
- 电脑IP配置:将电脑IP设置为192.168.1.xxx(如192.168.1.200),子网掩码255.255.255.0
- 测试工具:使用
网络调试助手(如NetAssist)- 选择
TCP客户端,IP地址填写192.168.1.100,端口8080 - 点击
连接,成功后会收到STM32发送的欢迎信息 - 在发送框输入任意字符,点击
发送,会收到STM32回显的相同字符
- 选择
- Ping测试:在电脑CMD中输入
ping 192.168.1.100,能收到回复说明网络通
九、常见问题排查
9.1 硬件问题
- 网线未插好或损坏:更换网线测试
- LAN8720A复位引脚未配置:检查PG0引脚电平是否正确
- RMII接口接线错误:对照1.3节重新检查接线
- 电源不稳定:确保3.3V供电稳定,纹波小于100mV
9.2 软件问题
- 时钟配置错误:检查PCLK2是否为84MHz
- LwIP配置错误:检查
lwipopts.h中关键参数(如MEM_SIZE) - IP地址冲突:确保192.168.1.100未被其他设备占用
- 端口被占用:更换TCP服务器端口(如8081)测试
总结
- 本次实战基于STM32F407+LAN8720A+LwIP实现了TCP服务器功能,核心步骤包括硬件接线、CubeMX配置、底层驱动编写、LwIP移植和应用层开发,所有代码可直接复用。
- 关键要点:RMII接口的正确配置、LAN8720A的复位时序、LwIP协议栈的内存参数配置、TCP连接的轮询处理。
- 测试环节需先确保硬件链路通(Ping测试),再测试TCP通信,排查问题时优先检查硬件接线和时钟配置。
通过以上完整步骤,零基础小白也能一步步实现STM32的以太网通信功能,在此基础上可扩展HTTP服务器、UDP通信、MQTT协议等更多功能。