ZYNQ下KSZ9031 MMD读取失败排查与解决
做ZYNQ网络核的时候我最常被问的一句话就是“ksz9031 mmd读取不了到底是PHY坏了还是我写错了” 这句话我听得耳朵起茧。KSZ9031RNX这颗PHY在ZYNQ板卡上实在太常见了配合LwIP做千兆以太网几乎人手一块。而MMD寄存器又偏偏是调RGMII延时、查Link状态、看协商结果的必经之路读不到那一步后面所有调试都像瞎子摸象。这篇内容不是什么教科书是我实际调板子过程中踩出来的结论。如果你也在ZYNQ裸机或Linux下配LwIP面对KSZ9031RNX时发现MMD读出来全是0xFFFF或者某些工具里直接报错那么这篇文章大概率能帮你少走半天弯路。我会把MMD的间接访问原理、读不到的常见原因以及一套可以直接抄的读取代码都写清楚。1. 先把这套硬件链路认全ZYNQ、LwIP和KSZ9031RNX是什么关系1.1 为什么ZYNQ平台特别爱用KSZ9031RNXZYNQ的PS端自带GEMGigabit Ethernet MAC控制器配合外部PHY芯片就能出网口。KSZ9031RNX是Microchip原Micrel的10/100/1000M三速PHY支持RGMII接口芯片内部有灵活的时序调校寄存器非常适合接在ZYNQ这样的FPGAARM平台后面。典型的接法是ZYNQ PS的GEM0或GEM1通过MIO或EMIO引出RGMII信号包括TXD、RXD、TX_CTL、RX_CTL、GTX_CLK、RXC等另外还有一对MDC/MDIO管理总线专门用来读PHY的状态寄存器和配置PHY的工作模式。LwIP是跑在ARM核上的协议栈它本身不直接操作PHY而是通过Xilinx提供的XEmacPs驱动访问GEMGEM再通过MDIO总线去访问KSZ9031的寄存器。所以“ksz9031 mmd读取不了”这个问题通常不是LwIP本身的锅而是GEM的MDIO通路或者PHY的间接访问序列出了问题。1.2 MMD寄存器是什么为什么直接读不到KSZ9031RNX遵循IEEE 802.3 Clause 22管理接口普通的PHY寄存器一共32个地址范围0x00到0x1F。像控制寄存器、状态寄存器、PHY ID、自协商能力寄存器这些都在这32个寄存器里。但现代千兆PHY还有很多高级功能比如RGMII时钟延时、链路质量监控、中断管理、1000BASE-T状态等这些寄存器数量远超32个。于是芯片引入了MMDMDIO Manageable Device的概念也就是Clause 45定义的地址空间。MMD下每个Device地址对应一个大的寄存器数组总共有65536个寄存器位想靠Clause 22那一条总线直接访问是不可能的。KSZ9031的做法是在Clause 22的寄存器空间里开两个窗口寄存器一个写设备地址一个写目标寄存器地址然后再通过数据窗口读或写真正的MMD寄存器。这个机制本身不难难就难在不少人把窗口地址记错了。我见过最多的坑是拿着别家PHY的习惯写0x1D/0x1E去访问。在Linux内核的micrel驱动里KSZ9031走的是0x0D/0x0E这套间接窗口而不是0x1D/0x1E。如果你用习惯性写法去读结果当然是读不到或者读出来全是对不上的值。2. KSZ9031 MMD间接访问的正确姿势2.1 理解四步读法与NOINCR位在写代码之前先把这个间接访问的时序逻辑讲透。内核里phy_read_mmd_indirect这个函数就是标准的课本答案我建议你直接抄它的逻辑。读MMD寄存器要分四步往Clause 22寄存器0x0D写入MMD设备地址比如0x02表示PMA/PMD。往Clause 22寄存器0x0E写入目标MMD寄存器的偏移地址。再次往寄存器0x0D写入设备地址并置位0x4000位这个位的含义是“关闭自动递增”。如果不置这一位某些PHY在读了第一个寄存器之后会自动把地址加1后续再读可能就不在你的目标地址上了。从寄存器0x0E读回16位数据。写MMD寄存器也是类似的套路前三步一样最后一步是往0x0E写入要写的16位数据。这里我多加一句0x4000这个位虽然名字叫NOINCR但它在整个访问流程里非常关键尤其对KSZ9031这种“规矩比较多”的PHY。以前我在调一个i.MX平台的KSZ9031时因为软件工程师觉得多写一次寄存器完全多余就把第三步省略了结果读出来的寄存器值时对时错。后来把这一步补上问题立刻消失。所以在ZYNQ下面写代码也别自作聪明省这一拍。2.2 在ZYNQ裸机上用XEmacPs读写MMDZYNQ裸机环境下最直接的方式就是用Xilinx SDK自带的XEmacPs_MdioWrite和XEmacPs_MdioRead接口。下面这段代码我在Zynq-7000上实测可用PHY地址按你板子的实际strap配置填常见的是0x07也有一批板子用0x00或0x04一定要看原理图。#include xemacps.h #include xemacps_hw.h #include xil_printf.h static XEmacPs g_EmacPs; int mdio_mmd_read(u32 phy_addr, u32 dev_addr, u32 reg_addr, u16 *value) { u16 tmp; if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0D, (u16)dev_addr) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0E, (u16)reg_addr) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0D, (u16)(dev_addr | 0x4000)) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioRead(g_EmacPs, phy_addr, 0x0E, tmp) ! XST_SUCCESS) { return -1; } *value tmp; return 0; } int mdio_mmd_write(u32 phy_addr, u32 dev_addr, u32 reg_addr, u16 data) { if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0D, (u16)dev_addr) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0E, (u16)reg_addr) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0D, (u16)(dev_addr | 0x4000)) ! XST_SUCCESS) { return -1; } if (XEmacPs_MdioWrite(g_EmacPs, phy_addr, 0x0E, data) ! XST_SUCCESS) { return -1; } return 0; }调用前先确认g_EmacPs已经通过XEmacPs_CfgInitialize初始化而且GEM时钟、MDIO引脚复用都已经配好。用的时候先读PHY ID验证总线u16 id1 0, id2 0; XEmacPs_MdioRead(g_EmacPs, 0x07, 0x02, id1); XEmacPs_MdioRead(g_EmacPs, 0x07, 0x03, id2); xil_printf(PHY ID1 0x%04X, ID2 0x%04X\r\n, id1, id2);KSZ9031RNX的正常值是0x0022和0x1622。如果你看到这两个值起码说明MDIO管理总线通了。接下来再读MMD比如读PMA/PMD设备下的控制寄存器u16 mmd_val 0; if (mdio_mmd_read(0x07, 0x02, 0x0004, mmd_val) 0) { xil_printf(MMD 02:0004 0x%04X\r\n, mmd_val); } else { xil_printf(MMD read failed\r\n); }如果能打出非0xFFFF的值就说明这套间接访问流程完全没问题剩下的就是寄存器偏移地址找得对不对。2.3 在Linux下怎么间接验证如果你在ZYNQ上跑的是Linux调试MMD会更方便。内核自带的phy_read_mmd之类的接口已经帮你封装好了驱动里直接调就行。用命令行工具的话有的板子没有装mdio-tools就得先确认工具的命令格式。我对mdio-tools的版本印象是不同版本参数顺序略有差异但基本都会有一个mdio子命令用来读PHY寄存器有mmd子命令用来读MMD寄存器。如果你手头的工具版本比较特殊建议先用--help查一下不要凭记忆敲。不管用哪种方式Linux下如果你要对寄存器做读写最好在PHY驱动完成初始化但网口没有频繁访问PHY的时候操作或者直接写一个小的内核模块调用phy_read_mmd。千万避免在用户态用GPIO模拟MDIO同时内核驱动又在轮询PHY状态两条管理通路同时访问同一颗PHY很容易让别人以为MMD读不到。3. 为什么你一步一步照着写还是读不到3.1 先做基础寄存器排查别一上来就怪MMD我调试的时候有个习惯叫“从简单问题往复杂问题推”。遇到MMD读不到先不碰MMD而是先读PHY的基本寄存器。如果0x02/0x03这两个PHY ID寄存器都读不到那问题大概率不是MMD访问序列而是MDIO总线根本没通。可能的原因包括PHY地址不对MDIO总线上根本没有设备在回应这个地址。MDC/MDIO引脚复用没配好或者被EMIO接到了FPGA侧但FPGA里没做约束。PHY的复位引脚一直被拉低芯片还没启动。上拉电阻缺失MDIO数据线在空闲时无法维持高电平。MDC时钟频率太高PHY跟不上读回的数据不稳定。解决顺序也很固定先看原理图确认PHY地址再量复位引脚和电源接着用示波器抓MDC波形最后再用软件一次一次减小MDC分频因子。我遇到过一块板子MDC是从PL侧用户逻辑引出来的频率跑到了几十兆PHY基本处于“你说什么我听不清”的状态基础寄存器偶尔能读对偶尔全是0xFFFF非常有迷惑性。3.2 基础寄存器能读MMD却读不到检查窗口地址如果基础寄存器能稳定读到0x0022/0x1622那就说明MDC、MDIO、PHY全部在线。此时MMD读不到最大的嫌疑就是间接访问窗口地址写错。很多人在这一步掏出厂商提供的KSZ9031数据手册看到里面写着0x1D和0x1E就直接用这两个地址去操作。但你需要非常仔细地区分这是Clause 22的物理寄存器地址还是某种“用户接口”说明。Linux内核实际使用的间接访问窗口是0x0D和0x0E这是大量发行版和BSP跑过的路线优先相信内核的写法。如果你自己写工具把这个地址改对问题通常立刻解决。另外还有一种情况是寄存器偏移根本没传对。MMD寄存器地址是16位的比如0x0004、0x0008这和Clause 22里的寄存器地址不一样。不少人偷懒把偏移写成了0x04代码里的类型又是u8一进函数就被截断了当然读不到。写MMD工具时所有传给间接窗口的地址都得用16位无符号类型来存和传。3.3 读出来是0xFFFF到底算什么MDIO读设备地址不存在的PHY时数据线如果没有任何设备应答读回来的数据线会在空闲位上被上拉电阻拉高最终得到0xFFFF。所以看到0xFFFF先不要急着怀疑是所有寄存器内容都是这个值很可能就是“总线没应答”。但反过来如果MMD目标寄存器本身不允许读或者PHY还在复位中也可能返回异常值。我试过把PHY复位引脚接在GPIO上软件复位释放得太快上电后马上读MMD前几十毫秒读到的全是0xFFFF。等把复位释放时间从10ms延长到100ms之后一切正常。碰到0xFFFF建议在代码里复位后加延时再读不要指望PHY上电瞬间就能给你稳定数据。4. 常见问题与排查技巧实录4.1 我整理的一张排查速查表为了方便现场调试我把碰到过的问题整理成了一张表。每一种现象我都尽量写了最直接的判断和处理方式不一定覆盖所有板子但遇到类似情况可以先按这个方向试。现象可能原因处理方式读0x02/0x03都返回0xFFFFPHY地址错、复位未释放、MDIO上拉缺失查原理图PHYAD strap值量复位脚检查MDIO上拉电阻抓MDC波形基础寄存器能读MMD读全是0xFFFF间接访问窗口地址写错或用0x1D/0x1E替代了0x0D/0x0E改回0x0D/0x0E按内核micrel驱动流程操作MMD读值偶发错位时好时坏漏写NOINCR位或地址被自动递增第二步之后补写0x0D dev_addrMDC时钟太高读到乱码GEM的MDC分频配置过高减小MDC时钟最好降到2.5MHz附近再试复位后立刻读MMD失败PHY启动未完成复位拉高后延时50ms以上再操作读回的值全为0目标寄存器本身为0或读写地址错误数据手册核对MMD设备号和寄存器偏移读一个已知非零寄存器验证这张表看起来简单但每一条背后都对应真实踩坑经历。尤其是第一条很多新手拿到板子就写代码结果PHY地址根本没对上后面所有操作都白做。4.2 为什么LwIP跑着跑着就“抢”了MDIO在ZYNQ裸机上用LwIP时XEmacPs驱动会周期性轮询PHY的Link状态。这个轮询本身也会通过MDIO总线去读PHY寄存器。如果此时你的调试代码也在读MMD两边如果没有任何同步机制就可能出现同一时刻两条逻辑都在操作MDIO。有人可能会问裸机不是单线程吗但你的定时器中断里完全可能跑着LwIP内部函数主循环里又跑着自己的MDO工具两者一交错时序就被打乱。我就在一个工程里遇到过主程序循环里每100ms读一次MMDLwIP每500ms查一次Link看起来互不干扰实际在某个特定相位点上MMD读出来的值偶尔会缺一两位。解决办法有两种要么在读取MMD时暂时关掉PHY轮询中断要么用同一个互斥锁保护所有MDIO访问。如果你只是自己在调试台上敲命令那影响不大但如果要把这段代码固化到产品里必须做好临界区保护。4.3 板子默认能出网不代表MMD就没必要调有一种很迷惑的情况LwIP明明能Ping通网口也能跑千兆但MMD读起来还是不对。这时你会觉得反正能跑MMD读不到也没关系。但一旦你发现吞吐率上不去或者长时间传输出现CRC错误回头还是要来调MMD。KSZ9031有个很常见的需求是调整RGMII的TX/RX时钟延时。一些板子通过外围电阻已经做了默认配置软件不用改但也有板子为了做兼容把配置留给软件。这个时候如果你读不了MMD就没法判断当前的时钟相位到底偏了多少千兆稳定性就只能靠运气。调试这类问题时不要只盯着Link up没up要看寄存器里自协商结果和各项状态标志。MMD读通了你才有能力回答“为什么偶尔掉线”“为什么千兆过不了温循”这类问题。5. 个人经验我后来是怎么定位这个问题的5.1 一次从现象到根因的完整复盘我最早碰到“ksz9031 mmd读取不了”是在一块自研ZYNQ板卡上用的是PS端GEM0PHY地址拨码开关设成0x07。板子回来后LwIP初始化正常但我想读MMD的寄存器做量产测试发现读0x02:0x0004时返回0xFFFF。第一反应是板子焊接问题量了MDC和MDIO波形都在PHY电源也正常。然后我用示波器观察MDIO数据线上的应答位发现每次操作0x0D时PHY都有应答但操作0x0E时偶尔没有。这就很奇怪了。后来我翻内核驱动代码发现我没有写第三步的dev_addr | 0x4000。我原来的代码用了一个简化版只写两次间接窗口寄存器然后就急着去读数据。补上NOINCR这一拍之后MMD读取稳定通过问题消失。这个案例让我后来在所有平台调PHY时都先以内核里的通用实现为对照不自己发明花活。5.2 给新人的一个小建议如果你现在还没定位到问题我建议先输出一遍“最小验证程序”只初始化GEM读PHY ID读一个MMD已知寄存器打印出来。不要在这段程序里启动LwIP不要开任何PHY轮询定时器也不要连接剩余功能。把问题范围缩到最小才最好查。如果你用的PHY地址不是0x07可以试试手册里的通用PHY地址0x00。不过最靠谱的还是看原理图上PHYAD引脚接高接低这部分代码不需要写得多复杂但基础判断一定要扎实。最后再分享一个小技巧读MMD的时候可以同时读两遍同一个寄存器如果两次结果不一致说明时序或总线有问题别急着信其中某一次的数据。这个习惯帮我挡掉了很多回数据抖动造成的假问题。KSZ9031的MMD调试其实也就这么点事理顺了之后后续再改寄存器就顺手多了。

