1. 从一个真实的调试场景说起去年冬天我在一块RK3568的开发板上调一个以太网问题。现象很典型系统启动后ifconfig能看到eth0但ethtool eth0读出来的链路状态永远是Link detected: no插上网线后指示灯也不亮。内核日志里没有明显的报错dmesg | grep phy只打印了一行stmmaceth fe010000.ethernet: PHY ID 0x0000——PHY的ID读出来是全零这基本等于MDIO总线上什么都没读到。这个现象把我拉回到了最底层的问题Linux的PHY驱动到底是怎么通过MDIO总线去访问PHY寄存器的为什么ID会读成全零是设备树里PHY地址配错了还是MDIO时钟分频不对还是PHY根本没上电要回答这些问题光看驱动代码不够得把MDIO总线的时序、PHY寄存器的标准布局、以及RK3568这颗SoC的MAC控制器是怎么把MDIO信号发出去的全部串起来看。这篇博文就是那次调试的完整复盘我会从MDIO总线的物理层讲起一路走到RK3568的寄存器操作和Linux PHY子系统的驱动模型把中间每一个环节都拆开。适合读这篇内容的人正在做嵌入式Linux网络驱动开发的工程师、在RK3568或类似瑞芯微平台上调试以太网的同行、以及想搞清楚Linux网络子系统底层机制的学习者。不需要你事先精通MDIO协议但最好对Linux设备驱动模型和寄存器操作有基本概念。2. MDIO总线到底是个什么东西2.1 两根线撑起的管理通道MDIO的全称是Management Data Input/Output它是IEEE 802.3标准里定义的一套串行管理接口。物理上就两根线一根是MDCManagement Data Clock一根是MDIOManagement Data Input/Output。MAC控制器作为主设备PHY作为从设备MAC通过这两根线去读写PHY内部的标准寄存器。你可以把它理解成一条极简的I2C总线——只有时钟线和数据线没有片选靠帧格式里的PHY地址来区分总线上挂的多个PHY。一条MDIO总线上最多可以挂32个PHY5位地址每个PHY内部有32个标准寄存器5位寄存器地址地址空间是5510位。为什么以太网要单独搞一套管理接口而不是复用数据通道原因很实际PHY的配置和状态查询是低频操作不需要走高速数据路径。把管理通道独立出来MAC在正常收发数据的同时可以随时去读PHY的链路状态、协商结果、错误计数互不干扰。而且MDIO的时钟频率通常只有1MHz到2.5MHz对信号完整性的要求远低于千兆数据线。2.2 帧格式每一个bit都有讲究MDIO的一帧是64个bit结构非常固定。我把它拆成几段来说字段位数含义Preamble32前导码全1用于同步ST2起始码固定为01OP2操作码10读01写PHYAD5PHY地址REGAD5寄存器地址TA2转向位读操作时MAC释放总线DATA16数据Idle-空闲前导码32个bit全为1作用是让PHY的接收逻辑锁定时钟相位。ST固定为01这是802.3标准规定的不能改。OP两位决定是读还是写。接下来5位PHY地址和5位寄存器地址然后是两个转向位TA。TA这两位是MDIO协议里最容易被忽略但最关键的细节。读操作时MAC发完寄存器地址后要释放MDIO线由PHY来驱动。第一个TA位MAC输出高阻实际表现为线上是1第二个TA位PHY输出0表示它接管了总线。写操作时MAC不释放总线TA两位都是10。注意很多PHY芯片对前导码的要求是可以配置的有些支持省略前导码来加快访问速度但在标准模式下必须发满32个1。如果你在调试时发现PHY完全没响应先确认前导码有没有发对。2.3 Clause 22和Clause 45的区别上面说的是Clause 22的帧格式这是最经典的MDIO协议。后来为了支持万兆和更复杂的PHY802.3又定义了Clause 45帧格式变成了间接寻址先写一个地址寄存器再读写数据寄存器。Clause 45的帧里ST变成了00OP扩展到了2位但含义不同还多了一个DEVAD字段来区分设备类型。RK3568的GMAC控制器同时支持Clause 22和Clause 45具体用哪种取决于PHY芯片。大部分千兆PHY比如RTL8211、YT8511用的是Clause 22万兆PHY才会用到Clause 45。你在设备树里配PHY的时候如果PHY是Clause 45的需要额外指定device_type。3. Linux PHY子系统的分层架构3.1 三层结构MAC驱动、PHY驱动、PHY核心层Linux内核的网络子系统把PHY的管理抽象成了三层最底层是MAC驱动比如RK3568用的stmmac驱动。它负责操作MAC控制器里的寄存器包括MDIO控制器的配置、MDIO帧的发送和接收。MAC驱动不关心PHY具体是什么型号它只提供read和write两个回调函数。中间层是PHY核心层drivers/net/phy/phy.c和phy_device.c它维护了所有已注册PHY驱动的链表负责在MDIO总线上扫描PHY、匹配驱动、调用驱动提供的回调。核心层还实现了通用的PHY状态机处理链路检测、自协商、节能等通用逻辑。最上层是PHY驱动比如drivers/net/phy/realtek.c里就包含了RTL8211系列的支持。PHY驱动知道具体芯片的寄存器布局负责配置芯片特有的功能比如RGMII延时、LED行为、中断使能等。这种分层的意义在于换一颗PHY芯片只需要换最上层的PHY驱动MAC驱动和核心层完全不用动。反过来换一个SoC平台MAC驱动要重写但PHY驱动可以复用。3.2 设备树里的PHY描述在RK3568上MAC和PHY的连接关系是通过设备树描述的。一个典型的配置长这样gmac1 { phy-mode rgmii; clock_in_out output; snps,reset-gpio gpio3 RK_PB7 GPIO_ACTIVE_LOW; snps,reset-active-low; snps,reset-delays-us 0 20000 100000; assigned-clocks cru SCLK_GMAC1_RX_TX; assigned-clock-parents cru SCLK_GMAC1_RGMII_SPEED; phy-handle rgmii_phy1; status okay; }; mdio1 { rgmii_phy1: ethernet-phy0 { compatible ethernet-phy-ieee802.3-c22; reg 0x0; }; };这里有几个关键点。phy-mode指定了MAC和PHY之间的数据接口类型RK3568支持RGMII和RMII。phy-handle指向MDIO节点下的PHY子节点。reg 0x0就是PHY在MDIO总线上的地址。snps,reset-gpio和snps,reset-delays-us是PHY复位相关的配置。这三个延时值分别对应复位拉低前的等待时间、复位拉低保持时间、复位拉高后的等待时间。单位是微秒。很多PHY芯片要求复位拉高后等待至少10ms才能访问寄存器如果这个时间不够读出来的ID就是全零或者乱码。实操心得我在RK3568上遇到过好几次PHY ID读成全零的情况最后查出来都是复位延时不够。把snps,reset-delays-us的第三个值从10000改成100000之后问题就消失了。这个值宁可给大一点PHY复位后多等一会儿不会有副作用。3.3 PHY驱动的匹配过程内核启动时PHY核心层会做这几件事遍历MDIO总线上所有可能的PHY地址0到31对每个地址读取寄存器1和寄存器2PHYIDR1和PHYIDR2把读到的ID和已注册的PHY驱动里的phy_id和phy_id_mask做匹配匹配成功后调用驱动的probe函数PHYIDR1是寄存器2PHYIDR2是寄存器3组合起来是一个32位的ID。高16位是OUI组织唯一标识符低16位是厂商自定义的型号和版本号。比如RTL8211F的ID是0x001cc916YT8511的ID是0x0000a011。如果读出来的ID是0x0000或者0xffff说明MDIO通信根本没成功。0xffff通常是总线上拉电阻把线拉高了但PHY没响应0x0000则可能是PHY没上电或者复位没完成。4. RK3568的MDIO控制器寄存器操作4.1 GMAC控制器里的MDIO模块RK3568的GMAC控制器基于Synopsys DesignWare MAC内部集成了一个MDIO主控制器。这个控制器负责把软件写进去的命令转换成MDIO总线上的时序波形。相关的寄存器主要有两个MAC_MDIO_ADDRESS偏移0x200配置PHY地址、寄存器地址、操作码、时钟分频MAC_MDIO_DATA偏移0x204读写的数据MAC_MDIO_ADDRESS的位域布局大致是这样的位域名称含义[15:8]PAPHY地址[20:16]RDA寄存器地址[21]CR时钟范围0分频系数用CSR1用默认[25:22]CSR时钟分频系数[27:26]ST起始码[29:28]GOC操作码01写11读时钟分频的计算是MDIO调试里最容易出错的地方。MDIO的时钟频率不能超过2.5MHz而GMAC的时钟源通常是50MHz或100MHz。分频系数CSR的计算公式是MDC频率 GMAC时钟 / (2 * (CSR 1))假设GMAC时钟是50MHz想要MDC频率在1MHz左右那么CSR 50 / (2 * 1) - 1 24。如果CSR配错了MDC频率太高PHY可能来不及响应读出来的数据就是错的。4.2 一次完整的MDIO读操作在stmmac驱动里MDIO读操作的代码路径大致是这样的static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) { struct net_device *ndev bus-priv; struct stmmac_priv *priv netdev_priv(ndev); unsigned int mii_address priv-hw-mii.addr; unsigned int mii_data priv-hw-mii.data; u32 value; int data; value (phyaddr 8) | (phyreg 16); value | MII_READ_CMD; value | (priv-clk_csr 22); writel(value, priv-ioaddr mii_address); /* 等待操作完成 */ do { value readl(priv-ioaddr mii_address); } while (value MII_BUSY); data readl(priv-ioaddr mii_data); return data 0xffff; }这段代码做了几件事把PHY地址、寄存器地址、操作码、时钟分频拼成一个32位的值写到MAC_MDIO_ADDRESS寄存器。然后轮询这个寄存器的BUSY位等硬件完成MDIO帧的发送和接收。最后从MAC_MDIO_DATA寄存器读出16位数据。硬件层面MDIO控制器收到这个命令后会自动生成前导码、起始码、操作码、地址然后在TA位之后采样MDIO线上的数据把结果放到MAC_MDIO_DATA寄存器里。注意轮询BUSY位的时候一定要加超时保护。如果PHY没接或者MDIO线断了BUSY位可能永远不释放代码就死循环了。标准驱动里通常用readl_poll_timeout来做带超时的轮询。4.3 用devmem直接操作寄存器验证调试阶段我经常用devmem工具直接读写寄存器来验证MDIO控制器是否工作正常。假设GMAC1的寄存器基地址是0xfe010000那么# 读MAC_MDIO_ADDRESS确认当前配置 devmem 0xfe010200 # 发起一次读PHY地址0、寄存器2的操作 # PHYAD0, REGAD2, GOC11(读), CSR24 # value (08) | (216) | (326) | (2422) | (121) devmem 0xfe010200 32 0x0C620200 # 读MAC_MDIO_DATA看返回的数据 devmem 0xfe010204如果返回的PHY ID是0x001cc916说明MDIO通信正常。如果是0x0000或0xffff就要检查PHY的供电、复位、时钟。这个方法的好处是不依赖内核驱动可以在驱动还没加载或者驱动有问题的时候直接从硬件层面确认MDIO总线是否通。我在排查一个PHY ID读不到的问题时就是先用devmem确认了MDIO控制器本身没问题然后把范围缩小到了PHY的复位电路上。5. PHY标准寄存器的含义与调试价值5.1 前16个寄存器的标准定义IEEE 802.3规定了PHY的前16个寄存器0到15的标准含义所有符合标准的PHY都必须实现这些寄存器。后面的寄存器是厂商自定义的。地址名称含义0BMCR基本控制复位、自协商使能、速率选择1BMSR基本状态链路状态、自协商完成2PHYIDR1PHY ID高16位3PHYIDR2PHY ID低16位4ANAR自协商通告5ANLPAR链路伙伴自协商能力6ANER自协商扩展7ANNPTR自协商下一页8ANNPRR链路伙伴下一页9MSCR主从控制10MSR主从状态11-14-保留15ESCR扩展状态BMCR的bit15是软复位写1后PHY会复位所有寄存器完成后自动清零。bit12是自协商使能bit13是速率选择bit8是双工模式。调试时如果链路起不来第一件事就是读BMSR看bit2链路状态和bit5自协商完成。5.2 通过寄存器判断链路问题回到开头那个案例。我用devmem读到的PHY ID是全零说明MDIO通信失败。但MDIO控制器本身是好的因为读操作能完成BUSY位正常释放所以问题在PHY侧。排查步骤是这样的用万用表量PHY的供电引脚3.3V正常量复位引脚发现复位拉高后只有1ms就释放了而RTL8211要求至少10ms检查设备树里的snps,reset-delays-us第三个值是1000010ms但实际测量只有1ms追查发现复位GPIO的驱动能力不足加上拉电阻后恢复正常这个案例说明PHY ID读不到不一定是MDIO协议的问题很可能是PHY本身没有进入正常工作状态。寄存器读出来全零只是表象。实操心得调试PHY问题时准备一个示波器或者逻辑分析仪直接抓MDC和MDIO两根线。正常的MDIO读操作你应该能看到32个时钟的前导码、01起始码、10操作码、5位PHY地址、5位寄存器地址然后TA位之后PHY拉低一个时钟接着16位数据。如果波形不对问题在MAC侧如果波形对但PHY没响应问题在PHY侧。5.3 厂商自定义寄存器的调试标准寄存器之外每颗PHY都有大量厂商自定义寄存器。比如RTL8211F的寄存器31是扩展寄存器地址选择先写31选择要访问的扩展寄存器页再读写对应的数据寄存器。YT8511的寄存器0x11控制RGMII延时。这些自定义寄存器在调试RGMII时序问题时特别有用。RK3568和PHY之间的RGMII接口有TX和RX两组延时如果PCB走线长度不匹配就需要通过PHY的延时寄存器来补偿。通常的做法是# 以YT8511为例读寄存器0x11 devmem 0xfe010200 32 0x0C620220 # PHYAD0, REGAD0x11 devmem 0xfe010204 # 写寄存器0x11设置TX延时 devmem 0xfe010200 32 0x08620220 # 写操作 devmem 0xfe010204 32 0x0000XX00具体写什么值要看PHY的数据手册和实际的眼图测试结果。一般来说TX延时和RX延时的组合有十几种需要逐一尝试找到最稳定的配置。6. 常见问题与排查技巧实录6.1 PHY ID读出来是0xffff或0x0000这是最常见的问题原因通常有这几类现象可能原因排查方法ID0xffffMDIO线上拉电阻正常但PHY无响应检查PHY供电、复位、时钟ID0x0000PHY未上电或复位未完成量供电和复位引脚ID0x0000MDIO时钟分频错误用示波器量MDC频率ID0xffffPHY地址配错扫描0-31所有地址ID随机变化MDIO线受干扰检查走线和上拉电阻PHY地址配错是很隐蔽的问题。有些开发板的原理图上PHY地址是通过上下拉电阻配置的如果电阻贴错或者虚焊PHY的实际地址就和设备树里写的不一样。我写了一个小脚本在uboot里扫描所有32个地址for i in $(seq 0 31); do val$(mdio read $i 2 2/dev/null) if [ $val ! 0xffff ] [ $val ! 0x0000 ]; then echo PHY found at address $i, ID$val fi done6.2 链路能起来但速率不对有时候PHY ID能读到ethtool也能看到Link detected但协商出来的速率是100M而不是1000M。这种情况通常是RGMII延时不对或者自协商配置有问题。先读BMSR和ANLPAR看双方协商出了什么能力。如果ANLPAR显示对端只支持100M那问题在对端。如果双方都支持1000M但协商结果是100M就要检查RGMII的时序。RGMII在1000M模式下数据在时钟的上下沿都采样时序窗口很窄。TX延时和RX延时的组合不对就会导致数据采样错误PHY自动降速到100M。解决方法是调整PHY的延时寄存器或者调整MAC侧的延时配置。RK3568的GMAC支持在MAC侧配置TX和RX延时通过设备树里的tx_delay和rx_delay参数。这两个值的单位是皮秒典型范围是0到2000ps。我一般从1500ps开始试用iperf3打流测试看有没有丢包。6.3 系统启动时PHY驱动probe失败如果dmesg里看到PHY probe failed或者no PHY found但用devmem能读到PHY ID那问题可能在设备树配置或者驱动匹配上。检查设备树里phy-handle指向的节点是否正确reg值是否和实际PHY地址一致。还要确认PHY驱动的phy_id和phy_id_mask是否覆盖了你这颗PHY的ID。有些国产PHY的ID不在标准驱动列表里需要手动添加。实操心得如果PHY驱动没有匹配上内核会用通用的PHY驱动genphy来驱动。通用驱动能完成基本的自协商和链路管理但厂商特有的功能比如RGMII延时、LED控制就用不了。所以看到Generic PHY的日志时要确认是不是驱动没匹配上。6.4 MDIO总线被多个PHY共享时的地址冲突一条MDIO总线上挂多个PHY时每个PHY的地址必须唯一。如果两个PHY地址相同读写就会冲突。有些交换机芯片内置多个PHY地址通过硬件引脚配置。调试时要确认每个PHY的地址配置引脚状态。另外有些PHY在复位后会短暂地响应所有地址这时候如果去扫描总线可能会看到多个地址都有响应。等PHY完全启动后再扫描一次地址就正常了。7. 从寄存器操作反推MDIO时序7.1 用逻辑分析仪抓一次完整的读操作前面讲了MDIO帧格式但纸面上的协议和实际波形之间还是有差距。我用逻辑分析仪抓了一次RK3568读PHY寄存器2的完整波形把每个字段对应的时间点标出来这样理解起来更直观。MDC频率设为1MHz一个时钟周期1微秒。32个前导码占32微秒然后是2位ST、2位OP、5位PHYAD、5位REGAD、2位TA、16位DATA总共64个时钟周期64微秒。加上帧间隔一次读操作大约70微秒。在波形上前导码阶段MDIO一直是高电平。ST阶段是01先低后高。OP阶段是10先高后低。然后是PHY地址和寄存器地址每个bit在MDC的上升沿被采样。TA阶段第一个时钟MDIO是高阻被上拉电阻拉高第二个时钟PHY把它拉低。最后16个时钟是数据从高位到低位依次输出。7.2 时序参数的计算与验证MDIO协议对时序有明确要求。MDC的占空比没有严格规定但高低电平时间都不能太短。MDIO数据在MDC上升沿之前要稳定建立时间至少10ns保持时间至少10ns。在RK3568上MDC频率由CSR分频系数决定。我实测了几组配置GMAC时钟CSR理论MDC实测MDC通信是否正常50MHz241.0MHz0.98MHz正常50MHz121.92MHz1.89MHz正常50MHz45.0MHz4.85MHz不稳定100MHz491.0MHz0.99MHz正常100MHz242.0MHz1.96MHz正常可以看到MDC频率超过2.5MHz后通信就开始不稳定了。虽然有些PHY标称支持更高的MDC频率但实际调试时还是保守一点好1MHz到2MHz是最稳妥的范围。7.3 从时序反推硬件问题如果逻辑分析仪抓到的波形和预期不符可以反推硬件问题。比如前导码阶段MDIO不是全高上拉电阻缺失或阻值太大ST阶段不是01MAC控制器的MDIO模块配置错误TA阶段PHY没有拉低PHY没上电或地址不匹配数据阶段波形畸变MDIO走线太长或受到干扰我遇到过一次MDIO数据偶尔出错的问题抓波形发现数据位的上升沿有振铃。最后查出来是MDIO走线没有做阻抗匹配加了一个33欧姆的串联电阻后问题解决。8. 驱动代码里的关键细节8.1 phy_connect和phy_attach的区别Linux PHY子系统里有两个连接PHY的函数phy_connect和phy_attach。前者是给网络设备用的会自动启动PHY状态机处理链路变化后者是给非网络设备用的只提供寄存器读写接口。phy_connect的调用链大致是MAC驱动的probe函数里调用phy_connect核心层根据设备树找到PHY设备匹配驱动然后启动phy_state_machine内核线程。这个线程每隔1秒轮询一次PHY状态检测链路变化更新net_device的link状态。8.2 PHY状态机的工作流程PHY状态机的核心逻辑在phy_state_machine函数里。它维护了一个状态变量在PHY_UP、PHY_RUNNING、PHY_NOLINK、PHY_CHANGELINK等状态之间切换。启动时状态是PHY_UP然后调用phy_start_aneg启动自协商。自协商完成后进入PHY_RUNNING开始定期读BMSR检查链路状态。如果链路断了进入PHY_NOLINK继续轮询等待链路恢复。这个状态机是PHY子系统的核心它把链路管理的复杂性从MAC驱动里剥离了出来。MAC驱动只需要在链路状态变化时收到回调做相应的MAC配置调整。8.3 中断模式与轮询模式PHY支持中断模式链路状态变化时PHY拉低中断引脚MAC收到中断后去读BMSR确认状态。中断模式比轮询模式省电响应也更快。但中断模式需要PHY的中断引脚正确连接到SoC的GPIO中断控制器。RK3568的设备树里可以配置PHY中断rgmii_phy1: ethernet-phy0 { compatible ethernet-phy-ieee802.3-c22; reg 0x0; interrupt-parent gpio3; interrupts RK_PB6 IRQ_TYPE_LEVEL_LOW; };如果中断配置不对PHY状态机会退回到轮询模式功能不受影响但响应会慢一些。调试时如果发现链路状态更新不及时可以检查中断配置。9. 几个容易踩的坑第一个坑是复位延时不够。前面已经说过PHY复位后需要等待一段时间才能访问寄存器。不同PHY的要求不一样RTL8211要求10msYT8511要求更短但保险起见给50ms到100ms。这个延时在设备树里配改起来很方便但不知道的人会在这里卡很久。第二个坑是MDIO时钟分频算错。分频系数的计算公式里有个2倍的关系容易漏掉。而且不同SoC的公式可能略有差异要以具体芯片的手册为准。RK3568的GMAC手册里写得很清楚但如果不看手册直接抄别人的配置可能会因为时钟源不同而出错。第三个坑是PHY地址和硬件不一致。原理图上的地址配置电阻和实际贴片不一致或者设备树里写错了地址。用扫描脚本把所有地址扫一遍是最快的确认方法。第四个坑是RGMII延时配置。这个没有万能值必须根据PCB走线和PHY型号来调。我的经验是先用PHY的默认延时配置如果链路不稳定再调MAC侧的延时。调的时候用iperf3打流观察有没有丢包和重传。第五个坑是设备树里PHY节点没使能。有些开发板的设备树里MDIO节点默认是disabled需要手动改成okay。这个看起来很低级但确实经常发生尤其是拿到别人的设备树直接改的时候。10. 调试工具链的搭建10.1 内核配置选项调试PHY驱动需要打开一些内核配置CONFIG_PHYLIBy CONFIG_STMMAC_ETHy CONFIG_MDIO_BITBANGy CONFIG_MDIO_GPIOy CONFIG_PHY_ROCKCHIPy CONFIG_DEBUG_FSy CONFIG_DYNAMIC_DEBUGyCONFIG_DEBUG_FS打开后可以在/sys/kernel/debug/下看到PHY相关的调试信息。CONFIG_DYNAMIC_DEBUG可以动态打开驱动里的调试打印不用重新编译内核。10.2 常用调试命令# 查看PHY状态 cat /sys/class/mdio_bus/*/phy*/phy_id cat /sys/class/mdio_bus/*/phy*/phy_interface # 用ethtool查看链路状态 ethtool eth0 ethtool -S eth0 # 用mii-tool查看和设置PHY mii-tool -v eth0 # 用phytool读写PHY寄存器 phytool read eth0/0/2 phytool write eth0/0/0 0x9140phytool是我最常用的工具它可以直接读写PHY寄存器不需要devmem那样手动拼地址。phytool read eth0/0/2就是读eth0对应的PHY地址0的寄存器2。10.3 动态调试打印打开stmmac驱动的调试打印echo file drivers/net/ethernet/stmicro/stmmac/* p /sys/kernel/debug/dynamic_debug/control然后dmesg就能看到MDIO读写的详细日志包括每次读写的PHY地址、寄存器地址和返回值。这个在排查驱动逻辑问题时非常有用。11. 从RK3568延伸到其他平台RK3568的GMAC控制器是Synopsys DesignWare IP很多SoC都用这个IP比如RK3399、RK3588、STM32MP1等。MDIO控制器的寄存器布局基本一致差异主要在时钟分频的计算和寄存器基地址上。掌握了RK3568上的调试方法换到其他平台时只需要改几个地方确认GMAC的寄存器基地址、确认时钟源频率、确认设备树里的PHY配置。MDIO协议本身和PHY寄存器的标准定义是不变的。我在RK3588上调试2.5G PHY时就复用了RK3568上的大部分经验。2.5G PHY用的是Clause 45帧格式不同但底层的MDIO时序和寄存器操作逻辑是一样的。理解了Clause 22再看Clause 45就很容易上手。最后分享一个我在实际调试中总结的小技巧把常用的PHY寄存器读写操作封装成脚本放在开发板的/usr/bin/下。比如phyid脚本一键读取所有PHY的IDphylink脚本一键查看链路状态和协商结果。这样每次调试不用重复敲命令效率能提高不少。
企业数字化 ERP 产品动态
相关推荐
如何监听缩放与加载事件:largeImage4cj五大监听器实战教程 如何监听缩放与加载事件:largeImage4cj五大监听器实战教程 【免费下载链接】large-image-cj 图像加载库,支持加载、缩放和拖动 项目地址: https://gitcode.com/Cangjie-TPC/large-image-cj
largeImage4cj 是一个面向仓颉(Cangjie&… · 2026/9/25 1:38:38
零成本家庭监控:树莓派抓取RTSP流自动备份至免费网盘 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:38:26
华为U2000网管安装与开局配置:环境准备、License导入及网元接入 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:38:26
【SOCP二阶锥规划】配电网分布式光伏接入承载力评估Matlab实现 ✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、算法改进、程序设计科研仿真。🍎 往期回顾关注个人主页:完整代码获取 定制创新 论文复现私信🍊个人信条:做科研,… · 2026/9/25 2:16:58
IDURAR 开源 ERP/CRM 软件架构全解析:基于 MERN 技术栈的发票、客户与财务管理实战指南 后端前端企业应用CRM 【免费下载链接】idurar-erp-crm Free Open Source ERP CRM Software Accounting Invoicing | Node.Js React 项目地址: https://gitcode.com/gh_mirrors/id/idurar-erp-crm 点击查看 免费下载 导读
IDURAR 是一个基于 MERN 技术栈࿰… · 2026/9/25 2:16:58
Mage AI 集成指南:配置 BigQuery Source 实现数据集读取与全量同步 数据工程数据编排ETL任务调度批处理流处理数据集成后端 【免费下载链接】mage-ai 🧙 Build, run, and manage data pipelines for integrating and transforming data. 项目地址: https://gitcode.com/gh_mirrors/ma/mage-ai 点击查看 免费下载 本指南以… · 2026/9/25 2:16:58
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37