BLOG8
跳转至

以太网接口硬件设计

概述

以太网(Ethernet)是目前应用最广泛的局域网技术,由Xerox公司于1970年代开发。以太网以其高速率、标准化、成本低的特点,成为工业控制、物联网、嵌入式系统等领域的首选网络通信方案。

完成本文学习后,你将能够:

  • 理解以太网的工作原理和硬件架构
  • 掌握PHY芯片的选型和应用
  • 学会MII/RMII接口的配置
  • 理解网络变压器的作用和设计
  • 实现STM32的以太网通信
  • 掌握以太网接口的EMC设计

背景知识

以太网 vs 其他网络接口

特性 以太网 CAN USB RS485
速度 10/100/1000 Mbps 最高1Mbps 最高480Mbps 最高10Mbps
传输距离 100m(双绞线) 最远10km 5m 1200m
拓扑结构 星型 总线型 树型 总线型
协议栈 TCP/IP CAN协议 USB协议 Modbus等
应用领域 网络通信 汽车、工业 外设连接 工业控制
成本 中等 低 低 低
复杂度 高 中 高 低

以太网的优势

高速率: - 10BASE-T:10 Mbps - 100BASE-TX:100 Mbps(Fast Ethernet) - 1000BASE-T:1000 Mbps(Gigabit Ethernet)

标准化: - IEEE 802.3标准 - 全球统一规范 - 互操作性好

灵活性: - 支持TCP/IP协议栈 - 可接入互联网 - 支持多种应用协议

成本低: - 芯片成本低 - 使用标准网线 - 易于维护

以太网硬件架构

系统框图

完整的以太网系统:

┌─────────────────────────────────────────────────────┐
│                   嵌入式系统                          │
│                                                      │
│  ┌──────────┐      ┌──────────┐      ┌──────────┐  │
│  │   应用   │      │  TCP/IP  │      │   驱动   │  │
│  │   层     │ ←──→ │  协议栈  │ ←──→ │   层     │  │
│  └──────────┘      └──────────┘      └──────────┘  │
│                                           ↕          │
│  ┌──────────────────────────────────────────────┐  │
│  │              MCU (STM32)                      │  │
│  │  ┌────────────────────────────────────────┐  │  │
│  │  │         以太网MAC控制器                 │  │  │
│  │  │  (Media Access Control)                │  │  │
│  │  └────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────┘  │
│                      ↕ MII/RMII                    │
└──────────────────────┼────────────────────────────┘
                       ↓
┌──────────────────────┼────────────────────────────┐
│              PHY芯片 (物理层)                       │
│  ┌────────────────────────────────────────────┐  │
│  │         以太网PHY收发器                     │  │
│  │  (Physical Layer Transceiver)             │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────┼────────────────────────────┘
                       ↓ 差分信号
┌──────────────────────┼────────────────────────────┐
│              网络变压器                             │
│  ┌────────────────────────────────────────────┐  │
│  │         隔离变压器 (Transformer)            │  │
│  │  - 电气隔离                                 │  │
│  │  - 阻抗匹配                                 │  │
│  │  - 共模抑制                                 │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────┼────────────────────────────┘
                       ↓
┌──────────────────────┼────────────────────────────┐
│              RJ45接口                              │
│  ┌────────────────────────────────────────────┐  │
│  │         RJ45连接器                          │  │
│  │  - 8P8C接口                                 │  │
│  │  - 集成LED指示灯                            │  │
│  └────────────────────────────────────────────┘  │
└──────────────────────┼────────────────────────────┘
                       ↓
                   网络电缆

MAC和PHY的分工

MAC(Media Access Control): - 数据帧的封装和解封装 - MAC地址管理 - 流量控制 - 冲突检测(半双工模式) - 通常集成在MCU内部

PHY(Physical Layer): - 物理信号的编码和解码 - 差分信号的发送和接收 - 自动协商(速度和双工模式) - 链路状态检测 - 独立芯片

MII和RMII接口

MII接口(Media Independent Interface)

MII信号定义:

MCU (MAC)                    PHY芯片

TX_CLK  ←────────────────── 发送时钟(25MHz)
TXD[3:0] ───────────────→   发送数据(4位)
TX_EN   ───────────────→    发送使能
TX_ER   ───────────────→    发送错误(可选)

RX_CLK  ←────────────────── 接收时钟(25MHz)
RXD[3:0] ←──────────────    接收数据(4位)
RX_DV   ←──────────────     接收数据有效
RX_ER   ←──────────────     接收错误(可选)

COL     ←──────────────     冲突检测
CRS     ←──────────────     载波侦听

MDC     ───────────────→    管理时钟
MDIO    ←──────────────→    管理数据

总计:16个信号线

MII时序特性:

100Mbps模式:
- TX_CLK/RX_CLK:25MHz
- 数据宽度:4位
- 每个时钟周期传输4位
- 4位 × 25MHz = 100Mbps

10Mbps模式:
- TX_CLK/RX_CLK:2.5MHz
- 数据宽度:4位
- 每个时钟周期传输4位
- 4位 × 2.5MHz = 10Mbps

RMII接口(Reduced MII)

RMII信号定义:

MCU (MAC)                    PHY芯片

REF_CLK ←────────────────── 参考时钟(50MHz)
TXD[1:0] ───────────────→   发送数据(2位)
TX_EN   ───────────────→    发送使能

RXD[1:0] ←──────────────    接收数据(2位)
CRS_DV  ←──────────────     载波侦听/数据有效

MDC     ───────────────→    管理时钟
MDIO    ←──────────────→    管理数据

总计:7个信号线

RMII时序特性:

100Mbps模式:
- REF_CLK:50MHz
- 数据宽度:2位
- 每个时钟周期传输2位
- 2位 × 50MHz = 100Mbps

10Mbps模式:
- REF_CLK:50MHz
- 数据宽度:2位
- 每10个时钟周期传输2位
- 2位 × 5MHz = 10Mbps