相关新闻

1条命令搞定安装:yomiyasu 的4种部署方式与最快上手教程

1条命令搞定安装:yomiyasu 的4种部署方式与最快上手教程

1条命令搞定安装:yomiyasu 的4种部署方式与最快上手教程 【免费下载链接】yomiyasu AI生成の日本語を自然な日本語へ推敲するAgent Skill / Agent Skill for Refining AI-Generated Japanese into Natural Japanese 项目地址: https://gitcode.com/gh_mirrors/yo/…

2026/10/4 5:32:53 阅读更多 →
STM32F103RC寄存器级GPIO实战:从PC7点亮讲透时钟、AFIO与硬件本质

STM32F103RC寄存器级GPIO实战:从PC7点亮讲透时钟、AFIO与硬件本质

1. 这不是“点灯教程”,而是真正打开STM32F103RC大门的第一把钥匙你搜“STM32F103RC学习(一)”,十有八九会跳出来一堆“点亮LED”“HAL库入门”“CubeMX生成代码”的速成帖。但我要先说清楚:如果你刚拆开开发板、手边只…

2026/10/4 5:32:53 阅读更多 →
ANSYS Maxwell 2D永磁材料建模六要素详解

ANSYS Maxwell 2D永磁材料建模六要素详解

1. 这不是“点几下就完事”的材料添加——永磁材料在Maxwell 2D里到底要管什么?你打开ANSYS Maxwell 2D,新建一个电机转子模型,想加一块钕铁硼磁钢——结果卡在“Materials”窗口里,点了Add Material,弹出一堆参数&…

