以太网通信实战:STM32F407+LAN8720A+LwIP,TCP/IP协议栈应用

文章目录

    • 一、开发环境与硬件准备
      • [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 新建工程

  1. 打开STM32CubeMX,选择File -> New Project,搜索STM32F407ZGT6并选中
  2. 选择Start Project进入配置界面

3.2 时钟配置

  1. 进入Clock Configuration,设置外部高速时钟(HSE)为25MHz
  2. 配置系统时钟(SYSCLK)为168MHz,HCLK=168MHz,PCLK2=84MHz(ETH时钟依赖PCLK2)
  3. ETH MAC时钟配置:ETH_CLK = PCLK2 (84MHz) / 2 = 42MHz(满足RMII接口时钟要求)

3.3 引脚配置

  1. 进入Pinout & Configuration,依次配置1.3节中所有RMII接口引脚,模式均为Alternate Function Mode
  2. 配置LAN8720A复位引脚(PG0)为GPIO_Output模式
  3. 为所有ETH相关引脚分配复用功能(AF11):
    • PA1/PA2/PA3/PA7: AF11
    • PB11: AF11
    • PC4/PC5: AF11
    • PG11/PG13: AF11

3.4 外设配置

  1. 启用ETH外设:Connectivity -> ETH,选择RMII模式
  2. 关闭ETH_PTP(无需精确时间协议)
  3. 配置NVIC:开启ETH全局中断,优先级设置为中等(如抢占优先级2,子优先级0)

3.5 LwIP协议栈配置

  1. 进入Middleware -> LwIP,启用LwIP协议栈
  2. 选择TCPUDP协议(按需选择,本文以TCP为例)
  3. IP地址配置:
    • 静态IP:192.168.1.100
    • 子网掩码:255.255.255.0
    • 网关:192.168.1.1
  4. 关闭DHCP(新手先使用静态IP,降低复杂度)
  5. 配置LwIP内存池:MEM_SIZE=16384,MEMP_NUM_TCP_PCB=5

3.6 生成代码

  1. 进入Project Manager,设置工程名(如STM32F407_LAN8720A_LwIP),选择保存路径
  2. 工具链选择MDK-ARM v5
  3. 点击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, &reg_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, &ethernetif_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 编译代码

  1. 在Keil中打开生成的工程,添加上述所有代码文件到对应目录
  2. 点击BuildRebuild编译工程,确保无错误和警告
    • 常见错误:缺少delay.c/delay.h,需自行实现简单的延时函数
    • 常见警告:未使用的变量,可忽略或注释掉

8.2 下载代码

  1. 连接J-Link/ST-Link到STM32F407开发板
  2. 在Keil中配置调试器:Options for Target -> Debug,选择对应调试器
  3. 点击Download下载代码到开发板

8.3 网络测试

  1. 硬件连接:用网线将开发板连接到路由器/电脑网口
  2. 电脑IP配置:将电脑IP设置为192.168.1.xxx(如192.168.1.200),子网掩码255.255.255.0
  3. 测试工具:使用网络调试助手(如NetAssist)
    • 选择TCP客户端,IP地址填写192.168.1.100,端口8080
    • 点击连接,成功后会收到STM32发送的欢迎信息
    • 在发送框输入任意字符,点击发送,会收到STM32回显的相同字符
  4. Ping测试:在电脑CMD中输入ping 192.168.1.100,能收到回复说明网络通

九、常见问题排查

9.1 硬件问题

  1. 网线未插好或损坏:更换网线测试
  2. LAN8720A复位引脚未配置:检查PG0引脚电平是否正确
  3. RMII接口接线错误:对照1.3节重新检查接线
  4. 电源不稳定:确保3.3V供电稳定,纹波小于100mV

9.2 软件问题

  1. 时钟配置错误:检查PCLK2是否为84MHz
  2. LwIP配置错误:检查lwipopts.h中关键参数(如MEM_SIZE)
  3. IP地址冲突:确保192.168.1.100未被其他设备占用
  4. 端口被占用:更换TCP服务器端口(如8081)测试

总结

  1. 本次实战基于STM32F407+LAN8720A+LwIP实现了TCP服务器功能,核心步骤包括硬件接线、CubeMX配置、底层驱动编写、LwIP移植和应用层开发,所有代码可直接复用。
  2. 关键要点:RMII接口的正确配置、LAN8720A的复位时序、LwIP协议栈的内存参数配置、TCP连接的轮询处理。
  3. 测试环节需先确保硬件链路通(Ping测试),再测试TCP通信,排查问题时优先检查硬件接线和时钟配置。

通过以上完整步骤,零基础小白也能一步步实现STM32的以太网通信功能,在此基础上可扩展HTTP服务器、UDP通信、MQTT协议等更多功能。

相关推荐
笨笨没好名字2 小时前
# 单片机入门:51单片机与开发板介绍
单片机·嵌入式硬件·51单片机
我在人间贩卖青春2 小时前
认识MCU
单片机·嵌入式硬件
WYH2873 小时前
STM32无感无刷电调实现方法
stm32·单片机·嵌入式硬件
橙露4 小时前
计算机网络核心:TCP三次握手与四次挥手图文详解(四千字深度分析)
网络·tcp/ip·计算机网络
科技块儿4 小时前
社交媒体账号安全如何通过IP查询工具检测异常登录?
服务器·网络·数据库·tcp/ip·安全
三佛科技-134163842124 小时前
HN15N10DA_TO-252封装100V 15A 增强MOSFET场效应管详细分析(HN15N10DA在小家电的应用)
嵌入式硬件·物联网·智能家居·pcb工艺
逐步前行4 小时前
STM32_时钟树
stm32·单片机·嵌入式硬件
lljss20204 小时前
ADC通道切换在GD32和PY32(STM32)区别
stm32·单片机·嵌入式硬件
森利威尔电子-4 小时前
5V到100V+通吃!森利威尔SL4008B升压芯片,电源设计的“万能钥匙”
嵌入式硬件·集成电路·芯片·电源芯片