MII vs RMII对比

特性 MII RMII
信号线数量 16根 7根
时钟频率 25MHz/2.5MHz 50MHz
数据宽度 4位 2位
PCB布线 复杂 简单
功耗 较高 较低
成本 较高 较低
应用 高性能系统 嵌入式系统

推荐选择: - 嵌入式系统:优先选择RMII - 高性能系统:可选择MII - 成本敏感:选择RMII

PHY芯片选型

常用PHY芯片

1. LAN8720A(最常用):

参数 值
接口 RMII
速度 10/100 Mbps
工作电压 3.3V
封装 QFN-24
特点 成本低,应用广泛
厂商 Microchip

2. DP83848(TI):

参数 值
接口 MII/RMII
速度 10/100 Mbps
工作电压 3.3V
封装 LQFP-48
特点 工业级,可靠性高
厂商 Texas Instruments

3. RTL8201F(Realtek):

参数 值
接口 MII/RMII
速度 10/100 Mbps
工作电压 3.3V
封装 QFN-32
特点 集成LED驱动
厂商 Realtek

4. KSZ8081(Microchip):

参数 值
接口 MII/RMII
速度 10/100 Mbps
工作电压 3.3V/2.5V
封装 QFN-24
特点 低功耗,支持HP Auto-MDIX
厂商 Microchip

PHY芯片引脚功能

LAN8720A引脚图(RMII模式):

        LAN8720A (QFN-24)
     ┌──────────────────┐
VDD  ┤1              24├ VDDCR
TXD0 ┤2              23├ LED2/nINTSEL
TXD1 ┤3              22├ LED1/REGOFF
TX_EN┤4              21├ XTAL2
RXD0 ┤5              20├ XTAL1
RXD1 ┤6              19├ MDC
CRS_DV┤7             18├ MDIO
REF_CLK┤8            17├ nINT/REFCLKO
     │                 │
GND  ┤9              16├ nRST
     └──────────────────┘

关键引脚:
- VDD: 3.3V电源
- VDDCR: 内部1.2V稳压器输出
- TXD[1:0]: 发送数据
- RXD[1:0]: 接收数据
- REF_CLK: 50MHz参考时钟
- MDC/MDIO: 管理接口
- nRST: 复位(低电平有效)

PHY地址配置

MDIO地址设置:

PHY地址通过硬件引脚配置:

LAN8720A地址配置:
- 地址0:RXD0=0, RXD1=0
- 地址1:RXD0=1, RXD1=0
- 地址2:RXD0=0, RXD1=1
- 地址3:RXD0=1, RXD1=1

配置方法:
在nRST释放时,RXD0和RXD1的电平
决定PHY地址

示例(地址1):
RXD0 ──┬── 10kΩ ── VDD
       │
RXD1 ──┴── 10kΩ ── GND

网络变压器设计

变压器的作用

为什么需要网络变压器:

1. 电气隔离:
   - 隔离PHY和网线
   - 保护设备免受浪涌冲击
   - 防止地环路

2. 阻抗匹配:
   - PHY侧:50Ω
   - 网线侧:100Ω
   - 变压器实现阻抗转换

3. 共模抑制:
   - 抑制共模干扰
   - 改善EMC性能
   - 提高信号质量

4. 直流隔离:
   - 隔离直流分量
   - 只传输交流信号

变压器类型

1. 分立变压器:

PHY芯片 ──→ 变压器 ──→ RJ45接口

优点:
- 灵活性高
- 可选择不同规格
- 易于更换

缺点:
- 占用空间大
- 需要额外的中心抽头电容
- 布线复杂

2. 集成变压器(带RJ45):

PHY芯片 ──→ 集成模块(变压器+RJ45)

优点:
- 节省空间
- 简化设计
- 减少布线
- 集成LED指示灯

缺点:
- 成本稍高
- 灵活性较低

推荐型号:
- HanRun HR911105A
- Pulse J0011D21BNL
- Wurth 7499111121

变压器电路设计

标准变压器连接:

PHY (LAN8720A)              变压器              RJ45

TXP ──┬─── 49.9Ω ───┬──→ ┌───┐ ──→ TD+ ──→ Pin 1
      │             │     │   │
      └─ 0.1μF ─┬─ GND    │ T │
                │         │ X │
TXN ──┬─── 49.9Ω ───┴──→ └───┘ ──→ TD- ──→ Pin 2
      │
      └─ 0.1μF ─── GND

RXP ←─┬─── 49.9Ω ───┬──← ┌───┐ ←── RD+ ←── Pin 3
      │             │     │   │
      └─ 0.1μF ─┬─ GND    │ R │
                │         │ X │
RXN ←─┬─── 49.9Ω ───┴──← └───┘ ←── RD- ←── Pin 6
      │
      └─ 0.1μF ─── GND

中心抽头电容:
- 发送侧:0.1μF × 2
- 接收侧:0.1μF × 2
- 作用:提供直流偏置路径

终端电阻:
- 49.9Ω × 4
- 作用:阻抗匹配

RJ45接口引脚定义

8P8C标准接口:

RJ45接口(正面视图)
┌─────────────┐
│ 8 7 6 5 4 3 2 1 │
└─────────────┘

引脚定义(100BASE-TX):
Pin 1: TD+  (发送正)
Pin 2: TD-  (发送负)
Pin 3: RD+  (接收正)
Pin 4: NC   (未使用)
Pin 5: NC   (未使用)
Pin 6: RD-  (接收负)
Pin 7: NC   (未使用)
Pin 8: NC   (未使用)

注意:
- 只使用4根线(1,2,3,6)
- 其他引脚在1000BASE-T中使用

硬件电路设计

基本以太网电路

STM32 + LAN8720A完整电路:

STM32F407                    LAN8720A              变压器+RJ45