2026/10/4 5:32:53 阅读更多 →

最新新闻

寄存器堆设计实验全解析:从Verilog代码到FPGA调试

寄存器堆设计实验全解析:从Verilog代码到FPGA调试

如果你正在和杭电的计算机组成原理课程设计实验七搏斗,应该已经意识到“寄存器堆”绝不是简单的一堆寄存器叠在一起。这个模块放在CPU里,要求在一个时钟周期内同时读出两个操作数、写入一个结果,端口之间的读写时序、复位逻辑、零号寄存器的特…

2026/10/4 6:08:14 阅读更多 →
通达信成功率98%的底部指标公式无未来

通达信成功率98%的底部指标公式无未来

C3:REF(C,1); 涨停:IF((C-C3)*100/C3>(10-0.01*100/C3),1,0); STICKLINE(涨停,OPEN,CLOSE,2.5,0),COLORRED; 跌停:IF((C3-C)*100/C3>(10-0.01*100/C3),1,0); STICKLINE(跌停,OPEN,CLOSE,2.5,0),COLOR66FF33;

2026/10/4 6:08:14 阅读更多 →
Azure KARS多运行时架构:编码智能体生产级落地的Kubernetes实践

Azure KARS多运行时架构:编码智能体生产级落地的Kubernetes实践

