1. 车载以太网PHY基础与100BASE-T1链路建立
任务目标
用最小可复现的软件模型完成 100BASE-T1 PHY 的上电初始化、链路建立与链路状态寄存器读取,最终确认 Link Up。真实硬件上,PHY 通过 MDIO 管理接口暴露寄存器;本案例用一段可编译运行的 C 代码模拟 PHY 寄存器与链路状态机,把“上电→复位→配置→自协商/链路建立→读状态”的完整闭环跑通,便于在没有板子的情况下验证逻辑,也便于后续替换为真实 MDIO 读写。
关键寄存器与流程
- BMCR(0x00):bit15 软复位,bit12 自协商使能,bit9 重启自协商。
- BMSR(0x01):bit2 Link Status,bit5 自协商完成。
- PHY 专用寄存器 0x10:bit0 主从模式(1=Master),bit1 链路使能。
- 流程:上电→软复位→等待复位完成→配置主从与自协商→触发链路建立→轮询 BMSR 直到 Link Up。
完整实现
下面代码用结构体保存 PHY 寄存器,用 mdio_read/mdio_write 模拟 MDIO 访问,用 phy_tick 模拟链路建立需要的时间。核心入口 phy_init_and_link 完成初始化并轮询到 Link Up。
#include <stdio.h>
#include <stdint.h>
/* 寄存器地址 */
#define REG_BMCR 0x00
#define REG_BMSR 0x01
#define REG_PHYCR 0x10 /* 100BASE-T1 专用:主从/使能 */
/* BMCR 位 */
#define BMCR_RESET (1u << 15)
#define BMCR_ANENABLE (1u << 12)
#define BMCR_ANRESTART (1u << 9)
/* BMSR 位 */
#define BMSR_LINK (1u << 2)
#define BMSR_ANEGDONE (1u << 5)
/* PHYCR 位 */
#define PHYCR_MASTER (1u << 0)
#define PHYCR_LINKEN (1u << 1)
typedef struct {
uint16_t reg[32];
int link_timer; /* 链路建立倒计时,模拟物理层握手时间 */
} Phy;
static Phy g_phy;
/* 模拟 MDIO 读 */
static uint16_t mdio_read(Phy *p, uint8_t addr) {
return p->reg[addr & 0x1F];
}
/* 模拟 MDIO 写,并处理复位与链路触发副作用 */
static void mdio_write(Phy *p, uint8_t addr, uint16_t val) {
p->reg[addr & 0x1F] = val;
if (addr == REG_BMCR && (val & BMCR_RESET)) {
/* 软复位:清状态,复位位自清 */
p->reg[REG_BMSR] = 0;
p->reg[REG_BMCR] &= ~BMCR_RESET;
p->link_timer = 0;
}
if (addr == REG_BMCR && (val & BMCR_ANRESTART)) {
/* 触发链路建立:需要若干 tick 才 Link Up */
p->link_timer = 3;
p->reg[REG_BMSR] &= ~(BMSR_LINK | BMSR_ANEGDONE);
}
}
/* 模拟时间推进:倒计时到 0 时链路建立完成 */
static void phy_tick(Phy *p) {
if (p->link_timer > 0) {
p->link_timer--;
if (p->link_timer == 0) {
p->reg[REG_BMSR] |= (BMSR_LINK | BMSR_ANEGDONE);
}
}
}
/* 上电初始化并建立链路,返回 0 成功,-1 超时 */
static int phy_init_and_link(Phy *p, int is_master, int max_poll) {
/* 1. 软复位 */
mdio_write(p, REG_BMCR, BMCR_RESET);
/* 2. 配置主从模式并开启链路 */
uint16_t cr = PHYCR_LINKEN | (is_master ? PHYCR_MASTER : 0);
mdio_write(p, REG_PHYCR, cr);
/* 3. 使能自协商并重启,触发链路建立 */
mdio_write(p, REG_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
/* 4. 轮询 BMSR 直到 Link Up */
for (int i = 0; i < max_poll; i++) {
phy_tick(p);
uint16_t bmsr = mdio_read(p, REG_BMSR);
if (bmsr & BMSR_LINK) {
printf("Link Up after %d poll(s), BMSR=0x%04X\n", i + 1, bmsr);
return 0;
}
}
printf("Link timeout, BMSR=0x%04X\n", mdio_read(p, REG_BMSR));
return -1;
}
int main(void) {
int rc = phy_init_and_link(&g_phy, 1 /* Master */, 10);
printf("PHYCR=0x%04X BMCR=0x%04X\n",
mdio_read(&g_phy, REG_PHYCR),
mdio_read(&g_phy, REG_BMCR));
return rc == 0 ? 0 : 1;
}
运行与验证
gcc -Wall -O2 phy_link.c -o phy_link && ./phy_link
判定标准:程序退出码为 0,输出包含 Link Up,且打印的 BMSR 中 bit2(Link) 与 bit5(自协商完成) 均为 1。若把 max_poll 改为小于 3,会走超时分支并返回 1,可用于验证失败路径。
接入真实硬件
把 mdio_read/mdio_write 替换为真实 MDIO 控制器读写即可:REG_BMCR/REG_BMSR 是 IEEE 802.3 标准寄存器,REG_PHYCR(0x10) 的主从位需按具体 PHY 数据手册确认。真实链路建立时间由物理层决定,轮询上限应结合手册的链路建立时间设置。