PA1  (REF_CLK) ←──────────── REF_CLK              
PA2  (MDIO)    ←──────────→  MDIO                 
PC1  (MDC)     ───────────→  MDC                  

PG11 (TX_EN)   ───────────→  TX_EN                
PG13 (TXD0)    ───────────→  TXD0 ──┬─ 49.9Ω ─→ TD+
PG14 (TXD1)    ───────────→  TXD1 ──┴─ 49.9Ω ─→ TD-

PC4  (RXD0)    ←──────────── RXD0 ←─┬─ 49.9Ω ←─ RD+
PC5  (RXD1)    ←──────────── RXD1 ←─┴─ 49.9Ω ←─ RD-
PA7  (CRS_DV)  ←──────────── CRS_DV               

PG8  (nRST)    ───────────→  nRST                 
                             │                     
3.3V ─┬─ 0.1μF ─┬─ VDD ─────┤                     
      └─ 10μF ──┘            │                     
                             │                     
GND ──────────────── GND ────┤                     
                             │                     
                          VDDCR ─┬─ 10μF ─┬─ GND  
                                 └─ 0.1μF ┘        

25MHz晶振:                                        
XTAL1 ──┬─── 晶振 ───┬─── XTAL2                   
        │            │                             
        ├─ 22pF ─┬─ GND                            
        │        │                                 
        └────────┴─ 22pF ─── GND                   

电源设计

1. 3.3V主电源:

5V ──┬─── LDO ───┬─── 3.3V ──→ PHY VDD
     │  (AMS1117) │
     │            ├─── 10μF ──┬─ GND
     │            │           │
     │            └─── 0.1μF ─┘
    GND

要求:
- 输出电流:≥200mA
- 纹波:<50mV
- 去耦电容:10μF + 0.1μF

2. 内部稳压器:

LAN8720A内部1.2V稳压器:

VDDCR ──┬─── 10μF ──┬─── GND
        │           │
        └─── 0.1μF ─┘

注意:
- VDDCR输出1.2V
- 必须外接去耦电容
- 10μF钽电容或陶瓷电容
- 0.1μF高频去耦

时钟设计

1. 外部晶振(可选):

25MHz晶振电路:

XTAL1 ──┬─── 晶振 ───┬─── XTAL2
        │   (25MHz)  │
        │            │
        ├─ 22pF ─┬─ GND
        │        │
        └────────┴─ 22pF ─── GND

要求:
- 频率:25MHz ± 50ppm
- 负载电容:18-22pF
- ESR:<100Ω

2. 外部时钟输入(推荐):

50MHz时钟源 ──→ REF_CLK (LAN8720A)
                    │
                    └──→ REF_CLK (STM32)

优点:
- 节省成本
- 减少元件
- 提高可靠性

时钟源:
- 有源晶振
- 时钟发生器
- MCU输出时钟

PCB布局设计

布局原则

1. 整体布局:

推荐布局顺序:
1. RJ45接口放置在板边
2. 网络变压器紧邻RJ45
3. PHY芯片靠近变压器
4. MCU尽量靠近PHY

布局示意:
┌─────────────────────────┐
│                         │
│  [RJ45+变压器]          │ ← 板边
│       ↓                 │
│  [PHY芯片]              │ ← 距离<5cm
│       ↓                 │
│  [MCU]                  │ ← 尽量靠近
│                         │
└─────────────────────────┘

2. 差分对布局:

差分对走线要求:
- 保持平行
- 等长等宽
- 避免分叉
- 避免过孔

正确示例:
TD+ ═══════════════════
TD- ═══════════════════

错误示例:
TD+ ═══╗     ╔═════════
       ╚═════╝
TD- ═══════════════════

走线设计

1. 差分对走线:

走线参数(4层板):
- 线宽:8mil(0.2mm)
- 间距:8mil(0.2mm)
- 差分阻抗:100Ω ± 10%
- 长度差:< 5mm

走线技巧:
1. 使用差分对走线工具
2. 保持等长
3. 避免直角转弯(使用45°或圆弧)
4. 避免跨越分割

2. 时钟走线:

REF_CLK走线要求:
- 尽量短
- 远离干扰源
- 包地处理
- 阻抗控制50Ω

走线示意:
PHY ═══ REF_CLK ═══ MCU
        ↓
       GND (包地)

3. 地平面处理:

地平面要求:
- 差分对下方保持完整地平面
- 不要分割地平面
- 多层板各层地平面互连

地平面示意:
┌─────────────────────┐
│  Top: 差分对         │
├─────────────────────┤
│  GND: 完整地平面     │ ← 参考平面
├─────────────────────┤
│  Power: 电源层       │
├─────────────────────┤
│  Bottom: 信号层      │
└─────────────────────┘

EMC优化

1. 共模扼流圈:

添加共模扼流圈(CMC):

PHY ─── TD+ ───┬─ CMC ─┬─── 变压器
               │       │
PHY ─── TD- ───┴───────┴─── 变压器

作用:
- 抑制共模干扰
- 改善EMC性能
- 不影响差分信号

推荐型号:
- Murata DLW5BSN900SQ2L
- TDK ACM2012-900-2P

2. ESD保护:

ESD保护电路:

RJ45 ──┬─── TVS ───┬─── 变压器
       │           │
      GND         GND

推荐器件:
- PESD5V0S4UF(4通道)
- TPD4E05U06(4通道)
- 钳位电压:<6V
- 电容:<5pF

3. 屏蔽设计:

屏蔽措施:
1. 使用带屏蔽的RJ45接口
2. 屏蔽层连接到GND
3. 在接口周围铺地
4. 使用屏蔽网线

屏蔽接口示意:
┌─────────────┐
│  RJ45接口   │
│  ┌───────┐  │
│  │ 屏蔽层 │  │ ← 连接到GND
│  └───────┘  │
└─────────────┘

STM32以太网配置

硬件初始化

GPIO和以太网外设配置:

// 1. 使能时钟
RCC->AHB1ENR |= RCC_AHB1ENR_ETHMACEN |      // 以太网MAC
                RCC_AHB1ENR_ETHMACTXEN |    // 以太网MAC TX
                RCC_AHB1ENR_ETHMACRXEN;     // 以太网MAC RX

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN |       // GPIOA
                RCC_AHB1ENR_GPIOCEN |       // GPIOC
                RCC_AHB1ENR_GPIOGEN;        // GPIOG

// 2. 配置GPIO(RMII模式)
// PA1 (REF_CLK) - 复用功能
GPIOA->MODER &= ~(3 << 2);
GPIOA->MODER |= (2 << 2);
GPIOA->AFR[0] |= (11 << 4);  // AF11

// PA2 (MDIO) - 复用功能
GPIOA->MODER &= ~(3 << 4);
GPIOA->MODER |= (2 << 4);
GPIOA->AFR[0] |= (11 << 8);  // AF11

// PA7 (CRS_DV) - 复用功能
GPIOA->MODER &= ~(3 << 14);
GPIOA->MODER |= (2 << 14);
GPIOA->AFR[0] |= (11 << 28);  // AF11

// PC1 (MDC) - 复用功能
GPIOC->MODER &= ~(3 << 2);
GPIOC->MODER |= (2 << 2);
GPIOC->AFR[0] |= (11 << 4);  // AF11

// PC4 (RXD0) - 复用功能
GPIOC->MODER &= ~(3 << 8);
GPIOC->MODER |= (2 << 8);
GPIOC->AFR[0] |= (11 << 16);  // AF11

// PC5 (RXD1) - 复用功能
GPIOC->MODER &= ~(3 << 10);
GPIOC->MODER |= (2 << 10);
GPIOC->AFR[0] |= (11 << 20);  // AF11

// PG11 (TX_EN) - 复用功能
GPIOG->MODER &= ~(3 << 22);
GPIOG->MODER |= (2 << 22);
GPIOG->AFR[1] |= (11 << 12);  // AF11

// PG13 (TXD0) - 复用功能
GPIOG->MODER &= ~(3 << 26);
GPIOG->MODER |= (2 << 26);
GPIOG->AFR[1] |= (11 << 20);  // AF11

// PG14 (TXD1) - 复用功能
GPIOG->MODER &= ~(3 << 28);
GPIOG->MODER |= (2 << 28);
GPIOG->AFR[1] |= (11 << 24);  // AF11

// 3. 配置GPIO速度(高速)
GPIOA->OSPEEDR |= (3 << 2) | (3 << 4) | (3 << 14);
GPIOC->OSPEEDR |= (3 << 2) | (3 << 8) | (3 << 10);
GPIOG->OSPEEDR |= (3 << 22) | (3 << 26) | (3 << 28);

PHY芯片初始化

// PHY寄存器地址
#define PHY_BCR         0x00  // 基本控制寄存器
#define PHY_BSR         0x01  // 基本状态寄存器
#define PHY_ID1         0x02  // PHY标识符1
#define PHY_ID2         0x03  // PHY标识符2
#define PHY_ANAR        0x04  // 自动协商通告寄存器
#define PHY_ANLPAR      0x05  // 自动协商链路伙伴能力寄存器

// PHY地址(根据硬件配置)
#define PHY_ADDRESS     0x01

// 读取PHY寄存器
uint16_t ETH_ReadPHYRegister(uint8_t reg) {
    uint32_t timeout = 0;

    // 等待MDIO空闲
    while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
        if (++timeout > 1000) return 0xFFFF;
    }

    // 配置MDIO读操作
    ETH->MACMIIAR = (PHY_ADDRESS << 11) |  // PHY地址
                    (reg << 6) |            // 寄存器地址
                    ETH_MACMIIAR_CR_Div42 | // 时钟分频
                    ETH_MACMIIAR_MB;        // 开始操作

    // 等待操作完成
    timeout = 0;
    while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
        if (++timeout > 1000) return 0xFFFF;
    }

    // 返回读取的数据
    return (uint16_t)(ETH->MACMIIDR);
}

// 写入PHY寄存器
bool ETH_WritePHYRegister(uint8_t reg, uint16_t value) {
    uint32_t timeout = 0;

    // 等待MDIO空闲
    while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
        if (++timeout > 1000) return false;
    }

    // 写入数据
    ETH->MACMIIDR = value;

    // 配置MDIO写操作
    ETH->MACMIIAR = (PHY_ADDRESS << 11) |  // PHY地址
                    (reg << 6) |            // 寄存器地址
                    ETH_MACMIIAR_MW |       // 写操作
                    ETH_MACMIIAR_CR_Div42 | // 时钟分频
                    ETH_MACMIIAR_MB;        // 开始操作

    // 等待操作完成
    timeout = 0;
    while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
        if (++timeout > 1000) return false;
    }

    return true;
}

// PHY初始化
bool ETH_PHY_Init(void) {
    uint16_t reg_value;
    uint32_t timeout = 0;

    // 1. 读取PHY ID
    uint16_t id1 = ETH_ReadPHYRegister(PHY_ID1);
    uint16_t id2 = ETH_ReadPHYRegister(PHY_ID2);
    printf("PHY ID: 0x%04X%04X\n", id1, id2);

    // 2. 软复位PHY
    ETH_WritePHYRegister(PHY_BCR, 0x8000);

    // 等待复位完成
    do {
        reg_value = ETH_ReadPHYRegister(PHY_BCR);
        if (++timeout > 1000) return false;
    } while (reg_value & 0x8000);

    // 3. 配置自动协商
    ETH_WritePHYRegister(PHY_ANAR, 0x01E1);  // 10/100Mbps, 全/半双工

    // 4. 启动自动协商
    ETH_WritePHYRegister(PHY_BCR, 0x1200);  // 使能自动协商

    // 5. 等待链路建立
    timeout = 0;
    do {
        reg_value = ETH_ReadPHYRegister(PHY_BSR);
        HAL_Delay(10);
        if (++timeout > 500) {
            printf("Link timeout\n");
            return false;
        }
    } while (!(reg_value & 0x0004));  // 等待链路状态位

    printf("Link established\n");
    return true;
}