编码智能体这两年是个热词,但大多数讨论都停留在"让AI帮我写个函数""自动补全一段逻辑"这种层面。真正把智能体放到生产环境里跑,问题就完全不一样了——它要能读写文件、执行命令、调用外部服务、在多个隔离环境之间来回切换&#…

2026/10/4 6:08:14 阅读更多 →
小米路由器神器:Misstar tools与MIXBOX插件工具箱详解

小米路由器神器:Misstar tools与MIXBOX插件工具箱详解

身边有朋友总跟我抱怨,说家里电视盒子看视频时老是弹广告、手机上刷网页也偶尔会被各种推广跳转烦到,问我路由器层面有没有办法一次性解决。我通常反问一句:“你路由器是不是小米的?”如果是,我就直接推荐他们关注两个…

2026/10/4 6:08:14 阅读更多 →
26年降重降AI二合一横评打分:8款工具分数差在哪

26年降重降AI二合一横评打分:8款工具分数差在哪

毕业论文提交前夜,查重报告飘红一片,AI检测又亮起警示——这是每年论文季反复上演的场景。降重与降AI原本是两道工序,如今市面多数工具宣称可以一并解决,实际效果却各有参差。本文以打分制形式,对8款主流降重降AI二合一…

2026/10/4 6:08:14 阅读更多 →
摄像头模组AF与FF方案解析:从对焦原理到调试测试全指南

摄像头模组AF与FF方案解析:从对焦原理到调试测试全指南

搞摄像头模组调试这些年,我遇到过太多人拿到项目就开始调驱动、跑测试,结果产品出来要么近处清晰远处糊,要么对焦马达从头到尾在“拉风箱”。这些问题绕来绕去,其实都指向同一个源头:Camera AF(自动对焦&am…

2026/10/4 6:07:14 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →