STM32实战:基于STM32F407的LWIP以太网通信(TCP Server)

文章目录

    • 一、前言
      • [1.1 技术背景](#1.1 技术背景)
      • [1.2 本文目标](#1.2 本文目标)
      • [1.3 技术栈](#1.3 技术栈)
    • 二、环境准备
      • [2.1 硬件准备](#2.1 硬件准备)
      • [2.2 软件安装](#2.2 软件安装)
        • [2.2.1 安装STM32CubeIDE](#2.2.1 安装STM32CubeIDE)
        • [2.2.2 下载LWIP源码](#2.2.2 下载LWIP源码)
    • 三、项目创建与配置
      • [3.1 创建STM32CubeIDE工程](#3.1 创建STM32CubeIDE工程)
      • [3.2 配置以太网外设](#3.2 配置以太网外设)
      • [3.3 配置LWIP](#3.3 配置LWIP)
      • [3.4 生成代码](#3.4 生成代码)
    • 四、LWIP移植与配置
      • [4.1 工程结构](#4.1 工程结构)
      • [4.2 配置lwipopts.h](#4.2 配置lwipopts.h)
      • [4.3 实现TCP Server](#4.3 实现TCP Server)
      • [4.4 主程序实现](#4.4 主程序实现)
      • [4.5 系统架构流程图](#4.5 系统架构流程图)
    • 五、编译与下载
      • [5.1 编译工程](#5.1 编译工程)
      • [5.2 网络配置](#5.2 网络配置)
      • [5.3 测试连接](#5.3 测试连接)
    • 六、故障排查与问题解决
    • 七、总结
      • [7.1 核心知识点回顾](#7.1 核心知识点回顾)
      • [7.2 扩展学习方向](#7.2 扩展学习方向)
      • [7.3 学习资源](#7.3 学习资源)

一、前言

1.1 技术背景

在物联网和工业控制领域,以太网通信是实现设备互联的重要手段。STM32F407系列微控制器集成了MAC(媒体访问控制器),配合外部PHY芯片可以实现以太网通信。LWIP(Lightweight IP)是一个开源的TCP/IP协议栈,专为嵌入式系统设计,具有资源占用少、功能完善等特点。

本文将介绍如何在STM32F407上移植LWIP协议栈,实现TCP Server功能,用于接收客户端连接、处理数据请求。

1.2 本文目标

通过本教程,你将学会:

  • STM32F407以太网外设的配置
  • LWIP协议栈的移植与配置
  • TCP Server的实现
  • 网络数据包的收发处理
  • 多客户端连接管理

1.3 技术栈

硬件平台:

  • STM32F407VET6(主控芯片)
  • LAN8720(以太网PHY芯片)
  • RJ45网络接口

软件环境:

  • STM32CubeIDE v1.10.0+
  • LWIP v2.1.2+
  • HAL库

二、环境准备

2.1 硬件准备

设备 数量 说明
STM32F407VET6开发板 1块 主控芯片,168MHz
LAN8720模块 1个 以太网PHY
网线 1根 连接路由器或PC
ST-Link V2 1个 程序下载与调试

硬件连接图:

复制代码
STM32F407VET6       LAN8720
    PA1   ─────────── ETH_RMII_REF_CLK
    PA2   ─────────── ETH_MDIO
    PA7   ─────────── ETH_RMII_CRS_DV
    PB11  ─────────── ETH_RMII_TX_EN
    PB12  ─────────── ETH_RMII_TXD0
    PB13  ─────────── ETH_RMII_TXD1
    PC1   ─────────── ETH_MDC
    PC4   ─────────── ETH_RMII_RXD0
    PC5   ─────────── ETH_RMII_RXD1
    
    3.3V  ─────────── VCC
    GND   ─────────── GND
    
    PA0   ─────────── ETH_RST (PHY复位)

2.2 软件安装

2.2.1 安装STM32CubeIDE
  1. 访问ST官网下载STM32CubeIDE
  2. 安装并配置HAL库
2.2.2 下载LWIP源码

LWIP已集成在STM32CubeMX中,无需单独下载。


三、项目创建与配置

3.1 创建STM32CubeIDE工程

  1. 打开STM32CubeIDE
  2. 创建新工程,选择STM32F407VET6
  3. 工程名称:STM32_LWIP_TCP_Server

3.2 配置以太网外设

  1. 打开 .ioc 文件

  2. 配置ETH外设:

    • 选择 ETH -> Mode -> RMII
  3. 配置引脚:

    PA1 - ETH_RMII_REF_CLK
    PA2 - ETH_MDIO
    PA7 - ETH_RMII_CRS_DV
    PB11 - ETH_RMII_TX_EN
    PB12 - ETH_RMII_TXD0
    PB13 - ETH_RMII_TXD1
    PC1 - ETH_MDC
    PC4 - ETH_RMII_RXD0
    PC5 - ETH_RMII_RXD1
    PA0 - GPIO_Output (PHY复位)

3.3 配置LWIP

  1. 在CubeMX中,点击 Middleware -> LWIP
  2. 启用LWIP
  3. 配置参数:

General Settings:

复制代码
LWIP_DHCP: Enabled
LWIP_DNS: Enabled
MEM_SIZE: 16000
MEMP_NUM_PBUF: 16
MEMP_NUM_UDP_PCB: 4
MEMP_NUM_TCP_PCB: 5
MEMP_NUM_TCP_PCB_LISTEN: 8
MEMP_NUM_TCP_SEG: 16

IP配置:

复制代码
IP_ADDR: 192.168.1.100
NETMASK: 255.255.255.0
GATEWAY: 192.168.1.1

3.4 生成代码

  1. 点击 Project -> Generate Code
  2. 等待代码生成完成

四、LWIP移植与配置

4.1 工程结构

复制代码
STM32_LWIP_TCP_Server/
├── Core/
│   ├── Inc/
│   │   └── main.h
│   └── Src/
│       └── main.c
├── LWIP/
│   ├── App/
│   │   └── lwip.c/.h
│   └── Target/
│       └── ethernetif.c/.h
└── Middlewares/
    └── Third_Party/
        └── LwIP/
            └── ...

4.2 配置lwipopts.h

📝 修改文件:LWIP/Target/lwipopts.h

c 复制代码
/*
 * lwipopts.h - LWIP配置文件
 */

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/* 平台相关配置 */
#define LWIP_PLATFORM_BYTESWAP          0
#define SYS_LIGHTWEIGHT_PROT            1

/* 内存配置 */
#define MEM_SIZE                        (16 * 1024)
#define MEMP_NUM_PBUF                   16
#define MEMP_NUM_UDP_PCB                4
#define MEMP_NUM_TCP_PCB                5
#define MEMP_NUM_TCP_PCB_LISTEN         8
#define MEMP_NUM_TCP_SEG                16
#define MEMP_NUM_SYS_TIMEOUT            8
#define MEMP_NUM_NETBUF                 8
#define MEMP_NUM_NETCONN                8

/* Pbuf配置 */
#define PBUF_POOL_SIZE                  16
#define PBUF_POOL_BUFSIZE               1524

/* TCP配置 */
#define TCP_MSS                         1460
#define TCP_SND_BUF                     (8 * TCP_MSS)
#define TCP_SND_QUEUELEN                (4 * TCP_SND_QUEUELEN)
#define TCP_WND                         (8 * TCP_MSS)
#define TCP_OVERSIZE                    TCP_MSS

/* IP配置 */
#define LWIP_IPV4                       1
#define IP_REASSEMBLY                 0
#define IP_FRAG                       0

/* ARP配置 */
#define ARP_TABLE_SIZE                  10
#define ARP_QUEUEING                    0

/* DHCP配置 */
#define LWIP_DHCP                       1
#define LWIP_AUTOIP                     0
#define LWIP_DHCP_AUTOIP_COOP           0

/* DNS配置 */
#define LWIP_DNS                        1

/* UDP配置 */
#define LWIP_UDP                        1
#define UDP_TTL                         255

/* TCP配置 */
#define LWIP_TCP                        1
#define TCP_TTL                         255
#define TCP_QUEUE_OOSEQ                 0

/* 统计 */
#define LWIP_STATS                      0
#define LWIP_PROVIDE_ERRNO              1

/* 检查 */
#define LWIP_CHECKSUM_ON_COPY           0

/* 钩子函数 */
#define LWIP_CALLBACK_API               1

/* 线程配置 */
#define TCPIP_THREAD_STACK_SIZE         1024
#define TCPIP_THREAD_PRIO               3
#define TCPIP_MBOX_SIZE                 16

/* 底层接口 */
#define LWIP_NETIF_STATUS_CALLBACK      1
#define LWIP_NETIF_LINK_CALLBACK        1

/* Socket API */
#define LWIP_SOCKET                     0
#define LWIP_NETCONN                    1

/* 调试 */
#define LWIP_DEBUG                      0

#endif /* __LWIPOPTS_H__ */

4.3 实现TCP Server

📄 创建文件:Core/Src/tcp_server.c

c 复制代码
/*
 * tcp_server.c - TCP Server实现
 * 
 * 功能:
 * - TCP服务器初始化
 * - 多客户端连接管理
 * - 数据接收与发送
 * - 连接状态监控
 */

#include "tcp_server.h"
#include "lwip/tcp.h"
#include "lwip/api.h"
#include <string.h>
#include <stdio.h>

/* 服务器配置 */
#define TCP_SERVER_PORT     8080
#define TCP_MAX_CLIENTS     5
#define TCP_RX_BUFFER_SIZE  1024

/* 客户端状态 */
typedef enum {
    CLIENT_STATE_IDLE = 0,
    CLIENT_STATE_CONNECTED,
    CLIENT_STATE_CLOSING
} client_state_t;

/* 客户端结构体 */
typedef struct {
    struct tcp_pcb *pcb;
    client_state_t state;
    uint8_t rx_buffer[TCP_RX_BUFFER_SIZE];
    uint16_t rx_len;
    uint32_t rx_count;
    uint32_t tx_count;
} tcp_client_t;

/* 服务器状态 */
static struct tcp_pcb *server_pcb = NULL;
static tcp_client_t clients[TCP_MAX_CLIENTS];
static uint8_t client_count = 0;

/* 统计信息 */
static uint32_t total_connections = 0;
static uint32_t total_rx_bytes = 0;
static uint32_t total_tx_bytes = 0;

/* 函数声明 */
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
static void tcp_server_error(void *arg, err_t err);
static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_server_process(tcp_client_t *client, struct tcp_pcb *tpcb);
static void tcp_server_close(tcp_client_t *client, struct tcp_pcb *tpcb);

/* 查找空闲客户端槽 */
static tcp_client_t* find_free_client_slot(void)
{
    for (int i = 0; i < TCP_MAX_CLIENTS; i++) {
        if (clients[i].state == CLIENT_STATE_IDLE) {
            return &clients[i];
        }
    }
    return NULL;
}

/* 根据PCB查找客户端 */
static tcp_client_t* find_client_by_pcb(struct tcp_pcb *pcb)
{
    for (int i = 0; i < TCP_MAX_CLIENTS; i++) {
        if (clients[i].pcb == pcb) {
            return &clients[i];
        }
    }
    return NULL;
}

/* TCP Server初始化 */
err_t tcp_server_init(void)
{
    err_t err;
    
    /* 初始化客户端数组 */
    memset(clients, 0, sizeof(clients));
    
    /* 创建TCP控制块 */
    server_pcb = tcp_new();
    if (server_pcb == NULL) {
        printf("[TCP] Failed to create PCB\\n");
        return ERR_MEM;
    }
    
    /* 绑定端口 */
    err = tcp_bind(server_pcb, IP_ADDR_ANY, TCP_SERVER_PORT);
    if (err != ERR_OK) {
        printf("[TCP] Failed to bind port %d, err=%d\\n", TCP_SERVER_PORT, err);
        tcp_close(server_pcb);
        return err;
    }
    
    /* 开始监听 */
    server_pcb = tcp_listen(server_pcb);
    if (server_pcb == NULL) {
        printf("[TCP] Failed to listen\\n");
        return ERR_MEM;
    }
    
    /* 设置接受回调 */
    tcp_accept(server_pcb, tcp_server_accept);
    
    printf("[TCP] Server started on port %d\\n", TCP_SERVER_PORT);
    printf("[TCP] Max clients: %d\\n", TCP_MAX_CLIENTS);
    
    return ERR_OK;
}

/* 接受连接回调 */
static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    (void)arg;
    
    if (err != ERR_OK || newpcb == NULL) {
        return ERR_VAL;
    }
    
    /* 查找空闲客户端槽 */
    tcp_client_t *client = find_free_client_slot();
    if (client == NULL) {
        printf("[TCP] Max clients reached, rejecting connection\\n");
        tcp_close(newpcb);
        return ERR_MEM;
    }
    
    /* 初始化客户端 */
    memset(client, 0, sizeof(tcp_client_t));
    client->pcb = newpcb;
    client->state = CLIENT_STATE_CONNECTED;
    client_count++;
    total_connections++;
    
    /* 设置回调函数 */
    tcp_arg(newpcb, client);
    tcp_recv(newpcb, tcp_server_recv);
    tcp_sent(newpcb, tcp_server_sent);
    tcp_err(newpcb, tcp_server_error);
    tcp_poll(newpcb, tcp_server_poll, 2);  /* 每1秒轮询 */
    
    /* 设置接收缓冲区 */
    tcp_nagle_disable(newpcb);
    
    /* 获取客户端IP */
    ip_addr_t client_ip = newpcb->remote_ip;
    printf("[TCP] Client connected from %d.%d.%d.%d:%d\\n",
           ip4_addr1(&client_ip), ip4_addr2(&client_ip),
           ip4_addr3(&client_ip), ip4_addr4(&client_ip),
           newpcb->remote_port);
    printf("[TCP] Active clients: %d/%d\\n", client_count, TCP_MAX_CLIENTS);
    
    /* 发送欢迎消息 */
    const char *welcome = "Welcome to STM32 TCP Server!\\r\\n";
    tcp_write(newpcb, welcome, strlen(welcome), TCP_WRITE_FLAG_COPY);
    
    return ERR_OK;
}

/* 接收数据回调 */
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    tcp_client_t *client = (tcp_client_t *)arg;
    
    if (p == NULL) {
        /* 连接关闭 */
        printf("[TCP] Client disconnected\\n");
        tcp_server_close(client, tpcb);
        return ERR_OK;
    }
    
    if (err != ERR_OK) {
        pbuf_free(p);
        return err;
    }
    
    /* 接收数据 */
    uint16_t len = p->tot_len;
    if (client->rx_len + len > TCP_RX_BUFFER_SIZE) {
        printf("[TCP] RX buffer overflow\\n");
        pbuf_free(p);
        return ERR_MEM;
    }
    
    pbuf_copy_partial(p, &client->rx_buffer[client->rx_len], len, 0);
    client->rx_len += len;
    client->rx_count += len;
    total_rx_bytes += len;
    
    /* 确认接收 */
    tcp_recved(tpcb, len);
    pbuf_free(p);
    
    /* 处理数据 */
    return tcp_server_process(client, tpcb);
}

/* 数据处理 */
static err_t tcp_server_process(tcp_client_t *client, struct tcp_pcb *tpcb)
{
    /* 查找换行符 */
    char *newline = memchr(client->rx_buffer, '\\n', client->rx_len);
    if (newline == NULL) {
        /* 等待更多数据 */
        return ERR_OK;
    }
    
    /* 计算命令长度 */
    uint16_t cmd_len = newline - (char *)client->rx_buffer + 1;
    
    /* 提取命令 */
    char cmd[64];
    uint16_t copy_len = cmd_len < sizeof(cmd) - 1 ? cmd_len : sizeof(cmd) - 1;
    memcpy(cmd, client->rx_buffer, copy_len);
    cmd[copy_len] = '\\0';
    
    /* 移除末尾的换行符 */
    char *cr = strchr(cmd, '\\r');
    if (cr) *cr = '\\0';
    char *lf = strchr(cmd, '\\n');
    if (lf) *lf = '\\0';
    
    printf("[TCP] Received: %s\\n", cmd);
    
    /* 处理命令 */
    char response[256];
    
    if (strcasecmp(cmd, "help") == 0) {
        snprintf(response, sizeof(response),
            "Commands:\\r\\n"
            "  help     - Show this help\\r\\n"
            "  status   - Show server status\\r\\n"
            "  info     - Show system info\\r\\n"
            "  led on   - Turn LED on\\r\\n"
            "  led off  - Turn LED off\\r\\n"
            "  time     - Get current time\\r\\n"
            "  echo     - Echo test\\r\\n"
            "  quit     - Disconnect\\r\\n");
    }
    else if (strcasecmp(cmd, "status") == 0) {
        snprintf(response, sizeof(response),
            "Server Status:\\r\\n"
            "  Active clients: %d/%d\\r\\n"
            "  Total connections: %lu\\r\\n"
            "  RX bytes: %lu\\r\\n"
            "  TX bytes: %lu\\r\\n",
            client_count, TCP_MAX_CLIENTS,
            total_connections, total_rx_bytes, total_tx_bytes);
    }
    else if (strcasecmp(cmd, "info") == 0) {
        snprintf(response, sizeof(response),
            "System Info:\\r\\n"
            "  MCU: STM32F407VET6\\r\\n"
            "  Clock: 168MHz\\r\\n"
            "  LWIP Version: %s\\r\\n",
            LWIP_VERSION_STRING);
    }
    else if (strcasecmp(cmd, "led on") == 0) {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        snprintf(response, sizeof(response), "LED is ON\\r\\n");
    }
    else if (strcasecmp(cmd, "led off") == 0) {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        snprintf(response, sizeof(response), "LED is OFF\\r\\n");
    }
    else if (strcasecmp(cmd, "time") == 0) {
        uint32_t tick = HAL_GetTick();
        snprintf(response, sizeof(response), "Uptime: %lu ms\\r\\n", tick);
    }
    else if (strcasecmp(cmd, "echo") == 0) {
        snprintf(response, sizeof(response), "Echo test OK\\r\\n");
    }
    else if (strcasecmp(cmd, "quit") == 0) {
        snprintf(response, sizeof(response), "Goodbye!\\r\\n");
        tcp_write(tpcb, response, strlen(response), TCP_WRITE_FLAG_COPY);
        tcp_server_close(client, tpcb);
        
        /* 移动缓冲区 */
        memmove(client->rx_buffer, &client->rx_buffer[cmd_len], 
                client->rx_len - cmd_len);
        client->rx_len -= cmd_len;
        return ERR_OK;
    }
    else {
        snprintf(response, sizeof(response), "Unknown command: %s\\r\\n", cmd);
    }
    
    /* 发送响应 */
    err_t err = tcp_write(tpcb, response, strlen(response), TCP_WRITE_FLAG_COPY);
    if (err == ERR_OK) {
        client->tx_count += strlen(response);
        total_tx_bytes += strlen(response);
        tcp_output(tpcb);
    }
    
    /* 移动缓冲区 */
    memmove(client->rx_buffer, &client->rx_buffer[cmd_len], 
            client->rx_len - cmd_len);
    client->rx_len -= cmd_len;
    
    return ERR_OK;
}

/* 发送完成回调 */
static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
    (void)arg;
    (void)tpcb;
    (void)len;
    return ERR_OK;
}

/* 错误回调 */
static void tcp_server_error(void *arg, err_t err)
{
    tcp_client_t *client = (tcp_client_t *)arg;
    
    printf("[TCP] Error: %d\\n", err);
    
    if (client != NULL) {
        client->pcb = NULL;
        client->state = CLIENT_STATE_IDLE;
        if (client_count > 0) client_count--;
    }
}

/* 轮询回调 */
static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb)
{
    (void)tpcb;
    tcp_client_t *client = (tcp_client_t *)arg;
    
    if (client != NULL && client->state == CLIENT_STATE_CLOSING) {
        tcp_server_close(client, tpcb);
    }
    
    return ERR_OK;
}

/* 关闭连接 */
static void tcp_server_close(tcp_client_t *client, struct tcp_pcb *tpcb)
{
    if (tpcb == NULL) {
        return;
    }
    
    /* 清除回调 */
    tcp_arg(tpcb, NULL);
    tcp_sent(tpcb, NULL);
    tcp_recv(tpcb, NULL);
    tcp_err(tpcb, NULL);
    tcp_poll(tpcb, NULL, 0);
    
    /* 关闭连接 */
    if (tcp_close(tpcb) != ERR_OK) {
        tcp_abort(tpcb);
    }
    
    /* 清除客户端 */
    if (client != NULL) {
        client->pcb = NULL;
        client->state = CLIENT_STATE_IDLE;
        client->rx_len = 0;
    }
    
    if (client_count > 0) client_count--;
    printf("[TCP] Connection closed, active clients: %d\\n", client_count);
}

/* 获取统计信息 */
void tcp_server_get_stats(uint32_t *connections, uint32_t *rx, uint32_t *tx)
{
    if (connections) *connections = total_connections;
    if (rx) *rx = total_rx_bytes;
    if (tx) *tx = total_tx_bytes;
}

/* 获取客户端数量 */
uint8_t tcp_server_get_client_count(void)
{
    return client_count;
}

📄 创建文件:Core/Inc/tcp_server.h

c 复制代码
/*
 * tcp_server.h - TCP Server头文件
 */

#ifndef __TCP_SERVER_H__
#define __TCP_SERVER_H__

#include "lwip/err.h"
#include <stdint.h>

/* TCP Server初始化 */
err_t tcp_server_init(void);

/* 获取统计信息 */
void tcp_server_get_stats(uint32_t *connections, uint32_t *rx, uint32_t *tx);

/* 获取客户端数量 */
uint8_t tcp_server_get_client_count(void);

#endif /* __TCP_SERVER_H__ */

4.4 主程序实现

📝 修改文件:Core/Src/main.c

c 复制代码
/*
 * main.c - 主程序入口
 */

#include "main.h"
#include "lwip.h"
#include "tcp_server.h"
#include <stdio.h>

/* 私有函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

/* 主函数 */
int main(void)
{
    /* MCU配置 */
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    
    /* 初始化LWIP */
    MX_LWIP_Init();
    
    /* 初始化TCP Server */
    tcp_server_init();
    
    printf("\\n");
    printf("========================================\\n");
    printf("  STM32F407 TCP Server\\n");
    printf("  IP: 192.168.1.100\\n");
    printf("  Port: 8080\\n");
    printf("========================================\\n");
    printf("\\n");
    
    /* 主循环 */
    while (1)
    {
        /* 处理LWIP任务 */
        MX_LWIP_Process();
        
        /* 周期性打印状态 */
        static uint32_t last_print = 0;
        if (HAL_GetTick() - last_print > 10000) {
            last_print = HAL_GetTick();
            
            uint32_t connections, rx, tx;
            tcp_server_get_stats(&connections, &rx, &tx);
            
            printf("[Status] Clients: %d, Connections: %lu, RX: %lu, TX: %lu\\n",
                   tcp_server_get_client_count(), connections, rx, tx);
        }
        
        HAL_Delay(1);
    }
}

/* GPIO初始化 */
static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    /* GPIO时钟使能 */
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    /* 配置LED引脚(PC13) */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    
    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

/* 系统时钟配置 */
void SystemClock_Config(void)
{
    /* 由CubeMX生成 */
}

/* 错误处理 */
void Error_Handler(void)
{
    __disable_irq();
    while (1) {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        HAL_Delay(100);
    }
}

4.5 系统架构流程图

应用层
LWIP协议栈
驱动层
硬件层
STM32F407

168MHz
LAN8720

PHY芯片
RJ45接口
ETH外设驱动

HAL库
DMA传输
GPIO控制
网络接口层

ethernetif.c
IP层
TCP层
内存管理
TCP Server

tcp_server.c
客户端1
客户端2
客户端3


五、编译与下载

5.1 编译工程

  1. 在STM32CubeIDE中,点击 Project -> Build All
  2. 等待编译完成

5.2 网络配置

  1. 将开发板通过网线连接到路由器
  2. 确保PC与开发板在同一网段
  3. 配置PC的IP地址为 192.168.1.x

5.3 测试连接

使用telnet或netcat测试:

bash 复制代码
# 使用telnet连接
telnet 192.168.1.100 8080

# 或使用nc
nc 192.168.1.100 8080

测试命令:

复制代码
help      - 显示帮助
status    - 显示服务器状态
info      - 显示系统信息
led on    - 点亮LED
led off   - 熄灭LED
time      - 获取运行时间
echo      - 回声测试
quit      - 断开连接

六、故障排查与问题解决

6.1 网络连接问题

问题1:无法连接服务器

错误现象:

  • telnet连接超时
  • ping不通开发板

原因分析:

  • IP地址配置错误
  • 网线连接问题
  • PHY芯片未初始化

解决方案:

方案1:检查网络配置

c 复制代码
// 确认IP配置
#define IP_ADDR0   192
#define IP_ADDR1   168
#define IP_ADDR2   1
#define IP_ADDR3   100

// 检查DHCP是否启用
#define LWIP_DHCP   1  // 如果启用DHCP,IP可能不同

方案2:检查PHY初始化

c 复制代码
// 在ethernetif.c中添加调试输出
printf("PHY Address: %d\\n", PHY_ADDRESS);
printf("PHY ID: 0x%04X\\n", phy_id);

问题2:数据接收异常

错误现象:

  • 数据丢失
  • 接收不完整

原因分析:

  • 接收缓冲区太小
  • DMA配置错误
  • 中断处理不及时

解决方案:

c 复制代码
// 增大接收缓冲区
#define TCP_RX_BUFFER_SIZE  2048

// 在lwipopts.h中配置
#define PBUF_POOL_SIZE      32
#define PBUF_POOL_BUFSIZE   1524

6.2 内存问题

问题3:内存不足

错误现象:

  • 无法建立新连接
  • 系统崩溃

解决方案:

c 复制代码
// 增加内存池大小
#define MEM_SIZE            (32 * 1024)
#define MEMP_NUM_TCP_PCB    10

七、总结

7.1 核心知识点回顾

  1. 以太网配置:掌握STM32 ETH外设和PHY芯片的配置
  2. LWIP移植:理解LWIP协议栈的架构和配置方法
  3. TCP编程:掌握TCP Server的实现和多客户端管理
  4. 网络调试:学会使用网络工具进行调试

7.2 扩展学习方向

  • UDP通信:实现UDP Server/Client
  • HTTP服务器:在TCP基础上实现HTTP协议
  • MQTT客户端:接入物联网平台
  • 网络安全性:实现TLS/SSL加密

7.3 学习资源

官方文档:

相关推荐
Wave8452 小时前
嵌入式底层核心架构详解 (Cortex-M3)
stm32·架构
渡己之道2 小时前
笔记-lvgl移植到stm32f407
c语言·笔记·stm32
项目題供诗2 小时前
STM32-按键控制LED&光敏传感器控制蜂鸣器(五)
stm32·单片机·嵌入式硬件
猪八戒1.03 小时前
Allergo Cadence学习笔记
嵌入式硬件
危桥带雨3 小时前
WDG理论知识
stm32·单片机·嵌入式硬件
minglie13 小时前
单片机内存管理CmemTable
单片机·嵌入式硬件
济6173 小时前
FreeRTOS 控制任务设计 (2)--- 运动学逆解 + PID 闭环 + PWM 驱动全流程实现
stm32·单片机·嵌入式·freertos
szxinmai主板定制专家3 小时前
基于RK3588超小体积,轻巧,长续航的无人机AI模块,支持视频跟踪
arm开发·人工智能·嵌入式硬件·fpga开发·无人机
淼淼爱喝水4 小时前
eNSP 防火墙 NAT 策略配置(Easy IP/No-PAT/NAPT/ 黑洞路由)
服务器·网络·tcp/ip·ensp·防火墙·nat