MAC控制器配置

// MAC初始化
void ETH_MAC_Init(void) {
    // 1. 软复位MAC
    ETH->DMABMR |= ETH_DMABMR_SR;
    while (ETH->DMABMR & ETH_DMABMR_SR);

    // 2. 配置MAC
    ETH->MACCR = ETH_MACCR_FES |      // 快速以太网(100Mbps)
                 ETH_MACCR_DM |       // 全双工模式
                 ETH_MACCR_IPCO |     // IPv4校验和卸载
                 ETH_MACCR_RD |       // 禁用重试
                 ETH_MACCR_APCS;      // 自动去除CRC

    // 3. 配置MAC帧过滤
    ETH->MACFFR = ETH_MACFFR_RA;      // 接收所有帧(混杂模式)

    // 4. 配置MAC地址
    ETH->MACA0HR = (MAC_ADDR[5] << 8) | MAC_ADDR[4];
    ETH->MACA0LR = (MAC_ADDR[3] << 24) | (MAC_ADDR[2] << 16) |
                   (MAC_ADDR[1] << 8) | MAC_ADDR[0];

    // 5. 配置DMA
    ETH->DMAOMR = ETH_DMAOMR_RSF |    // 接收存储转发
                  ETH_DMAOMR_TSF |    // 发送存储转发
                  ETH_DMAOMR_OSF;     // 操作第二帧

    // 6. 配置DMA总线模式
    ETH->DMABMR = ETH_DMABMR_AAB |    // 地址对齐突发
                  ETH_DMABMR_FB |     // 固定突发
                  ETH_DMABMR_RDP_32Beat | // RX DMA突发长度
                  ETH_DMABMR_PBL_32Beat | // 可编程突发长度
                  ETH_DMABMR_USP;     // 使用独立PBL

    // 7. 使能MAC和DMA
    ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;  // 使能发送和接收
    ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR; // 启动DMA
}

数据发送和接收

// 以太网描述符结构
typedef struct {
    uint32_t Status;
    uint32_t ControlBufferSize;
    uint32_t Buffer1Addr;
    uint32_t Buffer2NextDescAddr;
} ETH_DMADescTypeDef;

// 发送和接收缓冲区
#define ETH_RXBUFNB        4
#define ETH_TXBUFNB        2
#define ETH_RX_BUF_SIZE    1524
#define ETH_TX_BUF_SIZE    1524

__attribute__((aligned(4))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB];
__attribute__((aligned(4))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB];
__attribute__((aligned(4))) uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE];
__attribute__((aligned(4))) uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE];

// 初始化DMA描述符
void ETH_DMATxDescListInit(void) {
    for (int i = 0; i < ETH_TXBUFNB; i++) {
        DMATxDscrTab[i].Status = ETH_DMATXDESC_TCH;
        DMATxDscrTab[i].Buffer1Addr = (uint32_t)(&Tx_Buff[i][0]);

        if (i < (ETH_TXBUFNB - 1)) {
            DMATxDscrTab[i].Buffer2NextDescAddr = (uint32_t)(&DMATxDscrTab[i + 1]);
        } else {
            DMATxDscrTab[i].Buffer2NextDescAddr = (uint32_t)(&DMATxDscrTab[0]);
        }
    }

    // 设置发送描述符地址
    ETH->DMATDLAR = (uint32_t)DMATxDscrTab;
}

void ETH_DMARxDescListInit(void) {
    for (int i = 0; i < ETH_RXBUFNB; i++) {
        DMARxDscrTab[i].Status = ETH_DMARXDESC_OWN;
        DMARxDscrTab[i].ControlBufferSize = ETH_DMARXDESC_RCH | ETH_RX_BUF_SIZE;
        DMARxDscrTab[i].Buffer1Addr = (uint32_t)(&Rx_Buff[i][0]);

        if (i < (ETH_RXBUFNB - 1)) {
            DMARxDscrTab[i].Buffer2NextDescAddr = (uint32_t)(&DMARxDscrTab[i + 1]);
        } else {
            DMARxDscrTab[i].Buffer2NextDescAddr = (uint32_t)(&DMARxDscrTab[0]);
        }
    }

    // 设置接收描述符地址
    ETH->DMARDLAR = (uint32_t)DMARxDscrTab;
}

// 发送数据帧
bool ETH_TransmitFrame(uint8_t *data, uint16_t len) {
    static uint32_t tx_index = 0;

    // 检查描述符是否可用
    if (DMATxDscrTab[tx_index].Status & ETH_DMATXDESC_OWN) {
        return false;  // 描述符忙
    }

    // 复制数据到发送缓冲区
    memcpy((uint8_t *)DMATxDscrTab[tx_index].Buffer1Addr, data, len);

    // 配置描述符
    DMATxDscrTab[tx_index].ControlBufferSize = len & ETH_DMATXDESC_TBS1;
    DMATxDscrTab[tx_index].Status = ETH_DMATXDESC_OWN |
                                     ETH_DMATXDESC_FS |
                                     ETH_DMATXDESC_LS |
                                     ETH_DMATXDESC_IC;

    // 启动发送
    if (ETH->DMASR & ETH_DMASR_TBUS) {
        ETH->DMASR = ETH_DMASR_TBUS;
        ETH->DMATPDR = 0;
    }

    // 更新索引
    tx_index = (tx_index + 1) % ETH_TXBUFNB;

    return true;
}

