资讯动态

dp83867cr 千兆以太网 PHY 驱动开发:从寄存器手册到 C 程序实战

发布时间:2026/10/13 3:49:25 来源:尧图企业网站定制
简介本资源面向嵌入式与物联网方向的驱动开发工程师提供德州仪器dp83867以太网控制器芯片的完整开发参考包含官方技术手册与C语言驱动源码两部分适合具备一定C语言与硬件接口基础、希望快速上手该芯片的中高级开发者。压缩包为rar格式共2个文件含1份pdf芯片手册与1个c源文件整体约1.35MB体积轻便便于随项目携带查阅。手册部分覆盖电气特性、引脚配置、寄存器描述与工作模式等关键内容是直接操作芯片的权威依据源码部分则展示初始化、数据收发、中断处理与错误处理等核心模块的实现思路可帮助读者理解MAC寄存器配置、DMA传输与网络帧收发流程。目前已有434人学习下载可作为驱动移植与以太网通信方案设计的实用参考。1. 从一份 PHY 芯片手册和一段 C 程序说起dp83867cr 到底怎么驱动很多做嵌入式以太网的工程师都有过这样的经历硬件同事把板子焊好网口灯不亮丢过来一份几十页的 PHY 芯片手册和一个能跑的 C 程序说你调一下。这份手册就是 dp83867cr.pdf这段程序就是围绕这颗千兆以太网 PHY 写的初始化与读写代码。标题里的两个东西一个是寄存器手册一个是驱动代码合起来就是怎么用软件把一颗 PHY 芯片从复位状态带到能正常收发数据。dp83867cr 是一颗常见的千兆以太网物理层收发器通过 RGMII 或 SGMII 接口和 MAC 相连通过 MDIO 管理接口读写内部寄存器。它解决的问题很具体MAC 层芯片比如很多 SoC 内置的 GMAC只负责数据链路层真正把数字信号变成差分模拟信号、协商速率双工、检测链路状态的活儿是 PHY 干的。适合谁看做嵌入式 Linux 网口调试的、写裸机 BSP 的、以及被网口不通卡住的硬件软件联调人员。下面我按手册怎么读、代码怎么写、坑在哪的顺序把这条链路讲透。2. 读懂 dp83867cr.pdf寄存器地图与初始化顺序2.1 先分清手册里的三类寄存器拿到 dp83867cr.pdf不要从第一页顺序读。先翻到寄存器列表把寄存器分成三类这是后面写代码的地图。第一类是标准寄存器地址 0x00 到 0x1F遵循 IEEE 802.3 的通用定义。比如 0x00 是 BMCR基本模式控制0x01 是 BMSR基本模式状态0x02 是 PHYIDR10x03 是 PHYIDR2。这部分任何 PHY 都差不多读 ID 就是读 0x02 和 0x03 拼起来。第二类是扩展寄存器地址 0x1F 是寄存器页选择REGCR/ADDAR 机制通过它切换到不同的页再访问页内偏移。dp83867cr 的很多功能比如 RGMII 延时、时钟输出、LED 配置都藏在扩展页里。手册里会有一张Extended Register Space的表标出每页每偏移的含义。第三类是状态和自协商相关寄存器比如 0x09 百兆控制、0x0A 千兆状态、0x11 PHY 状态。链路通不通、协商到多少速率主要看这几个。提示不同批次的芯片手册页码会变但寄存器地址和位定义是稳定的写代码时以位定义为准不要背页码。2.2 用 MDIO 读一个寄存器的完整时序MDIO 是两根线MDC 时钟和 MDIO 数据。MAC 作为主PHY 作为从。一次读操作的帧结构是固定的32 位前导码全 1、起始码 01、读操作码 10、5 位 PHY 地址、5 位寄存器地址、2 位转向、16 位数据。写操作把操作码换成 01转向位换成 10。很多 SoC 的 GMAC 控制器硬件帮你拼好了这个帧你只需要往一个数据寄存器写命令、往地址寄存器写目标然后轮询完成位。但如果是裸机用 GPIO 模拟 MDIO就得自己按位翻转。下面这段是 GPIO 模拟 MDIO 读寄存器的核心逻辑用 C 写/* 假设 mdio_clk / mdio_data 已经配置为输出/输入可切换 */ static void mdio_delay(void) { /* 根据主频调整通常几百纳秒MDC 不超过 2.5MHz */ for (volatile int i 0; i 50; i); } /* 写一个 bit 到 MDIO */ static void mdio_write_bit(int bit) { if (bit) MDIO_DATA_HIGH(); else MDIO_DATA_LOW(); mdio_delay(); MDIO_CLK_HIGH(); mdio_delay(); MDIO_CLK_LOW(); mdio_delay(); } /* 读一个 bit主机在时钟上升沿前释放数据线由 PHY 驱动 */ static int mdio_read_bit(void) { int bit; MDIO_DATA_INPUT(); /* 切换为输入 */ mdio_delay(); MDIO_CLK_HIGH(); mdio_delay(); bit MDIO_DATA_READ(); MDIO_CLK_LOW(); mdio_delay(); return bit; } /* 读 PHY 寄存器phy_addr 5 位reg_addr 5 位 */ int dp83867_mdio_read(int phy_addr, int reg_addr) { int i, data 0; MDIO_DATA_OUTPUT(); for (i 0; i 32; i) mdio_write_bit(1); /* 前导码 */ mdio_write_bit(0); mdio_write_bit(1); /* 起始码 01 */ mdio_write_bit(1); mdio_write_bit(0); /* 读操作 10 */ for (i 4; i 0; i--) mdio_write_bit((phy_addr i) 1); for (i 4; i 0; i--) mdio_write_bit((reg_addr i) 1); mdio_read_bit(); /* 转向位主机释放 */ mdio_read_bit(); /* PHY 驱动第二转向位 */ for (i 15; i 0; i--) { data (data 1) | mdio_read_bit(); } return data; }逻辑说明前导码 32 个 1 是给 PHY 同步用的少了它有些 PHY 直接不响应。起始码和操作码是固定协议。PHY 地址和寄存器地址都是高位先发。转向位是总线方向切换的关键读操作时主机在第一个转向位后必须把数据线切成输入否则会和 PHY 抢总线。参数说明mdio_delay的循环次数要保证 MDC 频率不超过手册规定的上限dp83867cr 一般支持到 25MHz但保守用 2.5MHz 更稳太快会导致 PHY 采样不到。2.3 初始化顺序复位、等自协商、配 RGMII 延时初始化不是随便写几个寄存器就行顺序错了会出玄学问题。我一般按这个顺序第一步软复位。写 BMCR0x00的 bit15 为 1然后轮询直到该位自动清零说明复位完成。复位后所有寄存器回到默认值。第二步读 PHY ID 确认通信正常。读 0x02 和 0x03拼成 32 位 ID和手册里的 OUI 对比。如果读出来是 0x0000 或 0xFFFF说明 MDIO 根本没通别往下走了。第三步配置 RGMII 延时。这是 dp83867cr 最容易翻车的地方。RGMII 有 TX 和 RX 两组数据线加时钟如果 MAC 侧和 PHY 侧的延时没配好会出现能协商上但丢包或者完全不通。dp83867cr 通过扩展寄存器配置内部延时常见做法是 TX 延时和 RX 延时各加约 2ns。具体寄存器在扩展页里手册会给出位定义。第四步触发自协商并等待完成。写 BMCR 的 bit12 为 1 启动自协商然后轮询 BMSR0x01的 bit5自协商完成和 0x11 的链路状态位。第五步读协商结果。读 0x0A 千兆状态和 0x01 的速率位确认协商到 1000M 全双工还是别的。int dp83867_init(int phy_addr) { int id1, id2, id; /* 1. 软复位 */ dp83867_mdio_write(phy_addr, 0x00, 0x8000); while (dp83867_mdio_read(phy_addr, 0x00) 0x8000); /* 2. 读 ID */ id1 dp83867_mdio_read(phy_addr, 0x02); id2 dp83867_mdio_read(phy_addr, 0x03); id (id1 16) | id2; if (id 0 || id 0xFFFFFFFF) return -1; /* MDIO 不通 */ /* 3. 配 RGMII 延时具体寄存器地址以手册为准 */ dp83867_write_ext(phy_addr, 0x01, 0x00, 0x0002); /* TX 延时示例 */ dp83867_write_ext(phy_addr, 0x01, 0x01, 0x0002); /* RX 延时示例 */ /* 4. 启动自协商 */ dp83867_mdio_write(phy_addr, 0x00, 0x1140); /* 自协商使能 重启 */ /* 5. 等待完成 */ for (int t 0; t 1000; t) { if (dp83867_mdio_read(phy_addr, 0x01) 0x0020) break; mdelay(10); } return 0; }逻辑说明复位后必须等 bit15 清零不能立刻写别的寄存器。ID 检查是判断 MDIO 通不通的第一道关。RGMII 延时配置是这颗 PHY 的核心写错就是丢包。自协商等待要有超时否则死循环。参数说明0x1140是自协商使能加重启的典型值具体位含义查 BMCR 定义超时 1000 次乘 10ms 是 10 秒够自协商完成。3. 把 C 程序跑起来从裸机到 Linux 驱动的落地路径3.1 裸机环境先点亮 MDIO 再谈数据裸机调试的顺序是先通管理再通数据。MDIO 通了你能读寄存器就成功了一半。我一般先写一个最简单的测试读 PHY ID打印出来。如果 ID 对说明 MDIO 时序、PHY 地址、供电都正常。PHY 地址怎么确定dp83867cr 的 PHY 地址由硬件上电时的几个引脚电平决定通常是 0 到 31 之间的某个值。手册里会有一张PHY Address Strap表。如果不知道可以暴力扫描 0 到 31读 0x02 和 0x03看哪个地址能读出合理的 ID。MDIO 通了之后再配 RGMII 延时然后启动自协商。自协商完成后读链路状态。如果链路 up但 ping 不通问题多半在 RGMII 延时或者 MAC 侧配置。3.2 Linux 环境设备树和 PHY 驱动怎么配合在 Linux 下dp83867cr 通常由内核的dp83867驱动支持。你要做的是在设备树里正确描述 MDIO 总线和 PHY 节点。mdio { phy0: ethernet-phy0 { compatible ethernet-phy-ieee802.3-c22; reg 0; /* PHY 地址 */ ti,rx-internal-delay 0x8; /* RX 延时单位看驱动文档 */ ti,tx-internal-delay 0xa; /* TX 延时 */ ti,fifo-depth 0x1; /* FIFO 深度 */ }; }; gmac { phy-handle phy0; phy-mode rgmii-id; /* 关键让 PHY 内部加延时 */ status okay; };逻辑说明phy-mode是核心。rgmii表示不加延时rgmii-id表示 PHY 内部加 TX 和 RX 延时rgmii-txid只加 TXrgmii-rxid只加 RX。选错了就是丢包或不通。ti,rx-internal-delay和ti,tx-internal-delay的具体数值要查内核驱动文档和手册不同板子走线长度不同值要调。参数说明reg 0是 PHY 地址必须和硬件 strap 一致fifo-depth影响吞吐一般用默认。设备树改完编译内核或覆盖 dtb启动后dmesg | grep phy看有没有识别到。如果看到PHY ID 0x...说明驱动认到了。然后ethtool eth0看链路状态和协商速率。3.3 用 ethtool 和 mii-tool 验证链路Linux 下验证 PHY 状态最直接的是ethtoolethtool eth0 # 看 Speed、Duplex、Link detected ethtool -S eth0 # 看收发包统计有没有 CRC 错误 mii-tool -v eth0 # 看协商结果老工具但直观如果Link detected: yes但Speed: 100Mb/s而你想要千兆检查网线是不是超五类以上、对端是不是千兆口、自协商有没有被强制成百兆。如果-S里rx_crc_errors一直涨基本是 RGMII 延时不对或者时钟质量差。4. 避坑与排查dp83867cr 调试中最容易翻车的五件事4.1 坑一MDIO 读出来全是 0xFFFF现象读任何寄存器都返回 0xFFFF 或 0x0000。原因MDIO 时序不对、PHY 地址错、PHY 没供电或复位没释放。解决先用示波器看 MDC 有没有波形频率是不是太高确认 PHY 地址 strap 引脚电平量 PHY 的供电和复位引脚。我遇到过 MDC 频率跑到 30MHz 以上PHY 直接不响应降到 2.5MHz 就好了。4.2 坑二链路能 up 但 ping 不通现象ethtool显示 Link detected yes但 ping 丢包或完全不通。原因RGMII 延时配置错误或者 MAC 侧和 PHY 侧的延时重复加了。解决确认phy-mode和 PHY 内部延时配置不冲突。如果 MAC 已经加了延时PHY 就不要再加反之亦然。用rgmii-id时PHY 内部加延时MAC 侧设备树不要再配延时。4.3 坑三千兆协商不上只能跑百兆现象链路 up 但速率只有 100M。原因网线质量差、对端设备限制、自协商被强制、或者 RGMII 时钟在千兆下质量不够。解决换六类线直连测试检查 BMCR 有没有被写成强制百兆用示波器看 RGMII 时钟在 125MHz 下的眼图走线太长或阻抗不匹配会导致千兆失败。4.4 坑四复位后寄存器写不进去现象软复位后写配置寄存器读回来还是默认值。原因复位没完成就写、或者写的是扩展寄存器但页没切对。解决复位后必须轮询 BMCR bit15 直到清零写扩展寄存器前先写 0x1F 切页写完再切回。dp83867cr 的扩展寄存器访问是间接的页选错就写到别的页去了。4.5 坑五LED 不亮或状态不对现象网口能通但 LED 不亮或者链路 up 了 LED 还是灭的。原因LED 配置在扩展寄存器里默认配置可能和板子 LED 接法不匹配。解决查手册的 LED 配置页根据 LED 是共阳还是共阴、接在哪个引脚改对应的极性位。这个不影响通信但影响现场判断建议一并配好。5. 进阶技巧用寄存器 dump 和环回测试快速定位问题调 PHY 最有效的手段不是猜是 dump。把关键寄存器的值全部读出来和手册默认值对比差异点就是线索。我一般写一个 dump 函数把 0x00 到 0x1F 全读一遍打印再读几个关键扩展页。void dp83867_dump(int phy_addr) { printf(Standard registers:\n); for (int r 0; r 0x20; r) { printf( [0x%02X] 0x%04X\n, r, dp83867_mdio_read(phy_addr, r)); } /* 扩展页示例切到页 1读偏移 0 到 3 */ dp83867_mdio_write(phy_addr, 0x1F, 0x0001); for (int o 0; o 4; o) { dp83867_mdio_write(phy_addr, 0x1E, o); printf( Ext page1 [0x%02X] 0x%04X\n, o, dp83867_mdio_read(phy_addr, 0x1F)); } dp83867_mdio_write(phy_addr, 0x1F, 0x0000); /* 切回页 0 */ }逻辑说明标准寄存器直接读。扩展寄存器要先写 0x1F 选页再写 0x1E 选偏移然后读 0x1F 拿数据。不同手册的间接访问寄存器地址可能不同以手册为准。参数说明dump 出来的值要和手册的默认值表对比重点看 BMCR、BMSR、PHYSTS 和 RGMII 延时相关寄存器。另一个利器是环回测试。dp83867cr 支持内部环回把 BMCR 的 bit14 置 1PHY 会把发送的数据直接环回给接收不经过网线。这样可以在不接网线的情况下验证 MAC 到 PHY 的数字通路。如果环回能通说明 RGMII 接口和寄存器配置没问题问题在模拟侧或网线如果环回不通问题在数字接口或延时配置。# Linux 下用 ethtool 做环回测试如果驱动支持 ethtool -t eth0 # 或者手动置环回位后 ping 自己我自己的习惯是每调一块新板子先 dump 一遍默认寄存器存档改配置后再 dump 一遍diff 一下。这样出了问题能快速回退不用凭记忆猜改了哪些寄存器。血泪经验是RGMII 延时这种参数不同板子走线不同值不能照抄一定要实测。示波器看 RGMII 时钟和数据的关系或者用误码率测试比盲调靠谱得多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