// 接收数据帧
uint16_t ETH_ReceiveFrame(uint8_t *buffer, uint16_t max_len) {
    static uint32_t rx_index = 0;
    uint16_t len = 0;

    // 检查是否有数据
    if (DMARxDscrTab[rx_index].Status & ETH_DMARXDESC_OWN) {
        return 0;  // 无数据
    }

    // 检查帧状态
    if ((DMARxDscrTab[rx_index].Status & ETH_DMARXDESC_ES) == 0 &&
        (DMARxDscrTab[rx_index].Status & ETH_DMARXDESC_LS) &&
        (DMARxDscrTab[rx_index].Status & ETH_DMARXDESC_FS)) {

        // 获取帧长度
        len = ((DMARxDscrTab[rx_index].Status & ETH_DMARXDESC_FL) >> 16) - 4;

        // 复制数据
        if (len <= max_len) {
            memcpy(buffer, (uint8_t *)DMARxDscrTab[rx_index].Buffer1Addr, len);
        } else {
            len = 0;  // 缓冲区太小
        }
    }

    // 释放描述符
    DMARxDscrTab[rx_index].Status = ETH_DMARXDESC_OWN;

    // 恢复接收
    if (ETH->DMASR & ETH_DMASR_RBUS) {
        ETH->DMASR = ETH_DMASR_RBUS;
        ETH->DMARPDR = 0;
    }

    // 更新索引
    rx_index = (rx_index + 1) % ETH_RXBUFNB;

    return len;
}

常见问题与调试

问题1:无法建立链路

可能原因:

  1. 硬件连接问题
  2. 检查:网线是否正常
  3. 检查:RJ45接口是否接触良好
  4. 解决:更换网线,检查接口

  5. PHY配置问题

  6. 检查:PHY地址是否正确
  7. 检查:时钟是否正常
  8. 解决:确认PHY地址配置,检查时钟信号

  9. 变压器问题

  10. 检查:变压器是否损坏
  11. 检查:终端电阻是否正确
  12. 解决:更换变压器,检查电阻值

调试步骤:

// 检查PHY状态
void Debug_CheckPHYStatus(void) {
    uint16_t bsr = ETH_ReadPHYRegister(PHY_BSR);

    printf("=== PHY状态 ===\n");

    if (bsr & 0x0004) {
        printf("链路状态: 已连接\n");
    } else {
        printf("链路状态: 未连接\n");
    }

    if (bsr & 0x0020) {
        printf("自动协商: 完成\n");
    } else {
        printf("自动协商: 进行中\n");
    }

    if (bsr & 0x0001) {
        printf("扩展能力: 支持\n");
    }

    // 读取链路伙伴能力
    uint16_t anlpar = ETH_ReadPHYRegister(PHY_ANLPAR);
    printf("链路伙伴能力: 0x%04X\n", anlpar);

    if (anlpar & 0x0100) {
        printf("  - 100BASE-TX 全双工\n");
    }
    if (anlpar & 0x0080) {
        printf("  - 100BASE-TX 半双工\n");
    }
    if (anlpar & 0x0040) {
        printf("  - 10BASE-T 全双工\n");
    }
    if (anlpar & 0x0020) {
        printf("  - 10BASE-T 半双工\n");
    }
}

问题2:数据传输错误

可能原因:

  1. 信号完整性问题
  2. 检查:差分阻抗是否为100Ω
  3. 检查:走线是否等长
  4. 解决:优化PCB布线

  5. EMI干扰

  6. 检查:是否有干扰源
  7. 检查:地平面是否完整
  8. 解决:添加共模扼流圈,改善屏蔽

  9. MAC配置问题

  10. 检查:MAC地址是否正确
  11. 检查:DMA配置是否正确
  12. 解决:检查配置参数

错误统计:

// 以太网错误计数器
typedef struct {
    uint32_t tx_errors;
    uint32_t rx_errors;
    uint32_t crc_errors;
    uint32_t collision_errors;
} ETH_ErrorCounter_t;

ETH_ErrorCounter_t eth_errors = {0};

// 错误监控
void ETH_MonitorErrors(void) {
    uint32_t dmasr = ETH->DMASR;

    // 检查发送错误
    if (dmasr & ETH_DMASR_TPS) {
        eth_errors.tx_errors++;
        printf("发送错误计数: %lu\n", eth_errors.tx_errors);
    }

    // 检查接收错误
    if (dmasr & ETH_DMASR_RPS) {
        eth_errors.rx_errors++;
        printf("接收错误计数: %lu\n", eth_errors.rx_errors);
    }

    // 清除错误标志
    ETH->DMASR = dmasr;
}

问题3:传输速率低

可能原因:

  1. 协商速度低
  2. 检查:协商结果是10Mbps还是100Mbps
  3. 解决:强制100Mbps模式

  4. DMA配置不当

  5. 检查:DMA突发长度
  6. 检查:缓冲区大小
  7. 解决:优化DMA配置

  8. CPU处理延迟

  9. 检查:中断处理时间
  10. 解决:使用DMA,优化中断处理

性能优化:

// 强制100Mbps全双工
void ETH_Force100MFullDuplex(void) {
    // 禁用自动协商,强制100Mbps全双工
    ETH_WritePHYRegister(PHY_BCR, 0x2100);

    // 配置MAC为100Mbps全双工
    ETH->MACCR |= ETH_MACCR_FES | ETH_MACCR_DM;

    printf("强制100Mbps全双工模式\n");
}

// 优化DMA配置
void ETH_OptimizeDMA(void) {
    // 增大DMA突发长度
    ETH->DMABMR &= ~ETH_DMABMR_PBL;
    ETH->DMABMR |= ETH_DMABMR_PBL_32Beat;

    // 使能存储转发模式
    ETH->DMAOMR |= ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;

    printf("DMA配置已优化\n");
}

实用技巧

1. LED指示灯配置

// 配置PHY LED指示灯
void ETH_ConfigureLED(void) {
    // LAN8720A LED配置
    // LED1: 链路状态/活动
    // LED2: 速度指示

    // 读取特殊模式寄存器
    uint16_t reg = ETH_ReadPHYRegister(0x1F);

    // 配置LED模式
    reg &= ~0x00F0;  // 清除LED配置位
    reg |= 0x0010;   // LED1: 链路/活动, LED2: 速度

    ETH_WritePHYRegister(0x1F, reg);

    printf("LED配置完成\n");
}

2. 网络唤醒(WoL)

// 配置网络唤醒
void ETH_ConfigureWakeOnLAN(void) {
    // 使能魔术包检测
    ETH->MACPMTCSR |= ETH_MACPMTCSR_MPE;

    // 使能PMT中断
    ETH->MACIMR &= ~ETH_MACIMR_PMTIM;

    printf("网络唤醒已使能\n");
}

// 进入低功耗模式
void ETH_EnterLowPower(void) {
    // 配置PHY进入低功耗模式
    uint16_t bcr = ETH_ReadPHYRegister(PHY_BCR);
    bcr |= 0x0800;  // 使能低功耗模式
    ETH_WritePHYRegister(PHY_BCR, bcr);

    // 配置MAC进入低功耗模式
    ETH->MACPMTCSR |= ETH_MACPMTCSR_PD;

    printf("进入低功耗模式\n");
}

3. 环回测试

// PHY环回测试
void ETH_LoopbackTest(void) {
    printf("开始环回测试...\n");

    // 使能PHY内部环回
    uint16_t bcr = ETH_ReadPHYRegister(PHY_BCR);
    bcr |= 0x4000;  // 使能环回
    ETH_WritePHYRegister(PHY_BCR, bcr);

    // 准备测试数据
    uint8_t tx_data[64];
    uint8_t rx_data[64];

    for (int i = 0; i < 64; i++) {
        tx_data[i] = i;
    }

    // 发送数据
    ETH_TransmitFrame(tx_data, 64);
    HAL_Delay(10);

    // 接收数据
    uint16_t len = ETH_ReceiveFrame(rx_data, 64);

    // 验证数据
    bool pass = true;
    if (len == 64) {
        for (int i = 0; i < 64; i++) {
            if (tx_data[i] != rx_data[i]) {
                pass = false;
                break;
            }
        }
    } else {
        pass = false;
    }

    // 禁用环回
    bcr &= ~0x4000;
    ETH_WritePHYRegister(PHY_BCR, bcr);

    if (pass) {
        printf("环回测试通过\n");
    } else {
        printf("环回测试失败\n");
    }
}

4. 链路状态监控

// 链路状态监控
void ETH_MonitorLinkStatus(void) {
    static bool last_link_state = false;
    bool current_link_state;

    // 读取链路状态
    uint16_t bsr = ETH_ReadPHYRegister(PHY_BSR);
    current_link_state = (bsr & 0x0004) != 0;

    // 检测链路变化
    if (current_link_state != last_link_state) {
        if (current_link_state) {
            printf("链路已连接\n");

            // 读取协商结果
            uint16_t anlpar = ETH_ReadPHYRegister(PHY_ANLPAR);

            if (anlpar & 0x0100) {
                printf("速度: 100Mbps 全双工\n");
            } else if (anlpar & 0x0080) {
                printf("速度: 100Mbps 半双工\n");
            } else if (anlpar & 0x0040) {
                printf("速度: 10Mbps 全双工\n");
            } else if (anlpar & 0x0020) {
                printf("速度: 10Mbps 半双工\n");
            }
        } else {
            printf("链路已断开\n");
        }

        last_link_state = current_link_state;
    }
}

性能优化

提高吞吐量

1. 使用零拷贝技术:

// 零拷贝发送(直接使用DMA缓冲区)
bool ETH_TransmitZeroCopy(uint8_t **buffer, uint16_t len) {
    static uint32_t tx_index = 0;

    // 检查描述符是否可用
    if (DMATxDscrTab[tx_index].Status & ETH_DMATXDESC_OWN) {
        return false;
    }

    // 返回缓冲区指针
    *buffer = (uint8_t *)DMATxDscrTab[tx_index].Buffer1Addr;

    // 配置描述符
    DMATxDscrTab[tx_index].ControlBufferSize = len & ETH_DMATXDESC_TBS1;
    DMATxDscrTab[tx_index].Status = ETH_DMATXDESC_OWN |
                                     ETH_DMATXDESC_FS |
                                     ETH_DMATXDESC_LS |
                                     ETH_DMATXDESC_IC;

    // 启动发送
    if (ETH->DMASR & ETH_DMASR_TBUS) {
        ETH->DMASR = ETH_DMASR_TBUS;
        ETH->DMATPDR = 0;
    }

    // 更新索引
    tx_index = (tx_index + 1) % ETH_TXBUFNB;

    return true;
}

2. 增大缓冲区:

// 增大接收缓冲区数量
#define ETH_RXBUFNB        8  // 从4增加到8
#define ETH_TXBUFNB        4  // 从2增加到4

// 增大缓冲区大小
#define ETH_RX_BUF_SIZE    2048  // 从1524增加到2048
#define ETH_TX_BUF_SIZE    2048

降低延迟

1. 使用中断模式:

// 使能以太网中断
void ETH_EnableInterrupt(void) {
    // 使能接收中断
    ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE;

    // 配置NVIC
    NVIC_EnableIRQ(ETH_IRQn);
    NVIC_SetPriority(ETH_IRQn, 0);  // 最高优先级
}

// 中断处理函数
void ETH_IRQHandler(void) {
    uint32_t dmasr = ETH->DMASR;

    // 接收中断
    if (dmasr & ETH_DMASR_RS) {
        // 立即处理接收数据
        ETH_ProcessReceivedData();

        // 清除中断标志
        ETH->DMASR = ETH_DMASR_RS | ETH_DMASR_NIS;
    }
}

2. 优化协议栈:

// 使用轻量级TCP/IP协议栈(如lwIP)
// 配置lwIP选项以降低延迟

#define LWIP_NETIF_TX_SINGLE_PBUF  1  // 单个pbuf发送
#define TCP_SND_QUEUELEN           8  // 发送队列长度
#define MEMP_NUM_TCP_SEG           16 // TCP段数量
#define PBUF_POOL_SIZE             16 // pbuf池大小

总结

通过本文学习,你已经掌握了:

  • ✅ 以太网基础:MAC/PHY架构、MII/RMII接口、信号定义
  • ✅ PHY芯片:选型方法、引脚功能、地址配置
  • ✅ 变压器设计:作用原理、类型选择、电路连接
  • ✅ 硬件设计:电源设计、时钟设计、PCB布局
  • ✅ STM32配置:GPIO配置、PHY初始化、MAC配置
  • ✅ 数据传输:DMA描述符、发送接收、错误处理
  • ✅ 故障排除:常见问题诊断和解决方法

关键要点

  1. 硬件架构:
  2. MAC负责数据链路层
  3. PHY负责物理层
  4. 变压器提供隔离和阻抗匹配

  5. 接口选择:

  6. 嵌入式系统优先选择RMII
  7. RMII只需7根信号线
  8. 50MHz参考时钟

  9. 变压器设计:

  10. 必须使用网络变压器
  11. 提供电气隔离
  12. 实现阻抗匹配

  13. PCB设计:

  14. 差分对等长等宽
  15. 完整的地平面
  16. 良好的EMC措施

实践建议

初学者练习

  1. 基础通信:
  2. 搭建STM32以太网节点
  3. 实现简单的数据收发
  4. 测试链路状态

  5. PHY配置:

  6. 读写PHY寄存器
  7. 配置自动协商
  8. 监控链路状态

  9. 性能测试:

  10. 测试传输速率
  11. 测试延迟
  12. 优化配置

进阶项目

  1. TCP/IP协议栈:
  2. 移植lwIP协议栈
  3. 实现HTTP服务器
  4. 实现MQTT客户端

  5. 工业以太网:

  6. 实现Modbus TCP
  7. 实现EtherCAT从站
  8. 实现PROFINET设备

  9. 网络应用:

  10. 实现网络摄像头
  11. 实现数据采集器
  12. 实现远程控制

延伸阅读

相关文章

协议实现

  • lwIP协议栈移植
  • Modbus TCP实现

硬件设计

参考资料

数据手册

  1. LAN8720A Datasheet - Microchip PHY芯片
  2. DP83848 Datasheet - TI PHY芯片
  3. STM32F4 Reference Manual - 以太网章节

技术标准

  1. IEEE 802.3:以太网标准
  2. IEEE 802.3u:Fast Ethernet(100BASE-TX)
  3. IEEE 802.3ab:Gigabit Ethernet(1000BASE-T)

在线工具

  1. Ethernet Frame Calculator - 以太网帧分析
  2. PCB Impedance Calculator - 阻抗计算器
  3. Network Cable Tester - 网线测试工具

开源项目

  1. lwIP - 轻量级TCP/IP协议栈
  2. uIP - 微型TCP/IP协议栈
  3. FreeRTOS+TCP - FreeRTOS TCP/IP栈

常见应用场景

1. 工业控制

应用: - PLC通信 - 工业网关 - 数据采集

配置:100BASE-TX,Modbus TCP

2. 物联网设备

应用: - 智能家居 - 环境监测 - 远程控制

配置:10/100BASE-TX,MQTT

3. 嵌入式服务器

应用: - Web服务器 - 文件服务器 - 视频流服务器

配置:100BASE-TX,HTTP/FTP

4. 网络存储

应用: - NAS设备 - 网络硬盘 - 数据备份

配置:1000BASE-T,NFS/SMB

5. 医疗设备

应用: - 医疗监护 - 设备互联 - 数据传输

配置:100BASE-TX,HL7协议

附录

A. PHY芯片对比表

型号 厂商 接口 速度 封装 特点
LAN8720A Microchip RMII 10/100 QFN-24 成本低
DP83848 TI MII/RMII 10/100 LQFP-48 工业级
RTL8201F Realtek MII/RMII 10/100 QFN-32 集成LED
KSZ8081 Microchip MII/RMII 10/100 QFN-24 低功耗
LAN8742A Microchip RMII 10/100 QFN-24 改进版

B. 网络变压器对比表

型号 类型 隔离电压 插入损耗 应用
HR911105A 集成RJ45 1500V <1dB 通用
J0011D21BNL 集成RJ45 1500V <1dB 工业
7499111121 集成RJ45 1500V <1dB 汽车
H1102NL 分立变压器 1500V <0.8dB 高性能

C. 故障排查检查清单

硬件检查: - [ ] 网线连接正常 - [ ] RJ45接口无损坏 - [ ] 变压器安装正确 - [ ] 终端电阻值正确(49.9Ω) - [ ] 电源电压正常(3.3V) - [ ] 时钟信号正常(50MHz)

软件检查: - [ ] PHY地址配置正确 - [ ] GPIO复用功能配置正确 - [ ] MAC地址配置正确 - [ ] DMA描述符初始化正确 - [ ] 中断使能 - [ ] 协议栈配置正确

测试方法: - [ ] PHY ID读取正常 - [ ] 链路状态正常 - [ ] 环回测试通过 - [ ] 数据收发正常 - [ ] 性能测试达标


反馈与支持: - 如果你在学习过程中遇到问题,欢迎在评论区留言 - 分享你的项目经验,与其他学习者交流 - 发现文档错误?请提交Issue帮助我们改进

版权声明:本文采用 CC BY-SA 4.0 协议,欢迎分享和改编,但请注明出处。